scons-doc-2.3.0/bin/0000755000175000017500000000000012114661557014772 5ustar dktrkranzdktrkranzscons-doc-2.3.0/bin/memoicmp.py0000644000175000017500000000463112114661557017156 0ustar dktrkranzdktrkranz#!/usr/bin/env python # # A script to compare the --debug=memoizer output found in # two different files. import sys def memoize_output(fname): mout = {} #lines=filter(lambda words: # len(words) == 5 and # words[1] == 'hits' and words[3] == 'misses', # map(string.split, open(fname,'r').readlines())) #for line in lines: # mout[line[-1]] = ( int(line[0]), int(line[2]) ) for line in open(fname,'r').readlines(): words = line.split() if len(words) == 5 and words[1] == 'hits' and words[3] == 'misses': mout[words[-1]] = ( int(words[0]), int(words[2]) ) return mout def memoize_cmp(filea, fileb): ma = memoize_output(filea) mb = memoize_output(fileb) print 'All output: %s / %s [delta]'%(filea, fileb) print '----------HITS---------- ---------MISSES---------' cfmt='%7d/%-7d [%d]' ma_o = [] mb_o = [] mab = [] for k in ma.keys(): if k in mb.keys(): if k not in mab: mab.append(k) else: ma_o.append(k) for k in mb.keys(): if k in ma.keys(): if k not in mab: mab.append(k) else: mb_o.append(k) mab.sort() ma_o.sort() mb_o.sort() for k in mab: hits = cfmt%(ma[k][0], mb[k][0], mb[k][0]-ma[k][0]) miss = cfmt%(ma[k][1], mb[k][1], mb[k][1]-ma[k][1]) print '%-24s %-24s %s'%(hits, miss, k) for k in ma_o: hits = '%7d/ --'%(ma[k][0]) miss = '%7d/ --'%(ma[k][1]) print '%-24s %-24s %s'%(hits, miss, k) for k in mb_o: hits = ' -- /%-7d'%(mb[k][0]) miss = ' -- /%-7d'%(mb[k][1]) print '%-24s %-24s %s'%(hits, miss, k) print '-'*(24+24+1+20) if __name__ == "__main__": if len(sys.argv) != 3: print """Usage: %s file1 file2 Compares --debug=memomize output from file1 against file2."""%sys.argv[0] sys.exit(1) memoize_cmp(sys.argv[1], sys.argv[2]) sys.exit(0) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/bin/caller-tree.py0000644000175000017500000000615012114661557017545 0ustar dktrkranzdktrkranz#!/usr/bin/env python # # Quick script to process the *summary* output from SCons.Debug.caller() # and print indented calling trees with call counts. # # The way to use this is to add something like the following to a function # for which you want information about who calls it and how many times: # # from SCons.Debug import caller # caller(0, 1, 2, 3, 4, 5) # # Each integer represents how many stack frames back SCons will go # and capture the calling information, so in the above example it will # capture the calls six levels up the stack in a central dictionary. # # At the end of any run where SCons.Debug.caller() is used, SCons will # print a summary of the calls and counts that looks like the following: # # Callers of Node/__init__.py:629(calc_signature): # 1 Node/__init__.py:683(calc_signature) # Callers of Node/__init__.py:676(gen_binfo): # 6 Node/FS.py:2035(current) # 1 Node/__init__.py:722(get_bsig) # # If you cut-and-paste that summary output and feed it to this script # on standard input, it will figure out how these entries hook up and # print a calling tree for each one looking something like: # # Node/__init__.py:676(gen_binfo) # Node/FS.py:2035(current) 6 # Taskmaster.py:253(make_ready_current) 18 # Script/Main.py:201(make_ready) 18 # # Note that you should *not* look at the call-count numbers in the right # hand column as the actual number of times each line *was called by* # the function on the next line. Rather, it's the *total* number # of times each function was found in the call chain for any of the # calls to SCons.Debug.caller(). If you're looking at more than one # function at the same time, for example, their counts will intermix. # So use this to get a *general* idea of who's calling what, not for # fine-grained performance tuning. import sys class Entry(object): def __init__(self, file_line_func): self.file_line_func = file_line_func self.called_by = [] self.calls = [] AllCalls = {} def get_call(flf): try: e = AllCalls[flf] except KeyError: e = AllCalls[flf] = Entry(flf) return e prefix = 'Callers of ' c = None for line in sys.stdin.readlines(): if line[0] == '#': pass elif line[:len(prefix)] == prefix: c = get_call(line[len(prefix):-2]) else: num_calls, flf = line.strip().split() e = get_call(flf) c.called_by.append((e, num_calls)) e.calls.append(c) stack = [] def print_entry(e, level, calls): print '%-72s%6s' % ((' '*2*level) + e.file_line_func, calls) if e in stack: print (' '*2*(level+1))+'RECURSION' print elif e.called_by: stack.append(e) for c in e.called_by: print_entry(c[0], level+1, c[1]) stack.pop() else: print for e in [ e for e in AllCalls.values() if not e.calls ]: print_entry(e, 0, '') # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/bin/scp-sourceforge0000755000175000017500000000172012114661557020026 0ustar dktrkranzdktrkranz#!/bin/bash set -x set -e if [ -z "$1" ]; then echo usage: $0 SourceForgeUserName exit fi SF_USER=$1 rm -rf sf for p in scons scons-src scons-local do mkdir -p sf/$p/$VERSION cp -p src/Announce.txt \ build/scons/CHANGES.txt \ build/scons/RELEASE.txt \ sf/$p/$VERSION done cp -p build/dist/scons-$VERSION-1.noarch.rpm \ build/dist/scons-$VERSION-1.src.rpm \ build/dist/scons-$VERSION.tar.gz \ build/dist/scons-$VERSION.win32.exe \ build/dist/scons-$VERSION.zip \ sf/scons/$VERSION cp -p build/dist/scons-local-$VERSION.tar.gz \ build/dist/scons-local-$VERSION.zip \ sf/scons-src/$VERSION cp -p build/dist/scons-src-$VERSION.tar.gz \ build/dist/scons-src-$VERSION.zip \ sf/scons-local/$VERSION # Transmit them in this order, since the most-recent is displayed at the top scp -r sf/scons-local/ sf/scons-src/ sf/scons/ \ $SF_USER,scons@frs.sourceforge.net:/home/pfs/project/s/sc/scons rm -rf sf scons-doc-2.3.0/bin/scons-doc.py0000644000175000017500000010006512114661557017236 0ustar dktrkranzdktrkranz#!/usr/bin/env python # # Copyright (c) 2010 The SCons Foundation # # 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. # # scons-doc.py - an SGML preprocessor for capturing SCons output # and inserting it into examples in our DocBook # documentation # # Synopsis: # # scons-doc [OPTIONS] [.in files] # # When no input files are given, the folder doc/user/* is searched for .in files. # # Available options: # # -d, --diff create examples for the .in file and output a unified # diff against the related .xml file # -r, --run create examples for the .in file, but do not change # any files # -s, --simple_diff use a simpler output for the diff mode (no unified # diff!) # -u, --update create examples for the .in file and update the # related .xml file # # This script looks for some SGML tags that describe SCons example # configurations and commands to execute in those configurations, and # uses TestCmd.py to execute the commands and insert the output from # those commands into the SGML that we output. This way, we can run a # script and update all of our example documentation output without # a lot of laborious by-hand checking. # # An "SCons example" looks like this, and essentially describes a set of # input files (program source files as well as SConscript files): # # # # env = Environment() # env.Program('foo') # # # int main() { printf("foo.c\n"); } # # # # The contents within the tag will get written # into a temporary directory whenever example output needs to be # generated. By default, the contents are not inserted into text # directly, unless you set the "printme" attribute on one or more files, # in which case they will get inserted within a tag. # This makes it easy to define the example at the appropriate # point in the text where you intend to show the SConstruct file. # # Note that you should usually give the a "name" # attribute so that you can refer to the example configuration later to # run SCons and generate output. # # If you just want to show a file's contents without worry about running # SCons, there's a shorter tag: # # # env = Environment() # env.Program('foo') # # # This is essentially equivalent to , # but it's more straightforward. # # SCons output is generated from the following sort of tag: # # # scons -Q foo # scons -Q foo # # # You tell it which example to use with the "example" attribute, and then # give it a list of tags to execute. You can also # supply an "os" tag, which specifies the type of operating system this # example is intended to show; if you omit this, default value is "posix". # # The generated SGML will show the command line (with the appropriate # command-line prompt for the operating system), execute the command in # a temporary directory with the example files, capture the standard # output from SCons, and insert it into the text as appropriate. # Error output gets passed through to your error output so you # can see if there are any problems executing the command. # import optparse import os import re import sgmllib import sys import time import glob sys.path.append(os.path.join(os.getcwd(), 'QMTest')) sys.path.append(os.path.join(os.getcwd(), 'build', 'QMTest')) scons_py = os.path.join('bootstrap', 'src', 'script', 'scons.py') if not os.path.exists(scons_py): scons_py = os.path.join('src', 'script', 'scons.py') scons_lib_dir = os.path.join(os.getcwd(), 'bootstrap', 'src', 'engine') if not os.path.exists(scons_lib_dir): scons_lib_dir = os.path.join(os.getcwd(), 'src', 'engine') os.environ['SCONS_LIB_DIR'] = scons_lib_dir import TestCmd # The regular expression that identifies entity references in the # standard sgmllib omits the underscore from the legal characters. # Override it with our own regular expression that adds underscore. sgmllib.entityref = re.compile('&([a-zA-Z][-_.a-zA-Z0-9]*)[^-_a-zA-Z0-9]') # Classes for collecting different types of data we're interested in. class DataCollector(object): """Generic class for collecting data between a start tag and end tag. We subclass for various types of tags we care about.""" def __init__(self): self.data = "" def afunc(self, data): self.data = self.data + data class Example(DataCollector): """An SCons example. This is essentially a list of files that will get written to a temporary directory to collect output from one or more SCons runs.""" def __init__(self): DataCollector.__init__(self) self.files = [] self.dirs = [] class File(DataCollector): """A file, that will get written out to a temporary directory for one or more SCons runs.""" def __init__(self, name): DataCollector.__init__(self) self.name = name class Directory(DataCollector): """A directory, that will get created in a temporary directory for one or more SCons runs.""" def __init__(self, name): DataCollector.__init__(self) self.name = name class Output(DataCollector): """Where the command output goes. This is essentially a list of commands that will get executed.""" def __init__(self): DataCollector.__init__(self) self.commandlist = [] class Command(DataCollector): """A tag for where the command output goes. This is essentially a list of commands that will get executed.""" def __init__(self): DataCollector.__init__(self) self.output = None Prompt = { 'posix' : '% ', 'win32' : 'C:\\>' } # The magick SCons hackery that makes this work. # # So that our examples can still use the default SConstruct file, we # actually feed the following into SCons via stdin and then have it # SConscript() the SConstruct file. This stdin wrapper creates a set # of ToolSurrogates for the tools for the appropriate platform. These # Surrogates print output like the real tools and behave like them # without actually having to be on the right platform or have the right # tool installed. # # The upshot: The wrapper transparently changes the world out from # under the top-level SConstruct file in an example just so we can get # the command output. Stdin = """\ import os import re import SCons.Action import SCons.Defaults import SCons.Node.FS platform = '%(osname)s' Sep = { 'posix' : '/', 'win32' : '\\\\', }[platform] # Slip our own __str__() method into the EntryProxy class used to expand # $TARGET{S} and $SOURCE{S} to translate the path-name separators from # what's appropriate for the system we're running on to what's appropriate # for the example system. orig = SCons.Node.FS.EntryProxy class MyEntryProxy(orig): def __str__(self): return str(self._subject).replace(os.sep, Sep) SCons.Node.FS.EntryProxy = MyEntryProxy # Slip our own RDirs() method into the Node.FS.File class so that the # expansions of $_{CPPINC,F77INC,LIBDIR}FLAGS will have the path-name # separators translated from what's appropriate for the system we're # running on to what's appropriate for the example system. orig_RDirs = SCons.Node.FS.File.RDirs def my_RDirs(self, pathlist, orig_RDirs=orig_RDirs): return [str(x).replace(os.sep, Sep) for x in orig_RDirs(self, pathlist)] SCons.Node.FS.File.RDirs = my_RDirs class Curry(object): def __init__(self, fun, *args, **kwargs): self.fun = fun self.pending = args[:] self.kwargs = kwargs.copy() def __call__(self, *args, **kwargs): if kwargs and self.kwargs: kw = self.kwargs.copy() kw.update(kwargs) else: kw = kwargs or self.kwargs return self.fun(*self.pending + args, **kw) def Str(target, source, env, cmd=""): result = [] for cmd in env.subst_list(cmd, target=target, source=source): result.append(' '.join(map(str, cmd))) return '\\n'.join(result) class ToolSurrogate(object): def __init__(self, tool, variable, func, varlist): self.tool = tool if not isinstance(variable, list): variable = [variable] self.variable = variable self.func = func self.varlist = varlist def __call__(self, env): t = Tool(self.tool) t.generate(env) for v in self.variable: orig = env[v] try: strfunction = orig.strfunction except AttributeError: strfunction = Curry(Str, cmd=orig) # Don't call Action() through its global function name, because # that leads to infinite recursion in trying to initialize the # Default Environment. env[v] = SCons.Action.Action(self.func, strfunction=strfunction, varlist=self.varlist) def __repr__(self): # This is for the benefit of printing the 'TOOLS' # variable through env.Dump(). return repr(self.tool) def Null(target, source, env): pass def Cat(target, source, env): target = str(target[0]) f = open(target, "wb") for src in map(str, source): f.write(open(src, "rb").read()) f.close() def CCCom(target, source, env): target = str(target[0]) fp = open(target, "wb") def process(source_file, fp=fp): for line in open(source_file, "rb").readlines(): m = re.match(r'#include\s[<"]([^<"]+)[>"]', line) if m: include = m.group(1) for d in [str(env.Dir('$CPPPATH')), '.']: f = os.path.join(d, include) if os.path.exists(f): process(f) break elif line[:11] != "STRIP CCCOM": fp.write(line) for src in map(str, source): process(src) fp.write('debug = ' + ARGUMENTS.get('debug', '0') + '\\n') fp.close() public_class_re = re.compile('^public class (\S+)', re.MULTILINE) def JavaCCom(target, source, env): # This is a fake Java compiler that just looks for # public class FooBar # lines in the source file(s) and spits those out # to .class files named after the class. tlist = list(map(str, target)) not_copied = {} for t in tlist: not_copied[t] = 1 for src in map(str, source): contents = open(src, "rb").read() classes = public_class_re.findall(contents) for c in classes: for t in [x for x in tlist if x.find(c) != -1]: open(t, "wb").write(contents) del not_copied[t] for t in not_copied.keys(): open(t, "wb").write("\\n") def JavaHCom(target, source, env): tlist = map(str, target) slist = map(str, source) for t, s in zip(tlist, slist): open(t, "wb").write(open(s, "rb").read()) def JarCom(target, source, env): target = str(target[0]) class_files = [] for src in map(str, source): for dirpath, dirnames, filenames in os.walk(src): class_files.extend([ os.path.join(dirpath, f) for f in filenames if f.endswith('.class') ]) f = open(target, "wb") for cf in class_files: f.write(open(cf, "rb").read()) f.close() # XXX Adding COLOR, COLORS and PACKAGE to the 'cc' varlist(s) by hand # here is bogus. It's for the benefit of doc/user/command-line.in, which # uses examples that want to rebuild based on changes to these variables. # It would be better to figure out a way to do it based on the content of # the generated command-line, or else find a way to let the example markup # language in doc/user/command-line.in tell this script what variables to # add, but that's more difficult than I want to figure out how to do right # now, so let's just use the simple brute force approach for the moment. ToolList = { 'posix' : [('cc', ['CCCOM', 'SHCCCOM'], CCCom, ['CCFLAGS', 'CPPDEFINES', 'COLOR', 'COLORS', 'PACKAGE']), ('link', ['LINKCOM', 'SHLINKCOM'], Cat, []), ('ar', ['ARCOM', 'RANLIBCOM'], Cat, []), ('tar', 'TARCOM', Null, []), ('zip', 'ZIPCOM', Null, []), ('BitKeeper', 'BITKEEPERCOM', Cat, []), ('CVS', 'CVSCOM', Cat, []), ('RCS', 'RCS_COCOM', Cat, []), ('SCCS', 'SCCSCOM', Cat, []), ('javac', 'JAVACCOM', JavaCCom, []), ('javah', 'JAVAHCOM', JavaHCom, []), ('jar', 'JARCOM', JarCom, []), ('rmic', 'RMICCOM', Cat, []), ], 'win32' : [('msvc', ['CCCOM', 'SHCCCOM', 'RCCOM'], CCCom, ['CCFLAGS', 'CPPDEFINES', 'COLOR', 'COLORS', 'PACKAGE']), ('mslink', ['LINKCOM', 'SHLINKCOM'], Cat, []), ('mslib', 'ARCOM', Cat, []), ('tar', 'TARCOM', Null, []), ('zip', 'ZIPCOM', Null, []), ('BitKeeper', 'BITKEEPERCOM', Cat, []), ('CVS', 'CVSCOM', Cat, []), ('RCS', 'RCS_COCOM', Cat, []), ('SCCS', 'SCCSCOM', Cat, []), ('javac', 'JAVACCOM', JavaCCom, []), ('javah', 'JAVAHCOM', JavaHCom, []), ('jar', 'JARCOM', JarCom, []), ('rmic', 'RMICCOM', Cat, []), ], } toollist = ToolList[platform] filter_tools = '%(tools)s'.split() if filter_tools: toollist = [x for x in toollist if x[0] in filter_tools] toollist = [ToolSurrogate(*t) for t in toollist] toollist.append('install') def surrogate_spawn(sh, escape, cmd, args, env): pass def surrogate_pspawn(sh, escape, cmd, args, env, stdout, stderr): pass SCons.Defaults.ConstructionEnvironment.update({ 'PLATFORM' : platform, 'TOOLS' : toollist, 'SPAWN' : surrogate_spawn, 'PSPAWN' : surrogate_pspawn, }) SConscript('SConstruct') """ # "Commands" that we will execute in our examples. def command_scons(args, c, test, dict): save_vals = {} delete_keys = [] try: ce = c.environment except AttributeError: pass else: for arg in c.environment.split(): key, val = arg.split('=') try: save_vals[key] = os.environ[key] except KeyError: delete_keys.append(key) os.environ[key] = val test.run(interpreter = sys.executable, program = scons_py, # We use ToolSurrogates to capture win32 output by "building" # examples using a fake win32 tool chain. Suppress the # warnings that come from the new revamped VS support so # we can build doc on (Linux) systems that don't have # Visual C installed. arguments = '--warn=no-visual-c-missing -f - ' + ' '.join(args), chdir = test.workpath('WORK'), stdin = Stdin % dict) os.environ.update(save_vals) for key in delete_keys: del(os.environ[key]) out = test.stdout() out = out.replace(test.workpath('ROOT'), '') out = out.replace(test.workpath('WORK/SConstruct'), '/home/my/project/SConstruct') lines = out.split('\n') if lines: while lines[-1] == '': lines = lines[:-1] #err = test.stderr() #if err: # sys.stderr.write(err) return lines def command_touch(args, c, test, dict): if args[0] == '-t': t = int(time.mktime(time.strptime(args[1], '%Y%m%d%H%M'))) times = (t, t) args = args[2:] else: time.sleep(1) times = None for file in args: if not os.path.isabs(file): file = os.path.join(test.workpath('WORK'), file) if not os.path.exists(file): open(file, 'wb') os.utime(file, times) return [] def command_edit(args, c, test, dict): try: add_string = c.edit[:] except AttributeError: add_string = 'void edit(void) { ; }\n' if add_string[-1] != '\n': add_string = add_string + '\n' for file in args: if not os.path.isabs(file): file = os.path.join(test.workpath('WORK'), file) contents = open(file, 'rb').read() open(file, 'wb').write(contents + add_string) return [] def command_ls(args, c, test, dict): def ls(a): return [' '.join(sorted([x for x in os.listdir(a) if x[0] != '.']))] if args: l = [] for a in args: l.extend(ls(test.workpath('WORK', a))) return l else: return ls(test.workpath('WORK')) def command_sleep(args, c, test, dict): time.sleep(int(args[0])) CommandDict = { 'scons' : command_scons, 'touch' : command_touch, 'edit' : command_edit, 'ls' : command_ls, 'sleep' : command_sleep, } def ExecuteCommand(args, c, t, dict): try: func = CommandDict[args[0]] except KeyError: func = lambda args, c, t, dict: [] return func(args[1:], c, t, dict) class MySGML(sgmllib.SGMLParser): """A subclass of the standard Python sgmllib SGML parser. This extends the standard sgmllib parser to recognize, and do cool stuff with, the added tags that describe our SCons examples, commands, and other stuff. """ def __init__(self, outfp): sgmllib.SGMLParser.__init__(self) self.examples = {} self.afunclist = [] self.outfp = outfp # The first set of methods here essentially implement pass-through # handling of most of the stuff in an SGML file. We're really # only concerned with the tags specific to SCons example processing, # the methods for which get defined below. def handle_data(self, data): try: f = self.afunclist[-1] except IndexError: self.outfp.write(data) else: f(data) def handle_comment(self, data): self.outfp.write('') def handle_decl(self, data): self.outfp.write('') def unknown_starttag(self, tag, attrs): try: f = self.example.afunc except AttributeError: f = self.outfp.write if not attrs: f('<' + tag + '>') else: f('<' + tag) for name, value in attrs: f(' ' + name + '=' + '"' + value + '"') f('>') def unknown_endtag(self, tag): self.outfp.write('') def unknown_entityref(self, ref): self.outfp.write('&' + ref + ';') def unknown_charref(self, ref): self.outfp.write('&#' + ref + ';') # Here is where the heavy lifting begins. The following methods # handle the begin-end tags of our SCons examples. def for_display(self, contents): contents = contents.replace('__ROOT__', '') contents = contents.replace('<', '<') contents = contents.replace('>', '>') return contents def start_scons_example(self, attrs): t = [t for t in attrs if t[0] == 'name'] if t: name = t[0][1] try: e = self.examples[name] except KeyError: e = self.examples[name] = Example() else: e = Example() for name, value in attrs: setattr(e, name, value) self.e = e self.afunclist.append(e.afunc) def end_scons_example(self): e = self.e files = [f for f in e.files if f.printme] if files: self.outfp.write('') for f in files: if f.printme: i = len(f.data) - 1 while f.data[i] == ' ': i = i - 1 output = self.for_display(f.data[:i+1]) self.outfp.write(output) if e.data and e.data[0] == '\n': e.data = e.data[1:] self.outfp.write(e.data + '') delattr(self, 'e') self.afunclist = self.afunclist[:-1] def start_file(self, attrs): try: e = self.e except AttributeError: self.error(" tag outside of ") t = [t for t in attrs if t[0] == 'name'] if not t: self.error("no name attribute found") try: e.prefix except AttributeError: e.prefix = e.data e.data = "" f = File(t[0][1]) f.printme = None for name, value in attrs: setattr(f, name, value) e.files.append(f) self.afunclist.append(f.afunc) def end_file(self): self.e.data = "" self.afunclist = self.afunclist[:-1] def start_directory(self, attrs): try: e = self.e except AttributeError: self.error(" tag outside of ") t = [t for t in attrs if t[0] == 'name'] if not t: self.error("no name attribute found") try: e.prefix except AttributeError: e.prefix = e.data e.data = "" d = Directory(t[0][1]) for name, value in attrs: setattr(d, name, value) e.dirs.append(d) self.afunclist.append(d.afunc) def end_directory(self): self.e.data = "" self.afunclist = self.afunclist[:-1] def start_scons_example_file(self, attrs): t = [t for t in attrs if t[0] == 'example'] if not t: self.error("no example attribute found") exname = t[0][1] try: e = self.examples[exname] except KeyError: self.error("unknown example name '%s'" % exname) fattrs = [t for t in attrs if t[0] == 'name'] if not fattrs: self.error("no name attribute found") fname = fattrs[0][1] f = [f for f in e.files if f.name == fname] if not f: self.error("example '%s' does not have a file named '%s'" % (exname, fname)) self.f = f[0] def end_scons_example_file(self): f = self.f self.outfp.write('') self.outfp.write(f.data + '') delattr(self, 'f') def start_scons_output(self, attrs): t = [t for t in attrs if t[0] == 'example'] if not t: self.error("no example attribute found") exname = t[0][1] try: e = self.examples[exname] except KeyError: self.error("unknown example name '%s'" % exname) # Default values for an example. o = Output() o.preserve = None o.os = 'posix' o.tools = '' o.e = e # Locally-set. for name, value in attrs: setattr(o, name, value) self.o = o self.afunclist.append(o.afunc) def end_scons_output(self): # The real raison d'etre for this script, this is where we # actually execute SCons to fetch the output. o = self.o e = o.e t = TestCmd.TestCmd(workdir='', combine=1) if o.preserve: t.preserve() t.subdir('ROOT', 'WORK') t.rootpath = t.workpath('ROOT').replace('\\', '\\\\') for d in e.dirs: dir = t.workpath('WORK', d.name) if not os.path.exists(dir): os.makedirs(dir) for f in e.files: i = 0 while f.data[i] == '\n': i = i + 1 lines = f.data[i:].split('\n') i = 0 while lines[0][i] == ' ': i = i + 1 lines = [l[i:] for l in lines] path = f.name.replace('__ROOT__', t.rootpath) if not os.path.isabs(path): path = t.workpath('WORK', path) dir, name = os.path.split(path) if dir and not os.path.exists(dir): os.makedirs(dir) content = '\n'.join(lines) content = content.replace('__ROOT__', t.rootpath) path = t.workpath('WORK', path) t.write(path, content) if hasattr(f, 'chmod'): os.chmod(path, int(f.chmod, 0)) i = len(o.prefix) while o.prefix[i-1] != '\n': i = i - 1 self.outfp.write('' + o.prefix[:i]) p = o.prefix[i:] # Regular expressions for making the doc output consistent, # regardless of reported addresses or Python version. # Massage addresses in object repr strings to a constant. address_re = re.compile(r' at 0x[0-9a-fA-F]*\>') # Massage file names in stack traces (sometimes reported as absolute # paths) to a consistent relative path. engine_re = re.compile(r' File ".*/src/engine/SCons/') # Python 2.5 changed the stack trace when the module is read # from standard input from read "... line 7, in ?" to # "... line 7, in ". file_re = re.compile(r'^( *File ".*", line \d+, in) \?$', re.M) # Python 2.6 made UserList a new-style class, which changes the # AttributeError message generated by our NodeList subclass. nodelist_re = re.compile(r'(AttributeError:) NodeList instance (has no attribute \S+)') for c in o.commandlist: self.outfp.write(p + Prompt[o.os]) d = c.data.replace('__ROOT__', '') self.outfp.write('' + d + '\n') e = c.data.replace('__ROOT__', t.workpath('ROOT')) args = e.split() lines = ExecuteCommand(args, c, t, {'osname':o.os, 'tools':o.tools}) content = None if c.output: content = c.output elif lines: content = ( '\n' + p).join(lines) if content: content = address_re.sub(r' at 0x700000>', content) content = engine_re.sub(r' File "bootstrap/src/engine/SCons/', content) content = file_re.sub(r'\1 ', content) content = nodelist_re.sub(r"\1 'NodeList' object \2", content) content = self.for_display(content) self.outfp.write(p + content + '\n') if o.data[0] == '\n': o.data = o.data[1:] self.outfp.write(o.data + '') delattr(self, 'o') self.afunclist = self.afunclist[:-1] def start_scons_output_command(self, attrs): try: o = self.o except AttributeError: self.error(" tag outside of ") try: o.prefix except AttributeError: o.prefix = o.data o.data = "" c = Command() for name, value in attrs: setattr(c, name, value) o.commandlist.append(c) self.afunclist.append(c.afunc) def end_scons_output_command(self): self.o.data = "" self.afunclist = self.afunclist[:-1] def start_sconstruct(self, attrs): f = File('') self.f = f self.afunclist.append(f.afunc) def end_sconstruct(self): f = self.f self.outfp.write('') output = self.for_display(f.data) self.outfp.write(output + '') delattr(self, 'f') self.afunclist = self.afunclist[:-1] def process(filename, fout=sys.stdout): if filename == '-': f = sys.stdin else: try: f = open(filename, 'r') except EnvironmentError, e: sys.stderr.write('%s: %s\n' % (filename, e)) return 1 data = f.read() if f is not sys.stdin: f.close() if data.startswith(' 3 and version_tuple[3] != 'final': if mode == 'develop': version_tuple = version_tuple[:4] + ('yyyymmdd',) else: yyyy,mm,dd,_,_,_ = release_date version_tuple = version_tuple[:4] + ((yyyy*100 + mm)*100 + dd,) version_string = '.'.join(map(str, version_tuple)) if len(version_tuple) > 3: version_type = version_tuple[3] else: version_type = 'final' if DEBUG: print 'version string', version_string if version_type not in ['alpha', 'beta', 'candidate', 'final']: print("""ERROR: `%s' is not a valid release type in version tuple; \tit must be one of alpha, beta, candidate, or final""" % version_type) sys.exit(1) try: month_year = config['month_year'] except KeyError: if version_type == 'alpha': month_year = 'MONTH YEAR' else: month_year = time.strftime('%B %Y', release_date + (0,0,0)) if DEBUG: print 'month year', month_year try: copyright_years = config['copyright_years'] except KeyError: copyright_years = ', '.join(map(str, list(range(2001, release_date[0] + 1)))) if DEBUG: print 'copyright years', copyright_years class UpdateFile(object): """ XXX """ def __init__(self, file, orig = None): ''' ''' if orig is None: orig = file try: self.content = open(orig, 'rU').read() except IOError: # Couldn't open file; don't try to write anything in __del__ self.file = None raise else: self.file = file if file == orig: # so we can see if it changed self.orig = self.content else: # pretend file changed self.orig = '' def sub(self, pattern, replacement, count = 1): ''' XXX ''' self.content = re.sub(pattern, replacement, self.content, count) def replace_assign(self, name, replacement, count = 1): ''' XXX ''' self.sub('\n' + name + ' = .*', '\n' + name + ' = ' + replacement) # Determine the pattern to match a version _rel_types = '(alpha|beta|candidate|final)' match_pat = '\d+\.\d+\.\d+\.' + _rel_types + '\.(\d+|yyyymmdd)' match_rel = re.compile(match_pat) def replace_version(self, replacement = version_string, count = 1): ''' XXX ''' self.content = self.match_rel.sub(replacement, self.content, count) # Determine the release date and the pattern to match a date # Mon, 05 Jun 2010 21:17:15 -0700 # NEW DATE WILL BE INSERTED HERE if mode == 'develop': new_date = 'NEW DATE WILL BE INSERTED HERE' else: min = (time.daylight and time.altzone or time.timezone)//60 hr = min//60 min = -(min%60 + hr*100) new_date = (time.strftime('%a, %d %b %Y %X', release_date + (0,0,0)) + ' %+.4d' % min) _days = '(Sun|Mon|Tue|Wed|Thu|Fri|Sat)' _months = '(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oce|Nov|Dec)' match_date = _days+', \d\d '+_months+' \d\d\d\d \d\d:\d\d:\d\d [+-]\d\d\d\d' match_date = re.compile(match_date) def replace_date(self, replacement = new_date, count = 1): ''' XXX ''' self.content = self.match_date.sub(replacement, self.content, count) def __del__(self): ''' XXX ''' if self.file is not None and self.content != self.orig: print 'Updating ' + self.file + '...' open(self.file, 'w').write(self.content) if mode == 'post': # Set up for the next release series. if version_tuple[2]: # micro release, increment micro value minor = version_tuple[1] micro = version_tuple[2] + 1 else: # minor release, increment minor value minor = version_tuple[1] + 1 micro = 0 new_tuple = (version_tuple[0], minor, micro, 'alpha', 0) new_version = '.'.join(map(str, new_tuple[:4])) + '.yyyymmdd' # Update ReleaseConfig t = UpdateFile('ReleaseConfig') if DEBUG: t.file = '/tmp/ReleaseConfig' t.replace_assign('version_tuple', str(new_tuple)) # Update src/CHANGES.txt t = UpdateFile(os.path.join('src', 'CHANGES.txt')) if DEBUG: t.file = '/tmp/CHANGES.txt' t.sub('(\nRELEASE .*)', r"""\nRELEASE VERSION/DATE TO BE FILLED IN LATER\n From John Doe:\n - Whatever John Doe did.\n \1""") # Update src/RELEASE.txt t = UpdateFile(os.path.join('src', 'RELEASE.txt'), os.path.join('template', 'RELEASE.txt')) if DEBUG: t.file = '/tmp/RELEASE.txt' t.replace_version(new_version) # Update src/Announce.txt t = UpdateFile(os.path.join('src', 'Announce.txt')) if DEBUG: t.file = '/tmp/Announce.txt' t.sub('\nRELEASE .*', '\nRELEASE VERSION/DATE TO BE FILLED IN LATER') announce_pattern = """( Please note the following important changes scheduled for the next release: )""" announce_replace = (r"""\1 -- FEATURE THAT WILL CHANGE\n Please note the following important changes since release """ + '.'.join(map(str, version_tuple[:3])) + ':\n') t.sub(announce_pattern, announce_replace) # Write out the last update and exit t = None sys.exit() # Update src/CHANGES.txt t = UpdateFile(os.path.join('src', 'CHANGES.txt')) if DEBUG: t.file = '/tmp/CHANGES.txt' t.sub('\nRELEASE .*', '\nRELEASE ' + version_string + ' - ' + t.new_date) # Update src/RELEASE.txt t = UpdateFile(os.path.join('src', 'RELEASE.txt')) if DEBUG: t.file = '/tmp/RELEASE.txt' t.replace_version() # Update src/Announce.txt t = UpdateFile(os.path.join('src', 'Announce.txt')) if DEBUG: t.file = '/tmp/Announce.txt' t.sub('\nRELEASE .*', '\nRELEASE ' + version_string + ' - ' + t.new_date) # Update SConstruct t = UpdateFile('SConstruct') if DEBUG: t.file = '/tmp/SConstruct' t.replace_assign('month_year', repr(month_year)) t.replace_assign('copyright_years', repr(copyright_years)) t.replace_assign('default_version', repr(version_string)) # Update README t = UpdateFile('README.rst') if DEBUG: t.file = '/tmp/README.rst' t.sub('-' + t.match_pat + '\.', '-' + version_string + '.', count = 0) for suf in ['tar', 'win32', 'zip', 'rpm', 'exe', 'deb']: t.sub('-(\d+\.\d+\.\d+)\.%s' % suf, '-%s.%s' % (version_string, suf), count = 0) # Update QMTest/TestSCons.py t = UpdateFile(os.path.join('QMTest', 'TestSCons.py')) if DEBUG: t.file = '/tmp/TestSCons.py' t.replace_assign('copyright_years', repr(copyright_years)) t.replace_assign('default_version', repr(version_string)) #??? t.replace_assign('SConsVersion', repr(version_string)) t.replace_assign('python_version_unsupported', str(unsupported_version)) t.replace_assign('python_version_deprecated', str(deprecated_version)) # Update Script/Main.py t = UpdateFile(os.path.join('src', 'engine', 'SCons', 'Script', 'Main.py')) if DEBUG: t.file = '/tmp/Main.py' t.replace_assign('unsupported_python_version', str(unsupported_version)) t.replace_assign('deprecated_python_version', str(deprecated_version)) # Update doc/user/main.{in,xml} docyears = ', '.join(map(str, iter(range(2004, release_date[0] + 1)))) t = UpdateFile(os.path.join('doc', 'user', 'main.in')) if DEBUG: t.file = '/tmp/main.in' ## TODO debug these #t.sub('[^<]*', '' + docyears + '') #t.sub('[^<]*', '' + docyears + '') t = UpdateFile(os.path.join('doc', 'user', 'main.xml')) if DEBUG: t.file = '/tmp/main.xml' ## TODO debug these #t.sub('[^<]*', '' + docyears + '') #t.sub('[^<]*', '' + docyears + '') # Write out the last update t = None # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/bin/calibrate.py0000644000175000017500000000617012114661557017276 0ustar dktrkranzdktrkranz#!/usr/bin/env python # # Copyright (c) 2009 The SCons Foundation # # 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. from __future__ import division import optparse import os import re import subprocess import sys variable_re = re.compile('^VARIABLE: (.*)$', re.M) elapsed_re = re.compile('^ELAPSED: (.*)$', re.M) def main(argv=None): if argv is None: argv = sys.argv parser = optparse.OptionParser(usage="calibrate.py [-h] [-p PACKAGE], [--min time] [--max time] timings/*/*-run.py") parser.add_option('--min', type='float', default=9.5, help="minimum acceptable execution time (default 9.5)") parser.add_option('--max', type='float', default=10.00, help="maximum acceptable execution time (default 10.00)") parser.add_option('-p', '--package', type="string", help="package type") opts, args = parser.parse_args(argv[1:]) os.environ['TIMESCONS_CALIBRATE'] = '1' for arg in args: if len(args) > 1: print arg + ':' command = [sys.executable, 'runtest.py'] if opts.package: command.extend(['-p', opts.package]) command.append(arg) run = 1 good = 0 while good < 3: p = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) output = p.communicate()[0] vm = variable_re.search(output) em = elapsed_re.search(output) try: elapsed = float(em.group(1)) except AttributeError: print output raise print "run %3d: %7.3f: %s" % (run, elapsed, ' '.join(vm.groups())) if opts.min < elapsed and elapsed < opts.max: good += 1 else: good = 0 for v in vm.groups(): var, value = v.split('=', 1) value = int((int(value) * opts.max) // elapsed) os.environ[var] = str(value) run += 1 return 0 if __name__ == "__main__": sys.exit(main()) scons-doc-2.3.0/bin/xml_export-LICENSE0000644000175000017500000000523412114661557020202 0ustar dktrkranzdktrkranzCopyright (c) 2002 Open Source Development Network, Inc. ("OSDN") 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: 1. The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 2. Neither the names of VA Software Corporation, OSDN, SourceForge.net, the SourceForge.net Site Documentation project, nor the names of its contributors may be used to endorse or promote products derived from the Software without specific prior written permission of OSDN. 3. The name and trademarks of copyright holders may NOT be used in advertising or publicity pertaining to the Software without specific, written prior permission. Title to copyright in the Software and any associated documentation will at all times remain with copyright holders. 4. If any files are modified, you must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. We recommend that you provide URLs to the location from which the code is derived. 5. Altered versions of the Software must be plainly marked as such, and must not be misrepresented as being the original Software. 6. The origin of the Software must not be misrepresented; you must not claim that you wrote the original Software. If you use the Software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 7. The data files supplied as input to, or produced as output from, the programs of the Software do not automatically fall under the copyright of the Software, but belong to whomever generated them, and may be sold commercially, and may be aggregated with the Software. 8. 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 OR DOCUMENTATION. This Software consists of contributions made by OSDN and many individuals on behalf of OSDN. Specific attributions are listed in the accompanying credits file. scons-doc-2.3.0/bin/scons-cdist0000644000175000017500000001716312114661557017156 0ustar dktrkranzdktrkranz#!/bin/sh # # Copyright (c) 2005 The SCons Foundation # # 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. PROG=`basename $0` NOARGFLAGS="afhlnqrstz" ARGFLAGS="p:" ALLFLAGS="${NOARGFLAGS}${ARGFLAGS}" USAGE="Usage: ${PROG} [-${NOARGFLAGS}] [-p project] change" HELP="$USAGE -a Update the latest Aegis baseline (aedist) file. -f Force update, skipping up-front sanity check. -h Print this help message and exit. -l Update the local CVS repository. -n Don't execute, just echo commands. -p project Set the Aegis project. -q Quiet, don't print commands before executing them. -r Rsync the Aegis repository to SourceForge. -s Update the sourceforge.net CVS repository. -t Update the tigris.org CVS repository. -z Update the latest .tar.gz and .zip files. " DO="" PRINT="echo" EXECUTE="eval" SANITY_CHECK="yes" while getopts $ALLFLAGS FLAG; do case $FLAG in a | l | r | s | t | z ) DO="${DO}${FLAG}" ;; f ) SANITY_CHECK="no" ;; h ) echo "${HELP}" exit 0 ;; n ) EXECUTE=":" ;; p ) AEGIS_PROJECT="${OPTARG}" ;; q ) PRINT=":" ;; * ) echo "FLAG = ${FLAG}" >&2 echo "${USAGE}" >&2 exit 1 ;; esac done shift `expr ${OPTIND} - 1` if test "X$1" = "X"; then echo "${USAGE}" >&2 exit 1 fi if test "X${AEGIS_PROJECT}" = "X"; then echo "$PROG: No AEGIS_PROJECT set." >&2 echo "${USAGE}" >&2 exit 1 fi if test "X$DO" = "X"; then DO="alrstz" fi cmd() { $PRINT "$*" $EXECUTE "$*" } CHANGE=$1 if test "X${SANITY_CHECK}" = "Xyes"; then SCM="cvs" SCMROOT="/home/scons/CVSROOT/scons" DELTA=`aegis -l -ter cd ${CHANGE} | sed -n 's/.*, Delta \([0-9]*\)\./\1/p'` if test "x${DELTA}" = "x"; then echo "${PROG}: Could not find delta for change ${CHANGE}." >&2 echo "Has this finished integrating? Change ${CHANGE} not distributed." >&2 exit 1 fi PREV_DELTA=`expr ${DELTA} - 1` COMMAND="scons-scmcheck -D ${PREV_DELTA} -d q -p ${AEGIS_PROJECT} -s ${SCM} ${SCMROOT}" $PRINT "${COMMAND}" OUTPUT=`${COMMAND}` if test "X${OUTPUT}" != "X"; then echo "${PROG}: ${SCMROOT} is not up to date:" >&2 echo "${OUTPUT}" >& 2 echo "Did you skip any changes? Change ${CHANGE} not distributed." >&2 exit 1 fi fi if test X$EXECUTE != "X:" -a "X$SSH_AGENT_PID" = "X"; then eval `ssh-agent` ssh-add trap 'eval `ssh-agent -k`; exit' 0 1 2 3 15 fi cd BASELINE=`aesub -p ${AEGIS_PROJECT} -c ${CHANGE} '${Project trunk_name}'` TMPBLAE="/tmp/${BASELINE}.ae" TMPCAE="/tmp/${AEGIS_PROJECT}.C${CHANGE}.ae" # Original values for SourceForge. #SFLOGIN="stevenknight" #SFHOST="scons.sourceforge.net" #SFDEST="/home/groups/s/sc/scons/htdocs" SCONSLOGIN="scons" SCONSHOST="manam.pair.com" #SCONSDEST="public_html/production" SCONSDEST="public_ftp" # # Copy the baseline .ae to the constant location on SourceForge. # case "${DO}" in *a* ) cmd "aedist -s -bl -p ${AEGIS_PROJECT} > ${TMPBLAE}" cmd "scp ${TMPBLAE} ${SCONSLOGIN}@${SCONSHOST}:${SCONSDEST}/${BASELINE}.ae" cmd "rm ${TMPBLAE}" ;; esac # # Copy the latest .tar.gz and .zip files to the constant location on # SourceForge. # case "${DO}" in *z* ) BUILD_DIST=`aegis -p ${AEGIS_PROJECT} -cd -bl`/build/dist SCONS_SRC_TAR_GZ=`echo ${AEGIS_PROJECT} | sed 's/scons./scons-src-/'`*.tar.gz SCONS_SRC_ZIP=`echo ${AEGIS_PROJECT} | sed 's/scons./scons-src-/'`*.zip cmd "scp ${BUILD_DIST}/${SCONS_SRC_TAR_GZ} ${SCONSLOGIN}@${SCONSHOST}:${SCONSDEST}/scons-src-latest.tar.gz" cmd "scp ${BUILD_DIST}/${SCONS_SRC_ZIP} ${SCONSLOGIN}@${SCONSHOST}:${SCONSDEST}/scons-src-latest.zip" esac # # Sync Aegis tree with SourceForge. # # Cribbed and modified from Peter Miller's same-named script in # /home/groups/a/ae/aegis/aegis at SourceForge. # # Guide to what this does with rsync: # # --rsh=ssh use ssh for the transfer # -l copy symlinks as symlinks # -p preserve permissions # -r recursive # -t preserve times # -z compress data # --stats file transfer statistics # --exclude exclude files matching the pattern # --delete delete files that don't exist locally # --delete-excluded delete files that match the --exclude patterns # --progress show progress during the transfer # -v verbose # # We no longer use the --stats option. # case "${DO}" in *r* ) LOCAL=/home/scons/scons REMOTE=/home/groups/s/sc/scons/scons cmd "/usr/bin/rsync --rsh='ssh -l stevenknight' \ -l -p -r -t -z \ --exclude build \ --exclude '*,D' \ --exclude '*.pyc' \ --exclude aegis.log \ --exclude '.sconsign*' \ --delete --delete-excluded \ --progress -v \ ${LOCAL}/. scons.sourceforge.net:${REMOTE}/." ;; esac # # Sync the CVS tree with the local repository. # case "${DO}" in *l* ) ( export CVSROOT=/home/scons/CVSROOT/scons #cmd "ae2cvs -X -aegis -p ${AEGIS_PROJECT} -c ${CHANGE} -u $HOME/SCons/baldmt.com/scons" cmd "ae-cvs-ci ${AEGIS_PROJECT} ${CHANGE}" ) ;; esac # # Sync the Subversion tree with Tigris.org. # case "${DO}" in *t* ) ( SVN=http://scons.tigris.org/svn/scons case ${AEGIS_PROJECT} in scons.0.96 ) SVN_URL=${SVN}/branches/core ;; scons.0.96.513 ) SVN_URL=${SVN}/branches/sigrefactor ;; * ) echo "$PROG: Don't know SVN branch for '${AEGIS_PROJECT}'" >&2 exit 1 ;; esac SVN_CO_FLAGS="--username stevenknight" #cmd "ae2cvs -X -aegis -p ${AEGIS_PROJECT} -c ${CHANGE} -u $HOME/SCons/tigris.org/scons" cmd "ae-svn-ci ${AEGIS_PROJECT} ${CHANGE} ${SVN_URL} ${SVN_CO_FLAGS}" ) ;; esac # # Sync the CVS tree with SourceForge. # case "${DO}" in *s* ) ( export CVS_RSH=ssh export CVSROOT=:ext:stevenknight@scons.cvs.sourceforge.net:/cvsroot/scons #cmd "ae2cvs -X -aegis -p ${AEGIS_PROJECT} -c ${CHANGE} -u $HOME/SCons/sourceforge.net/scons" cmd "ae-cvs-ci ${AEGIS_PROJECT} ${CHANGE}" ) ;; esac # # Send the change .ae to the scons-aedist mailing list # # The subject requires editing by hand... # #aedist -s -p ${AEGIS_PROJECT} ${CHANGE} > ${TMPCAE} #aegis -l -p ${AEGIS_PROJECT} -c ${CHANGE} cd | # pine -attach_and_delete ${TMPCAE} scons-aedist@lists.sourceforge.net scons-doc-2.3.0/bin/scons_dev_master.py0000644000175000017500000001320712114661557020705 0ustar dktrkranzdktrkranz#!/bin/sh # # A script for turning a generic Ubuntu system into a master for # SCons development. import getopt import sys from Command import CommandRunner, Usage INITIAL_PACKAGES = [ 'subversion', ] INSTALL_PACKAGES = [ 'wget', ] PYTHON_PACKAGES = [ 'g++', 'gcc', 'make', 'zlib1g-dev', ] BUILDING_PACKAGES = [ 'docbook', 'docbook-dsssl', 'docbook-utils', 'docbook-xml', 'groff-base', 'jade', 'jadetex', 'man2html', 'python-epydoc', 'rpm', 'sp', 'tar', # additional packages that Bill Deegan's web page suggests #'docbook-to-man', #'docbook-xsl', #'docbook2x', #'tetex-bin', #'tetex-latex', # for ubuntu 9.10 # 'texlive-lang-french' ] DOCUMENTATION_PACKAGES = [ 'docbook-doc', 'epydoc-doc', 'gcc-doc', 'pkg-config', 'python-doc', 'sun-java5-doc', 'sun-java6-doc', 'swig-doc', 'texlive-doc', ] TESTING_PACKAGES = [ 'bison', 'cssc', 'cvs', 'flex', 'g++', 'gcc', 'gcj', 'ghostscript', # 'libgcj7-dev', 'm4', 'openssh-client', 'openssh-server', 'python-profiler', 'python-all-dev', 'rcs', 'rpm', # 'sun-java5-jdk', 'sun-java6-jdk', 'swig', 'texlive-base-bin', 'texlive-extra-utils', 'texlive-latex-base', 'texlive-latex-extra', 'zip', ] BUILDBOT_PACKAGES = [ 'buildbot', 'cron', ] default_args = [ 'upgrade', 'checkout', 'building', 'testing', 'python-versions', 'scons-versions', ] def main(argv=None): if argv is None: argv = sys.argv short_options = 'hnqy' long_options = ['help', 'no-exec', 'password=', 'quiet', 'username=', 'yes', 'assume-yes'] helpstr = """\ Usage: scons_dev_master.py [-hnqy] [--password PASSWORD] [--username USER] [ACTIONS ...] ACTIONS (in default order): upgrade Upgrade the system checkout Check out SCons building Install packages for building SCons testing Install packages for testing SCons scons-versions Install versions of SCons python-versions Install versions of Python ACTIONS (optional): buildbot Install packages for running BuildBot """ scons_url = 'http://scons.tigris.org/svn/scons/trunk' sudo = 'sudo' password = '""' username = 'guest' yesflag = '' try: try: opts, args = getopt.getopt(argv[1:], short_options, long_options) except getopt.error, msg: raise Usage(msg) for o, a in opts: if o in ('-h', '--help'): print helpstr sys.exit(0) elif o in ('-n', '--no-exec'): CommandRunner.execute = CommandRunner.do_not_execute elif o in ('--password'): password = a elif o in ('-q', '--quiet'): CommandRunner.display = CommandRunner.do_not_display elif o in ('--username'): username = a elif o in ('-y', '--yes', '--assume-yes'): yesflag = o except Usage, err: sys.stderr.write(str(err.msg) + '\n') sys.stderr.write('use -h to get help\n') return 2 if not args: args = default_args initial_packages = ' '.join(INITIAL_PACKAGES) install_packages = ' '.join(INSTALL_PACKAGES) building_packages = ' '.join(BUILDING_PACKAGES) testing_packages = ' '.join(TESTING_PACKAGES) buildbot_packages = ' '.join(BUILDBOT_PACKAGES) python_packages = ' '.join(PYTHON_PACKAGES) cmd = CommandRunner(locals()) for arg in args: if arg == 'upgrade': cmd.run('%(sudo)s apt-get %(yesflag)s upgrade') elif arg == 'checkout': cmd.run('%(sudo)s apt-get %(yesflag)s install %(initial_packages)s') cmd.run('svn co --username guest --password "" %(scons_url)s') elif arg == 'building': cmd.run('%(sudo)s apt-get %(yesflag)s install %(building_packages)s') elif arg == 'testing': cmd.run('%(sudo)s apt-get %(yesflag)s install %(testing_packages)s') elif arg == 'buildbot': cmd.run('%(sudo)s apt-get %(yesflag)s install %(buildbot_packages)s') elif arg == 'python-versions': if install_packages: cmd.run('%(sudo)s apt-get %(yesflag)s install %(install_packages)s') install_packages = None cmd.run('%(sudo)s apt-get %(yesflag)s install %(python_packages)s') try: import install_python except ImportError: msg = 'Could not import install_python; skipping python-versions.\n' sys.stderr.write(msg) else: install_python.main(['install_python.py', '-a']) elif arg == 'scons-versions': if install_packages: cmd.run('%(sudo)s apt-get %(yesflag)s install %(install_packages)s') install_packages = None try: import install_scons except ImportError: msg = 'Could not import install_scons; skipping scons-versions.\n' sys.stderr.write(msg) else: install_scons.main(['install_scons.py', '-a']) else: msg = '%s: unknown argument %s\n' sys.stderr.write(msg % (argv[0], repr(arg))) sys.exit(1) if __name__ == "__main__": sys.exit(main()) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/bin/scons-proc.py0000644000175000017500000004257412114661557017446 0ustar dktrkranzdktrkranz#!/usr/bin/env python # # Process a list of Python and/or XML files containing SCons documentation. # # This script creates formatted lists of the Builders, functions, Tools # or construction variables documented in the specified XML files. # # Dependening on the options, the lists are output in either # DocBook-formatted generated XML files containing the summary text # and/or .mod files contining the ENTITY definitions for each item, # or in man-page-formatted output. # import getopt import os import re import string import sys import xml.sax try: from io import StringIO # usable as of 2.6; takes unicode only except ImportError: # No 'io' module or no StringIO in io exec('from cStringIO import StringIO') import SConsDoc base_sys_path = [os.getcwd() + '/build/test-tar-gz/lib/scons'] + sys.path helpstr = """\ Usage: scons-proc.py [--man|--xml] [-b file(s)] [-f file(s)] [-t file(s)] [-v file(s)] [infile ...] Options: -b file(s) dump builder information to the specified file(s) -f file(s) dump function information to the specified file(s) -t file(s) dump tool information to the specified file(s) -v file(s) dump variable information to the specified file(s) --man print info in man page format, each -[btv] argument is a single file name --xml (default) print info in SML format, each -[btv] argument is a pair of comma-separated .gen,.mod file names """ opts, args = getopt.getopt(sys.argv[1:], "b:f:ht:v:", ['builders=', 'help', 'man', 'xml', 'tools=', 'variables=']) buildersfiles = None functionsfiles = None output_type = '--xml' toolsfiles = None variablesfiles = None for o, a in opts: if o in ['-b', '--builders']: buildersfiles = a elif o in ['-f', '--functions']: functionsfiles = a elif o in ['-h', '--help']: sys.stdout.write(helpstr) sys.exit(0) elif o in ['--man', '--xml']: output_type = o elif o in ['-t', '--tools']: toolsfiles = a elif o in ['-v', '--variables']: variablesfiles = a h = SConsDoc.SConsDocHandler() saxparser = xml.sax.make_parser() saxparser.setContentHandler(h) saxparser.setErrorHandler(h) xml_preamble = """\ """ xml_postamble = """\ """ for f in args: _, ext = os.path.splitext(f) if ext == '.py': dir, _ = os.path.split(f) if dir: sys.path = [dir] + base_sys_path module = SConsDoc.importfile(f) h.set_file_info(f, len(xml_preamble.split('\n'))) try: content = module.__scons_doc__ except AttributeError: content = None else: del module.__scons_doc__ else: h.set_file_info(f, len(xml_preamble.split('\n'))) content = open(f).read() if content: content = content.replace('&', '&') # Strip newlines after comments so they don't turn into # spurious paragraph separators. content = content.replace('-->\n', '-->') input = xml_preamble + content + xml_postamble try: saxparser.parse(StringIO(unicode(input))) except: sys.stderr.write("error in %s\n" % f) raise Warning = """\ """ Regular_Entities_Header = """\ """ Link_Entities_Header = """\ """ class SCons_XML(object): def __init__(self, entries, **kw): self.values = entries for k, v in kw.items(): setattr(self, k, v) def fopen(self, name): if name == '-': return sys.stdout return open(name, 'w') class SCons_XML_to_XML(SCons_XML): def write(self, files): gen, mod = files.split(',') g.write_gen(gen) g.write_mod(mod) def write_gen(self, filename): if not filename: return f = self.fopen(filename) for v in self.values: f.write('\n\n' % (v.prefix, v.idfunc())) f.write('%s\n' % v.xml_term()) f.write('\n') for chunk in v.summary.body: f.write(str(chunk)) if v.sets: s = ['&cv-link-%s;' % x for x in v.sets] f.write('\n') f.write('Sets: ' + ', '.join(s) + '.\n') f.write('\n') if v.uses: u = ['&cv-link-%s;' % x for x in v.uses] f.write('\n') f.write('Uses: ' + ', '.join(u) + '.\n') f.write('\n') f.write('\n') f.write('\n') def write_mod(self, filename): description = self.values[0].description if not filename: return f = self.fopen(filename) f.write(Warning) f.write('\n') f.write(Regular_Entities_Header % description) f.write('\n') for v in self.values: f.write('%s">\n' % (v.prefix, v.idfunc(), v.tag, v.entityfunc(), v.tag)) if self.env_signatures: f.write('\n') for v in self.values: f.write('env.%s">\n' % (v.prefix, v.idfunc(), v.tag, v.entityfunc(), v.tag)) f.write('\n') f.write(Warning) f.write('\n') f.write(Link_Entities_Header % description) f.write('\n') for v in self.values: f.write('<%s>%s\'>\n' % (v.prefix, v.idfunc(), v.prefix, v.idfunc(), v.tag, v.entityfunc(), v.tag)) if self.env_signatures: f.write('\n') for v in self.values: f.write('<%s>env.%s\'>\n' % (v.prefix, v.idfunc(), v.prefix, v.idfunc(), v.tag, v.entityfunc(), v.tag)) f.write('\n') f.write(Warning) class SCons_XML_to_man(SCons_XML): def write(self, filename): """ Converts the contents of the specified filename from DocBook XML to man page macros. This does not do an intelligent job. In particular, it doesn't actually use the structured nature of XML to handle arbitrary input. Instead, we're using text replacement and regular expression substitutions to convert observed patterns into the macros we want. To the extent that we're relatively consistent with our input .xml, this works, but could easily break if handed input that doesn't match these specific expectations. """ if not filename: return f = self.fopen(filename) chunks = [] for v in self.values: chunks.extend(v.man_separator()) chunks.extend(v.initial_man_chunks()) chunks.extend(list(map(str, v.summary.body))) body = ''.join(chunks) # Simple transformation of examples into our defined macros for those. body = body.replace('', '.ES') body = body.replace('', '.EE') # Replace groupings of tags and surrounding newlines # with single blank lines. body = body.replace('\n\n\n', '\n\n') body = body.replace('\n', '') body = body.replace('', '\n') body = body.replace('\n', '') # Convert and its child tags. body = body.replace('\n', '.RS 10\n') # Handling needs to be rationalized and made # consistent. Right now, the values map to arbitrary, # ad-hoc idioms in the current man page. body = re.compile(r'\n([^<]*)\n\n').sub(r'.TP 6\n.B \1\n', body) body = re.compile(r'\n([^<]*)\n\n').sub(r'.IP \1\n', body) body = re.compile(r'\n([^<]*)\n\n').sub(r'.HP 6\n.B \1\n', body) body = body.replace('\n', '') body = body.replace('\n', '') body = body.replace('\n', '.RE\n') # Get rid of unnecessary .IP macros, and unnecessary blank lines # in front of .IP macros. body = re.sub(r'\.EE\n\n+(?!\.IP)', '.EE\n.IP\n', body) body = body.replace('\n.EE\n.IP\n.ES\n', '\n.EE\n\n.ES\n') body = body.replace('\n.IP\n\'\\"', '\n\n\'\\"') # Convert various named entities and tagged names to nroff # in-line font conversions (\fB, \fI, \fP). body = re.sub('&(scons|SConstruct|SConscript|Dir|jar|Make|lambda);', r'\\fB\1\\fP', body) body = re.sub('&(TARGET|TARGETS|SOURCE|SOURCES);', r'\\fB$\1\\fP', body) body = re.sub('&(target|source);', r'\\fI\1\\fP', body) body = re.sub('&b(-link)?-([^;]*);', r'\\fB\2\\fP()', body) body = re.sub('&cv(-link)?-([^;]*);', r'\\fB$\2\\fP', body) body = re.sub('&f(-link)?-env-([^;]*);', r'\\fBenv.\2\\fP()', body) body = re.sub('&f(-link)?-([^;]*);', r'\\fB\2\\fP()', body) body = re.sub(r'<(application|command|envar|filename|function|literal|option)>([^<]*)', r'\\fB\2\\fP', body) body = re.sub(r'<(classname|emphasis|varname)>([^<]*)', r'\\fI\2\\fP', body) # Convert groupings of font conversions (\fB, \fI, \fP) to # man page .B, .BR, .I, .IR, .R, .RB and .RI macros. body = re.compile(r'^\\f([BI])([^\\]* [^\\]*)\\fP\s*$', re.M).sub(r'.\1 "\2"', body) body = re.compile(r'^\\f([BI])(.*)\\fP\s*$', re.M).sub(r'.\1 \2', body) body = re.compile(r'^\\f([BI])(.*)\\fP(\S+)$', re.M).sub(r'.\1R \2 \3', body) body = re.compile(r'^(\.B)( .*)\\fP(.*)\\fB(.*)$', re.M).sub(r'\1R\2 \3 \4', body) body = re.compile(r'^(\.B)R?( .*)\\fP(.*)\\fI(.*)$', re.M).sub(r'\1I\2\3 \4', body) body = re.compile(r'^(\.I)( .*)\\fP\\fB(.*)\\fP\\fI(.*)$', re.M).sub(r'\1R\2 \3 \4', body) body = re.compile(r'^(\S+)\\f([BI])(.*)\\fP$', re.M).sub(r'.R\2 \1 \3', body) body = re.compile(r'^(\S+)\\f([BI])(.*)\\fP([^\s\\]+)$', re.M).sub(r'.R\2 \1 \3 \4', body) body = re.compile(r'^(\.R[BI].*[\S])\s+$;', re.M).sub(r'\1', body) # Convert < and > entities to literal < and > characters. body = body.replace('<', '<') body = body.replace('>', '>') # Backslashes. Oh joy. body = re.sub(r'\\(?=[^f])', r'\\\\', body) body = re.compile("^'\\\\\\\\", re.M).sub("'\\\\", body) body = re.compile(r'^\.([BI]R?) ([^"]\S*\\\\\S+[^"])$', re.M).sub(r'.\1 "\2"', body) # Put backslashes in front of various hyphens that need # to be long em-dashes. body = re.compile(r'^\.([BI]R?) --', re.M).sub(r'.\1 \-\-', body) body = re.compile(r'^\.([BI]R?) -', re.M).sub(r'.\1 \-', body) body = re.compile(r'\\f([BI])-', re.M).sub(r'\\f\1\-', body) f.write(body) class Proxy(object): def __init__(self, subject): """Wrap an object as a Proxy object""" self.__subject = subject def __getattr__(self, name): """Retrieve an attribute from the wrapped object. If the named attribute doesn't exist, AttributeError is raised""" return getattr(self.__subject, name) def get(self): """Retrieve the entire wrapped object""" return self.__subject def __cmp__(self, other): if issubclass(other.__class__, self.__subject.__class__): return cmp(self.__subject, other) return cmp(self.__dict__, other.__dict__) class SConsThing(Proxy): def idfunc(self): return self.name def xml_term(self): return '%s' % self.name class Builder(SConsThing): description = 'builder' prefix = 'b-' tag = 'function' def xml_term(self): return ('<%s>%s()\n<%s>env.%s()' % (self.tag, self.name, self.tag, self.tag, self.name, self.tag)) def entityfunc(self): return self.name def man_separator(self): return ['\n', "'\\" + '"'*69 + '\n'] def initial_man_chunks(self): return [ '.IP %s()\n.IP env.%s()\n' % (self.name, self.name) ] class Function(SConsThing): description = 'function' prefix = 'f-' tag = 'function' def args_to_xml(self, arg): s = ''.join(arg.body).strip() result = [] for m in re.findall('([a-zA-Z/_]+=?|[^a-zA-Z/_]+)', s): if m[0] in string.letters: if m[-1] == '=': result.append('%s=' % m[:-1]) else: result.append('%s' % m) else: result.append(m) return ''.join(result) def xml_term(self): try: arguments = self.arguments except AttributeError: arguments = ['()'] result = [''] for arg in arguments: try: signature = arg.signature except AttributeError: signature = "both" s = self.args_to_xml(arg) if signature in ('both', 'global'): result.append('%s%s\n' % (self.name, s)) #
if signature in ('both', 'env'): result.append('env.%s%s' % (self.name, s)) result.append('
') return ''.join(result) def entityfunc(self): return self.name def man_separator(self): return ['\n', "'\\" + '"'*69 + '\n'] def args_to_man(self, arg): """Converts the contents of an tag, which specifies a function's calling signature, into a series of tokens that alternate between literal tokens (to be displayed in roman or bold face) and variable names (to be displayed in italics). This is complicated by the presence of Python "keyword=var" arguments, where "keyword=" should be displayed literally, and "var" should be displayed in italics. We do this by detecting the keyword= var portion and appending it to the previous string, if any. """ s = ''.join(arg.body).strip() result = [] for m in re.findall('([a-zA-Z/_]+=?|[^a-zA-Z/_]+)', s): if m[-1] == '=' and result: if result[-1][-1] == '"': result[-1] = result[-1][:-1] + m + '"' else: result[-1] += m else: if ' ' in m: m = '"%s"' % m result.append(m) return ' '.join(result) def initial_man_chunks(self): try: arguments = self.arguments except AttributeError: arguments = ['()'] result = [] for arg in arguments: try: signature = arg.signature except AttributeError: signature = "both" s = self.args_to_man(arg) if signature in ('both', 'global'): result.append('.TP\n.RI %s%s\n' % (self.name, s)) if signature in ('both', 'env'): result.append('.TP\n.IR env .%s%s\n' % (self.name, s)) return result class Tool(SConsThing): description = 'tool' prefix = 't-' tag = 'literal' def idfunc(self): return self.name.replace('+', 'X') def entityfunc(self): return self.name def man_separator(self): return ['\n'] def initial_man_chunks(self): return ['.IP %s\n' % self.name] class Variable(SConsThing): description = 'construction variable' prefix = 'cv-' tag = 'envar' def entityfunc(self): return '$' + self.name def man_separator(self): return ['\n'] def initial_man_chunks(self): return ['.IP %s\n' % self.name] if output_type == '--man': processor_class = SCons_XML_to_man elif output_type == '--xml': processor_class = SCons_XML_to_XML else: sys.stderr.write("Unknown output type '%s'\n" % output_type) sys.exit(1) if buildersfiles: g = processor_class([ Builder(b) for b in sorted(h.builders.values()) ], env_signatures=True) g.write(buildersfiles) if functionsfiles: g = processor_class([ Function(b) for b in sorted(h.functions.values()) ], env_signatures=True) g.write(functionsfiles) if toolsfiles: g = processor_class([ Tool(t) for t in sorted(h.tools.values()) ], env_signatures=False) g.write(toolsfiles) if variablesfiles: g = processor_class([ Variable(v) for v in sorted(h.cvars.values()) ], env_signatures=False) g.write(variablesfiles) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/bin/xmlagenda.py0000755000175000017500000000715612114661557017320 0ustar dktrkranzdktrkranz#!/usr/bin/env python # Query the scons.tigris.org database for the issues of interest. # The typical triage query is found on http://www.scons.org/wiki/BugParty # Download the issues from Issuezilla as XML; this creates a file # named 'issues.xml'. Run this script in the dir containing # issues.xml (or pass full path as arg to this script) to translate # 'issues.xml' into a CSV file named 'editlist.csv'. Upload the CSV # into a Google spreadsheet. # In the spreadsheet: # Select all the columns and pick "align-->top" # Select the ID and votes columns and pick "align-->right" # Select the priority column and pick "align-->center" # Select the first row and click on the "bold" button # Grab the lines between the column headers to adjust the column widths # Grab the sort bar on the far left (just above the "1" for row one) # and move it down one row. (Row one becomes a floating header) # Voila! # The team members # FIXME: These names really should be external to this script team = sorted('Steven Gary Greg Ken Jim Bill Jason Dirk Anatoly'.split()) # The elements to be picked out of the issue PickList = [ # sort key -- these are used to sort the entry 'target_milestone', 'priority', 'votes_desc', 'creation_ts', # payload -- these are displayed 'issue_id', 'votes', 'issue_type', 'target_milestone', 'priority', 'assigned_to', 'short_desc', ] # Conbert a leaf element into its value as a text string # We assume it's "short enough" that there's only one substring def Value(element): v = element.firstChild if v is None: return '' return v.nodeValue # Parse the XML issues file and produce a DOM for it import sys if len(sys.argv) > 1: xml = sys.argv[1] else: xml = 'issues.xml' from xml.dom.minidom import parse xml = parse(xml) # Go through the issues in the DOM, pick out the elements we want, # and put them in our list of issues. issues = [] for issuezilla in xml.childNodes: # The Issuezilla element contains the issues if issuezilla.nodeType != issuezilla.ELEMENT_NODE: continue for issue in issuezilla.childNodes: # The issue elements contain the info for an issue if issue.nodeType != issue.ELEMENT_NODE: continue # Accumulate the pieces we want to include d = {} for element in issue.childNodes: if element.nodeName in PickList: d[element.nodeName] = Value(element) # convert 'votes' to numeric, ascending and descending try: v = int('0' + d['votes']) except KeyError: pass else: d['votes_desc'] = -v d['votes'] = v # Marshal the elements and add them to the list issues.append([ d[ix] for ix in PickList ]) issues.sort() # Transcribe the issues into comma-separated values. # FIXME: parameterize the output file name import csv writer = csv.writer(open('editlist.csv', 'w')) # header writer.writerow(['ID', 'Votes', 'Type/Member', 'Milestone', 'Pri', 'Owner', 'Summary/Comments']) for issue in issues: row = issue[4:] # strip off sort key #row[0] = """=hyperlink("http://scons.tigris.org/issues/show_bug.cgi?id=%s","%s")""" % (row[0],row[0]) if row[3] == '-unspecified-': row[3] = 'triage' writer.writerow(['','','','','','','']) writer.writerow(row) writer.writerow(['','','consensus','','','','']) writer.writerow(['','','','','','','']) for member in team: writer.writerow(['','',member,'','','','']) print "Exported %d issues to editlist.csv. Ready to upload to Google."%len(issues) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/bin/xml_export-README0000644000175000017500000000414312114661557020053 0ustar dktrkranzdktrkranzThis copy of xml_export was snarfed from adocman-0.10 from SourceForge. We're checking in a copy as a convenience for any future SCons project administrator who may need to download exported XML data. The original, unmodified contents of the README file for that release of adocman are as follows: adocman - Automation tool for SourceForge.net DocManager handling Copyright (C) 2002 Open Source Development Network, Inc. ("OSDN") File manifest: Alexandria perl-based API for performing operations against the SourceForge.net site, currently including basic Client operations (i.e. login/logout) and DocManager operations adocman The adocman program, providing the means to perform DocManager operations from the command-line or scripts (by project developers or admins listed as DocManager Editors) xml_export The xml_export program, providing the means to automate downloads of data from the XML data export facility on SourceForge.net (by project administrators) adocman.html Manual for adocman, including background information, command-line options detail, etc. xml_export.html Manual for xml_export, including basic info about command-line options. See adocman.html for additional information. LICENSE License terms for adocman README This file TODO List of ongoing work in improving adocman. NOTE: Please contact the maintainer before starting any effort to improve this code. We have significantly modified the structure and design of this program for the next release; structure and command-line interface are subject to change without notice. A list of the prerequisites required to execute 'adocman' may be found at in the PREREQUISITES section of the adocman manual (adocman.html). Though not listed, a recent installation of 'perl' is also a prerequisite. Support for this program may be obtained as per the SUPPORT AND BUGS section of the adocman.html manual. Any questions or concerns regarding this software should be escalated as per the SUPPORT AND BUGS section of the provided manual. The authoritative source of this software is: https://sourceforge.net/projects/sitedocs scons-doc-2.3.0/bin/scons-review.sh0000755000175000017500000000114412114661557017755 0ustar dktrkranzdktrkranz#!/bin/sh case "$1" in '') exec svn diff --diff-cmd diff -x -c $* ;; -m) svn diff --diff-cmd diff -x -c $* | alpine scons-dev ;; *) echo "Error: unknown option '$1"; exit 1 ;; esac # OLD CODE FOR USE WITH AEGIS # #if test $# -ne 1; then # echo "Usage: scons-review change#" >&2 # exit 1 #fi #if test "X$AEGIS_PROJECT" = "X"; then # echo "scons-review: AEGIS_PROJECT is not set" >&2 # exit 1 #fi #DIR=`aegis -cd -dd $*` #if test "X${DIR}" = "X"; then # echo "scons-review: No Aegis directory for '$*'" >&2 # exit 1 #fi #(cd ${DIR} && find * -name '*,D' | sort | xargs cat) | pine scons-dev scons-doc-2.3.0/bin/ae-svn-ci0000755000175000017500000001335312114661557016507 0ustar dktrkranzdktrkranz# # aegis - project change supervisor # Copyright (C) 2004 Peter Miller; # All rights reserved. # # As a specific exception to the GPL, you are allowed to copy # this source file into your own project and modify it, without # releasing your project under the GPL, unless there is some other # file or condition which would require it. # # MANIFEST: shell script to commit changes to Subversion # # This script is expected to be run by the integrate_pass_notify_command # and as such the baseline has already assumed the shape asked for by # the change. # # integrate_pass_notify_command = # "$bin/ae-svn-ci $project $change http://svn.site.com/svn/trunk --username svn_user"; # # Alternatively, you may wish to tailor this script to the individual # needs of your project. Make it a source file, e.g. "etc/ae-svn-ci.sh" # and then use the following: # # integrate_pass_notify_command = # "$sh ${s etc/ae-svn-ci} $project $change http://svn.site.com/svn/trunk --username svn_user"; # USAGE="Usage: $0 [-hnq] []" PRINT="echo" EXECUTE="eval" while getopts "hnq" FLAG do case ${FLAG} in h ) echo "${USAGE}" exit 0 ;; n ) EXECUTE=":" ;; q ) PRINT=":" ;; * ) echo "$0: unknown option ${FLAG}" >&2 exit 1 ;; esac done shift `expr ${OPTIND} - 1` case $# in [012]) echo "${USAGE}" 1>&2 exit 1 ;; *) project=$1 change=$2 svn_url=$3 shift 3 svn_co_flags=$* ;; esac here=`pwd` AEGIS_PROJECT=$project export AEGIS_PROJECT AEGIS_CHANGE=$change export AEGIS_CHANGE module=`echo $project | sed 's|[.].*||'` baseline=`aegis -cd -bl` if test X${TMPDIR} = X; then TMPDIR=/var/tmp; fi TMP=${TMPDIR}/ae-svn-ci.$$ mkdir ${TMP} cd ${TMP} PWD=`pwd` if test X${PWD} != X${TMP}; then echo "$0: ended up in ${PWD}, not ${TMP}" >&2 exit 1 fi fail() { set +x cd $here rm -rf ${TMP} echo "FAILED" 1>&2 exit 1 } trap "fail" 1 2 3 15 Command() { ${PRINT} "$*" ${EXECUTE} "$*" } # # Create a new Subversion work area. # # Note: this assumes the module is checked-out into a directory of the # same name. Is there a way to ask Subversion where it is going to put a # module, so we can always get the "cd" right? # ${PRINT} svn co $svn_url $module $svn_co_flags ${EXECUTE} svn co $svn_url $module $svn_co_flags > LOG 2>&1 if test $? -ne 0; then cat LOG; fail; fi ${EXECUTE} cd $module # # Now we need to extract the sources from Aegis and drop them into the # Subversion work area. There are two ways to do this. # # The first way is to use the generated tarball. # This has the advantage that it has the Makefile.in file in it, and # will work immediately. # # The second way is to use aetar, which will give exact sources, and # omit all derived files. This will *not* include the Makefile.in, # and so will not be readily compilable. # # gunzip < $baseline/export/${project}.tar.gz | tardy -rp ${project} | tar xf - aetar -send -comp-alg=gzip -o - | tar xzf - # # If any new directories have been created we will need to add them # to Subversion before we can add the new files which we know are in them, # or they would not have been created. Do this only if the -n option # isn't used, because if it is, we won't have actually checked out the # source and we'd erroneously report that all of them need to be added. # if test "X${EXECUTE}" != "X:" then find . -name .svn -prune -o -type d -print | xargs --max-args=1 | while read dir do if [ ! -d "$dir/.svn" ] then Command svn add -N "$dir" fi done fi # # Use the Aegis meta-data to perform some commands that Subversion can't # figure out for itself. We use an inline "aer" report script to identify # when a remove-create pair are actually due to a move. # aegis -rpt -nph -f - <<_EOF_ | auto cs; cs = project[project_name()].state.branch.change[change_number()]; columns({width = 1000;}); auto file, moved; for (file in cs.src) { if (file.move != "") moved[file.move] = 1; } auto action; for (file in cs.src) { if (file.action == "remove" && file.move != "") action = "move"; else action = file.action; /* * Suppress printing of any files created as the result of a move. * These are printed as the destination when printing the line for * the file that was *removed* as a result of the move. */ if (action != "create" || ! moved[file.file_name]) print(sprintf("%s %s \\"%s\\" \\"%s\\"", file.usage, action, file.file_name, file.move)); } _EOF_ while read line do eval set -- "$line" usage="$1" action="$2" srcfile="$3" dstfile="$4" case $action in create) Command svn add $srcfile ;; remove) Command rm -f $srcfile Command svn remove $srcfile ;; move) Command mv $dstfile $dstfile.move Command svn move $srcfile $dstfile Command cp $dstfile.move $dstfile Command rm -f $dstfile.move ;; *) ;; esac done # # Extract the brief description. We'd like to do this using aesub # or something, like so: # # message=`aesub '${version} - ${change description}'` # # but the expansion of ${change description} has a lame hard-coded max of # 80 characters, so we have to do this by hand. (This has the slight # benefit of preserving backslashes in front of any double-quotes in # the text; that will have to be handled if we go back to using aesub.) # description=`aegis -ca -l | sed -n 's/brief_description = "\(.*\)";$/\1/p'` version=`aesub '${version}'` message="$version - $description" # # Now commit all the changes. # Command svn commit -m \"$message\" # # All done. Clean up and go home. # cd $here rm -rf ${TMP} exit 0 scons-doc-2.3.0/bin/xml_export0000644000175000017500000002071012114661557017116 0ustar dktrkranzdktrkranz#!/usr/bin/perl -w # # xml_export - Retrieve data from the SF.net XML export for project data # # Copyright (C) 2002 Open Source Development Network, Inc. ("OSDN") # # 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 license details found # below in the section marked "$LICENSE_TEXT". # # SCons: modified the following RCS Id line so it won't expand during # our checkins. # # $_Id: adocman,v 1.51 2002/06/07 18:56:35 moorman Exp _$ # # Written by Nate Oostendorp # and Jacob Moorman ########################################################################### use strict; use Alexandria::Client; use HTTP::Request::Common; my $client = new Alexandria::Client; util_verifyvariables("groupid"); my $res = $ua->simple_request(GET "$config{hosturl}/export/xml_export.php?group_id=$config{groupid}"); if (not $res->is_success()) { die "Failed to connect: ".$res->as_string(); } print $res->content; ########################################################################### __END__ =head1 NAME xml_export - Retrieve data for a project via the SF.net XML export facility =head1 DESCRIPTION B provides a simple mechanism to download data from the XML data export facility on SourceForge.net. This utility is needed (in place of a downloader like wget or curl) since authentication by a project administrator is required to access the XML export facility. =head1 SYNOPSIS xml_export [options] > output_file OPTIONS --login Login to the SourceForge.net site --logout Logout of the SourceForge.net site --groupid=GROUPID Group ID of the project whose data you wish to export =head1 ERROR LEVELS The following error levels are returned upon exit of this program: 0 success 1 failure: general (requested DocManager operation failed) 2 failure: authentication failure 3 failure: must --login before performing this operation 4 failure: bad command-line option specified or variable setting problem 5 failure: error in accessing/creating a file or directory 6 failure: failed to enter requested input before timeout expired =head1 AUTHORITATIVE SOURCE The original version of B may be found in the materials provided from the SourceForge.net Site Documentation project (sitedocs) on the SourceForge.net site. The latest version of this program may be found in the CVS repository for the sitedocs project on SourceForge.net. The sitedocs project pages may be accessed at: http://sourceforge.net/projects/sitedocs =head1 SECURITY For security-related information for this application, please review the documentation provided for the adocman utility. =head1 EXAMPLES The following are examples for using this program to export project data via the XML data export facility on SourceForge.net. It is presumed that you have a valid SourceForge.net user account, which is listed as a project administrator on the project in question. This tool will only work for project administrators. The group ID for the project may be derived from the URL for the Admin page for the project, or by viewing the Project Admin page for the project (look for the text "Your Group ID is: xxxxxx"). To login to the SourceForge.net site via the command-line: adocman --username=myusername --password=mypassword --login \ --groupid=8675309 To login to the SourceForge.net site, and be prompted to enter your password interactively: adocman --username=myusername --interactive --login --groupid=8675309 To perform an export (after logging-in): xml_export --groupid=8675309 > output.xml To logout of SourceForge.net: adocman --logout Additional capabilities (including the use of configuration files to specify information that would otherwise be provided interactively or on the command-line) are detailed in the documentation provided for the adocman utility. To obtain output for debugging a problem, perform the same command as originally tested, but first add the --verbose flag, and determine whether you are able to solve the issue on your own. If the problem persists, see the "SUPPORT AND BUGS" section, below. =head1 SUPPORT AND BUGS This program was written by a member of the SourceForge.net staff team. This software has been released under an Open Source license, for the greater benefit of the SourceForge.net developer community. The SourceForge.net Site Documentation project is the caretaker of this software. Issues related to the use of this program, or bugs found in using this program, may be reported to the SourceForge.net Site Documentation project using their Support Request Tracker at: https://sourceforge.net/tracker/?func=add&group_id=52614&atid=467457 Any support that is provided for this program is provided as to further enhance the stability and functionality of this program for SourceForge.net users. The SourceForge.net Site Documentation project makes use of this software for its own internal purposes, in managing the Site Documentation collection for the SourceForge.net site. =head1 AUTHOR Nathan Oostendorp and Jacob Moorman =head1 PREREQUISITES C, C, C, C, C These prerequisites may be installed in an interactive, but automated fashion through the use of perl's CPAN module, invoked as: perl -MCPAN -e shell; =head1 LICENSE Copyright (c) 2002 Open Source Development Network, Inc. ("OSDN") 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: 1. The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 2. Neither the names of VA Software Corporation, OSDN, SourceForge.net, the SourceForge.net Site Documentation project, nor the names of its contributors may be used to endorse or promote products derived from the Software without specific prior written permission of OSDN. 3. The name and trademarks of copyright holders may NOT be used in advertising or publicity pertaining to the Software without specific, written prior permission. Title to copyright in the Software and any associated documentation will at all times remain with copyright holders. 4. If any files are modified, you must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. We recommend that you provide URLs to the location from which the code is derived. 5. Altered versions of the Software must be plainly marked as such, and must not be misrepresented as being the original Software. 6. The origin of the Software must not be misrepresented; you must not claim that you wrote the original Software. If you use the Software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 7. The data files supplied as input to, or produced as output from, the programs of the Software do not automatically fall under the copyright of the Software, but belong to whomever generated them, and may be sold commercially, and may be aggregated with the Software. 8. 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 OR DOCUMENTATION. This Software consists of contributions made by OSDN and many individuals on behalf of OSDN. Specific attributions are listed in the accompanying credits file. =head1 HISTORY B<2002-12-03> Completed version 0.10 - move to classes, added POD =cut scons-doc-2.3.0/bin/upload-release-files.sh0000755000175000017500000000357612114661557021346 0ustar dktrkranzdktrkranz#!/bin/sh if [ $# -lt 2 ]; then echo Usage: $0 VERSION SF_USERNAME exit 1 fi VERSION=$1; shift SF_USER=$1; shift RSYNC='rsync' RSYNCOPTS='-v -e ssh' SF_MACHINE='frs.sourceforge.net' SF_TOPDIR='/home/frs/project/scons' # the build products are here: cd build/dist cp -f ../../src/CHANGES.txt ../../src/RELEASE.txt ../../src/Announce.txt . set -x # Upload main scons release files: $RSYNC $RSYNCOPTS \ scons-$VERSION-1.noarch.rpm \ scons-$VERSION-1.src.rpm \ scons-$VERSION-setup.exe \ scons-$VERSION.tar.gz \ scons-$VERSION.zip \ Announce.txt CHANGES.txt RELEASE.txt \ $SF_USER@$SF_MACHINE:$SF_TOPDIR/scons/$VERSION/ # Local packages: $RSYNC $RSYNCOPTS \ scons-local-$VERSION.tar.gz \ scons-local-$VERSION.zip \ Announce.txt CHANGES.txt RELEASE.txt \ $SF_USER@$SF_MACHINE:$SF_TOPDIR/scons-local/$VERSION/ # Source packages: $RSYNC $RSYNCOPTS \ scons-src-$VERSION.tar.gz \ scons-src-$VERSION.zip \ Announce.txt CHANGES.txt RELEASE.txt \ $SF_USER@$SF_MACHINE:$SF_TOPDIR/scons-src/$VERSION/ # # scons.org stuff: # # Doc: copy the doc tgz over; we'll unpack later $RSYNC $RSYNCOPTS \ scons-doc-$VERSION.tar.gz \ scons@scons.org:public_html/production/doc/$VERSION/ # Copy the changelog $RSYNC $RSYNCOPTS \ CHANGES.txt \ scons@scons.org:public_html/production/ # Note that Announce.txt gets copied over to RELEASE.txt. # This should be fixed at some point. $RSYNC $RSYNCOPTS \ Announce.txt \ scons@scons.org:public_html/production/RELEASE.txt # Unpack the doc and repoint doc symlinks: ssh scons@scons.org " cd public_html/production/doc cd $VERSION tar xvf scons-doc-$VERSION.tar.gz cd .. rm latest; ln -s $VERSION latest rm production; ln -s $VERSION production for f in HTML PDF PS TEXT; do rm $f; ln -s $VERSION/$f $f; done " echo '*****' echo '***** Now manually update index.php, includes/versions.php and news-raw.xhtml on scons.org.' echo '*****' scons-doc-2.3.0/bin/install_scons.py0000644000175000017500000001633212114661557020224 0ustar dktrkranzdktrkranz#!/usr/bin/env python # # A script for unpacking and installing different historic versions of # SCons in a consistent manner for side-by-side development testing. # # This abstracts the changes we've made to the SCons setup.py scripts in # different versions so that, no matter what version is specified, it ends # up installing the necessary script(s) and library into version-specific # names that won't interfere with other things. # # By default, we expect to extract the .tar.gz files from a Downloads # subdirectory in the current directory. # # Note that this script cleans up after itself, removing the extracted # directory in which we do the build. # # This was written for a Linux system (specifically Ubuntu) but should # be reasonably generic to any POSIX-style system with a /usr/local # hierarchy. import getopt import os import shutil import sys import tarfile import urllib from Command import CommandRunner, Usage all_versions = [ '0.01', '0.02', '0.03', '0.04', '0.05', '0.06', '0.07', '0.08', '0.09', '0.10', '0.11', '0.12', '0.13', '0.14', '0.90', '0.91', '0.92', '0.93', '0.94', #'0.94.1', '0.95', #'0.95.1', '0.96', '0.96.1', '0.96.90', '0.96.91', '0.96.92', '0.96.93', '0.96.94', '0.96.95', '0.96.96', '0.97', '0.97.0d20070809', '0.97.0d20070918', '0.97.0d20071212', '0.98.0', '0.98.1', '0.98.2', '0.98.3', '0.98.4', '0.98.5', '1.0.0', '1.0.0.d20080826', '1.0.1', '1.0.1.d20080915', '1.0.1.d20081001', '1.1.0', '1.1.0.d20081104', '1.1.0.d20081125', '1.1.0.d20081207', '1.2.0', '1.2.0.d20090113', '1.2.0.d20090223', '1.2.0.d20090905', '1.2.0.d20090919', '1.2.0.d20091224', '1.2.0.d20100117', '1.2.0.d20100306', '1.3.0', '1.3.0.d20100404', '1.3.0.d20100501', '1.3.0.d20100523', '1.3.0.d20100606', '2.0.0.alpha.20100508', '2.0.0.beta.20100531', '2.0.0.beta.20100605', '2.0.0.final.0', ] def main(argv=None): if argv is None: argv = sys.argv all = False downloads_dir = 'Downloads' downloads_url = 'http://downloads.sourceforge.net/scons' if sys.platform == 'win32': sudo = '' prefix = sys.prefix else: sudo = 'sudo' prefix = '/usr/local' python = sys.executable short_options = 'ad:hnp:q' long_options = ['all', 'help', 'no-exec', 'prefix=', 'quiet'] helpstr = """\ Usage: install_scons.py [-ahnq] [-d DIR] [-p PREFIX] [VERSION ...] -a, --all Install all SCons versions. -d DIR, --downloads=DIR Downloads directory. -h, --help Print this help and exit -n, --no-exec No execute, just print command lines -p PREFIX, --prefix=PREFIX Installation prefix. -q, --quiet Quiet, don't print command lines """ try: try: opts, args = getopt.getopt(argv[1:], short_options, long_options) except getopt.error, msg: raise Usage(msg) for o, a in opts: if o in ('-a', '--all'): all = True elif o in ('-d', '--downloads'): downloads_dir = a elif o in ('-h', '--help'): print helpstr sys.exit(0) elif o in ('-n', '--no-exec'): CommandRunner.execute = CommandRunner.do_not_execute elif o in ('-p', '--prefix'): prefix = a elif o in ('-q', '--quiet'): CommandRunner.display = CommandRunner.do_not_display except Usage, err: sys.stderr.write(str(err.msg) + '\n') sys.stderr.write('use -h to get help\n') return 2 if all: if args: msg = 'install-scons.py: -a and version arguments both specified' sys.stderr.write(msg) sys.exit(1) args = all_versions cmd = CommandRunner() for version in args: scons = 'scons-' + version tar_gz = os.path.join(downloads_dir, scons + '.tar.gz') tar_gz_url = "%s/%s.tar.gz" % (downloads_url, scons) cmd.subst_dictionary(locals()) if not os.path.exists(tar_gz): if not os.path.exists(downloads_dir): cmd.run('mkdir %(downloads_dir)s') cmd.run((urllib.urlretrieve, tar_gz_url, tar_gz), 'wget -O %(tar_gz)s %(tar_gz_url)s') def extract(tar_gz): tarfile.open(tar_gz, "r:gz").extractall() cmd.run((extract, tar_gz), 'tar zxf %(tar_gz)s') cmd.run('cd %(scons)s') if version in ('0.01', '0.02', '0.03', '0.04', '0.05', '0.06', '0.07', '0.08', '0.09', '0.10'): # 0.01 through 0.10 install /usr/local/bin/scons and # /usr/local/lib/scons. The "scons" script knows how to # look up the library in a version-specific directory, but # we have to move both it and the library directory into # the right version-specific name by hand. cmd.run('%(python)s setup.py build') cmd.run('%(sudo)s %(python)s setup.py install --prefix=%(prefix)s') cmd.run('%(sudo)s mv %(prefix)s/bin/scons %(prefix)s/bin/scons-%(version)s') cmd.run('%(sudo)s mv %(prefix)s/lib/scons %(prefix)s/lib/scons-%(version)s') elif version in ('0.11', '0.12', '0.13', '0.14', '0.90'): # 0.11 through 0.90 install /usr/local/bin/scons and # /usr/local/lib/scons-%(version)s. We just need to move # the script to a version-specific name. cmd.run('%(python)s setup.py build') cmd.run('%(sudo)s %(python)s setup.py install --prefix=%(prefix)s') cmd.run('%(sudo)s mv %(prefix)s/bin/scons %(prefix)s/bin/scons-%(version)s') elif version in ('0.91', '0.92', '0.93', '0.94', '0.94.1', '0.95', '0.95.1', '0.96', '0.96.1', '0.96.90'): # 0.91 through 0.96.90 install /usr/local/bin/scons, # /usr/local/bin/sconsign and /usr/local/lib/scons-%(version)s. # We need to move both scripts to version-specific names. cmd.run('%(python)s setup.py build') cmd.run('%(sudo)s %(python)s setup.py install --prefix=%(prefix)s') cmd.run('%(sudo)s mv %(prefix)s/bin/scons %(prefix)s/bin/scons-%(version)s') cmd.run('%(sudo)s mv %(prefix)s/bin/sconsign %(prefix)s/bin/sconsign-%(version)s') lib_scons = os.path.join(prefix, 'lib', 'scons') if os.path.isdir(lib_scons): cmd.run('%(sudo)s mv %(prefix)s/lib/scons %(prefix)s/lib/scons-%(version)s') else: # Versions from 0.96.91 and later support what we want # with a --no-scons-script option. cmd.run('%(python)s setup.py build') cmd.run('%(sudo)s %(python)s setup.py install --prefix=%(prefix)s --no-scons-script') cmd.run('cd ..') cmd.run((shutil.rmtree, scons), 'rm -rf %(scons)s') if __name__ == "__main__": sys.exit(main()) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/bin/timebuild0000644000175000017500000000316012114661557016673 0ustar dktrkranzdktrkranz#!/bin/sh # # Profile running SCons to build itself from the current package. # # This runs "aegis -build" to build a current scons-src-*.tar.gz # package, unpacks it in the supplied directory name, and then # starts a profiled run of an SCons build, followed by another. # This results in two profiles: # # NAME/NAME-0.prof # profile of a build-everything run # # NAME/NAME-1.prof # profile of an all-up-to-date run # # This also copies the build scons-src-*.tar.gz file to the NAME # subdirectory, and tars up everything under src/ as NAME/src.tar.gz, # so that repeated runs with different in-progress changes can serve # as their own crude version control, so you don't lose that exact # combination of features which performed best. if test X$1 = X; then echo "Must supply name!" >&2 exit 1 fi VERSION=0.90 DIR=$1 SRC="scons-src-$VERSION" SRC_TAR_GZ="${SRC}.tar.gz" B_D_SRC_TAR_GZ="build/dist/${SRC_TAR_GZ}" echo "Building ${B_D_SRC_TAR_GZ}: " `date` aegis -build ${B_D_SRC_TAR_GZ} echo "mkdir ${DIR}: " `date` mkdir ${DIR} echo "cp ${B_D_SRC_TAR_GZ} ${DIR}: " `date` cp ${B_D_SRC_TAR_GZ} ${DIR} echo "tar cf ${DIR}/src.tar.gz: " `date` tar cf ${DIR}/src.tar.gz src cd ${DIR} echo "tar zxf ${SRC_TAR_GZ}: " `date` tar zxf ${SRC_TAR_GZ} cd ${SRC} SCRIPT="src/script/scons.py" ARGS="version=$VERSION" export SCONS_LIB_DIR=`pwd`/src/engine echo "Build run starting: " `date` python $SCRIPT --profile=../$DIR-0.prof $ARGS > ../$DIR-0.log 2>&1 echo "Up-to-date run starting: " `date` python $SCRIPT --profile=../$DIR-1.prof $ARGS > ../$DIR-1.log 2>&1 echo "Finished $DIR at: " `date` scons-doc-2.3.0/bin/svn-bisect.py0000755000175000017500000000414212114661557017425 0ustar dktrkranzdktrkranz#!/usr/bin/env python # -*- Python -*- from __future__ import division import sys from math import log, ceil from optparse import OptionParser import subprocess # crack the command line parser = OptionParser( usage="%prog lower-revision upper-revision test_script [arg1 ...]", description="Binary search for a bug in a SVN checkout") (options,script_args) = parser.parse_args() # make sure we have sufficient parameters if len(script_args) < 1: parser.error("Need a lower revision") elif len(script_args) < 2: parser.error("Need an upper revision") elif len(script_args) < 3: parser.error("Need a script to run") # extract our starting values lower = int(script_args[0]) upper = int(script_args[1]) script = script_args[2:] # print an error message and quit def error(s): print >>sys.stderr, "******", s, "******" sys.exit(1) # update to the specified version and run test def testfail(revision): "Return true if test fails" print "Updating to revision", revision if subprocess.call(["svn","up","-qr",str(revision)]) != 0: m = "SVN did not update properly to revision %d" raise RuntimeError(m % revision) return subprocess.call(script,shell=False) != 0 # confirm that the endpoints are different print "****** Checking upper bracket", upper upperfails = testfail(upper) print "****** Checking lower bracket", lower lowerfails = testfail(lower) if upperfails == lowerfails: error("Upper and lower revisions must bracket the failure") # binary search for transition msg = "****** max %d revisions to test (bug bracketed by [%d,%d])" while upper-lower > 1: print msg % (ceil(log(upper-lower,2)), lower, upper) mid = (lower + upper)//2 midfails = testfail(mid) if midfails == lowerfails: lower = mid lowerfails = midfails else: upper = mid upperfails = midfails # show which revision was first to fail if upperfails != lowerfails: lower = upper print "The error was caused by revision", lower # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/bin/Command.py0000644000175000017500000000746112114661557016732 0ustar dktrkranzdktrkranz#!/usr/bin/env python # # XXX Python script template # # XXX Describe what the script does here. # import getopt import os import shlex import sys class Usage(Exception): def __init__(self, msg): self.msg = msg class CommandRunner(object): """ Representation of a command to be executed. """ def __init__(self, dictionary={}): self.subst_dictionary(dictionary) def subst_dictionary(self, dictionary): self._subst_dictionary = dictionary def subst(self, string, dictionary=None): """ Substitutes (via the format operator) the values in the specified dictionary into the specified command. The command can be an (action, string) tuple. In all cases, we perform substitution on strings and don't worry if something isn't a string. (It's probably a Python function to be executed.) """ if dictionary is None: dictionary = self._subst_dictionary if dictionary: try: string = string % dictionary except TypeError: pass return string def do_display(self, string): if isinstance(string, tuple): func = string[0] args = string[1:] s = '%s(%s)' % (func.__name__, ', '.join(map(repr, args))) else: s = self.subst(string) if not s.endswith('\n'): s += '\n' sys.stdout.write(s) sys.stdout.flush() def do_not_display(self, string): pass def do_execute(self, command): if isinstance(command, str): command = self.subst(command) cmdargs = shlex.split(command) if cmdargs[0] == 'cd': command = (os.chdir,) + tuple(cmdargs[1:]) elif cmdargs[0] == 'mkdir': command = (os.mkdir,) + tuple(cmdargs[1:]) if isinstance(command, tuple): func = command[0] args = command[1:] return func(*args) else: return os.system(command) def do_not_execute(self, command): pass display = do_display execute = do_execute def run(self, command, display=None): """ Runs this command, displaying it first. The actual display() and execute() methods we call may be overridden if we're printing but not executing, or vice versa. """ if display is None: display = command self.display(display) return self.execute(command) def main(argv=None): if argv is None: argv = sys.argv short_options = 'hnq' long_options = ['help', 'no-exec', 'quiet'] helpstr = """\ Usage: script-template.py [-hnq] -h, --help Print this help and exit -n, --no-exec No execute, just print command lines -q, --quiet Quiet, don't print command lines """ try: try: opts, args = getopt.getopt(argv[1:], short_options, long_options) except getopt.error, msg: raise Usage(msg) for o, a in opts: if o in ('-h', '--help'): print helpstr sys.exit(0) elif o in ('-n', '--no-exec'): Command.execute = Command.do_not_execute elif o in ('-q', '--quiet'): Command.display = Command.do_not_display except Usage, err: sys.stderr.write(err.msg) sys.stderr.write('use -h to get help') return 2 commands = [ ] for command in [ Command(c) for c in commands ]: status = command.run(command) if status: sys.exit(status) if __name__ == "__main__": sys.exit(main()) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/bin/objcounts.py0000644000175000017500000000572012114661557017356 0ustar dktrkranzdktrkranz#!/usr/bin/env python # # Copyright (c) 2005 The SCons Foundation # # 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. import re import sys filenames = sys.argv[1:] if len(sys.argv) != 3: print """Usage: objcounts.py file1 file2 Compare the --debug=object counts from two build logs. """ sys.exit(0) def fetch_counts(fname): contents = open(fname).read() m = re.search('\nObject counts:(\n\s[^\n]*)*', contents, re.S) lines = m.group().split('\n') list = [l.split() for l in lines if re.match('\s+\d', l)] d = {} for l in list: d[l[-1]] = list(map(int, l[:-1])) return d c1 = fetch_counts(sys.argv[1]) c2 = fetch_counts(sys.argv[2]) common = {} for k in c1.keys(): try: common[k] = (c1[k], c2[k]) except KeyError: # Transition: we added the module to the names of a bunch of # the logged objects. Assume that c1 might be from an older log # without the modules in the names, and look for an equivalent # in c2. if not '.' in k: s = '.'+k l = len(s) for k2 in c2.keys(): if k2[-l:] == s: common[k2] = (c1[k], c2[k2]) del c1[k] del c2[k2] break else: del c1[k] del c2[k] def diffstr(c1, c2): try: d = c2 - c1 except TypeError: d = '' else: if d: d = '[%+d]' % d else: d = '' return " %5s/%-5s %-8s" % (c1, c2, d) def printline(c1, c2, classname): print \ diffstr(c1[2], c2[2]) + \ diffstr(c1[3], c2[3]) + \ ' ' + classname for k in sorted(common.keys()): c = common[k] printline(c[0], c[1], k) for k in sorted(list(c1.keys())): printline(c1[k], ['--']*4, k) for k in sorted(list(c2.keys())): printline(['--']*4, c2[k], k) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/bin/memlogs.py0000644000175000017500000000326312114661557017013 0ustar dktrkranzdktrkranz#!/usr/bin/env python # # Copyright (c) 2005 The SCons Foundation # # 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. import getopt import sys filenames = sys.argv[1:] if not filenames: print """Usage: memlogs.py file [...] Summarizes the --debug=memory numbers from one or more build logs. """ sys.exit(0) fmt = "%12s %12s %12s %12s %s" print fmt % ("pre-read", "post-read", "pre-build", "post-build", "") for fname in sys.argv[1:]: lines = [l for l in open(fname).readlines() if l[:7] == 'Memory '] t = tuple([l.split()[-1] for l in lines]) + (fname,) print fmt % t # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/bin/restore.sh0000644000175000017500000000434212114661557017014 0ustar dktrkranzdktrkranz#!/usr/bin/env sh # # Simple hack script to restore __revision__, __COPYRIGHT_, 2.3.0 # and other similar variables to what gets checked in to source. This # comes in handy when people send in diffs based on the released source. # if test "X$*" = "X"; then DIRS="src test" else DIRS="$*" fi SEPARATOR="================================================================================" header() { arg_space="$1 " dots=`echo "$arg_space" | sed 's/./\./g'` echo "$SEPARATOR" | sed "s;$dots;$arg_space;" } for i in `find $DIRS -name '*.py'`; do header $i ed $i <&2 exit 1 ;; esac done shift `expr ${OPTIND} - 1` case $# in 2) project=$1 change=$2 ;; *) echo "${USAGE}" 1>&2 exit 1 ;; esac here=`pwd` AEGIS_PROJECT=$project export AEGIS_PROJECT AEGIS_CHANGE=$change export AEGIS_CHANGE module=`echo $project | sed 's|[.].*||'` baseline=`aegis -cd -bl` if test X${TMPDIR} = X; then TMPDIR=/var/tmp; fi TMP=${TMPDIR}/ae-cvs-ci.$$ mkdir ${TMP} cd ${TMP} PWD=`pwd` if test X${PWD} != X${TMP}; then echo "$0: ended up in ${PWD}, not ${TMP}" >&2 exit 1 fi fail() { set +x cd $here rm -rf ${TMP} echo "FAILED" 1>&2 exit 1 } trap "fail" 1 2 3 15 Command() { ${PRINT} "$*" ${EXECUTE} "$*" } # # Create a new CVS work area. # # Note: this assumes the module is checked-out into a directory of the # same name. Is there a way to ask CVS where is is going to put a # modules, so we can always get the "cd" right? # ${PRINT} cvs co $module ${EXECUTE} cvs co $module > LOG 2>&1 if test $? -ne 0; then cat LOG; fail; fi ${EXECUTE} cd $module # # Now we need to extract the sources from Aegis and drop them into the # CVS work area. There are two ways to do this. # # The first way is to use the generated tarball. # This has the advantage that it has the Makefile.in file in it, and # will work immediately. # # The second way is to use aetar, which will give exact sources, and # omit all derived files. This will *not* include the Makefile.in, # and so will not be readily compilable. # # gunzip < $baseline/export/${project}.tar.gz | tardy -rp ${project} | tar xf - aetar -send -comp-alg=gzip -o - | tar xzf - # # If any new directories have been created we will need to add them # to CVS before we can add the new files which we know are in them, # or they would not have been created. Do this only if the -n option # isn't used, because if it is, we won't have actually checked out the # source and we'd erroneously report that all of them need to be added. # if test "X${EXECUTE}" != "X:" then find . \( -name CVS -o -name Attic \) -prune -o -type d -print | xargs --max-args=1 | while read dir do if [ ! -d "$dir/CVS" ] then Command cvs add "$dir" fi done fi # # Use the Aegis meta-data to perform some CVS commands that CVS can't # figure out for itself. # aegis -l cf -unf | sed 's| -> [0-9][0-9.]*||' | while read usage action rev filename do if test "x$filename" = "x" then filename="$rev" fi case $action in create) Command cvs add $filename ;; remove) Command rm -f $filename Command cvs remove $filename ;; *) ;; esac done # # Extract the brief description. We'd like to do this using aesub # or something, like so: # # message=`aesub '${version} - ${change description}'` # # but the expansion of ${change description} has a lame hard-coded max of # 80 characters, so we have to do this by hand. (This has the slight # benefit of preserving backslashes in front of any double-quotes in # the text; that will have to be handled if we go back to using aesub.) # description=`aegis -ca -l | sed -n 's/brief_description = "\(.*\)";$/\1/p'` version=`aesub '${version}'` message="$version - $description" # # Now commit all the changes. # Command cvs -q commit -m \"$message\" # # All done. Clean up and go home. # cd $here rm -rf ${TMP} exit 0 scons-doc-2.3.0/bin/scons-diff.py0000644000175000017500000001355412114661557017407 0ustar dktrkranzdktrkranz#!/usr/bin/env python # # scons-diff.py - diff-like utility for comparing SCons trees # # This supports most common diff options (with some quirks, like you can't # just say -c and have it use a default value), but canonicalizes the # various version strings within the file like __revision__, __build__, # etc. so that you can diff trees without having to ignore changes in # version lines. # import difflib import getopt import os.path import re import sys Usage = """\ Usage: scons-diff.py [OPTIONS] dir1 dir2 Options: -c NUM, --context=NUM Print NUM lines of copied context. -h, --help Print this message and exit. -n Don't canonicalize SCons lines. -q, --quiet Print only whether files differ. -r, --recursive Recursively compare found subdirectories. -s Report when two files are the same. -u NUM, --unified=NUM Print NUM lines of unified context. """ opts, args = getopt.getopt(sys.argv[1:], 'c:dhnqrsu:', ['context=', 'help', 'recursive', 'unified=']) diff_type = None edit_type = None context = 2 recursive = False report_same = False diff_options = [] def diff_line(left, right): if diff_options: opts = ' ' + ' '.join(diff_options) else: opts = '' print 'diff%s %s %s' % (opts, left, right) for o, a in opts: if o in ('-c', '-u'): diff_type = o context = int(a) diff_options.append(o) elif o in ('-h', '--help'): print Usage sys.exit(0) elif o in ('-n'): diff_options.append(o) edit_type = o elif o in ('-q'): diff_type = o diff_line = lambda l, r: None elif o in ('-r', '--recursive'): recursive = True diff_options.append(o) elif o in ('-s'): report_same = True try: left, right = args except ValueError: sys.stderr.write(Usage) sys.exit(1) def quiet_diff(a, b, fromfile='', tofile='', fromfiledate='', tofiledate='', n=3, lineterm='\n'): """ A function with the same calling signature as difflib.context_diff (diff -c) and difflib.unified_diff (diff -u) but which prints output like the simple, unadorned 'diff" command. """ if a == b: return [] else: return ['Files %s and %s differ\n' % (fromfile, tofile)] def simple_diff(a, b, fromfile='', tofile='', fromfiledate='', tofiledate='', n=3, lineterm='\n'): """ A function with the same calling signature as difflib.context_diff (diff -c) and difflib.unified_diff (diff -u) but which prints output like the simple, unadorned 'diff" command. """ sm = difflib.SequenceMatcher(None, a, b) def comma(x1, x2): return x1+1 == x2 and str(x2) or '%s,%s' % (x1+1, x2) result = [] for op, a1, a2, b1, b2 in sm.get_opcodes(): if op == 'delete': result.append("%sd%d\n" % (comma(a1, a2), b1)) result.extend(['< ' + l for l in a[a1:a2]]) elif op == 'insert': result.append("%da%s\n" % (a1, comma(b1, b2))) result.extend(['> ' + l for l in b[b1:b2]]) elif op == 'replace': result.append("%sc%s\n" % (comma(a1, a2), comma(b1, b2))) result.extend(['< ' + l for l in a[a1:a2]]) result.append('---\n') result.extend(['> ' + l for l in b[b1:b2]]) return result diff_map = { '-c' : difflib.context_diff, '-q' : quiet_diff, '-u' : difflib.unified_diff, } diff_function = diff_map.get(diff_type, simple_diff) baseline_re = re.compile('(# |@REM )/home/\S+/baseline/') comment_rev_re = re.compile('(# |@REM )(\S+) 0.96.[CD]\d+ \S+ \S+( knight)') revision_re = re.compile('__revision__ = "[^"]*"') build_re = re.compile('__build__ = "[^"]*"') date_re = re.compile('__date__ = "[^"]*"') def lines_read(file): return open(file).readlines() def lines_massage(file): text = open(file).read() text = baseline_re.sub('\\1', text) text = comment_rev_re.sub('\\1\\2\\3', text) text = revision_re.sub('__revision__ = "bin/scons-diff.py"', text) text = build_re.sub('__build__ = "0.96.92.DXXX"', text) text = date_re.sub('__date__ = "2006/08/25 02:59:00"', text) return text.splitlines(1) lines_map = { '-n' : lines_read, } lines_function = lines_map.get(edit_type, lines_massage) def do_diff(left, right, diff_subdirs): if os.path.isfile(left) and os.path.isfile(right): diff_file(left, right) elif not os.path.isdir(left): diff_file(left, os.path.join(right, os.path.split(left)[1])) elif not os.path.isdir(right): diff_file(os.path.join(left, os.path.split(right)[1]), right) elif diff_subdirs: diff_dir(left, right) def diff_file(left, right): l = lines_function(left) r = lines_function(right) d = diff_function(l, r, left, right, context) try: text = ''.join(d) except IndexError: sys.stderr.write('IndexError diffing %s and %s\n' % (left, right)) else: if text: diff_line(left, right) print text, elif report_same: print 'Files %s and %s are identical' % (left, right) def diff_dir(left, right): llist = os.listdir(left) rlist = os.listdir(right) u = {} for l in llist: u[l] = 1 for r in rlist: u[r] = 1 for x in sorted([ x for x in u.keys() if x[-4:] != '.pyc' ]): if x in llist: if x in rlist: do_diff(os.path.join(left, x), os.path.join(right, x), recursive) else: print 'Only in %s: %s' % (left, x) else: print 'Only in %s: %s' % (right, x) do_diff(left, right, True) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/bin/ae2cvs0000644000175000017500000003462012114661557016105 0ustar dktrkranzdktrkranz#! /usr/bin/env perl $revision = "src/ae2cvs.pl 0.04.D001 2005/08/14 15:13:36 knight"; $copyright = "Copyright 2001, 2002, 2003, 2004, 2005 Steven Knight."; # # All rights reserved. This program is free software; you can # redistribute and/or modify under the same terms as Perl itself. # use strict; use File::Find; use File::Spec; use Pod::Usage (); use vars qw( @add_list @args @cleanup @copy_list @libraries @mkdir_list @remove_list %seen_dir $ae_copy $aedir $aedist $cnum $comment $commit $common $copyright $cvs_command $cvsmod $cvsroot $delta $description $exec $help $indent $infile $proj $pwd $quiet $revision $summary $usedir $usepath ); $aedist = 1; $cvsroot = undef; $exec = undef; $indent = ""; sub version { print "ae2cvs: $revision\n"; print "$copyright\n"; exit 0; } { use Getopt::Long; Getopt::Long::Configure('no_ignore_case'); my $ret = GetOptions ( "aedist" => sub { $aedist = 1 }, "aegis" => sub { $aedist = 0 }, "change=i" => \$cnum, "d=s" => \$cvsroot, "file=s" => \$infile, "help|?" => \$help, "library=s" => \@libraries, "module=s" => \$cvsmod, "noexecute" => sub { $exec = 0 }, "project=s" => \$proj, "quiet" => \$quiet, "usedir=s" => \$usedir, "v|version" => \&version, "x|execute" => sub { $exec++ if ! defined $exec || $exec != 0 }, "X|EXECUTE" => sub { $exec = 2 if ! defined $exec || $exec != 0 }, ); Pod::Usage::pod2usage(-verbose => 0) if $help || ! $ret; $exec = 0 if ! defined $exec; } $cvs_command = $cvsroot ? "cvs -d $cvsroot -Q" : "cvs -Q"; # # Wrap up the $quiet logic in one place. # sub printit { return if $quiet; my $string = join('', @_); $string =~ s/^/$indent/msg if $indent; print $string; } # # Wrappers for executing various builtin Perl functions in # accordance with the -n, -q and -x options. # sub execute { my $cmd = shift; printit "$cmd\n"; if (! $exec) { return 1; } ! system($cmd); } sub _copy { my ($source, $dest) = @_; printit "cp $source $dest\n"; if ($exec) { use File::Copy; copy($source, $dest); } } sub _chdir { my $dir = shift; printit "cd $dir\n"; if ($exec) { chdir($dir) || die "ae2cvs: could not chdir($dir): $!"; } } sub _mkdir { my $dir = shift; printit "mkdir $dir\n"; if ($exec) { mkdir($dir); } } # # Put some input data through an external filter and capture the output. # sub filter { my ($cmd, $input) = @_; use FileHandle; use IPC::Open2; my $pid = open2(*READ, *WRITE, $cmd) || die "Cannot exec '$cmd': $!\n"; print WRITE $input; close(WRITE); my $output = join('', ); close(READ); return $output; } # # Parse a change description, in both 'aegis -l cd" and "aedist" formats. # # Returns an array containing the project name, the change number # (if any), the delta number (if any), the SUMMARY, the DESCRIPTION # and the lines describing the files in the change. # sub parse_change { my $output = shift; my ($p, $c, $d, $c_or_d, $sum, $desc, $filesection, @flines); # The project name line comes after NAME in "aegis -l cd" format, # and PROJECT in "aedist" format. In both cases, the project name # and the change/delta name are separated a comma. ($p = $output) =~ s/(?:NAME|PROJECT)\n([^\n]*)\n.*/$1/ms; ($p, $c_or_d) = (split(/,/, $p)); # In "aegis -l cd" format, the project name actually comes after # the string "Project" and is itself enclosed in double quotes. $p =~ s/Project "([^"]*)"/$1/; # The change or delta string was the right-hand side of the comma. # "aegis -l cd" format spells it "Change 123." or "Delta 123." while # "aedist" format spells it "change 123." if ($c_or_d =~ /\s*[Cc]hange (\d+).*/) { $c = $1 }; if ($c_or_d =~ /\s*[Dd]elta (\d+).*/) { $d = $1 }; # The SUMMARY line is always followed the DESCRIPTION section. # It seems to always be a single line, but we grab everything in # between just in case. ($sum = $output) =~ s/.*\nSUMMARY\n//ms; $sum =~ s/\nDESCRIPTION\n.*//ms; # The DESCRIPTION section is followed ARCHITECTURE in "aegis -l cd" # format and by CAUSE in "aedist" format. Explicitly under it if the # string is only "none," which means they didn't supply a description. ($desc = $output) =~ s/.*\nDESCRIPTION\n//ms; $desc =~ s/\n(ARCHITECTURE|CAUSE)\n.*//ms; chomp($desc); if ($desc eq "none" || $desc eq "none\n") { $desc = undef } # The FILES section is followed by HISTORY in "aegis -l cd" format. # It seems to be the last section in "aedist" format, but stripping # a non-existent HISTORY section doesn't hurt. ($filesection = $output) =~ s/.*\nFILES\n//ms; $filesection =~ s/\nHISTORY\n.*//ms; @flines = split(/\n/, $filesection); ($p, $c, $d, $sum, $desc, \@flines) } # # # $pwd = Cwd::cwd(); # # Fetch the file list either from our aedist input # or directly from the project itself. # my @filelines; if ($aedist) { local ($/); undef $/; my $infile_redir = ""; my $contents; if (! $infile || $infile eq "-") { $contents = join('', ); } else { open(FILE, "<$infile") || die "Cannot open '$infile': $!\n"; binmode(FILE); $contents = join('', ); close(FILE); if (! File::Spec->file_name_is_absolute($infile)) { $infile = File::Spec->catfile($pwd, $infile); } $infile_redir = " < $infile"; } my $output = filter("aedist -l -unf", $contents); my ($p, $c, $d, $s, $desc, $fl) = parse_change($output); $proj = $p if ! defined $proj; $summary = $s; $description = $desc; @filelines = @$fl; if (! $exec) { printit qq(MYTMP="/tmp/ae2cvs-ae.\$\$"\n), qq(mkdir \$MYTMP\n), qq(cd \$MYTMP\n); printit q(perl -MMIME::Base64 -e 'undef $/; ($c = <>) =~ s/.*\n\n//ms; print decode_base64($c)'), $infile_redir, qq( | zcat), qq( | cpio -i -d --quiet\n); $aedir = '$MYTMP'; push(@cleanup, $aedir); } else { $aedir = File::Spec->catfile(File::Spec->tmpdir, "ae2cvs-ae.$$"); _mkdir($aedir); push(@cleanup, $aedir); _chdir($aedir); use MIME::Base64; $contents =~ s/.*\n\n//ms; $contents = filter("zcat", decode_base64($contents)); open(CPIO, "|cpio -i -d --quiet"); print CPIO $contents; close(CPIO); } $ae_copy = sub { foreach my $dest (@_) { my $source = File::Spec->catfile($aedir, "src", $dest); execute(qq(cp $source $dest)); } } } else { $cnum = $ENV{AEGIS_CHANGE} if ! defined $cnum; $proj = $ENV{AEGIS_PROJECT} if ! defined $proj; $common = "-lib " . join(" -lib ", @libraries) if @libraries; $common = "$common -proj $proj" if $proj; my $output = `aegis -l cd $cnum -unf $common`; my ($p, $c, $d, $s, $desc, $fl) = parse_change($output); $delta = $d; $summary = $s; $description = $desc; @filelines = @$fl; if (! $delta) { print STDERR "ae2cvs: No delta number, exiting.\n"; exit 1; } $ae_copy = sub { execute(qq(aegis -cp -ind -delta $delta $common @_)); } } if (! $usedir) { $usedir = File::Spec->catfile(File::Spec->tmpdir, "ae2cvs.$$"); _mkdir($usedir); push(@cleanup, $usedir); } _chdir($usedir); $usepath = $usedir; if (! File::Spec->file_name_is_absolute($usepath)) { $usepath = File::Spec->catfile($pwd, $usepath); } if (! -d File::Spec->catfile($usedir, "CVS")) { $cvsmod = (split(/\./, $proj))[0] if ! defined $cvsmod; execute(qq($cvs_command co $cvsmod)); _chdir($cvsmod); $usepath = File::Spec->catfile($usepath, $cvsmod); } # # Figure out what we have to do to accomplish everything. # foreach (@filelines) { my @arr = split(/\s+/, $_); my $type = shift @arr; # source / test my $act = shift @arr; # modify / create my $file = pop @arr; if ($act eq "create" or $act eq "modify") { # XXX Do we really only need to do this for # ($act eq "create") files? my (undef, $dirs, undef) = File::Spec->splitpath($file); my $absdir = $usepath; my $reldir; my $d; foreach $d (File::Spec->splitdir($dirs)) { next if ! $d; $absdir = File::Spec->catdir($absdir, $d); $reldir = $reldir ? File::Spec->catdir($reldir, $d) : $d; if (! -d $absdir && ! $seen_dir{$reldir}) { $seen_dir{$reldir} = 1; push(@mkdir_list, $reldir); } } push(@copy_list, $file); if ($act eq "create") { push(@add_list, $file); } } elsif ($act eq "remove") { push(@remove_list, $file); } else { print STDERR "Unsure how to '$act' the '$file' file.\n"; } } # Now go through and mkdir() the directories, # adding them to the CVS tree as we do. if (@mkdir_list) { if (! $exec) { printit qq(# The following "mkdir" and "cvs -Q add" calls are not\n), qq(# necessary for any directories that already exist in the\n), qq(# CVS tree but which aren't present locally.\n); } foreach (@mkdir_list) { if (! $exec) { printit qq(if test ! -d $_; then\n); $indent = " "; } _mkdir($_); execute(qq($cvs_command add $_)); if (! $exec) { $indent = ""; printit qq(fi\n); } } if (! $exec) { printit qq(# End of directory creation.\n); } } # Copy in any files in the change, before we try to "cvs add" them. $ae_copy->(@copy_list) if @copy_list; if (@add_list) { execute(qq($cvs_command add @add_list)); } if (@remove_list) { execute(qq(rm -f @remove_list)); execute(qq($cvs_command remove @remove_list)); } # Last, commit the whole bunch. $comment = $summary; $comment .= "\n" . $description if $description; $commit = qq($cvs_command commit -m '$comment' .); if ($exec == 1) { printit qq(# Execute the following to commit the changes:\n), qq(# $commit\n); } else { execute($commit); } _chdir($pwd); # # Directory cleanup. # sub END { my $dir; foreach $dir (@cleanup) { printit "rm -rf $dir\n"; if ($exec) { finddepth(sub { # print STDERR "unlink($_)\n" if (!-d $_); # print STDERR "rmdir($_)\n" if (-d $_ && $_ ne "."); unlink($_) if (!-d $_); rmdir($_) if (-d $_ && $_ ne "."); 1; }, $dir); rmdir($dir) || print STDERR "Could not remove $dir: $!\n"; } } } __END__; =head1 NAME ae2cvs - convert an Aegis change set to CVS commands =head1 SYNOPSIS ae2cvs [-aedist|-aegis] [-c change] [-d cvs_root] [-f file] [-l lib] [-m module] [-n] [-p proj] [-q] [-u dir] [-v] [-x] [-X] -aedist use aedist format from input (default) -aegis query aegis repository directly -c change change number -d cvs_root CVS root directory -f file read aedist from file ('-' == stdin) -l lib Aegis library directory -m module CVS module -n no execute -p proj project name -q quiet, don't print commands -u dir use dir for CVS checkin -v print version string and exit -x execute the commands, but don't commit; two or more -x options commit changes -X execute the commands and commit changes =head1 DESCRIPTION The C utility can convert an Aegis change into a set of CVS (and other) commands to make the corresponding change(s) to a carbon-copy CVS repository. This can be used to keep a front-end CVS repository in sync with changes made to an Aegis project, either manually or automatically using the C attribute of the Aegis project. By default, C makes no changes to any software, and only prints out the necessary commands. These commands can be examined first for safety, and then fed to any Bourne shell variant (sh, ksh, or bash) to make the actual CVS changes. An option exists to have C execute the commands directly. =head1 OPTIONS The C utility supports the following options: =over 4 =item -aedist Reads an aedist change set. By default, the change set is read from standard input, or a file specified with the C<-f> option. =item -aegis Reads the change directly from the Aegis repository by executing the proper C commands. =item -c change Specify the Aegis change number to be used. The value of the C environment variable is used by default. =item -d cvsroot Specify the CVS root directory to be used. This option is passed explicitly to each executed C command. The default behavior is to omit any C<-d> options and let the executed C commands use the C environment variable as they normally would. =item -f file Reads the aedist change set from the specified C, or from standard input if C is C<'-'>. =item -l lib Specifies an Aegis library directory to be searched for global states files and user state files. =item -m module Specifies the name of the CVS module to be brought up-to-date. The default is to use the Aegis project name, minus any branch numbers; for example, given an Aegis project name of C, the default CVS module name is C. =item -n No execute. Commands are printed (including a command for a final commit of changes), but not executed. This is the default. =item -p proj Specifies the name of the Aegis project from which this change is taken. The value of the C environment variable is used by default. =item -q Quiet. Commands are not printed. =item -u dir Use the already checked-out CVS tree that exists at C for the checkins and commits. The default is to use a separately-created temporary directory. =item -v Print the version string and exit. =item -x Execute the commands to bring the CVS repository up to date, except for the final commit of the changes. Two or more C<-x> options will cause the change to be committed. =item -X Execute the commands to bring the CVS repository up to date, including the final commit of the changes. =back =head1 ENVIRONMENT VARIABLES =over 4 =item AE2CVS_FLAGS Specifies any options to be used to initialize the C utility. Options on the command line override these values. =back =head1 AUTHOR Steven Knight (knight at baldmt dot com) =head1 BUGS If errors occur during the execution of the Aegis or CVS commands, and the -X option is used, a partial change (consisting of those files for which the command(s) succeeded) will be committed. It would be safer to generate code to detect the error and print a warning. When a file has been deleted in Aegis, the standard whiteout file can cause a regex failure in this script. It doesn't necessarily happen all the time, though, so this needs more investigation. =head1 TODO Add an explicit test for using ae2cvs in the Aegis integrate_pass_notify_command field to support fully keeping a repository in sync automatically. =head1 COPYRIGHT Copyright 2001, 2002, 2003, 2004, 2005 Steven Knight. =head1 SEE ALSO aegis(1), cvs(1) scons-doc-2.3.0/bin/makedocs0000644000175000017500000000062012114661557016501 0ustar dktrkranzdktrkranz#! /bin/sh # This script uses HappyDoc to create the HTML class documentation for # SCons. It must be run from the src/engine directory. base=`basename $PWD` if [ "$base" != "engine" ]; then echo "You must run this script from the engine directory." exit fi DEVDIR=../../doc/developer if [ ! -d $DEVDIR ]; then mkdir $DEVDIR fi SRCFILE=../../bin/files happydoc -d $DEVDIR `cat $SRCFILE` scons-doc-2.3.0/bin/linecount.py0000644000175000017500000001020412114661557017341 0ustar dktrkranzdktrkranz#!/usr/bin/env python # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # Count statistics about SCons test and source files. This must be run # against a fully-populated tree (for example, one that's been freshly # checked out). # # A test file is anything under the src/ directory that begins with # 'test_' or ends in 'Tests.py', or anything under the test/ directory # that ends in '.py'. Note that runtest.py script does *not*, by default, # consider the files that begin with 'test_' to be tests, because they're # tests of SCons packaging and installation, not functional tests of # SCons code. # # A source file is anything under the src/engine/ or src/script/ # directories that ends in '.py' but does NOT begin with 'test_' # or end in 'Tests.py'. # # We report the number of tests and sources, the total number of lines # in each category, the number of non-blank lines, and the number of # non-comment lines. The last figure (non-comment) lines is the most # interesting one for most purposes. from __future__ import division __revision__ = "bin/linecount.py 2013/03/03 09:48:35 garyo" import os.path fmt = "%-16s %5s %7s %9s %11s %11s" class Collection(object): def __init__(self, name, files=None, pred=None): self._name = name if files is None: files = [] self.files = files if pred is None: pred = lambda x: True self.pred = pred def __call__(self, fname): return self.pred(fname) def __len__(self): return len(self.files) def collect(self, directory): for dirpath, dirnames, filenames in os.walk(directory): try: dirnames.remove('.svn') except ValueError: pass self.files.extend([ os.path.join(dirpath, f) for f in filenames if self.pred(f) ]) def lines(self): try: return self._lines except AttributeError: self._lines = lines = [] for file in self.files: file_lines = open(file).readlines() lines.extend([s.lstrip() for s in file_lines]) return lines def non_blank(self): return [s for s in self.lines() if s != ''] def non_comment(self): return [s for s in self.lines() if s == '' or s[0] != '#'] def non_blank_non_comment(self): return [s for s in self.lines() if s != '' and s[0] != '#'] def printables(self): return (self._name + ':', len(self.files), len(self.lines()), len(self.non_blank()), len(self.non_comment()), len(self.non_blank_non_comment())) def is_Tests_py(x): return x[-8:] == 'Tests.py' def is_test_(x): return x[:5] == 'test_' def is_python(x): return x[-3:] == '.py' def is_source(x): return is_python(x) and not is_Tests_py(x) and not is_test_(x) src_Tests_py_tests = Collection('src/ *Tests.py', pred=is_Tests_py) src_test_tests = Collection('src/ test_*.py', pred=is_test_) test_tests = Collection('test/ tests', pred=is_python) sources = Collection('sources', pred=is_source) src_Tests_py_tests.collect('src') src_test_tests.collect('src') test_tests.collect('test') sources.collect('src/engine') sources.collect('src/script') src_tests = Collection('src/ tests', src_Tests_py_tests.files + src_test_tests.files) all_tests = Collection('all tests', src_tests.files + test_tests.files) def ratio(over, under): return "%.2f" % (float(len(over)) / float(len(under))) print fmt % ('', '', '', '', '', 'non-blank') print fmt % ('', 'files', 'lines', 'non-blank', 'non-comment', 'non-comment') print print fmt % src_Tests_py_tests.printables() print fmt % src_test_tests.printables() print print fmt % src_tests.printables() print fmt % test_tests.printables() print print fmt % all_tests.printables() print fmt % sources.printables() print print fmt % ('ratio:', ratio(all_tests, sources), ratio(all_tests.lines(), sources.lines()), ratio(all_tests.non_blank(), sources.non_blank()), ratio(all_tests.non_comment(), sources.non_comment()), ratio(all_tests.non_blank_non_comment(), sources.non_blank_non_comment()) ) scons-doc-2.3.0/bin/SConsDoc.py0000644000175000017500000002675412114661557017035 0ustar dktrkranzdktrkranz#!/usr/bin/env python # # Module for handling SCons documentation processing. # __doc__ = """ This module parses home-brew XML files that document various things in SCons. Right now, it handles Builders, functions, construction variables, and Tools, but we expect it to get extended in the future. In general, you can use any DocBook tag in the input, and this module just adds processing various home-brew tags to try to make life a little easier. Builder example: This is the summary description of an SCons Builder. It will get placed in the man page, and in the appropriate User's Guide appendix. The name of any builder may be interpolated anywhere in the document by specifying the &b-BUILDER; element. It need not be on a line by itself. Unlike normal XML, blank lines are significant in these descriptions and serve to separate paragraphs. They'll get replaced in DocBook output with appropriate tags to indicate a new paragraph. print "this is example code, it will be offset and indented" Function example: (arg1, arg2, key=value) This is the summary description of an SCons function. It will get placed in the man page, and in the appropriate User's Guide appendix. The name of any builder may be interpolated anywhere in the document by specifying the &f-FUNCTION; element. It need not be on a line by itself. Unlike normal XML, blank lines are significant in these descriptions and serve to separate paragraphs. They'll get replaced in DocBook output with appropriate tags to indicate a new paragraph. print "this is example code, it will be offset and indented" Construction variable example: This is the summary description of a construction variable. It will get placed in the man page, and in the appropriate User's Guide appendix. The name of any construction variable may be interpolated anywhere in the document by specifying the &t-VARIABLE; element. It need not be on a line by itself. Unlike normal XML, blank lines are significant in these descriptions and serve to separate paragraphs. They'll get replaced in DocBook output with appropriate tags to indicate a new paragraph. print "this is example code, it will be offset and indented" Tool example: This is the summary description of an SCons Tool. It will get placed in the man page, and in the appropriate User's Guide appendix. The name of any tool may be interpolated anywhere in the document by specifying the &t-TOOL; element. It need not be on a line by itself. Unlike normal XML, blank lines are significant in these descriptions and serve to separate paragraphs. They'll get replaced in DocBook output with appropriate tags to indicate a new paragraph. print "this is example code, it will be offset and indented" """ import imp import os.path import re import sys import xml.sax.handler class Item(object): def __init__(self, name): self.name = name self.sort_name = name.lower() if self.sort_name[0] == '_': self.sort_name = self.sort_name[1:] self.summary = [] self.sets = None self.uses = None def cmp_name(self, name): if name[0] == '_': name = name[1:] return name.lower() def __cmp__(self, other): return cmp(self.sort_name, other.sort_name) class Builder(Item): pass class Function(Item): def __init__(self, name): super(Function, self).__init__(name) self.arguments = [] class Tool(Item): def __init__(self, name): Item.__init__(self, name) self.entity = self.name.replace('+', 'X') class ConstructionVariable(Item): pass class Chunk(object): def __init__(self, tag, body=None): self.tag = tag if not body: body = [] self.body = body def __str__(self): body = ''.join(self.body) return "<%s>%s\n" % (self.tag, body, self.tag) def append(self, data): self.body.append(data) class Arguments(object): def __init__(self, signature, body=None): if not body: body = [] self.body = body self.signature = signature def __str__(self): s = ''.join(self.body).strip() result = [] for m in re.findall('([a-zA-Z/_]+|[^a-zA-Z/_]+)', s): if ' ' in m: m = '"%s"' % m result.append(m) return ' '.join(result) def append(self, data): self.body.append(data) class Summary(object): def __init__(self): self.body = [] self.collect = [] def append(self, data): self.collect.append(data) def end_para(self): text = ''.join(self.collect) paras = text.split('\n\n') if paras == ['\n']: return if paras[0] == '': self.body.append('\n') paras = paras[1:] paras[0] = '\n' + paras[0] if paras[-1] == '': paras = paras[:-1] paras[-1] = paras[-1] + '\n' last = '\n' else: last = None sep = None for p in paras: c = Chunk("para", p) if sep: self.body.append(sep) self.body.append(c) sep = '\n' if last: self.body.append(last) def begin_chunk(self, chunk): self.end_para() self.collect = chunk def end_chunk(self): self.body.append(self.collect) self.collect = [] class SConsDocHandler(xml.sax.handler.ContentHandler, xml.sax.handler.ErrorHandler): def __init__(self): self._start_dispatch = {} self._end_dispatch = {} keys = list(self.__class__.__dict__.keys()) start_tag_method_names = [k for k in keys if k[:6] == 'start_'] end_tag_method_names = [k for k in keys if k[:4] == 'end_'] for method_name in start_tag_method_names: tag = method_name[6:] self._start_dispatch[tag] = getattr(self, method_name) for method_name in end_tag_method_names: tag = method_name[4:] self._end_dispatch[tag] = getattr(self, method_name) self.stack = [] self.collect = [] self.current_object = [] self.builders = {} self.functions = {} self.tools = {} self.cvars = {} def startElement(self, name, attrs): try: start_element_method = self._start_dispatch[name] except KeyError: self.characters('<%s>' % name) else: start_element_method(attrs) def endElement(self, name): try: end_element_method = self._end_dispatch[name] except KeyError: self.characters('' % name) else: end_element_method() # # def characters(self, chars): self.collect.append(chars) def begin_collecting(self, chunk): self.collect = chunk def end_collecting(self): self.collect = [] def begin_chunk(self): pass def end_chunk(self): pass # # # def begin_xxx(self, obj): self.stack.append(self.current_object) self.current_object = obj def end_xxx(self): self.current_object = self.stack.pop() # # # def start_scons_doc(self, attrs): pass def end_scons_doc(self): pass def start_builder(self, attrs): name = attrs.get('name') try: builder = self.builders[name] except KeyError: builder = Builder(name) self.builders[name] = builder self.begin_xxx(builder) def end_builder(self): self.end_xxx() def start_scons_function(self, attrs): name = attrs.get('name') try: function = self.functions[name] except KeyError: function = Function(name) self.functions[name] = function self.begin_xxx(function) def end_scons_function(self): self.end_xxx() def start_tool(self, attrs): name = attrs.get('name') try: tool = self.tools[name] except KeyError: tool = Tool(name) self.tools[name] = tool self.begin_xxx(tool) def end_tool(self): self.end_xxx() def start_cvar(self, attrs): name = attrs.get('name') try: cvar = self.cvars[name] except KeyError: cvar = ConstructionVariable(name) self.cvars[name] = cvar self.begin_xxx(cvar) def end_cvar(self): self.end_xxx() def start_arguments(self, attrs): arguments = Arguments(attrs.get('signature', "both")) self.current_object.arguments.append(arguments) self.begin_xxx(arguments) self.begin_collecting(arguments) def end_arguments(self): self.end_xxx() def start_summary(self, attrs): summary = Summary() self.current_object.summary = summary self.begin_xxx(summary) self.begin_collecting(summary) def end_summary(self): self.current_object.end_para() self.end_xxx() def start_example(self, attrs): example = Chunk("programlisting") self.current_object.begin_chunk(example) def end_example(self): self.current_object.end_chunk() def start_uses(self, attrs): self.begin_collecting([]) def end_uses(self): self.current_object.uses = sorted(''.join(self.collect).split()) self.end_collecting() def start_sets(self, attrs): self.begin_collecting([]) def end_sets(self): self.current_object.sets = sorted(''.join(self.collect).split()) self.end_collecting() # Stuff for the ErrorHandler portion. def error(self, exception): linenum = exception._linenum - self.preamble_lines sys.stderr.write('%s:%d:%d: %s (error)\n' % (self.filename, linenum, exception._colnum, ''.join(exception.args))) def fatalError(self, exception): linenum = exception._linenum - self.preamble_lines sys.stderr.write('%s:%d:%d: %s (fatalError)\n' % (self.filename, linenum, exception._colnum, ''.join(exception.args))) def set_file_info(self, filename, preamble_lines): self.filename = filename self.preamble_lines = preamble_lines # lifted from Ka-Ping Yee's way cool pydoc module. def importfile(path): """Import a Python source file or compiled file given its path.""" magic = imp.get_magic() file = open(path, 'r') if file.read(len(magic)) == magic: kind = imp.PY_COMPILED else: kind = imp.PY_SOURCE file.close() filename = os.path.basename(path) name, ext = os.path.splitext(filename) file = open(path, 'r') try: module = imp.load_module(name, file, path, (ext, 'r', kind)) except ImportError, e: sys.stderr.write("Could not import %s: %s\n" % (path, e)) return None file.close() return module # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/bin/import-test.py0000644000175000017500000000522012114661557017632 0ustar dktrkranzdktrkranz#!/usr/bin/env python # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # tree2test.py - turn a directory tree into TestSCons code # # A quick script for importing directory hierarchies containing test # cases that people supply (typically in a .zip or .tar.gz file) into a # TestSCons.py script. No error checking or options yet, it just walks # the first command-line argument (assumed to be the directory containing # the test case) and spits out code looking like the following: # # test.subdir(['sub1'], # ['sub1', 'sub2']) # # test.write(['sub1', 'file1'], """\ # contents of file1 # """) # # test.write(['sub1', 'sub2', 'file2'], """\ # contents of file2 # """) # # There's no massaging of contents, so any files that themselves contain # """ triple-quotes will need to have their contents edited by hand. # __revision__ = "bin/import-test.py 2013/03/03 09:48:35 garyo" import os.path import sys directory = sys.argv[1] Top = None TopPath = None class Dir(object): def __init__(self, path): self.path = path self.entries = {} def call_for_each_entry(self, func): for name in sorted(self.entries.keys()): func(name, self.entries[name]) def lookup(dirname): global Top, TopPath if not Top: Top = Dir([]) TopPath = dirname + os.sep return Top dirname = dirname.replace(TopPath, '') dirs = dirname.split(os.sep) t = Top for d in dirs[:-1]: t = t.entries[d] node = t.entries[dirs[-1]] = Dir(dirs) return node def collect_dirs(l, dir): if dir.path: l.append(dir.path) def recurse(n, d): if d: collect_dirs(l, d) dir.call_for_each_entry(recurse) def print_files(dir): def print_a_file(n, d): if not d: l = dir.path + [n] sys.stdout.write('\ntest.write(%s, """\\\n' % l) p = os.path.join(directory, *l) sys.stdout.write(open(p, 'r').read()) sys.stdout.write('""")\n') dir.call_for_each_entry(print_a_file) def recurse(n, d): if d: print_files(d) dir.call_for_each_entry(recurse) for dirpath, dirnames, filenames in os.walk(directory): dir = lookup(dirpath) for f in fnames: dir.entries[f] = None subdir_list = [] collect_dirs(subdir_list, Top) subdir_list = [ str(l) for l in subdir_list ] sys.stdout.write('test.subdir(' + ',\n '.join(subdir_list) + ')\n') print_files(Top) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/bin/files0000644000175000017500000000451212114661557016021 0ustar dktrkranzdktrkranz./SCons/Action.py ./SCons/Builder.py ./SCons/Conftest.py ./SCons/Debug.py ./SCons/Defaults.py ./SCons/Environment.py ./SCons/Errors.py ./SCons/Executor.py ./SCons/Job.py ./SCons/Node/Alias.py ./SCons/Node/FS.py ./SCons/Node/Python.py ./SCons/Node/__init__.py ./SCons/Options/__init__.py ./SCons/Options/BoolOption.py ./SCons/Options/EnumOption.py ./SCons/Options/ListOption.py ./SCons/Options/PackageOption.py ./SCons/Options/PathOption.py ./SCons/Platform/__init__.py ./SCons/Platform/aix.py ./SCons/Platform/cygwin.py ./SCons/Platform/hpux.py ./SCons/Platform/irix.py ./SCons/Platform/os2.py ./SCons/Platform/posix.py ./SCons/Platform/sunos.py ./SCons/Platform/win32.py ./SCons/Scanner/C.py ./SCons/Scanner/D.py ./SCons/Scanner/Fortran.py ./SCons/Scanner/IDL.py ./SCons/Scanner/Prog.py ./SCons/Scanner/__init__.py ./SCons/Script/SConscript.py ./SCons/Script/__init__.py ./SCons/Sig/MD5.py ./SCons/Sig/TimeStamp.py ./SCons/Sig/__init__.py ./SCons/Taskmaster.py ./SCons/Tool/__init__.py ./SCons/Tool/aixc++.py ./SCons/Tool/aixcc.py ./SCons/Tool/aixf77.py ./SCons/Tool/aixlink.py ./SCons/Tool/ar.py ./SCons/Tool/as.py ./SCons/Tool/bcc32.py ./SCons/Tool/c++.py ./SCons/Tool/cc.py ./SCons/Tool/CVS.py ./SCons/Tool/dmd.py ./SCons/Tool/default.py ./SCons/Tool/dvipdf.py ./SCons/Tool/dvips.py ./SCons/Tool/f77.py ./SCons/Tool/g++.py ./SCons/Tool/g77.py ./SCons/Tool/gas.py ./SCons/Tool/gcc.py ./SCons/Tool/gnulink.py ./SCons/Tool/hpc++.py ./SCons/Tool/hpcc.py ./SCons/Tool/hplink.py ./SCons/Tool/icc.py ./SCons/Tool/icl.py ./SCons/Tool/ifl.py ./SCons/Tool/ilink.py ./SCons/Tool/ilink32.py ./SCons/Tool/jar.py ./SCons/Tool/javac.py ./SCons/Tool/JavaCommon.py ./SCons/Tool/javah.py ./SCons/Tool/latex.py ./SCons/Tool/lex.py ./SCons/Tool/link.py ./SCons/Tool/m4.py ./SCons/Tool/masm.py ./SCons/Tool/midl.py ./SCons/Tool/mingw.py ./SCons/Tool/mslib.py ./SCons/Tool/mslink.py ./SCons/Tool/msvc.py ./SCons/Tool/msvs.py ./SCons/Tool/nasm.py ./SCons/Tool/pdflatex.py ./SCons/Tool/pdftex.py ./SCons/Tool/qt.py ./SCons/Tool/rmic.py ./SCons/Tool/sgiar.py ./SCons/Tool/sgic++.py ./SCons/Tool/sgicc.py ./SCons/Tool/sgilink.py ./SCons/Tool/sunar.py ./SCons/Tool/sunc++.py ./SCons/Tool/suncc.py ./SCons/Tool/sunlink.py ./SCons/Tool/swig.py ./SCons/Tool/tar.py ./SCons/Tool/tex.py ./SCons/Tool/tlib.py ./SCons/Tool/yacc.py ./SCons/Util.py ./SCons/Warnings.py ./SCons/__init__.py ./SCons/exitfuncs.py scons-doc-2.3.0/bin/install_python.py0000644000175000017500000000643712114661557020425 0ustar dktrkranzdktrkranz#!/usr/bin/env python # # A script for unpacking and installing different historic versions of # Python in a consistent manner for side-by-side development testing. # # This was written for a Linux system (specifically Ubuntu) but should # be reasonably generic to any POSIX-style system with a /usr/local # hierarchy. import getopt import os import shutil import sys from Command import CommandRunner, Usage all_versions = [ '2.3.7', '2.4.5', #'2.5.2', '2.6', ] def main(argv=None): if argv is None: argv = sys.argv all = False downloads_dir = 'Downloads' downloads_url = 'http://www.python.org/ftp/python' sudo = 'sudo' prefix = '/usr/local' short_options = 'ad:hnp:q' long_options = ['all', 'help', 'no-exec', 'prefix=', 'quiet'] helpstr = """\ Usage: install_python.py [-ahnq] [-d DIR] [-p PREFIX] [VERSION ...] -a, --all Install all SCons versions. -d DIR, --downloads=DIR Downloads directory. -h, --help Print this help and exit -n, --no-exec No execute, just print command lines -p PREFIX, --prefix=PREFIX Installation prefix. -q, --quiet Quiet, don't print command lines """ try: try: opts, args = getopt.getopt(argv[1:], short_options, long_options) except getopt.error, msg: raise Usage(msg) for o, a in opts: if o in ('-a', '--all'): all = True elif o in ('-d', '--downloads'): downloads_dir = a elif o in ('-h', '--help'): print helpstr sys.exit(0) elif o in ('-n', '--no-exec'): CommandRunner.execute = CommandRunner.do_not_execute elif o in ('-p', '--prefix'): prefix = a elif o in ('-q', '--quiet'): CommandRunner.display = CommandRunner.do_not_display except Usage, err: sys.stderr.write(str(err.msg) + '\n') sys.stderr.write('use -h to get help\n') return 2 if all: if args: msg = 'install-scons.py: -a and version arguments both specified' sys.stderr.write(msg) sys.exit(1) args = all_versions cmd = CommandRunner() for version in args: python = 'Python-' + version tar_gz = os.path.join(downloads_dir, python + '.tgz') tar_gz_url = os.path.join(downloads_url, version, python + '.tgz') cmd.subst_dictionary(locals()) if not os.path.exists(tar_gz): if not os.path.exists(downloads_dir): cmd.run('mkdir %(downloads_dir)s') cmd.run('wget -O %(tar_gz)s %(tar_gz_url)s') cmd.run('tar zxf %(tar_gz)s') cmd.run('cd %(python)s') cmd.run('./configure --prefix=%(prefix)s %(configureflags)s 2>&1 | tee configure.out') cmd.run('make 2>&1 | tee make.out') cmd.run('%(sudo)s make install') cmd.run('%(sudo)s rm -f %(prefix)s/bin/{idle,pydoc,python,python-config,smtpd.py}') cmd.run('cd ..') cmd.run((shutil.rmtree, python), 'rm -rf %(python)s') if __name__ == "__main__": sys.exit(main()) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/bin/sfsum0000644000175000017500000001132712114661557016056 0ustar dktrkranzdktrkranz#!/usr/bin/env python # # sfsum.py: A script for parsing XML data exported from # SourceForge projects. # # Right now, this is hard-coded to generate a summary of open bugs. # # XML data for SourceForge project is available for download by project # administrators. Because it's intended for backup purposes, you have # to slurp the whole set of data, including info about all of the closed # items, the feature requests, etc., so it can get big. # # You can do this by hand (if you're an administrator) with a URL like # this (where 30337 is the group_id for SCons): # # http://sourceforge.net/export/xml_export.php?group_id=30337 # # They also have a Perl script, called xml_export, available as part # of a set of utilities called "adocman" which automate dealing with # SourceForge document management from the command line. "adocman" # is available at: # # https://sourceforge.net/projects/sitedocs/ # import xml.sax import xml.sax.saxutils import sys SFName = { 'Unassigned' : 'nobody', 'Chad Austin' : 'aegis', 'Charle Crain' : 'diewarzau', 'Steven Knight' : 'stevenknight', 'Steve Leblanc' : 'stevenleblanc', 'Jeff Petkau' : 'jpet', 'Anthony Roach' : 'anthonyroach', 'Steven Shaw' : 'steven_shaw', 'Terrel Shumway' : 'terrelshumway', 'Greg Spencer' : 'greg_spencer', 'Christoph Wiedemann' : 'wiedeman', } class Artifact(object): """Just a place to hold attributes that we find in the XML.""" pass Artifacts = {} def nws(text): """Normalize white space. This will become important if/when we enhance this to search for arbitrary fields.""" return ' '.join(text.split()) class ClassifyArtifacts(xml.sax.saxutils.DefaultHandler): """ Simple SAX subclass to classify the artifacts in SourceForge XML output. This reads up the fields in an XML description and turns the field descriptions into attributes of an Artificat object, on the fly. Artifacts are of the following types: Bugs Feature Requests Patches Support Requests We could, if we choose to, add additional types in the future by creating additional trackers. This class loses some info right now because we don't pay attention to the tag in the output, which contains a list of items that have tags in them. Right now, these just overwrite each other in the Arifact object we create. We also don't pay attention to any attributes of a tag other than the "name" attribute. We'll need to extend this class if we ever want to pay attention to those attributes. """ def __init__(self): self.artifact = None def startElement(self, name, attrs): self.text = "" if name == 'artifact': self.artifact = Artifact() elif not self.artifact is None and name == 'field': self.fname = attrs.get('name', None) def characters(self, ch): if not self.artifact is None: self.text = self.text + ch def endElement(self, name): global Artifacts if name == 'artifact': type = self.artifact.artifact_type try: list = Artifacts[type] except KeyError: Artifacts[type] = list = [] list.append(self.artifact) self.artifact = None elif not self.artifact is None and name == 'field': setattr(self.artifact, self.fname, self.text) if __name__ == '__main__': # Create a parser. parser = xml.sax.make_parser() # Tell the parser we are not interested in XML namespaces. parser.setFeature(xml.sax.handler.feature_namespaces, 0) # Instantiate our handler and tell the parser to use it. parser.setContentHandler(ClassifyArtifacts()) # Parse the input. parser.parse(sys.argv[1]) # Hard-coded search for 'Open' bugs. This should be easily # generalized once we figure out other things for this script to do. bugs = [x for x in Artifacts['Bugs'] if x.status == 'Open'] print list(Artifacts.keys()) print "%d open bugs" % len(bugs) # Sort them into a separate list for each assignee. Assigned = {} for bug in bugs: a = bug.assigned_to try: list = Assigned[a] except KeyError: Assigned[a] = list = [] list.append(bug) for a in SFName.keys(): try: b = Assigned[SFName[a]] except KeyError: pass else: print " %s" % a b.sort(lambda x, y: cmp(x.artifact_id, y.artifact_id)) for bug in b: print " %-6s %s" % (bug.artifact_id, bug.summary) scons-doc-2.3.0/bin/rsync-sourceforge0000644000175000017500000000206312114661557020375 0ustar dktrkranzdktrkranz#!/bin/sh # # Sync this directory tree with sourceforge. # # Cribbed and modified from Peter Miller's same-named script in # /home/groups/a/ae/aegis/aegis at SourceForge. # # Guide to what this does with rsync: # # --rsh=ssh use ssh for the transfer # -l copy symlinks as symlinks # -p preserve permissions # -r recursive # -t preserve times # -z compress data # --stats file transfer statistics # --exclude exclude files matching the pattern # --delete delete files that don't exist locally # --delete-excluded delete files that match the --exclude patterns # --progress show progress during the transfer # -v verbose # LOCAL=/home/scons/scons REMOTE=/home/groups/s/sc/scons/scons /usr/bin/rsync --rsh=ssh -l -p -r -t -z --stats \ --exclude build \ --exclude "*,D" \ --exclude "*.pyc" \ --exclude aegis.log \ --delete --delete-excluded \ --progress -v \ ${LOCAL}/. scons.sourceforge.net:${REMOTE}/. scons-doc-2.3.0/bin/scons-unzip.py0000644000175000017500000000356412114661557017644 0ustar dktrkranzdktrkranz#!/usr/bin/env python # # A quick script to unzip a .zip archive and put the files in a # subdirectory that matches the basename of the .zip file. # # This is actually generic functionality, it's not SCons-specific, but # I'm using this to make it more convenient to manage working on multiple # changes on Windows, where I don't have access to my Aegis tools. # import getopt import os.path import sys import zipfile helpstr = """\ Usage: scons-unzip.py [-o outdir] zipfile Options: -o DIR, --out DIR Change output directory name to DIR -v, --verbose Print file names when extracting """ opts, args = getopt.getopt(sys.argv[1:], "o:v", ['out=', 'verbose']) outdir = None printname = lambda x: x for o, a in opts: if o == '-o' or o == '--out': outdir = a elif o == '-v' or o == '--verbose': def printname(x): print x if len(args) != 1: sys.stderr.write("scons-unzip.py: \n") sys.exit(1) zf = zipfile.ZipFile(str(args[0]), 'r') if outdir is None: outdir, _ = os.path.splitext(os.path.basename(args[0])) def outname(n, outdir=outdir): l = [] while True: n, tail = os.path.split(n) if not n: break l.append(tail) l.append(outdir) l.reverse() return os.path.join(*l) for name in zf.namelist(): dest = outname(name) dir = os.path.dirname(dest) try: os.makedirs(dir) except: pass printname(dest) # if the file exists, then delete it before writing # to it so that we don't end up trying to write to a symlink: if os.path.isfile(dest) or os.path.islink(dest): os.unlink(dest) if not os.path.isdir(dest): open(dest, 'w').write(zf.read(name)) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/bin/time-scons.py0000644000175000017500000003007212114661557017427 0ustar dktrkranzdktrkranz#!/usr/bin/env python # # time-scons.py: a wrapper script for running SCons timings # # This script exists to: # # 1) Wrap the invocation of runtest.py to run the actual TimeSCons # timings consistently. It does this specifically by building # SCons first, so .pyc compilation is not part of the timing. # # 2) Provide an interface for running TimeSCons timings against # earlier revisions, before the whole TimeSCons infrastructure # was "frozen" to provide consistent timings. This is done # by updating the specific pieces containing the TimeSCons # infrastructure to the earliest revision at which those pieces # were "stable enough." # # By encapsulating all the logic in this script, our Buildbot # infrastructure only needs to call this script, and we should be able # to change what we need to in this script and have it affect the build # automatically when the source code is updated, without having to # restart either master or slave. import optparse import os import shutil import subprocess import sys import tempfile import xml.sax.handler SubversionURL = 'http://scons.tigris.org/svn/scons' # This is the baseline revision when the TimeSCons scripts first # stabilized and collected "real," consistent timings. If we're timing # a revision prior to this, we'll forcibly update the TimeSCons pieces # of the tree to this revision to collect consistent timings for earlier # revisions. TimeSCons_revision = 4569 # The pieces of the TimeSCons infrastructure that are necessary to # produce consistent timings, even when the rest of the tree is from # an earlier revision that doesn't have these pieces. TimeSCons_pieces = ['QMTest', 'timings', 'runtest.py'] class CommandRunner(object): """ Executor class for commands, including "commands" implemented by Python functions. """ verbose = True active = True def __init__(self, dictionary={}): self.subst_dictionary(dictionary) def subst_dictionary(self, dictionary): self._subst_dictionary = dictionary def subst(self, string, dictionary=None): """ Substitutes (via the format operator) the values in the specified dictionary into the specified command. The command can be an (action, string) tuple. In all cases, we perform substitution on strings and don't worry if something isn't a string. (It's probably a Python function to be executed.) """ if dictionary is None: dictionary = self._subst_dictionary if dictionary: try: string = string % dictionary except TypeError: pass return string def display(self, command, stdout=None, stderr=None): if not self.verbose: return if isinstance(command, tuple): func = command[0] args = command[1:] s = '%s(%s)' % (func.__name__, ', '.join(map(repr, args))) if isinstance(command, list): # TODO: quote arguments containing spaces # TODO: handle meta characters? s = ' '.join(command) else: s = self.subst(command) if not s.endswith('\n'): s += '\n' sys.stdout.write(s) sys.stdout.flush() def execute(self, command, stdout=None, stderr=None): """ Executes a single command. """ if not self.active: return 0 if isinstance(command, str): command = self.subst(command) cmdargs = shlex.split(command) if cmdargs[0] == 'cd': command = (os.chdir,) + tuple(cmdargs[1:]) if isinstance(command, tuple): func = command[0] args = command[1:] return func(*args) else: if stdout is sys.stdout: # Same as passing sys.stdout, except works with python2.4. subout = None elif stdout is None: # Open pipe for anything else so Popen works on python2.4. subout = subprocess.PIPE else: subout = stdout if stderr is sys.stderr: # Same as passing sys.stdout, except works with python2.4. suberr = None elif stderr is None: # Merge with stdout if stderr isn't specified. suberr = subprocess.STDOUT else: suberr = stderr p = subprocess.Popen(command, shell=(sys.platform == 'win32'), stdout=subout, stderr=suberr) p.wait() return p.returncode def run(self, command, display=None, stdout=None, stderr=None): """ Runs a single command, displaying it first. """ if display is None: display = command self.display(display) return self.execute(command, stdout, stderr) def run_list(self, command_list, **kw): """ Runs a list of commands, stopping with the first error. Returns the exit status of the first failed command, or 0 on success. """ status = 0 for command in command_list: s = self.run(command, **kw) if s and status == 0: status = s return 0 def get_svn_revisions(branch, revisions=None): """ Fetch the actual SVN revisions for the given branch querying "svn log." A string specifying a range of revisions can be supplied to restrict the output to a subset of the entire log. """ command = ['svn', 'log', '--xml'] if revisions: command.extend(['-r', revisions]) command.append(branch) p = subprocess.Popen(command, stdout=subprocess.PIPE) class SVNLogHandler(xml.sax.handler.ContentHandler): def __init__(self): self.revisions = [] def startElement(self, name, attributes): if name == 'logentry': self.revisions.append(int(attributes['revision'])) parser = xml.sax.make_parser() handler = SVNLogHandler() parser.setContentHandler(handler) parser.parse(p.stdout) return sorted(handler.revisions) def prepare_commands(): """ Returns a list of the commands to be executed to prepare the tree for testing. This involves building SCons, specifically the build/scons subdirectory where our packaging build is staged, and then running setup.py to create a local installed copy with compiled *.pyc files. The build directory gets removed first. """ commands = [] if os.path.exists('build'): commands.extend([ ['mv', 'build', 'build.OLD'], ['rm', '-rf', 'build.OLD'], ]) commands.append([sys.executable, 'bootstrap.py', 'build/scons']) commands.append([sys.executable, 'build/scons/setup.py', 'install', '--prefix=' + os.path.abspath('build/usr')]) return commands def script_command(script): """Returns the command to actually invoke the specified timing script using our "built" scons.""" return [sys.executable, 'runtest.py', '-x', 'build/usr/bin/scons', script] def do_revisions(cr, opts, branch, revisions, scripts): """ Time the SCons branch specified scripts through a list of revisions. We assume we're in a (temporary) directory in which we can check out the source for the specified revisions. """ stdout = sys.stdout stderr = sys.stderr status = 0 if opts.logsdir and not opts.no_exec and len(scripts) > 1: for script in scripts: subdir = os.path.basename(os.path.dirname(script)) logsubdir = os.path.join(opts.origin, opts.logsdir, subdir) if not os.path.exists(logsubdir): os.makedirs(logsubdir) for this_revision in revisions: if opts.logsdir and not opts.no_exec: log_name = '%s.log' % this_revision log_file = os.path.join(opts.origin, opts.logsdir, log_name) stdout = open(log_file, 'w') stderr = None commands = [ ['svn', 'co', '-q', '-r', str(this_revision), branch, '.'], ] if int(this_revision) < int(TimeSCons_revision): commands.append(['svn', 'up', '-q', '-r', str(TimeSCons_revision)] + TimeSCons_pieces) commands.extend(prepare_commands()) s = cr.run_list(commands, stdout=stdout, stderr=stderr) if s: if status == 0: status = s continue for script in scripts: if opts.logsdir and not opts.no_exec and len(scripts) > 1: subdir = os.path.basename(os.path.dirname(script)) lf = os.path.join(opts.origin, opts.logsdir, subdir, log_name) out = open(lf, 'w') err = None close_out = True else: out = stdout err = stderr close_out = False s = cr.run(script_command(script), stdout=out, stderr=err) if s and status == 0: status = s if close_out: out.close() out = None if int(this_revision) < int(TimeSCons_revision): # "Revert" the pieces that we previously updated to the # TimeSCons_revision, so the update to the next revision # works cleanly. command = (['svn', 'up', '-q', '-r', str(this_revision)] + TimeSCons_pieces) s = cr.run(command, stdout=stdout, stderr=stderr) if s: if status == 0: status = s continue if stdout not in (sys.stdout, None): stdout.close() stdout = None return status Usage = """\ time-scons.py [-hnq] [-r REVISION ...] [--branch BRANCH] [--logsdir DIR] [--svn] SCRIPT ...""" def main(argv=None): if argv is None: argv = sys.argv parser = optparse.OptionParser(usage=Usage) parser.add_option("--branch", metavar="BRANCH", default="trunk", help="time revision on BRANCH") parser.add_option("--logsdir", metavar="DIR", default='.', help="generate separate log files for each revision") parser.add_option("-n", "--no-exec", action="store_true", help="no execute, just print the command line") parser.add_option("-q", "--quiet", action="store_true", help="quiet, don't print the command line") parser.add_option("-r", "--revision", metavar="REVISION", help="time specified revisions") parser.add_option("--svn", action="store_true", help="fetch actual revisions for BRANCH") opts, scripts = parser.parse_args(argv[1:]) if not scripts: sys.stderr.write('No scripts specified.\n') sys.exit(1) CommandRunner.verbose = not opts.quiet CommandRunner.active = not opts.no_exec cr = CommandRunner() os.environ['TESTSCONS_SCONSFLAGS'] = '' branch = SubversionURL + '/' + opts.branch if opts.svn: revisions = get_svn_revisions(branch, opts.revision) elif opts.revision: # TODO(sgk): parse this for SVN-style revision strings revisions = [opts.revision] else: revisions = None if opts.logsdir and not os.path.exists(opts.logsdir): os.makedirs(opts.logsdir) if revisions: opts.origin = os.getcwd() tempdir = tempfile.mkdtemp(prefix='time-scons-') try: os.chdir(tempdir) status = do_revisions(cr, opts, branch, revisions, scripts) finally: os.chdir(opts.origin) shutil.rmtree(tempdir) else: commands = prepare_commands() commands.extend([ script_command(script) for script in scripts ]) status = cr.run_list(commands, stdout=sys.stdout, stderr=sys.stderr) return status if __name__ == "__main__": sys.exit(main()) scons-doc-2.3.0/bin/scons-test.py0000644000175000017500000001640712114661557017456 0ustar dktrkranzdktrkranz#!/usr/bin/env python # # A script that takes an scons-src-{version}.zip file, unwraps it in # a temporary location, and calls runtest.py to execute one or more of # its tests. # # The default is to download the latest scons-src archive from the SCons # web site, and to execute all of the tests. # # With a little more work, this will become the basis of an automated # testing and reporting system that anyone will be able to use to # participate in testing SCons on their system and regularly reporting # back the results. A --xml option is a stab at gathering a lot of # relevant information about the system, the Python version, etc., # so that problems on different platforms can be identified sooner. # import atexit import getopt import imp import os import os.path import sys import tempfile import time import zipfile try: # try Python 3.x style from urllib.request import urlretrieve except ImportError: # nope, must be 2.x; this hack is equivalent import imp # protect import from fixer urlretrieve = imp.load_module('urllib', *imp.find_module('urllib')).urlretrieve helpstr = """\ Usage: scons-test.py [-f zipfile] [-o outdir] [-v] [--xml] [runtest arguments] Options: -f FILE Specify input .zip FILE name -o DIR, --out DIR Change output directory name to DIR -v, --verbose Print file names when extracting --xml XML output """ opts, args = getopt.getopt(sys.argv[1:], "f:o:v", ['file=', 'out=', 'verbose', 'xml']) format = None outdir = None printname = lambda x: x inputfile = 'http://scons.sourceforge.net/scons-src-latest.zip' for o, a in opts: if o == '-f' or o == '--file': inputfile = a elif o == '-o' or o == '--out': outdir = a elif o == '-v' or o == '--verbose': def printname(x): print x elif o == '--xml': format = o startdir = os.getcwd() tempfile.template = 'scons-test.' tempdir = tempfile.mktemp() if not os.path.exists(tempdir): os.mkdir(tempdir) def cleanup(tempdir=tempdir): import shutil os.chdir(startdir) shutil.rmtree(tempdir) atexit.register(cleanup) # Fetch the input file if it happens to be across a network somewhere. # Ohmigod, does Python make this simple... inputfile, headers = urlretrieve(inputfile) # Unzip the header file in the output directory. We use our own code # (lifted from scons-unzip.py) to make the output subdirectory name # match the basename of the .zip file. zf = zipfile.ZipFile(inputfile, 'r') if outdir is None: name, _ = os.path.splitext(os.path.basename(inputfile)) outdir = os.path.join(tempdir, name) def outname(n, outdir=outdir): l = [] while True: n, tail = os.path.split(n) if not n: break l.append(tail) l.append(outdir) l.reverse() return os.path.join(*l) for name in zf.namelist(): dest = outname(name) dir = os.path.dirname(dest) try: os.makedirs(dir) except: pass printname(dest) # if the file exists, then delete it before writing # to it so that we don't end up trying to write to a symlink: if os.path.isfile(dest) or os.path.islink(dest): os.unlink(dest) if not os.path.isdir(dest): open(dest, 'w').write(zf.read(name)) os.chdir(outdir) # Load (by hand) the SCons modules we just unwrapped so we can # extract their version information. Note that we have to override # SCons.Script.main() with a do_nothing() function, because loading up # the 'scons' script will actually try to execute SCons... src_script = os.path.join(outdir, 'src', 'script') src_engine = os.path.join(outdir, 'src', 'engine') src_engine_SCons = os.path.join(src_engine, 'SCons') fp, pname, desc = imp.find_module('SCons', [src_engine]) SCons = imp.load_module('SCons', fp, pname, desc) fp, pname, desc = imp.find_module('Script', [src_engine_SCons]) SCons.Script = imp.load_module('Script', fp, pname, desc) def do_nothing(): pass SCons.Script.main = do_nothing fp, pname, desc = imp.find_module('scons', [src_script]) scons = imp.load_module('scons', fp, pname, desc) fp.close() # Default is to run all the tests by passing the -a flags to runtest.py. if not args: runtest_args = '-a' else: runtest_args = ' '.join(args) if format == '--xml': print "" print " " sys_keys = ['byteorder', 'exec_prefix', 'executable', 'maxint', 'maxunicode', 'platform', 'prefix', 'version', 'version_info'] for k in sys_keys: print " <%s>%s" % (k, sys.__dict__[k], k) print " " fmt = '%a %b %d %H:%M:%S %Y' print " " print " %s" % tempdir def print_version_info(tag, module): print " <%s>" % tag print " %s" % module.__version__ print " %s" % module.__build__ print " %s" % module.__buildsys__ print " %s" % module.__date__ print " %s" % module.__developer__ print " " % tag print " " print_version_info("script", scons) print_version_info("engine", SCons) print " " environ_keys = [ 'PATH', 'SCONSFLAGS', 'SCONS_LIB_DIR', 'PYTHON_ROOT', 'QTDIR', 'COMSPEC', 'INTEL_LICENSE_FILE', 'INCLUDE', 'LIB', 'MSDEVDIR', 'OS', 'PATHEXT', 'SystemRoot', 'TEMP', 'TMP', 'USERNAME', 'VXDOMNTOOLS', 'WINDIR', 'XYZZY' 'ENV', 'HOME', 'LANG', 'LANGUAGE', 'LOGNAME', 'MACHINE', 'OLDPWD', 'PWD', 'OPSYS', 'SHELL', 'TMPDIR', 'USER', ] print " " for key in sorted(environ_keys): value = os.environ.get(key) if value: print " " print " %s" % key print " %s" % value print " " print " " command = '"%s" runtest.py -q -o - --xml %s' % (sys.executable, runtest_args) #print command os.system(command) print "" else: def print_version_info(tag, module): print "\t%s: v%s.%s, %s, by %s on %s" % (tag, module.__version__, module.__build__, module.__date__, module.__developer__, module.__buildsys__) print "SCons by Steven Knight et al.:" print_version_info("script", scons) print_version_info("engine", SCons) command = '"%s" runtest.py %s' % (sys.executable, runtest_args) #print command os.system(command) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/config0000644000175000017500000002753512114661557015426 0ustar dktrkranzdktrkranz/* * MANIFEST: use of SCons in project config file to build itself * * SCons has a Repository feature, introduced in SCons 0.09, that was * designed to work well with Aegis. */ /* * The build_command field of the project config file is used to invoke * the relevant build command. This command tells SCons where to find * the rules. * * Our chicken-and-egg dilemma is this: we want to use the version of * SCons under development in an Aegis change to build itself. But the * pieces of SCons are likely only partly in this change, and partly in * baselines. * * Python only imports things on a module-by-module basis--which is to * say, once it finds __init__.py in a given directory, it assumes that * all other files in that module are in the same directory. But that's * not the way Aegis works, because if a file hasn't changed on the * branch, it will only be in its parent's baseline directory. * * Aegis' mechanism for working around this sort of problem is to make * symlinks to the proper baseline versions of each file, which makes * it look like everything is in the local tree. That's unattractive, * though, because we really want to eat our own dog food and use the * SCons -Y options to pull things from the baseline repositories. * * So our solution (suggested by Anthony Roach) is a bootstrap.py script * that does some Aegis-like searching through the baseline directories * and makes a bootstrap copy of the version of SCons under development * that we can use for building. After it makes this copy of SCons, it * executes it with the same command-line arguments we supplied (and * setting $SCONS_LIB_DIR to the right directory) so we can use it * here with command-line options as if it were SCons itself. (Note, * however, that bootstrap.py only understands the specific command-line * options already in use here, so if you change the call below to add * some other SCons options, you may have to modify bootstrap.py to * recognize them. * * The ${Source bootstrap.py} substitution finds bootstrap.py wherever * it may be in the Aegis baselines. * * The long -Y${SUBSTitute...} substitution takes the Aegis baseline * search path and turns it into the right -Y command-line options for * SCons. * * The rest of the substitutions (${DEVeloper}, etc.) should be obvious. * * Look in aesub(5) for more information about command substitutions. */ build_command = "python2.1 ${Source bootstrap.py} -Y${SUBSTitute : \\ -Y $Search_Path} date='${DAte %Y/%m/%d %H:%M:%S}' developer=${DEVeloper} version=${VERsion} change=${Change}"; /* * SCons removes its targets before constructing them, which qualifies it * for the following entry in the config file. The files must be removed * first, otherwise the baseline would cease to be self-consistent. */ link_integration_directory = true; /* * This is set temporarily to allow us to build using the SCons * currently checked in to the src directory. create_symlinks_before_build = true; */ /* * aegis - project change supervisor * This file is in the Public Domain, 1995, 1998 Peter Miller. * * MANIFEST: example of using rcs in the project config file * * The entries for the commands are listed below. RCS uses a slightly * different model than aegis wants, so some maneuvering is required. * The command strings in this section assume that the RCS commands ci and co * and rcs and rlog are in the command search PATH, but you may like to * hard-wire the paths, or set PATH at the start of each. You should also note * that the strings are always handed to the Bourne shell to be executed, and * are set to exit with an error immediately a sub-command fails. * * In these commands, the RCS file is kept unlocked, since only the owner will * be checking changes in. The RCS functionality for coordinating shared * access is not required. * * One advantage of using RCS version 5.6 or later is that binary files are * supported, should you want to have binary files in the baseline. * * The ${quote ...} construct is used to quote filenames which contain * shell special characters. A minimum of quoting is performed, so if * the filenames do not contail shell special characters, no quotes will * be used. */ /* * This command is used to create a new file history. * This command is always executed as the project owner. * The following substitutions are available: * * ${Input} * absolute path of the source file * ${History} * absolute path of the history file * * The "ci -f" option is used to specify that a copy is to be checked-in even * if there are no changes. * The "ci -u" option is used to specify that an unlocked copy will remain in * the baseline. * The "ci -d" option is used to specify that the file time rather than the * current time is to be used for the new revision. * The "ci -M" option is used to specify that the mode date on the original * file is not to be altered. * The "ci -t" option is used to specify that there is to be no description * text for the new RCS file. * The "ci -m" option is used to specify that the change number is to be stored * in the file log if this is actually an update (typically from aenf * after aerm on the same file name). * The "rcs -U" option is used to specify that the new RCS file is to have * unstrict locking. * The "rcs -kk" option is used to specify that keyword substitution is * disabled (only keyword names, not values, are substituted). */ history_create_command = "ci -f -u -d -M -m$c -t/dev/null ${quote $input} ${quote $history,v}; \ rcs -kk -U ${quote $history,v}"; /* * This command is used to get a specific edit back from history. * This command is always executed as the project owner. * The following substitutions are available: * * ${History} * absolute path of the history file * ${Edit} * edit number, as given by history_\%query_\%command * ${Output} * absolute path of the destination file * * The "co -r" option is used to specify the edit to be retrieved. * The "co -p" option is used to specify that the results be printed on the * standard output; this is because the destination filename will never * look anything like the history source filename. * The "rcs -kk" option is used to specify that keyword substitution is * disabled (only keyword names, not values, are substituted). */ history_get_command = "co -kk -r${quote $edit} -p ${quote $history,v} > ${quote $output}"; /* * This command is used to add a new "top-most" entry to the history file. * This command is always executed as the project owner. * The following substitutions are available: * * ${Input} * absolute path of source file * ${History} * absolute path of history file * * The "ci -f" option is used to specify that a copy is to be checked-in even * if there are no changes. * The "ci -u" option is used to specify that an unlocked copy will remain in * the baseline. * The "ci -d" option is used to specify that the file time rather than the * current time is to be used for the new revision. * The "ci -M" option is used to specify that the mode date on the original * file is not to be altered. * The "ci -m" option is used to specify that the change number is to be stored * in the file log, which allows rlog to be used to find the change * numbers to which each revision of the file corresponds. * * It is possible for a a very cautious approach has been taken, in which case * the history_put_command may be set to the same string specified above for * the history_create_command. */ history_put_command = "ci -f -u -d -M -m$c ${quote $input} ${quote $history,v}"; /* * This command is used to query what the history mechanism calls the top-most * edit of a history file. The result may be any arbitrary string, it need not * be anything like a number, just so long as it uniquely identifies the edit * for use by the history_get_command at a later date. The edit number is to * be printed on the standard output. This command is always executed as the * project owner. * * The following substitutions are available: * * ${History} * absolute path of the history file */ history_query_command = "rlog -r ${quote $history,v} | awk '/^head:/ {print $$2}'"; /* * RCS also provides a merge program, which can be used to provide a three-way * merge. It has an ouput format some sites prefer to the fmerge output. * * This command is used by aed(1) to produce a difference listing when a file * in the development directory is out of date compared to the current version * in the baseline. * * All of the command substitutions described in aesub(5) are available. * In addition, the following substitutions are also available: * * ${ORiginal} * The absolute path name of a file containing the common ancestor * version of ${MostRecent} and {$Input}. Usually the version originally * copied into the change. Usually in a temporary file. * ${Most_Recent} * The absolute path name of a file containing the most recent version. * Usually in the baseline. * ${Input} * The absolute path name of the edited version of the file. Usually in * the development directory. * ${Output} * The absolute path name of the file in which to write the difference * listing. Usually in the development directory. * * An exit status of 0 means successful, even of the files differ (and they * usually do). An exit status which is non-zero means something is wrong. * * The "merge -L" options are used to specify labels for the baseline and the * development directory, respecticvely, when conflict lines are inserted * into the result. * The "merge -p" options is used to specify that the results are to be printed * on the standard output. */ diff3_command = "set +e; \ merge -p -L baseline -L C$c ${quote $mostrecent} ${quote $original} \ ${quote $input} > ${quote $output}; \ test $? -le 1"; /* * The diff command in Red Hat 8.0 changed the exit status so it *fails* * when *it* thinks it's trying to diff a binary (non-ASCII-text) file. * The -a option disables this behavior and makes diff's exit status * behave like it used to, even on any binary files we have checked in. */ diff_command = "set +e; \ diff -a -c ${quote $original} ${quote $input} > ${quote $output}; \ test $? -le 1"; /* * We use a runtest.py script to execute tests. This takes care of * massaging environment variables and the like to test against the * unpacked package in the current directory. * * Note that we must include $spe in the batch_test_command line (so * that Aegis thinks we're smart about testing ourselves against the * baseline) but we don't actually need it. Our tests always run * relative to the package built under the current directory, which * is set appropriately during a baseline test. So we just use the * proper aesub variable to comment out the expanded $spe. */ test_command = "python1.5 ${Source runtest.py Absolute} --noqmtest -p tar-gz -t -v ${SUBSTitute '\\.[CD][0-9]+$' '' ${VERsion}} -q --sp ${Search_Path} --spe ${Search_Path_Executable} ${File_Name}"; batch_test_command = "python1.5 ${Source runtest.py Absolute} --noqmtest -p tar-gz -t -v ${SUBSTitute '\\.[CD][0-9]+$' '' ${VERsion}} -o ${Output} --aegis --sp ${Search_Path} --spe ${Search_Path_Executable} ${File_Names}"; new_test_filename = "test/CHANGETHIS.py"; /* * */ file_template = [ { pattern = [ "src/engine/*__init__.py" ]; body = "${read_file ${source template/__init__.py abs}}"; }, { pattern = [ "src/engine/*Tests.py" ]; body = "${read_file ${source template/Tests.py abs}}"; }, { pattern = [ "src/engine/*.py" ]; body = "${read_file ${source template/file.py abs}}"; }, { pattern = [ "test/*.py" ]; body = "${read_file ${source template/test.py abs}}"; }, ]; /* * Command for distributing changes from Aegis to all of the repositories * we want to mirror the information. * * XXX Uncomment after upgrading to an Aegis version that supports this. integrate_pass_notify_command = "$sh ${s bin/scons-cdist} -p $project $change"; * */ scons-doc-2.3.0/doc/0000755000175000017500000000000012114661557014767 5ustar dktrkranzdktrkranzscons-doc-2.3.0/doc/user/0000755000175000017500000000000012114661557015745 5ustar dktrkranzdktrkranzscons-doc-2.3.0/doc/user/builders-writing.xml0000644000175000017500000006526612114661557022000 0ustar dktrkranzdktrkranz Although &SCons; provides many useful methods for building common software products (programs, libraries, documents, etc.), you frequently want to be able to build some other type of file not supported directly by &SCons;. Fortunately, &SCons; makes it very easy to define your own &Builder; objects for any custom file types you want to build. (In fact, the &SCons; interfaces for creating &Builder; objects are flexible enough and easy enough to use that all of the the &SCons; built-in &Builder; objects are created using the mechanisms described in this section.)
Writing Builders That Execute External Commands The simplest &Builder; to create is one that executes an external command. For example, if we want to build an output file by running the contents of the input file through a command named foobuild, creating that &Builder; might look like: bld = Builder(action = 'foobuild < $SOURCE > $TARGET') All the above line does is create a free-standing &Builder; object. The next section will show us how to actually use it.
Attaching a Builder to a &ConsEnv; A &Builder; object isn't useful until it's attached to a &consenv; so that we can call it to arrange for files to be built. This is done through the &cv-link-BUILDERS; &consvar; in an environment. The &cv-BUILDERS; variable is a Python dictionary that maps the names by which you want to call various &Builder; objects to the objects themselves. For example, if we want to call the &Builder; we just defined by the name Foo, our &SConstruct; file might look like: bld = Builder(action = 'foobuild < $SOURCE > $TARGET') env = Environment(BUILDERS = {'Foo' : bld}) With the &Builder; attached to our &consenv; with the name Foo, we can now actually call it like so: env.Foo('file.foo', 'file.input') Then when we run &SCons; it looks like: % scons -Q foobuild < file.input > file.foo Note, however, that the default &cv-BUILDERS; variable in a &consenv; comes with a default set of &Builder; objects already defined: &b-link-Program;, &b-link-Library;, etc. And when we explicitly set the &cv-BUILDERS; variable when we create the &consenv;, the default &Builder;s are no longer part of the environment: bld = Builder(action = 'foobuild < $SOURCE > $TARGET') env = Environment(BUILDERS = {'Foo' : bld}) env.Foo('file.foo', 'file.input') env.Program('hello.c') % scons -Q AttributeError: 'SConsEnvironment' object has no attribute 'Program': File "/home/my/project/SConstruct", line 4: env.Program('hello.c') To be able to use both our own defined &Builder; objects and the default &Builder; objects in the same &consenv;, you can either add to the &cv-BUILDERS; variable using the &Append; function: env = Environment() bld = Builder(action = 'foobuild < $SOURCE > $TARGET') env.Append(BUILDERS = {'Foo' : bld}) env.Foo('file.foo', 'file.input') env.Program('hello.c') Or you can explicitly set the appropriately-named key in the &cv-BUILDERS; dictionary: env = Environment() bld = Builder(action = 'foobuild < $SOURCE > $TARGET') env['BUILDERS']['Foo'] = bld env.Foo('file.foo', 'file.input') env.Program('hello.c') Either way, the same &consenv; can then use both the newly-defined Foo &Builder; and the default &b-link-Program; &Builder;: % scons -Q foobuild < file.input > file.foo cc -o hello.o -c hello.c cc -o hello hello.o
Letting &SCons; Handle The File Suffixes By supplying additional information when you create a &Builder;, you can let &SCons; add appropriate file suffixes to the target and/or the source file. For example, rather than having to specify explicitly that you want the Foo &Builder; to build the file.foo target file from the file.input source file, you can give the .foo and .input suffixes to the &Builder;, making for more compact and readable calls to the Foo &Builder;: bld = Builder(action = 'foobuild < $SOURCE > $TARGET', suffix = '.foo', src_suffix = '.input') env = Environment(BUILDERS = {'Foo' : bld}) env.Foo('file1') env.Foo('file2') % scons -Q foobuild < file1.input > file1.foo foobuild < file2.input > file2.foo You can also supply a prefix keyword argument if it's appropriate to have &SCons; append a prefix to the beginning of target file names.
Builders That Execute Python Functions In &SCons;, you don't have to call an external command to build a file. You can, instead, define a Python function that a &Builder; object can invoke to build your target file (or files). Such a &buildfunc; definition looks like: def build_function(target, source, env): # Code to build "target" from "source" return None The arguments of a &buildfunc; are: target A list of Node objects representing the target or targets to be built by this builder function. The file names of these target(s) may be extracted using the Python &str; function. source A list of Node objects representing the sources to be used by this builder function to build the targets. The file names of these source(s) may be extracted using the Python &str; function. env The &consenv; used for building the target(s). The builder function may use any of the environment's construction variables in any way to affect how it builds the targets. The builder function must return a 0 or None value if the target(s) are built successfully. The builder function may raise an exception or return any non-zero value to indicate that the build is unsuccessful, Once you've defined the Python function that will build your target file, defining a &Builder; object for it is as simple as specifying the name of the function, instead of an external command, as the &Builder;'s action argument: def build_function(target, source, env): # Code to build "target" from "source" return None bld = Builder(action = build_function, suffix = '.foo', src_suffix = '.input') env = Environment(BUILDERS = {'Foo' : bld}) env.Foo('file') And notice that the output changes slightly, reflecting the fact that a Python function, not an external command, is now called to build the target file: % scons -Q build_function(["file.foo"], ["file.input"])
Builders That Create Actions Using a &Generator; &SCons; Builder objects can create an action "on the fly" by using a function called a &generator;. This provides a great deal of flexibility to construct just the right list of commands to build your target. A &generator; looks like: def generate_actions(source, target, env, for_signature): return 'foobuild < %s > %s' % (target[0], source[0]) The arguments of a &generator; are: source A list of Node objects representing the sources to be built by the command or other action generated by this function. The file names of these source(s) may be extracted using the Python &str; function. target A list of Node objects representing the target or targets to be built by the command or other action generated by this function. The file names of these target(s) may be extracted using the Python &str; function. env The &consenv; used for building the target(s). The generator may use any of the environment's construction variables in any way to determine what command or other action to return. for_signature A flag that specifies whether the generator is being called to contribute to a build signature, as opposed to actually executing the command. The &generator; must return a command string or other action that will be used to build the specified target(s) from the specified source(s). Once you've defined a &generator;, you create a &Builder; to use it by specifying the generator keyword argument instead of action. def generate_actions(source, target, env, for_signature): return 'foobuild < %s > %s' % (source[0], target[0]) bld = Builder(generator = generate_actions, suffix = '.foo', src_suffix = '.input') env = Environment(BUILDERS = {'Foo' : bld}) env.Foo('file') % scons -Q foobuild < file.input > file.foo Note that it's illegal to specify both an action and a generator for a &Builder;.
Builders That Modify the Target or Source Lists Using an &Emitter; &SCons; supports the ability for a Builder to modify the lists of target(s) from the specified source(s). You do this by defining an &emitter; function that takes as its arguments the list of the targets passed to the builder, the list of the sources passed to the builder, and the construction environment. The emitter function should return the modified lists of targets that should be built and sources from which the targets will be built. For example, suppose you want to define a Builder that always calls a foobuild program, and you want to automatically add a new target file named new_target and a new source file named new_source whenever it's called. The &SConstruct; file might look like this: def modify_targets(target, source, env): target.append('new_target') source.append('new_source') return target, source bld = Builder(action = 'foobuild $TARGETS - $SOURCES', suffix = '.foo', src_suffix = '.input', emitter = modify_targets) env = Environment(BUILDERS = {'Foo' : bld}) env.Foo('file') And would yield the following output: % scons -Q foobuild file.foo new_target - file.input new_source One very flexible thing that you can do is use a construction variable to specify different emitter functions for different construction variable. To do this, specify a string containing a construction variable expansion as the emitter when you call the &Builder; function, and set that construction variable to the desired emitter function in different construction environments: bld = Builder(action = 'my_command $SOURCES > $TARGET', suffix = '.foo', src_suffix = '.input', emitter = '$MY_EMITTER') def modify1(target, source, env): return target, source + ['modify1.in'] def modify2(target, source, env): return target, source + ['modify2.in'] env1 = Environment(BUILDERS = {'Foo' : bld}, MY_EMITTER = modify1) env2 = Environment(BUILDERS = {'Foo' : bld}, MY_EMITTER = modify2) env1.Foo('file1') env2.Foo('file2') import os env1['ENV']['PATH'] = env2['ENV']['PATH'] + os.pathsep + os.getcwd() env2['ENV']['PATH'] = env2['ENV']['PATH'] + os.pathsep + os.getcwd() bld = Builder(action = 'my_command $SOURCES > $TARGET', suffix = '.foo', src_suffix = '.input', emitter = '$MY_EMITTER') def modify1(target, source, env): return target, source + ['modify1.in'] def modify2(target, source, env): return target, source + ['modify2.in'] env1 = Environment(BUILDERS = {'Foo' : bld}, MY_EMITTER = modify1) env2 = Environment(BUILDERS = {'Foo' : bld}, MY_EMITTER = modify2) env1.Foo('file1') env2.Foo('file2') In this example, the modify1.in and modify2.in files get added to the source lists of the different commands: % scons -Q my_command file1.input modify1.in > file1.foo my_command file2.input modify2.in > file2.foo
Where To Put Your Custom Builders and Tools The site_scons directories give you a place to put Python modules and packages that you can import into your &SConscript; files (site_scons), add-on tools that can integrate into &SCons; (site_scons/site_tools), and a site_scons/site_init.py file that gets read before any &SConstruct; or &SConscript; file, allowing you to change &SCons;'s default behavior. Each system type (Windows, Mac, Linux, etc.) searches a canonical set of directories for site_scons; see the man page for details. The top-level SConstruct's site_scons dir is always searched last, and its dir is placed first in the tool path so it overrides all others. If you get a tool from somewhere (the &SCons; wiki or a third party, for instance) and you'd like to use it in your project, a site_scons dir is the simplest place to put it. Tools come in two flavors; either a Python function that operates on an &Environment; or a Python module or package containing two functions, exists() and generate(). A single-function Tool can just be included in your site_scons/site_init.py file where it will be parsed and made available for use. For instance, you could have a site_scons/site_init.py file like this: def TOOL_ADD_HEADER(env): """A Tool to add a header from $HEADER to the source file""" add_header = Builder(action=['echo "$HEADER" > $TARGET', 'cat $SOURCE >> $TARGET']) env.Append(BUILDERS = {'AddHeader' : add_header}) env['HEADER'] = '' # set default value and a &SConstruct; like this: # Use TOOL_ADD_HEADER from site_scons/site_init.py env=Environment(tools=['default', TOOL_ADD_HEADER], HEADER="=====") env.AddHeader('tgt', 'src') The TOOL_ADD_HEADER tool method will be called to add the AddHeader tool to the environment. A more full-fledged tool with exists() and generate() methods can be installed either as a module in the file site_scons/site_tools/toolname.py or as a package in the directory site_scons/site_tools/toolname. In the case of using a package, the exists() and generate() are in the file site_scons/site_tools/toolname/__init__.py. (In all the above case toolname is replaced by the name of the tool.) Since site_scons/site_tools is automatically added to the head of the tool search path, any tool found there will be available to all environments. Furthermore, a tool found there will override a built-in tool of the same name, so if you need to change the behavior of a built-in tool, site_scons gives you the hook you need. Many people have a library of utility Python functions they'd like to include in &SConscript;s; just put that module in site_scons/my_utils.py or any valid Python module name of your choice. For instance you can do something like this in site_scons/my_utils.py to add build_id and MakeWorkDir functions: from SCons.Script import * # for Execute and Mkdir def build_id(): """Return a build ID (stub version)""" return "100" def MakeWorkDir(workdir): """Create the specified dir immediately""" Execute(Mkdir(workdir)) And then in your &SConscript; or any sub-&SConscript; anywhere in your build, you can import my_utils and use it: import my_utils print "build_id=" + my_utils.build_id() my_utils.MakeWorkDir('/tmp/work') Note that although you can put this library in site_scons/site_init.py, it is no better there than site_scons/my_utils.py since you still have to import that module into your &SConscript;. Also note that in order to refer to objects in the SCons namespace such as &Environment; or &Mkdir; or &Execute; in any file other than a &SConstruct; or &SConscript; you always need to do from SCons.Script import * This is true in modules in site_scons such as site_scons/site_init.py as well. You can use any of the user- or machine-wide site dirs such as ~/.scons/site_scons instead of ./site_scons, or use the --site-dir option to point to your own dir. site_init.py and site_tools will be located under that dir. To avoid using a site_scons dir at all, even if it exists, use the --no-site-dir option.
scons-doc-2.3.0/doc/user/scanners.xml0000644000175000017500000003012012114661557020277 0ustar dktrkranzdktrkranz &SCons; has built-in scanners that know how to look in C, Fortran and IDL source files for information about other files that targets built from those files depend on--for example, in the case of files that use the C preprocessor, the .h files that are specified using #include lines in the source. You can use the same mechanisms that &SCons; uses to create its built-in scanners to write scanners of your own for file types that &SCons; does not know how to scan "out of the box."
A Simple Scanner Example Suppose, for example, that we want to create a simple scanner for .foo files. A .foo file contains some text that will be processed, and can include other files on lines that begin with include followed by a file name: include filename.foo Scanning a file will be handled by a Python function that you must supply. Here is a function that will use the Python re module to scan for the include lines in our example: import re include_re = re.compile(r'^include\s+(\S+)$', re.M) def kfile_scan(node, env, path, arg): contents = node.get_text_contents() return env.File(include_re.findall(contents)) It is important to note that you have to return a list of File nodes from the scanner function, simple strings for the file names won't do. As in the examples we are showing here, you can use the &File; function of your current Environment in order to create nodes on the fly from a sequence of file names with relative paths. The scanner function must accept the four specified arguments and return a list of implicit dependencies. Presumably, these would be dependencies found from examining the contents of the file, although the function can perform any manipulation at all to generate the list of dependencies. node An &SCons; node object representing the file being scanned. The path name to the file can be used by converting the node to a string using the str() function, or an internal &SCons; get_text_contents() object method can be used to fetch the contents. env The construction environment in effect for this scan. The scanner function may choose to use construction variables from this environment to affect its behavior. path A list of directories that form the search path for included files for this scanner. This is how &SCons; handles the &cv-link-CPPPATH; and &cv-link-LIBPATH; variables. arg An optional argument that you can choose to have passed to this scanner function by various scanner instances. A Scanner object is created using the &Scanner; function, which typically takes an skeys argument to associate the type of file suffix with this scanner. The Scanner object must then be associated with the &cv-link-SCANNERS; construction variable of a construction environment, typically by using the &Append; method: kscan = Scanner(function = kfile_scan, skeys = ['.k']) env.Append(SCANNERS = kscan) When we put it all together, it looks like: import re include_re = re.compile(r'^include\s+(\S+)$', re.M) def kfile_scan(node, env, path): contents = node.get_text_contents() includes = include_re.findall(contents) return env.File(includes) kscan = Scanner(function = kfile_scan, skeys = ['.k']) env = Environment(ENV = {'PATH' : '/usr/local/bin'}) env.Append(SCANNERS = kscan) env.Command('foo', 'foo.k', 'kprocess < $SOURCES > $TARGET')
Adding a search path to a scanner: &FindPathDirs; Many scanners need to search for included files or dependencies using a path variable; this is how &cv-link-CPPPATH; and &cv-link-LIBPATH; work. The path to search is passed to your scanner as the path argument. Path variables may be lists of nodes, semicolon-separated strings, or even contain SCons variables which need to be expanded. Fortunately, &SCons; provides the &FindPathDirs; function which itself returns a function to expand a given path (given as a SCons construction variable name) to a list of paths at the time the scanner is called. Deferring evaluation until that point allows, for instance, the path to contain $TARGET references which differ for each file scanned. Using &FindPathDirs; is quite easy. Continuing the above example, using KPATH as the construction variable with the search path (analogous to &cv-link-CPPPATH;), we just modify the &Scanner; constructor call to include a path keyword arg: kscan = Scanner(function = kfile_scan, skeys = ['.k'], path=FindPathDirs('KPATH')) FindPathDirs returns a callable object that, when called, will essentially expand the elements in env['KPATH'] and tell the scanner to search in those dirs. It will also properly add related repository and variant dirs to the search list. As a side note, the returned method stores the path in an efficient way so lookups are fast even when variable substitutions may be needed. This is important since many files get scanned in a typical build.
scons-doc-2.3.0/doc/user/less-simple.in0000644000175000017500000004024512114661557020537 0ustar dktrkranzdktrkranz In this chapter, you will see several examples of very simple build configurations using &SCons;, which will demonstrate how easy it is to use &SCons; to build programs from several different programming languages on different types of systems.
Specifying the Name of the Target (Output) File You've seen that when you call the &b-link-Program; builder method, it builds the resulting program with the same base name as the source file. That is, the following call to build an executable program from the &hello_c; source file will build an executable program named &hello; on POSIX systems, and an executable program named &hello_exe; on Windows systems: Program('hello.c') If you want to build a program with a different name than the base of the source file name, you simply put the target file name to the left of the source file name: Program('new_hello', 'hello.c') int main() { printf("Hello, world!\n"); } (&SCons; requires the target file name first, followed by the source file name, so that the order mimics that of an assignment statement in most programming languages, including Python: "program = source files".) Now &SCons; will build an executable program named &new_hello; when run on a POSIX system: scons -Q And &SCons; will build an executable program named &new_hello_exe; when run on a Windows system: scons -Q
Compiling Multiple Source Files You've just seen how to configure &SCons; to compile a program from a single source file. It's more common, of course, that you'll need to build a program from many input source files, not just one. To do this, you need to put the source files in a Python list (enclosed in square brackets), like so: Program(['prog.c', 'file1.c', 'file2.c']) int main() { printf("prog.c\n"); } void file1() { printf("file1.c\n"); } void file2() { printf("file2.c\n"); } A build of the above example would look like: scons -Q Notice that &SCons; deduces the output program name from the first source file specified in the list--that is, because the first source file was &prog_c;, &SCons; will name the resulting program &prog; (or &prog_exe; on a Windows system). If you want to specify a different program name, then (as we've seen in the previous section) you slide the list of source files over to the right to make room for the output program file name. (&SCons; puts the output file name to the left of the source file names so that the order mimics that of an assignment statement: "program = source files".) This makes our example: Program('program', ['prog.c', 'file1.c', 'file2.c']) int main() { printf("prog.c\n"); } void file1() { printf("file1.c\n"); } void file2() { printf("file2.c\n"); } On Linux, a build of this example would look like: scons -Q Or on Windows: scons -Q
Making a list of files with &Glob; You can also use the &Glob; function to find all files matching a certain template, using the standard shell pattern matching characters *, ? and [abc] to match any of a, b or c. [!abc] is also supported, to match any character except a, b or c. This makes many multi-source-file builds quite easy: Program('program', Glob('*.c')) The SCons man page has more details on using &Glob; with variant directories (see , below) and repositories (see , below), and returning strings rather than Nodes.
Specifying Single Files Vs. Lists of Files We've now shown you two ways to specify the source for a program, one with a list of files: Program('hello', ['file1.c', 'file2.c']) And one with a single file: Program('hello', 'hello.c') You could actually put a single file name in a list, too, which you might prefer just for the sake of consistency: Program('hello', ['hello.c']) &SCons; functions will accept a single file name in either form. In fact, internally, &SCons; treats all input as lists of files, but allows you to omit the square brackets to cut down a little on the typing when there's only a single file name. Although &SCons; functions are forgiving about whether or not you use a string vs. a list for a single file name, Python itself is more strict about treating lists and strings differently. So where &SCons; allows either a string or list: # The following two calls both work correctly: Program('program1', 'program1.c') Program('program2', ['program2.c']) Trying to do "Python things" that mix strings and lists will cause errors or lead to incorrect results: common_sources = ['file1.c', 'file2.c'] # THE FOLLOWING IS INCORRECT AND GENERATES A PYTHON ERROR # BECAUSE IT TRIES TO ADD A STRING TO A LIST: Program('program1', common_sources + 'program1.c') # The following works correctly, because it's adding two # lists together to make another list. Program('program2', common_sources + ['program2.c'])
Making Lists of Files Easier to Read One drawback to the use of a Python list for source files is that each file name must be enclosed in quotes (either single quotes or double quotes). This can get cumbersome and difficult to read when the list of file names is long. Fortunately, &SCons; and Python provide a number of ways to make sure that the &SConstruct; file stays easy to read. To make long lists of file names easier to deal with, &SCons; provides a &Split; function that takes a quoted list of file names, with the names separated by spaces or other white-space characters, and turns it into a list of separate file names. Using the &Split; function turns the previous example into: Program('program', Split('main.c file1.c file2.c')) (If you're already familiar with Python, you'll have realized that this is similar to the split() method in the Python standard string module. Unlike the split() member function of strings, however, the &Split; function does not require a string as input and will wrap up a single non-string object in a list, or return its argument untouched if it's already a list. This comes in handy as a way to make sure arbitrary values can be passed to &SCons; functions without having to check the type of the variable by hand.) Putting the call to the &Split; function inside the &b-Program; call can also be a little unwieldy. A more readable alternative is to assign the output from the &Split; call to a variable name, and then use the variable when calling the &b-Program; function: src_files = Split('main.c file1.c file2.c') Program('program', src_files) Lastly, the &Split; function doesn't care how much white space separates the file names in the quoted string. This allows you to create lists of file names that span multiple lines, which often makes for easier editing: src_files = Split("""main.c file1.c file2.c""") Program('program', src_files) (Note in this example that we used the Python "triple-quote" syntax, which allows a string to contain multiple lines. The three quotes can be either single or double quotes.)
Keyword Arguments &SCons; also allows you to identify the output file and input source files using Python keyword arguments. The output file is known as the target, and the source file(s) are known (logically enough) as the source. The Python syntax for this is: src_files = Split('main.c file1.c file2.c') Program(target = 'program', source = src_files) Because the keywords explicitly identify what each argument is, you can actually reverse the order if you prefer: src_files = Split('main.c file1.c file2.c') Program(source = src_files, target = 'program') Whether or not you choose to use keyword arguments to identify the target and source files, and the order in which you specify them when using keywords, are purely personal choices; &SCons; functions the same regardless.
Compiling Multiple Programs In order to compile multiple programs within the same &SConstruct; file, simply call the &Program; method multiple times, once for each program you need to build: Program('foo.c') Program('bar', ['bar1.c', 'bar2.c']) int main() { printf("foo.c\n"); } int main() { printf("bar1.c\n"); } void bar2() { printf("bar2.c\n"); } &SCons; would then build the programs as follows: scons -Q Notice that &SCons; does not necessarily build the programs in the same order in which you specify them in the &SConstruct; file. &SCons; does, however, recognize that the individual object files must be built before the resulting program can be built. We'll discuss this in greater detail in the "Dependencies" section, below.
Sharing Source Files Between Multiple Programs It's common to re-use code by sharing source files between multiple programs. One way to do this is to create a library from the common source files, which can then be linked into resulting programs. (Creating libraries is discussed in , below.) A more straightforward, but perhaps less convenient, way to share source files between multiple programs is simply to include the common files in the lists of source files for each program: Program(Split('foo.c common1.c common2.c')) Program('bar', Split('bar1.c bar2.c common1.c common2.c')) int main() { printf("foo.c\n"); } int main() { printf("bar1.c\n"); } int bar2() { printf("bar2.c\n"); } void common1() { printf("common1.c\n"); } void common22() { printf("common2.c\n"); } &SCons; recognizes that the object files for the &common1_c; and &common2_c; source files each need to be built only once, even though the resulting object files are each linked in to both of the resulting executable programs: scons -Q If two or more programs share a lot of common source files, repeating the common files in the list for each program can be a maintenance problem when you need to change the list of common files. You can simplify this by creating a separate Python list to hold the common file names, and concatenating it with other lists using the Python + operator: common = ['common1.c', 'common2.c'] foo_files = ['foo.c'] + common bar_files = ['bar1.c', 'bar2.c'] + common Program('foo', foo_files) Program('bar', bar_files) This is functionally equivalent to the previous example.
scons-doc-2.3.0/doc/user/errors.in0000644000175000017500000000240412114661557017611 0ustar dktrkranzdktrkranz XXX
XXX XXX
scons-doc-2.3.0/doc/user/variables.in0000644000175000017500000000356112114661557020252 0ustar dktrkranzdktrkranz This appendix contains descriptions of all of the construction variables that are potentially available "out of the box" in this version of SCons. Whether or not setting a construction variable in a construction environment will actually have an effect depends on whether any of the Tools and/or Builders that use the variable have been included in the construction environment. In this appendix, we have appended the initial $ (dollar sign) to the beginning of each variable name when it appears in the text, but left off the dollar sign in the left-hand column where the name appears for each entry. &variables-gen; scons-doc-2.3.0/doc/user/separate.xml0000644000175000017500000003527512114661557020307 0ustar dktrkranzdktrkranz It's often useful to keep any built files completely separate from the source files. In &SCons;, this is usually done by creating one or more separate variant directory trees that are used to hold the built objects files, libraries, and executable programs, etc. for a specific flavor, or variant, of build. &SCons; provides two ways to do this, one through the &SConscript; function that we've already seen, and the second through a more flexible &VariantDir; function. One historical note: the &VariantDir; function used to be called &BuildDir;. That name is still supported but has been deprecated because the &SCons; functionality differs from the model of a "build directory" implemented by other build systems like the GNU Autotools.
Specifying a Variant Directory Tree as Part of an &SConscript; Call The most straightforward way to establish a variant directory tree uses the fact that the usual way to set up a build hierarchy is to have an &SConscript; file in the source subdirectory. If you then pass a &variant_dir; argument to the &SConscript; function call: SConscript('src/SConscript', variant_dir='build') &SCons; will then build all of the files in the &build; subdirectory: % ls src SConscript hello.c % scons -Q cc -o build/hello.o -c build/hello.c cc -o build/hello build/hello.o % ls build SConscript hello hello.c hello.o But wait a minute--what's going on here? &SCons; created the object file build/hello.o in the &build; subdirectory, as expected. But even though our &hello_c; file lives in the &src; subdirectory, &SCons; has actually compiled a build/hello.c file to create the object file. What's happened is that &SCons; has duplicated the &hello_c; file from the &src; subdirectory to the &build; subdirectory, and built the program from there. The next section explains why &SCons; does this.
Why &SCons; Duplicates Source Files in a Variant Directory Tree &SCons; duplicates source files in variant directory trees because it's the most straightforward way to guarantee a correct build regardless of include-file directory paths, relative references between files, or tool support for putting files in different locations, and the &SCons; philosophy is to, by default, guarantee a correct build in all cases. The most direct reason to duplicate source files in variant directories is simply that some tools (mostly older versions) are written to only build their output files in the same directory as the source files. In this case, the choices are either to build the output file in the source directory and move it to the variant directory, or to duplicate the source files in the variant directory. Additionally, relative references between files can cause problems if we don't just duplicate the hierarchy of source files in the variant directory. You can see this at work in use of the C preprocessor #include mechanism with double quotes, not angle brackets: #include "file.h" The de facto standard behavior for most C compilers in this case is to first look in the same directory as the source file that contains the #include line, then to look in the directories in the preprocessor search path. Add to this that the &SCons; implementation of support for code repositories (described below) means not all of the files will be found in the same directory hierarchy, and the simplest way to make sure that the right include file is found is to duplicate the source files into the variant directory, which provides a correct build regardless of the original location(s) of the source files. Although source-file duplication guarantees a correct build even in these end-cases, it can usually be safely disabled. The next section describes how you can disable the duplication of source files in the variant directory.
Telling &SCons; to Not Duplicate Source Files in the Variant Directory Tree In most cases and with most tool sets, &SCons; can place its target files in a build subdirectory without duplicating the source files and everything will work just fine. You can disable the default &SCons; behavior by specifying duplicate=0 when you call the &SConscript; function: SConscript('src/SConscript', variant_dir='build', duplicate=0) When this flag is specified, &SCons; uses the variant directory like most people expect--that is, the output files are placed in the variant directory while the source files stay in the source directory: % ls src SConscript hello.c % scons -Q cc -c src/hello.c -o build/hello.o cc -o build/hello build/hello.o % ls build hello hello.o
The &VariantDir; Function Use the &VariantDir; function to establish that target files should be built in a separate directory from the source files: VariantDir('build', 'src') env = Environment() env.Program('build/hello.c') Note that when you're not using an &SConscript; file in the &src; subdirectory, you must actually specify that the program must be built from the build/hello.c file that &SCons; will duplicate in the &build; subdirectory. When using the &VariantDir; function directly, &SCons; still duplicates the source files in the variant directory by default: % ls src hello.c % scons -Q cc -o build/hello.o -c build/hello.c cc -o build/hello build/hello.o % ls build hello hello.c hello.o You can specify the same duplicate=0 argument that you can specify for an &SConscript; call: VariantDir('build', 'src', duplicate=0) env = Environment() env.Program('build/hello.c') In which case &SCons; will disable duplication of the source files: % ls src hello.c % scons -Q cc -o build/hello.o -c src/hello.c cc -o build/hello build/hello.o % ls build hello hello.o
Using &VariantDir; With an &SConscript; File Even when using the &VariantDir; function, it's much more natural to use it with a subsidiary &SConscript; file. For example, if the src/SConscript looks like this: env = Environment() env.Program('hello.c') Then our &SConstruct; file could look like: VariantDir('build', 'src') SConscript('build/SConscript') Yielding the following output: % ls src SConscript hello.c % scons -Q cc -o build/hello.o -c build/hello.c cc -o build/hello build/hello.o % ls build SConscript hello hello.c hello.o Notice that this is completely equivalent to the use of &SConscript; that we learned about in the previous section.
Using &Glob; with &VariantDir; The &Glob; file name pattern matching function works just as usual when using &VariantDir;. For example, if the src/SConscript looks like this: env = Environment() env.Program('hello', Glob('*.c')) Then with the same &SConstruct; file as in the previous section, and source files f1.c and f2.c in src, we would see the following output: % ls src SConscript f1.c f2.c f2.h % scons -Q cc -o build/f1.o -c build/f1.c cc -o build/f2.o -c build/f2.c cc -o build/hello build/f1.o build/f2.o % ls build SConscript f1.c f1.o f2.c f2.h f2.o hello The &Glob; function returns Nodes in the build/ tree, as you'd expect.
scons-doc-2.3.0/doc/user/functions.in0000644000175000017500000000253412114661557020311 0ustar dktrkranzdktrkranz This appendix contains descriptions of all of the function and construction environment methods in this version of &SCons; &functions-gen; scons-doc-2.3.0/doc/user/actions.xml0000644000175000017500000002573012114661557020136 0ustar dktrkranzdktrkranz &SCons; supports several types of &build_actions; that can be performed to build one or more target files. Usually, a &build_action; is a command-line string that invokes an external command. A build action can also be an external command specified as a list of arguments, or even a Python function. Build action objects are created by the &Action; function. This function is, in fact, what &SCons; uses to interpret the &action; keyword argument when you call the &Builder; function. So the following line that creates a simple Builder: b = Builder(action = 'build < $SOURCE > $TARGET') Is equivalent to: b = Builder(action = Action('build < $SOURCE > $TARGET')) The advantage of using the &Action; function directly is that it can take a number of additional options to modify the action's behavior in many useful ways.
Command Strings as Actions
Suppressing Command-Line Printing XXX
Ignoring Exit Status XXX
Argument Lists as Actions XXX
Python Functions as Actions XXX
Modifying How an Action is Printed
XXX: the &strfunction; keyword argument XXX
XXX: the &cmdstr; keyword argument XXX
Making an Action Depend on Variable Contents: the &varlist; keyword argument XXX
chdir=1 XXX
Batch Building of Multiple Targets from Separate Sources: the &batch_key; keyword argument XXX
Manipulating the Exit Status of an Action: the &exitstatfunc; keyword argument XXX
scons-doc-2.3.0/doc/user/variants.xml0000644000175000017500000001211212114661557020313 0ustar dktrkranzdktrkranz The &variant_dir; keyword argument of the &SConscript; function provides everything we need to show how easy it is to create variant builds using &SCons;. Suppose, for example, that we want to build a program for both Windows and Linux platforms, but that we want to build it in a shared directory with separate side-by-side build directories for the Windows and Linux versions of the program. platform = ARGUMENTS.get('OS', Platform()) include = "#export/$PLATFORM/include" lib = "#export/$PLATFORM/lib" bin = "#export/$PLATFORM/bin" env = Environment(PLATFORM = platform, BINDIR = bin, INCDIR = include, LIBDIR = lib, CPPPATH = [include], LIBPATH = [lib], LIBS = 'world') Export('env') env.SConscript('src/SConscript', variant_dir='build/$PLATFORM') This SConstruct file, when run on a Linux system, yields: % scons -Q OS=linux Install file: "build/linux/world/world.h" as "export/linux/include/world.h" cc -o build/linux/hello/hello.o -c -Iexport/linux/include build/linux/hello/hello.c cc -o build/linux/world/world.o -c -Iexport/linux/include build/linux/world/world.c ar rc build/linux/world/libworld.a build/linux/world/world.o ranlib build/linux/world/libworld.a Install file: "build/linux/world/libworld.a" as "export/linux/lib/libworld.a" cc -o build/linux/hello/hello build/linux/hello/hello.o -Lexport/linux/lib -lworld Install file: "build/linux/hello/hello" as "export/linux/bin/hello" The same SConstruct file on Windows would build: C:\>scons -Q OS=windows Install file: "build/windows/world/world.h" as "export/windows/include/world.h" cl /Fobuild\windows\hello\hello.obj /c build\windows\hello\hello.c /nologo /Iexport\windows\include cl /Fobuild\windows\world\world.obj /c build\windows\world\world.c /nologo /Iexport\windows\include lib /nologo /OUT:build\windows\world\world.lib build\windows\world\world.obj Install file: "build/windows/world/world.lib" as "export/windows/lib/world.lib" link /nologo /OUT:build\windows\hello\hello.exe /LIBPATH:export\windows\lib world.lib build\windows\hello\hello.obj embedManifestExeCheck(target, source, env) Install file: "build/windows/hello/hello.exe" as "export/windows/bin/hello.exe" scons-doc-2.3.0/doc/user/hierarchy.xml0000644000175000017500000005035012114661557020450 0ustar dktrkranzdktrkranz The source code for large software projects rarely stays in a single directory, but is nearly always divided into a hierarchy of directories. Organizing a large software build using &SCons; involves creating a hierarchy of build scripts using the &SConscript; function.
&SConscript; Files As we've already seen, the build script at the top of the tree is called &SConstruct;. The top-level &SConstruct; file can use the &SConscript; function to include other subsidiary scripts in the build. These subsidiary scripts can, in turn, use the &SConscript; function to include still other scripts in the build. By convention, these subsidiary scripts are usually named &SConscript;. For example, a top-level &SConstruct; file might arrange for four subsidiary scripts to be included in the build as follows: SConscript(['drivers/display/SConscript', 'drivers/mouse/SConscript', 'parser/SConscript', 'utilities/SConscript']) In this case, the &SConstruct; file lists all of the &SConscript; files in the build explicitly. (Note, however, that not every directory in the tree necessarily has an &SConscript; file.) Alternatively, the drivers subdirectory might contain an intermediate &SConscript; file, in which case the &SConscript; call in the top-level &SConstruct; file would look like: SConscript(['drivers/SConscript', 'parser/SConscript', 'utilities/SConscript']) And the subsidiary &SConscript; file in the drivers subdirectory would look like: SConscript(['display/SConscript', 'mouse/SConscript']) Whether you list all of the &SConscript; files in the top-level &SConstruct; file, or place a subsidiary &SConscript; file in intervening directories, or use some mix of the two schemes, is up to you and the needs of your software.
Path Names Are Relative to the &SConscript; Directory Subsidiary &SConscript; files make it easy to create a build hierarchy because all of the file and directory names in a subsidiary &SConscript; files are interpreted relative to the directory in which the &SConscript; file lives. Typically, this allows the &SConscript; file containing the instructions to build a target file to live in the same directory as the source files from which the target will be built, making it easy to update how the software is built whenever files are added or deleted (or other changes are made). For example, suppose we want to build two programs &prog1; and &prog2; in two separate directories with the same names as the programs. One typical way to do this would be with a top-level &SConstruct; file like this: SConscript(['prog1/SConscript', 'prog2/SConscript']) And subsidiary &SConscript; files that look like this: env = Environment() env.Program('prog1', ['main.c', 'foo1.c', 'foo2.c']) And this: env = Environment() env.Program('prog2', ['main.c', 'bar1.c', 'bar2.c']) Then, when we run &SCons; in the top-level directory, our build looks like: % scons -Q cc -o prog1/foo1.o -c prog1/foo1.c cc -o prog1/foo2.o -c prog1/foo2.c cc -o prog1/main.o -c prog1/main.c cc -o prog1/prog1 prog1/main.o prog1/foo1.o prog1/foo2.o cc -o prog2/bar1.o -c prog2/bar1.c cc -o prog2/bar2.o -c prog2/bar2.c cc -o prog2/main.o -c prog2/main.c cc -o prog2/prog2 prog2/main.o prog2/bar1.o prog2/bar2.o Notice the following: First, you can have files with the same names in multiple directories, like main.c in the above example. Second, unlike standard recursive use of &Make;, &SCons; stays in the top-level directory (where the &SConstruct; file lives) and issues commands that use the path names from the top-level directory to the target and source files within the hierarchy.
Top-Level Path Names in Subsidiary &SConscript; Files If you need to use a file from another directory, it's sometimes more convenient to specify the path to a file in another directory from the top-level &SConstruct; directory, even when you're using that file in a subsidiary &SConscript; file in a subdirectory. You can tell &SCons; to interpret a path name as relative to the top-level &SConstruct; directory, not the local directory of the &SConscript; file, by appending a &hash; (hash mark) to the beginning of the path name: env = Environment() env.Program('prog', ['main.c', '#lib/foo1.c', 'foo2.c']) In this example, the lib directory is directly underneath the top-level &SConstruct; directory. If the above &SConscript; file is in a subdirectory named src/prog, the output would look like: % scons -Q cc -o lib/foo1.o -c lib/foo1.c cc -o src/prog/foo2.o -c src/prog/foo2.c cc -o src/prog/main.o -c src/prog/main.c cc -o src/prog/prog src/prog/main.o lib/foo1.o src/prog/foo2.o (Notice that the lib/foo1.o object file is built in the same directory as its source file. See , below, for information about how to build the object file in a different subdirectory.)
Absolute Path Names Of course, you can always specify an absolute path name for a file--for example: env = Environment() env.Program('prog', ['main.c', '/usr/joe/lib/foo1.c', 'foo2.c']) Which, when executed, would yield: % scons -Q cc -o src/prog/foo2.o -c src/prog/foo2.c cc -o src/prog/main.o -c src/prog/main.c cc -o /usr/joe/lib/foo1.o -c /usr/joe/lib/foo1.c cc -o src/prog/prog src/prog/main.o /usr/joe/lib/foo1.o src/prog/foo2.o (As was the case with top-relative path names, notice that the /usr/joe/lib/foo1.o object file is built in the same directory as its source file. See , below, for information about how to build the object file in a different subdirectory.)
Sharing Environments (and Other Variables) Between &SConscript; Files In the previous example, each of the subsidiary &SConscript; files created its own construction environment by calling &Environment; separately. This obviously works fine, but if each program must be built with the same construction variables, it's cumbersome and error-prone to initialize separate construction environments in the same way over and over in each subsidiary &SConscript; file. &SCons; supports the ability to export variables from a parent &SConscript; file to its subsidiary &SConscript; files, which allows you to share common initialized values throughout your build hierarchy.
Exporting Variables There are two ways to export a variable, such as a construction environment, from an &SConscript; file, so that it may be used by other &SConscript; files. First, you can call the &Export; function with a list of variables, or a string of white-space separated variable names. Each call to &Export; adds one or more variables to a global list of variables that are available for import by other &SConscript; files. env = Environment() Export('env') You may export more than one variable name at a time: env = Environment() debug = ARGUMENTS['debug'] Export('env', 'debug') Because white space is not legal in Python variable names, the &Export; function will even automatically split a string into separate names for you: Export('env debug') Second, you can specify a list of variables to export as a second argument to the &SConscript; function call: SConscript('src/SConscript', 'env') Or as the &exports; keyword argument: SConscript('src/SConscript', exports='env') These calls export the specified variables to only the listed &SConscript; files. You may, however, specify more than one &SConscript; file in a list: SConscript(['src1/SConscript', 'src2/SConscript'], exports='env') This is functionally equivalent to calling the &SConscript; function multiple times with the same &exports; argument, one per &SConscript; file.
Importing Variables Once a variable has been exported from a calling &SConscript; file, it may be used in other &SConscript; files by calling the &Import; function: Import('env') env.Program('prog', ['prog.c']) The &Import; call makes the env construction environment available to the &SConscript; file, after which the variable can be used to build programs, libraries, etc. Like the &Export; function, the &Import; function can be used with multiple variable names: Import('env', 'debug') env = env.Clone(DEBUG = debug) env.Program('prog', ['prog.c']) And the &Import; function will similarly split a string along white-space into separate variable names: Import('env debug') env = env.Clone(DEBUG = debug) env.Program('prog', ['prog.c']) Lastly, as a special case, you may import all of the variables that have been exported by supplying an asterisk to the &Import; function: Import('*') env = env.Clone(DEBUG = debug) env.Program('prog', ['prog.c']) If you're dealing with a lot of &SConscript; files, this can be a lot simpler than keeping arbitrary lists of imported variables in each file.
Returning Values From an &SConscript; File Sometimes, you would like to be able to use information from a subsidiary &SConscript; file in some way. For example, suppose that you want to create one library from source files scattered throughout a number of subsidiary &SConscript; files. You can do this by using the &Return; function to return values from the subsidiary &SConscript; files to the calling file. If, for example, we have two subdirectories &foo; and &bar; that should each contribute a source file to a Library, what we'd like to be able to do is collect the object files from the subsidiary &SConscript; calls like this: env = Environment() Export('env') objs = [] for subdir in ['foo', 'bar']: o = SConscript('%s/SConscript' % subdir) objs.append(o) env.Library('prog', objs) We can do this by using the &Return; function in the foo/SConscript file like this: Import('env') obj = env.Object('foo.c') Return('obj') (The corresponding bar/SConscript file should be pretty obvious.) Then when we run &SCons;, the object files from the subsidiary subdirectories are all correctly archived in the desired library: % scons -Q cc -o bar/bar.o -c bar/bar.c cc -o foo/foo.o -c foo/foo.c ar rc libprog.a foo/foo.o bar/bar.o ranlib libprog.a
scons-doc-2.3.0/doc/user/command-line.in0000644000175000017500000021241512114661557020645 0ustar dktrkranzdktrkranz &SCons; provides a number of ways for the writer of the &SConscript; files to give the users who will run &SCons; a great deal of control over the build execution. The arguments that the user can specify on the command line are broken down into three types: Options Command-line options always begin with one or two - (hyphen) characters. &SCons; provides ways for you to examine and set options values from within your &SConscript; files, as well as the ability to define your own custom options. See , below. Variables Any command-line argument containing an = (equal sign) is considered a variable setting with the form variable=value. &SCons; provides direct access to all of the command-line variable settings, the ability to apply command-line variable settings to construction environments, and functions for configuring specific types of variables (Boolean values, path names, etc.) with automatic validation of the user's specified values. See , below. Targets Any command-line argument that is not an option or a variable setting (does not begin with a hyphen and does not contain an equal sign) is considered a target that the user (presumably) wants &SCons; to build. A list of Node objects representing the target or targets to build. &SCons; provides access to the list of specified targets, as well as ways to set the default list of targets from within the &SConscript; files. See , below.
Command-Line Options &SCons; has many command-line options that control its behavior. A &SCons; command-line option always begins with one or two - (hyphen) characters.
Not Having to Specify Command-Line Options Each Time: the &SCONSFLAGS; Environment Variable Users may find themselves supplying the same command-line options every time they run &SCons;. For example, you might find it saves time to specify a value of -j 2 to have &SCons; run up to two build commands in parallel. To avoid having to type -j 2 by hand every time, you can set the external environment variable &SCONSFLAGS; to a string containing command-line options that you want &SCons; to use. If, for example, you're using a POSIX shell that's compatible with the Bourne shell, and you always want &SCons; to use the -Q option, you can set the &SCONSFLAGS; environment as follows: def b(target, source, env): pass def s(target, source, env): return " ... [build output] ..." a = Action(b, strfunction = s) env = Environment(BUILDERS = {'A' : Builder(action=a)}) env.A('foo.out', 'foo.in') foo.in scons export SCONSFLAGS="-Q" scons Users of &csh;-style shells on POSIX systems can set the &SCONSFLAGS; environment as follows: $ setenv SCONSFLAGS "-Q" Windows users may typically want to set the &SCONSFLAGS; in the appropriate tab of the System Properties window.
Getting Values Set by Command-Line Options: the &GetOption; Function &SCons; provides the &GetOption; function to get the values set by the various command-line options. One common use of this is to check whether or not the -h or --help option has been specified. Normally, &SCons; does not print its help text until after it has read all of the &SConscript; files, because it's possible that help text has been added by some subsidiary &SConscript; file deep in the source tree hierarchy. Of course, reading all of the &SConscript; files takes extra time. If you know that your configuration does not define any additional help text in subsidiary &SConscript; files, you can speed up the command-line help available to users by using the &GetOption; function to load the subsidiary &SConscript; files only if the the user has not specified the -h or --help option, like so: if not GetOption('help'): SConscript('src/SConscript', export='env') In general, the string that you pass to the &GetOption; function to fetch the value of a command-line option setting is the same as the "most common" long option name (beginning with two hyphen characters), although there are some exceptions. The list of &SCons; command-line options and the &GetOption; strings for fetching them, are available in the section, below.
Setting Values of Command-Line Options: the &SetOption; Function You can also set the values of &SCons; command-line options from within the &SConscript; files by using the &SetOption; function. The strings that you use to set the values of &SCons; command-line options are available in the section, below. One use of the &SetOption; function is to specify a value for the -j or --jobs option, so that users get the improved performance of a parallel build without having to specify the option by hand. A complicating factor is that a good value for the -j option is somewhat system-dependent. One rough guideline is that the more processors your system has, the higher you want to set the -j value, in order to take advantage of the number of CPUs. For example, suppose the administrators of your development systems have standardized on setting a NUM_CPU environment variable to the number of processors on each system. A little bit of Python code to access the environment variable and the &SetOption; function provide the right level of flexibility: import os num_cpu = int(os.environ.get('NUM_CPU', 2)) SetOption('num_jobs', num_cpu) print "running with -j", GetOption('num_jobs') foo.in The above snippet of code sets the value of the --jobs option to the value specified in the $NUM_CPU environment variable. (This is one of the exception cases where the string is spelled differently from the from command-line option. The string for fetching or setting the --jobs value is num_jobs for historical reasons.) The code in this example prints the num_jobs value for illustrative purposes. It uses a default value of 2 to provide some minimal parallelism even on single-processor systems: scons -Q But if the $NUM_CPU environment variable is set, then we use that for the default number of jobs: export NUM_CPU="4" scons -Q But any explicit -j or --jobs value the user specifies an the command line is used first, regardless of whether or not the $NUM_CPU environment variable is set: scons -Q -j 7 export NUM_CPU="4" scons -Q -j 3
Strings for Getting or Setting Values of &SCons; Command-Line Options The strings that you can pass to the &GetOption; and &SetOption; functions usually correspond to the first long-form option name (beginning with two hyphen characters: --), after replacing any remaining hyphen characters with underscores. The full list of strings and the variables they correspond to is as follows: String for &GetOption; and &SetOption; Command-Line Option(s) cache_debug cache_disable cache_force cache_show clean , , config directory , diskcheck duplicate file , , , help , ignore_errors implicit_cache implicit_deps_changed implicit_deps_unchanged interactive , keep_going , max_drift no_exec , , , , no_site_dir num_jobs , profile_file question , random repository , , silent , , site_dir stack_size taskmastertrace_file warn
Adding Custom Command-Line Options: the &AddOption; Function &SCons; also allows you to define your own command-line options with the &AddOption; function. The &AddOption; function takes the same arguments as the optparse.add_option function from the standard Python library. The &AddOption; function is, in fact, implemented using a subclass of the optparse.OptionParser. Once you have added a custom command-line option with the &AddOption; function, the value of the option (if any) is immediately available using the standard &GetOption; function. (The value can also be set using &SetOption;, although that's not very useful in practice because a default value can be specified in directly in the &AddOption; call.) One useful example of using this functionality is to provide a for users: AddOption('--prefix', dest='prefix', type='string', nargs=1, action='store', metavar='DIR', help='installation prefix') env = Environment(PREFIX = GetOption('prefix')) installed_foo = env.Install('$PREFIX/usr/bin', 'foo.in') Default(installed_foo) foo.in The above code uses the &GetOption; function to set the $PREFIX construction variable to any value that the user specifies with a command-line option of --prefix. Because $PREFIX will expand to a null string if it's not initialized, running &SCons; without the option of --prefix will install the file in the /usr/bin/ directory: scons -Q -n But specifying --prefix=/tmp/install on the command line causes the file to be installed in the /tmp/install/usr/bin/ directory: scons -Q -n --prefix=/tmp/install
Command-Line <varname>variable</varname>=<varname>value</varname> Build Variables You may want to control various aspects of your build by allowing the user to specify variable=value values on the command line. For example, suppose you want users to be able to build a debug version of a program by running &SCons; as follows: % scons -Q debug=1 &SCons; provides an &ARGUMENTS; dictionary that stores all of the variable=value assignments from the command line. This allows you to modify aspects of your build in response to specifications on the command line. (Note that unless you want to require that users always specify a variable, you probably want to use the Python ARGUMENTS.get() function, which allows you to specify a default value to be used if there is no specification on the command line.) The following code sets the &cv-link-CCFLAGS; construction variable in response to the debug flag being set in the &ARGUMENTS; dictionary: env = Environment() debug = ARGUMENTS.get('debug', 0) if int(debug): env.Append(CCFLAGS = '-g') env.Program('prog.c') prog.c This results in the -g compiler option being used when debug=1 is used on the command line: scons -Q debug=0 scons -Q debug=0 scons -Q debug=1 scons -Q debug=1 Notice that &SCons; keeps track of the last values used to build the object files, and as a result correctly rebuilds the object and executable files only when the value of the debug argument has changed. The &ARGUMENTS; dictionary has two minor drawbacks. First, because it is a dictionary, it can only store one value for each specified keyword, and thus only "remembers" the last setting for each keyword on the command line. This makes the &ARGUMENTS; dictionary inappropriate if users should be able to specify multiple values on the command line for a given keyword. Second, it does not preserve the order in which the variable settings were specified, which is a problem if you want the configuration to behave differently in response to the order in which the build variable settings were specified on the command line. To accomodate these requirements, &SCons; provides an &ARGLIST; variable that gives you direct access to variable=value settings on the command line, in the exact order they were specified, and without removing any duplicate settings. Each element in the &ARGLIST; variable is itself a two-element list containing the keyword and the value of the setting, and you must loop through, or otherwise select from, the elements of &ARGLIST; to process the specific settings you want in whatever way is appropriate for your configuration. For example, the following code to let the user add to the &CPPDEFINES; construction variable by specifying multiple define= settings on the command line: cppdefines = [] for key, value in ARGLIST: if key == 'define': cppdefines.append(value) env = Environment(CPPDEFINES = cppdefines) env.Object('prog.c') prog.c Yields the following output: scons -Q define=FOO scons -Q define=FOO define=BAR Note that the &ARGLIST; and &ARGUMENTS; variables do not interfere with each other, but merely provide slightly different views into how the user specified variable=value settings on the command line. You can use both variables in the same &SCons; configuration. In general, the &ARGUMENTS; dictionary is more convenient to use, (since you can just fetch variable settings through a dictionary access), and the &ARGLIST; list is more flexible (since you can examine the specific order in which the user's command-line variabe settings).
Controlling Command-Line Build Variables Being able to use a command-line build variable like debug=1 is handy, but it can be a chore to write specific Python code to recognize each such variable, check for errors and provide appropriate messages, and apply the values to a construction variable. To help with this, &SCons; supports a class to define such build variables easily, and a mechanism to apply the build variables to a construction environment. This allows you to control how the build variables affect construction environments. For example, suppose that you want users to set a &RELEASE; construction variable on the command line whenever the time comes to build a program for release, and that the value of this variable should be added to the command line with the appropriate -D option (or other command line option) to pass the value to the C compiler. Here's how you might do that by setting the appropriate value in a dictionary for the &cv-link-CPPDEFINES; construction variable: vars = Variables(None, ARGUMENTS) vars.Add('RELEASE', 'Set to 1 to build for release', 0) env = Environment(variables = vars, CPPDEFINES={'RELEASE_BUILD' : '${RELEASE}'}) env.Program(['foo.c', 'bar.c']) foo.c bar.c This &SConstruct; file first creates a &Variables; object which uses the values from the command-line options dictionary &ARGUMENTS; (the vars = Variables(None, ARGUMENTS) call). It then uses the object's &Add; method to indicate that the &RELEASE; variable can be set on the command line, and that its default value will be 0 (the third argument to the &Add; method). The second argument is a line of help text; we'll learn how to use it in the next section. We then pass the created &Variables; object as a &variables; keyword argument to the &Environment; call used to create the construction environment. This then allows a user to set the &RELEASE; build variable on the command line and have the variable show up in the command line used to build each object from a C source file: scons -Q RELEASE=1 NOTE: Before &SCons; release 0.98.1, these build variables were known as "command-line build options." The class was actually named the &Options; class, and in the sections below, the various functions were named &BoolOption;, &EnumOption;, &ListOption;, &PathOption;, &PackageOption; and &AddOptions;. These older names still work, and you may encounter them in older &SConscript; files, but they have been officially deprecated as of &SCons; version 2.0.
Providing Help for Command-Line Build Variables To make command-line build variables most useful, you ideally want to provide some help text that will describe the available variables when the user runs scons -h. You could write this text by hand, but &SCons; provides an easier way. &Variables; objects support a &GenerateHelpText; method that will, as its name suggests, generate text that describes the various variables that have been added to it. You then pass the output from this method to the &Help; function: vars = Variables(None, ARGUMENTS) vars.Add('RELEASE', 'Set to 1 to build for release', 0) env = Environment(variables = vars) Help(vars.GenerateHelpText(env)) &SCons; will now display some useful text when the -h option is used: scons -Q -h Notice that the help output shows the default value, and the current actual value of the build variable.
Reading Build Variables From a File Giving the user a way to specify the value of a build variable on the command line is useful, but can still be tedious if users must specify the variable every time they run &SCons;. We can let users provide customized build variable settings in a local file by providing a file name when we create the &Variables; object: vars = Variables('custom.py') vars.Add('RELEASE', 'Set to 1 to build for release', 0) env = Environment(variables = vars, CPPDEFINES={'RELEASE_BUILD' : '${RELEASE}'}) env.Program(['foo.c', 'bar.c']) Help(vars.GenerateHelpText(env)) foo.c bar.c RELEASE = 1 This then allows the user to control the &RELEASE; variable by setting it in the &custom_py; file: Note that this file is actually executed like a Python script. Now when we run &SCons;: scons -Q And if we change the contents of &custom_py; to: vars = Variables('custom.py') vars.Add('RELEASE', 'Set to 1 to build for release', 0) env = Environment(variables = vars, CPPDEFINES={'RELEASE_BUILD' : '${RELEASE}'}) env.Program(['foo.c', 'bar.c']) Help(vars.GenerateHelpText(env)) foo.c bar.c RELEASE = 0 The object files are rebuilt appropriately with the new variable: scons -Q Finally, you can combine both methods with: vars = Variables('custom.py', ARGUMENTS) where values in the option file &custom_py; get overwritten by the ones specified on the command line.
Pre-Defined Build Variable Functions &SCons; provides a number of functions that provide ready-made behaviors for various types of command-line build variables.
True/False Values: the &BoolVariable; Build Variable Function It's often handy to be able to specify a variable that controls a simple Boolean variable with a &true; or &false; value. It would be even more handy to accomodate users who have different preferences for how to represent &true; or &false; values. The &BoolVariable; function makes it easy to accomodate these common representations of &true; or &false;. The &BoolVariable; function takes three arguments: the name of the build variable, the default value of the build variable, and the help string for the variable. It then returns appropriate information for passing to the &Add; method of a &Variables; object, like so: vars = Variables('custom.py') vars.Add(BoolVariable('RELEASE', 'Set to build for release', 0)) env = Environment(variables = vars, CPPDEFINES={'RELEASE_BUILD' : '${RELEASE}'}) env.Program('foo.c') foo.c With this build variable, the &RELEASE; variable can now be enabled by setting it to the value yes or t: scons -Q RELEASE=yes foo.o scons -Q RELEASE=t foo.o Other values that equate to &true; include y, 1, on and all. Conversely, &RELEASE; may now be given a &false; value by setting it to no or f: scons -Q RELEASE=no foo.o scons -Q RELEASE=f foo.o Other values that equate to &false; include n, 0, off and none. Lastly, if a user tries to specify any other value, &SCons; supplies an appropriate error message: scons -Q RELEASE=bad_value foo.o
Single Value From a List: the &EnumVariable; Build Variable Function Suppose that we want a user to be able to set a &COLOR; variable that selects a background color to be displayed by an application, but that we want to restrict the choices to a specific set of allowed colors. This can be set up quite easily using the &EnumVariable;, which takes a list of &allowed_values; in addition to the variable name, default value, and help text arguments: vars = Variables('custom.py') vars.Add(EnumVariable('COLOR', 'Set background color', 'red', allowed_values=('red', 'green', 'blue'))) env = Environment(variables = vars, CPPDEFINES={'COLOR' : '"${COLOR}"'}) env.Program('foo.c') foo.c The user can now explicity set the &COLOR; build variable to any of the specified allowed values: scons -Q COLOR=red foo.o scons -Q COLOR=blue foo.o scons -Q COLOR=green foo.o But, almost more importantly, an attempt to set &COLOR; to a value that's not in the list generates an error message: scons -Q COLOR=magenta foo.o The &EnumVariable; function also supports a way to map alternate names to allowed values. Suppose, for example, that we want to allow the user to use the word navy as a synonym for blue. We do this by adding a ↦ dictionary that will map its key values to the desired legal value: vars = Variables('custom.py') vars.Add(EnumVariable('COLOR', 'Set background color', 'red', allowed_values=('red', 'green', 'blue'), map={'navy':'blue'})) env = Environment(variables = vars, CPPDEFINES={'COLOR' : '"${COLOR}"'}) env.Program('foo.c') foo.c As desired, the user can then use navy on the command line, and &SCons; will translate it into blue when it comes time to use the &COLOR; variable to build a target: scons -Q COLOR=navy foo.o By default, when using the &EnumVariable; function, arguments that differ from the legal values only in case are treated as illegal values: scons -Q COLOR=Red foo.o scons -Q COLOR=BLUE foo.o scons -Q COLOR=nAvY foo.o The &EnumVariable; function can take an additional &ignorecase; keyword argument that, when set to 1, tells &SCons; to allow case differences when the values are specified: vars = Variables('custom.py') vars.Add(EnumVariable('COLOR', 'Set background color', 'red', allowed_values=('red', 'green', 'blue'), map={'navy':'blue'}, ignorecase=1)) env = Environment(variables = vars, CPPDEFINES={'COLOR' : '"${COLOR}"'}) env.Program('foo.c') foo.c Which yields the output: scons -Q COLOR=Red foo.o scons -Q COLOR=BLUE foo.o scons -Q COLOR=nAvY foo.o scons -Q COLOR=green foo.o Notice that an &ignorecase; value of 1 preserves the case-spelling that the user supplied. If you want &SCons; to translate the names into lower-case, regardless of the case used by the user, specify an &ignorecase; value of 2: vars = Variables('custom.py') vars.Add(EnumVariable('COLOR', 'Set background color', 'red', allowed_values=('red', 'green', 'blue'), map={'navy':'blue'}, ignorecase=2)) env = Environment(variables = vars, CPPDEFINES={'COLOR' : '"${COLOR}"'}) env.Program('foo.c') foo.c Now &SCons; will use values of red, green or blue regardless of how the user spells those values on the command line: scons -Q COLOR=Red foo.o scons -Q COLOR=nAvY foo.o scons -Q COLOR=GREEN foo.o
Multiple Values From a List: the &ListVariable; Build Variable Function Another way in which you might want to allow users to control a build variable is to specify a list of one or more legal values. &SCons; supports this through the &ListVariable; function. If, for example, we want a user to be able to set a &COLORS; variable to one or more of the legal list of values: vars = Variables('custom.py') vars.Add(ListVariable('COLORS', 'List of colors', 0, ['red', 'green', 'blue'])) env = Environment(variables = vars, CPPDEFINES={'COLORS' : '"${COLORS}"'}) env.Program('foo.c') foo.c A user can now specify a comma-separated list of legal values, which will get translated into a space-separated list for passing to the any build commands: scons -Q COLORS=red,blue foo.o scons -Q COLORS=blue,green,red foo.o In addition, the &ListVariable; function allows the user to specify explicit keywords of &all; or &none; to select all of the legal values, or none of them, respectively: scons -Q COLORS=all foo.o scons -Q COLORS=none foo.o And, of course, an illegal value still generates an error message: scons -Q COLORS=magenta foo.o
Path Names: the &PathVariable; Build Variable Function &SCons; supports a &PathVariable; function to make it easy to create a build variable to control an expected path name. If, for example, you need to define a variable in the preprocessor that controls the location of a configuration file: vars = Variables('custom.py') vars.Add(PathVariable('CONFIG', 'Path to configuration file', '__ROOT__/etc/my_config')) env = Environment(variables = vars, CPPDEFINES={'CONFIG_FILE' : '"$CONFIG"'}) env.Program('foo.c') foo.c /opt/location /opt/location This then allows the user to override the &CONFIG; build variable on the command line as necessary: scons -Q foo.o scons -Q CONFIG=__ROOT__/usr/local/etc/other_config foo.o By default, &PathVariable; checks to make sure that the specified path exists and generates an error if it doesn't: scons -Q CONFIG=__ROOT__/does/not/exist foo.o &PathVariable; provides a number of methods that you can use to change this behavior. If you want to ensure that any specified paths are, in fact, files and not directories, use the &PathVariable_PathIsFile; method: vars = Variables('custom.py') vars.Add(PathVariable('CONFIG', 'Path to configuration file', '__ROOT__/etc/my_config', PathVariable.PathIsFile)) env = Environment(variables = vars, CPPDEFINES={'CONFIG_FILE' : '"$CONFIG"'}) env.Program('foo.c') foo.c /opt/location Conversely, to ensure that any specified paths are directories and not files, use the &PathVariable_PathIsDir; method: vars = Variables('custom.py') vars.Add(PathVariable('DBDIR', 'Path to database directory', '__ROOT__/var/my_dbdir', PathVariable.PathIsDir)) env = Environment(variables = vars, CPPDEFINES={'DBDIR' : '"$DBDIR"'}) env.Program('foo.c') foo.c /opt/location If you want to make sure that any specified paths are directories, and you would like the directory created if it doesn't already exist, use the &PathVariable_PathIsDirCreate; method: vars = Variables('custom.py') vars.Add(PathVariable('DBDIR', 'Path to database directory', '__ROOT__/var/my_dbdir', PathVariable.PathIsDirCreate)) env = Environment(variables = vars, CPPDEFINES={'DBDIR' : '"$DBDIR"'}) env.Program('foo.c') foo.c /opt/location Lastly, if you don't care whether the path exists, is a file, or a directory, use the &PathVariable_PathAccept; method to accept any path that the user supplies: vars = Variables('custom.py') vars.Add(PathVariable('OUTPUT', 'Path to output file or directory', None, PathVariable.PathAccept)) env = Environment(variables = vars, CPPDEFINES={'OUTPUT' : '"$OUTPUT"'}) env.Program('foo.c') foo.c
Enabled/Disabled Path Names: the &PackageVariable; Build Variable Function Sometimes you want to give users even more control over a path name variable, allowing them to explicitly enable or disable the path name by using yes or no keywords, in addition to allow them to supply an explicit path name. &SCons; supports the &PackageVariable; function to support this: vars = Variables('custom.py') vars.Add(PackageVariable('PACKAGE', 'Location package', '__ROOT__/opt/location')) env = Environment(variables = vars, CPPDEFINES={'PACKAGE' : '"$PACKAGE"'}) env.Program('foo.c') foo.c /opt/location /opt/location When the &SConscript; file uses the &PackageVariable; funciton, user can now still use the default or supply an overriding path name, but can now explicitly set the specified variable to a value that indicates the package should be enabled (in which case the default should be used) or disabled: scons -Q foo.o scons -Q PACKAGE=__ROOT__/usr/local/location foo.o scons -Q PACKAGE=yes foo.o scons -Q PACKAGE=no foo.o
Adding Multiple Command-Line Build Variables at Once Lastly, &SCons; provides a way to add multiple build variables to a &Variables; object at once. Instead of having to call the &Add; method multiple times, you can call the &AddVariables; method with a list of build variables to be added to the object. Each build variable is specified as either a tuple of arguments, just like you'd pass to the &Add; method itself, or as a call to one of the pre-defined functions for pre-packaged command-line build variables. in any order: vars = Variables() vars.AddVariables( ('RELEASE', 'Set to 1 to build for release', 0), ('CONFIG', 'Configuration file', '/etc/my_config'), BoolVariable('warnings', 'compilation with -Wall and similiar', 1), EnumVariable('debug', 'debug output and symbols', 'no', allowed_values=('yes', 'no', 'full'), map={}, ignorecase=0), # case sensitive ListVariable('shared', 'libraries to build as shared libraries', 'all', names = list_of_libs), PackageVariable('x11', 'use X11 installed here (yes = search some places)', 'yes'), PathVariable('qtdir', 'where the root of Qt is installed', qtdir), )
Handling Unknown Command-Line Build Variables: the &UnknownVariables; Function Users may, of course, occasionally misspell variable names in their command-line settings. &SCons; does not generate an error or warning for any unknown variables the users specifies on the command line. (This is in no small part because you may be processing the arguments directly using the &ARGUMENTS; dictionary, and therefore &SCons; can't know in the general case whether a given "misspelled" variable is really unknown and a potential problem, or something that your &SConscript; file will handle directly with some Python code.) If, however, you're using a &Variables; object to define a specific set of command-line build variables that you expect users to be able to set, you may want to provide an error message or warning of your own if the user supplies a variable setting that is not among the defined list of variable names known to the &Variables; object. You can do this by calling the &UnknownVariables; method of the &Variables; object: vars = Variables(None) vars.Add('RELEASE', 'Set to 1 to build for release', 0) env = Environment(variables = vars, CPPDEFINES={'RELEASE_BUILD' : '${RELEASE}'}) unknown = vars.UnknownVariables() if unknown: print "Unknown variables:", unknown.keys() Exit(1) env.Program('foo.c') foo.c The &UnknownVariables; method returns a dictionary containing the keywords and values of any variables the user specified on the command line that are not among the variables known to the &Variables; object (from having been specified using the &Variables; object's&Add; method). In the examble above, we check for whether the dictionary returned by the &UnknownVariables; is non-empty, and if so print the Python list containing the names of the unknwown variables and then call the &Exit; function to terminate &SCons;: scons -Q NOT_KNOWN=foo Of course, you can process the items in the dictionary returned by the &UnknownVariables; function in any way appropriate to your build configuration, including just printing a warning message but not exiting, logging an error somewhere, etc. Note that you must delay the call of &UnknownVariables; until after you have applied the &Variables; object to a construction environment with the variables= keyword argument of an &Environment; call.
Command-Line Targets
Fetching Command-Line Targets: the &COMMAND_LINE_TARGETS; Variable &SCons; supports a &COMMAND_LINE_TARGETS; variable that lets you fetch the list of targets that the user specified on the command line. You can use the targets to manipulate the build in any way you wish. As a simple example, suppose that you want to print a reminder to the user whenever a specific program is built. You can do this by checking for the target in the &COMMAND_LINE_TARGETS; list: if 'bar' in COMMAND_LINE_TARGETS: print "Don't forget to copy `bar' to the archive!" Default(Program('foo.c')) Program('bar.c') foo.c foo.c Then, running &SCons; with the default target works as it always does, but explicity specifying the &bar; target on the command line generates the warning message: scons -Q scons -Q bar Another practical use for the &COMMAND_LINE_TARGETS; variable might be to speed up a build by only reading certain subsidiary &SConscript; files if a specific target is requested.
Controlling the Default Targets: the &Default; Function One of the most basic things you can control is which targets &SCons; will build by default--that is, when there are no targets specified on the command line. As mentioned previously, &SCons; will normally build every target in or below the current directory by default--that is, when you don't explicitly specify one or more targets on the command line. Sometimes, however, you may want to specify explicitly that only certain programs, or programs in certain directories, should be built by default. You do this with the &Default; function: env = Environment() hello = env.Program('hello.c') env.Program('goodbye.c') Default(hello) hello.c goodbye.c This &SConstruct; file knows how to build two programs, &hello; and &goodbye;, but only builds the &hello; program by default: scons -Q scons -Q scons -Q goodbye Note that, even when you use the &Default; function in your &SConstruct; file, you can still explicitly specify the current directory (.) on the command line to tell &SCons; to build everything in (or below) the current directory: scons -Q . You can also call the &Default; function more than once, in which case each call adds to the list of targets to be built by default: env = Environment() prog1 = env.Program('prog1.c') Default(prog1) prog2 = env.Program('prog2.c') prog3 = env.Program('prog3.c') Default(prog3) prog1.c prog2.c prog3.c Or you can specify more than one target in a single call to the &Default; function: env = Environment() prog1 = env.Program('prog1.c') prog2 = env.Program('prog2.c') prog3 = env.Program('prog3.c') Default(prog1, prog3) Either of these last two examples will build only the prog1 and prog3 programs by default: scons -Q scons -Q . You can list a directory as an argument to &Default;: env = Environment() env.Program(['prog1/main.c', 'prog1/foo.c']) env.Program(['prog2/main.c', 'prog2/bar.c']) Default('prog1') int main() { printf("prog1/main.c\n"); } int foo() { printf("prog1/foo.c\n"); } int main() { printf("prog2/main.c\n"); } int bar() { printf("prog2/bar.c\n"); } In which case only the target(s) in that directory will be built by default: scons -Q scons -Q scons -Q . Lastly, if for some reason you don't want any targets built by default, you can use the Python None variable: env = Environment() prog1 = env.Program('prog1.c') prog2 = env.Program('prog2.c') Default(None) prog1.c prog2.c Which would produce build output like: scons -Q scons -Q .
Fetching the List of Default Targets: the &DEFAULT_TARGETS; Variable &SCons; supports a &DEFAULT_TARGETS; variable that lets you get at the current list of default targets. The &DEFAULT_TARGETS; variable has two important differences from the &COMMAND_LINE_TARGETS; variable. First, the &DEFAULT_TARGETS; variable is a list of internal &SCons; nodes, so you need to convert the list elements to strings if you want to print them or look for a specific target name. Fortunately, you can do this easily by using the Python map function to run the list through str: prog1 = Program('prog1.c') Default(prog1) print "DEFAULT_TARGETS is", map(str, DEFAULT_TARGETS) prog1.c (Keep in mind that all of the manipulation of the &DEFAULT_TARGETS; list takes place during the first phase when &SCons; is reading up the &SConscript; files, which is obvious if we leave off the -Q flag when we run &SCons;:) scons Second, the contents of the &DEFAULT_TARGETS; list change in response to calls to the &Default; function, as you can see from the following &SConstruct; file: prog1 = Program('prog1.c') Default(prog1) print "DEFAULT_TARGETS is now", map(str, DEFAULT_TARGETS) prog2 = Program('prog2.c') Default(prog2) print "DEFAULT_TARGETS is now", map(str, DEFAULT_TARGETS) prog1.c prog2.c Which yields the output: scons In practice, this simply means that you need to pay attention to the order in which you call the &Default; function and refer to the &DEFAULT_TARGETS; list, to make sure that you don't examine the list before you've added the default targets you expect to find in it.
Fetching the List of Build Targets, Regardless of Origin: the &BUILD_TARGETS; Variable We've already been introduced to the &COMMAND_LINE_TARGETS; variable, which contains a list of targets specified on the command line, and the &DEFAULT_TARGETS; variable, which contains a list of targets specified via calls to the &Default; method or function. Sometimes, however, you want a list of whatever targets &SCons; will try to build, regardless of whether the targets came from the command line or a &Default; call. You could code this up by hand, as follows: if COMMAND_LINE_TARGETS: targets = COMMAND_LINE_TARGETS else: targets = DEFAULT_TARGETS &SCons;, however, provides a convenient &BUILD_TARGETS; variable that eliminates the need for this by-hand manipulation. Essentially, the &BUILD_TARGETS; variable contains a list of the command-line targets, if any were specified, and if no command-line targets were specified, it contains a list of the targets specified via the &Default; method or function. Because &BUILD_TARGETS; may contain a list of &SCons; nodes, you must convert the list elements to strings if you want to print them or look for a specific target name, just like the &DEFAULT_TARGETS; list: prog1 = Program('prog1.c') Program('prog2.c') Default(prog1) print "BUILD_TARGETS is", map(str, BUILD_TARGETS) prog1.c prog2.c Notice how the value of &BUILD_TARGETS; changes depending on whether a target is specified on the command line: scons -Q scons -Q prog2 scons -Q -c .
scons-doc-2.3.0/doc/user/depends.in0000644000175000017500000015545612114661557017737 0ustar dktrkranzdktrkranz So far we've seen how &SCons; handles one-time builds. But one of the main functions of a build tool like &SCons; is to rebuild only what is necessary when source files change--or, put another way, &SCons; should not waste time rebuilding things that don't need to be rebuilt. You can see this at work simply by re-invoking &SCons; after building our simple &hello; example: Program('hello.c') int main() { printf("Hello, world!\n"); } scons -Q scons -Q The second time it is executed, &SCons; realizes that the &hello; program is up-to-date with respect to the current &hello_c; source file, and avoids rebuilding it. You can see this more clearly by naming the &hello; program explicitly on the command line: scons -Q hello scons -Q hello Note that &SCons; reports "...is up to date" only for target files named explicitly on the command line, to avoid cluttering the output.
Deciding When an Input File Has Changed: the &Decider; Function Another aspect of avoiding unnecessary rebuilds is the fundamental build tool behavior of rebuilding things when an input file changes, so that the built software is up to date. By default, &SCons; keeps track of this through an MD5 &signature;, or checksum, of the contents of each file, although you can easily configure &SCons; to use the modification times (or time stamps) instead. You can even specify your own Python function for deciding if an input file has changed.
Using MD5 Signatures to Decide if a File Has Changed By default, &SCons; keeps track of whether a file has changed based on an MD5 checksum of the file's contents, not the file's modification time. This means that you may be surprised by the default &SCons; behavior if you are used to the &Make; convention of forcing a rebuild by updating the file's modification time (using the &touch; command, for example): scons -Q hello touch hello.c scons -Q hello Even though the file's modification time has changed, &SCons; realizes that the contents of the &hello_c; file have not changed, and therefore that the &hello; program need not be rebuilt. This avoids unnecessary rebuilds when, for example, someone rewrites the contents of a file without making a change. But if the contents of the file really do change, then &SCons; detects the change and rebuilds the program as required: scons -Q hello edit hello.c scons -Q hello Note that you can, if you wish, specify this default behavior (MD5 signatures) explicitly using the &Decider; function as follows: Program('hello.c') Decider('MD5') You can also use the string 'content' as a synonym for 'MD5' when calling the &Decider; function.
Ramifications of Using MD5 Signatures Using MD5 signatures to decide if an input file has changed has one surprising benefit: if a source file has been changed in such a way that the contents of the rebuilt target file(s) will be exactly the same as the last time the file was built, then any "downstream" target files that depend on the rebuilt-but-not-changed target file actually need not be rebuilt. So if, for example, a user were to only change a comment in a &hello_c; file, then the rebuilt &hello_o; file would be exactly the same as the one previously built (assuming the compiler doesn't put any build-specific information in the object file). &SCons; would then realize that it would not need to rebuild the &hello; program as follows: scons -Q hello edit hello.c scons -Q hello In essence, &SCons; "short-circuits" any dependent builds when it realizes that a target file has been rebuilt to exactly the same file as the last build. This does take some extra processing time to read the contents of the target (&hello_o;) file, but often saves time when the rebuild that was avoided would have been time-consuming and expensive.
Using Time Stamps to Decide If a File Has Changed If you prefer, you can configure &SCons; to use the modification time of a file, not the file contents, when deciding if a target needs to be rebuilt. &SCons; gives you two ways to use time stamps to decide if an input file has changed since the last time a target has been built. The most familiar way to use time stamps is the way &Make; does: that is, have &SCons; decide that a target must be rebuilt if a source file's modification time is newer than the target file. To do this, call the &Decider; function as follows: Object('hello.c') Decider('timestamp-newer') int main() { printf("Hello, world!\n"); } This makes &SCons; act like &Make; when a file's modification time is updated (using the &touch; command, for example): scons -Q hello.o touch hello.c scons -Q hello.o And, in fact, because this behavior is the same as the behavior of &Make;, you can also use the string 'make' as a synonym for 'timestamp-newer' when calling the &Decider; function: Object('hello.c') Decider('make') One drawback to using times stamps exactly like &Make; is that if an input file's modification time suddenly becomes older than a target file, the target file will not be rebuilt. This can happen if an old copy of a source file is restored from a backup archive, for example. The contents of the restored file will likely be different than they were the last time a dependent target was built, but the target won't be rebuilt because the modification time of the source file is not newer than the target. Because &SCons; actually stores information about the source files' time stamps whenever a target is built, it can handle this situation by checking for an exact match of the source file time stamp, instead of just whether or not the source file is newer than the target file. To do this, specify the argument 'timestamp-match' when calling the &Decider; function: Object('hello.c') Decider('timestamp-match') int main() { printf("Hello, world!\n"); } When configured this way, &SCons; will rebuild a target whenever a source file's modification time has changed. So if we use the touch -t option to change the modification time of &hello_c; to an old date (January 1, 1989), &SCons; will still rebuild the target file: scons -Q hello.o touch -t 198901010000 hello.c scons -Q hello.o In general, the only reason to prefer timestamp-newer instead of timestamp-match, would be if you have some specific reason to require this &Make;-like behavior of not rebuilding a target when an otherwise-modified source file is older.
Deciding If a File Has Changed Using Both MD Signatures and Time Stamps As a performance enhancement, &SCons; provides a way to use MD5 checksums of file contents but to read those contents only when the file's timestamp has changed. To do this, call the &Decider; function with 'MD5-timestamp' argument as follows: Program('hello.c') Decider('MD5-timestamp') int main() { printf("Hello, world!\n"); } So configured, &SCons; will still behave like it does when using Decider('MD5'): % scons -Q hello cc -o hello.o -c hello.c cc -o hello hello.o % touch hello.c % scons -Q hello scons: `hello' is up to date. % edit hello.c [CHANGE THE CONTENTS OF hello.c] % scons -Q hello cc -o hello.o -c hello.c cc -o hello hello.o However, the second call to &SCons; in the above output, when the build is up-to-date, will have been performed by simply looking at the modification time of the &hello_c; file, not by opening it and performing an MD5 checksum calcuation on its contents. This can significantly speed up many up-to-date builds. The only drawback to using Decider('MD5-timestamp') is that &SCons; will not rebuild a target file if a source file was modified within one second of the last time &SCons; built the file. While most developers are programming, this isn't a problem in practice, since it's unlikely that someone will have built and then thought quickly enough to make a substantive change to a source file within one second. Certain build scripts or continuous integration tools may, however, rely on the ability to apply changes to files automatically and then rebuild as quickly as possible, in which case use of Decider('MD5-timestamp') may not be appropriate.
Writing Your Own Custom &Decider; Function The different string values that we've passed to the &Decider; function are essentially used by &SCons; to pick one of several specific internal functions that implement various ways of deciding if a dependency (usually a source file) has changed since a target file has been built. As it turns out, you can also supply your own function to decide if a dependency has changed. For example, suppose we have an input file that contains a lot of data, in some specific regular format, that is used to rebuild a lot of different target files, but each target file really only depends on one particular section of the input file. We'd like to have each target file depend on only its section of the input file. However, since the input file may contain a lot of data, we want to open the input file only if its timestamp has changed. This could be done with a custom &Decider; function that might look something like this: Program('hello.c') def decide_if_changed(dependency, target, prev_ni): if self.get_timestamp() != prev_ni.timestamp: dep = str(dependency) tgt = str(target) if specific_part_of_file_has_changed(dep, tgt): return True return False Decider(decide_if_changed) int main() { printf("Hello, world!\n"); } Note that in the function definition, the dependency (input file) is the first argument, and then the ⌖. Both of these are passed to the functions as SCons &Node; objects, which we convert to strings using the Python str(). The third argument, prev_ni, is an object that holds the signature or timestamp information that was recorded about the dependency the last time the target was built. A prev_ni object can hold different information, depending on the type of thing that the dependency argument represents. For normal files, the prev_ni object has the following attributes: .csig The content signature, or MD5 checksum, of the contents of the dependency file the list time the ⌖ was built. .size The size in bytes of the dependency file the list time the target was built. .timestamp The modification time of the dependency file the list time the ⌖ was built. Note that ignoring some of the arguments in your custom &Decider; function is a perfectly normal thing to do, if they don't impact the way you want to decide if the dependency file has changed. Another thing to look out for is the fact that the three attributes above may not be present at the time of the first run. Without any prior build, no targets have been created and no .sconsign DB file exists yet. So, you should always check whether the prev_ni attribute in question is available. We finally present a small example for a csig-based decider function. Note how the signature information for the dependency file has to get initialized via get_csig during each function call (this is mandatory!). env = Environment() def config_file_decider(dependency, target, prev_ni): import os.path # We always have to init the .csig value... dep_csig = dependency.get_csig() # .csig may not exist, because no target was built yet... if 'csig' not in dir(prev_ni): return True # Target file may not exist yet if not os.path.exists(str(target.abspath)): return True if dep_csig != prev_ni.csig: # Some change on source file => update installed one return True return False def update_file(): f = open("test.txt","a") f.write("some line\n") f.close() update_file() # Activate our own decider function env.Decider(config_file_decider) env.Install("install","test.txt")
Mixing Different Ways of Deciding If a File Has Changed The previous examples have all demonstrated calling the global &Decider; function to configure all dependency decisions that &SCons; makes. Sometimes, however, you want to be able to configure different decision-making for different targets. When that's necessary, you can use the env.Decider method to affect only the configuration decisions for targets built with a specific construction environment. For example, if we arbitrarily want to build one program using MD5 checkums and another using file modification times from the same source we might configure it this way: env1 = Environment(CPPPATH = ['.']) env2 = env1.Clone() env2.Decider('timestamp-match') env1.Program('prog-MD5', 'program1.c') env2.Program('prog-timestamp', 'program2.c') #include "inc.h" int main() { printf("Hello, world!\n"); } #include "inc.h" int main() { printf("Hello, world!\n"); } #define INC 1 If both of the programs include the same inc.h file, then updating the modification time of inc.h (using the &touch; command) will cause only prog-timestamp to be rebuilt: scons -Q touch inc.h scons -Q
Older Functions for Deciding When an Input File Has Changed &SCons; still supports two functions that used to be the primary methods for configuring the decision about whether or not an input file has changed. These functions have been officially deprecated as &SCons; version 2.0, and their use is discouraged, mainly because they rely on a somewhat confusing distinction between how source files and target files are handled. These functions are documented here mainly in case you encounter them in older &SConscript; files.
The &SourceSignatures; Function The &SourceSignatures; function is fairly straightforward, and supports two different argument values to configure whether source file changes should be decided using MD5 signatures: Program('hello.c') SourceSignatures('MD5') Or using time stamps: Program('hello.c') SourceSignatures('timestamp') These are roughly equivalent to specifying Decider('MD5') or Decider('timestamp-match'), respectively, although it only affects how SCons makes decisions about dependencies on source files--that is, files that are not built from any other files.
The &TargetSignatures; Function The &TargetSignatures; function specifies how &SCons; decides when a target file has changed when it is used as a dependency of (input to) another target--that is, the &TargetSignatures; function configures how the signatures of "intermediate" target files are used when deciding if a "downstream" target file must be rebuilt. This easily-overlooked distinction between how &SCons; decides if the target itself must be rebuilt and how the target is then used to decide if a different target must be rebuilt is one of the confusing things that has led to the &TargetSignatures; and &SourceSignatures; functions being replaced by the simpler &Decider; function. The &TargetSignatures; function supports the same 'MD5' and 'timestamp' argument values that are supported by the &SourceSignatures;, with the same meanings, but applied to target files. That is, in the example: Program('hello.c') TargetSignatures('MD5') The MD5 checksum of the &hello_o; target file will be used to decide if it has changed since the last time the "downstream" &hello; target file was built. And in the example: Program('hello.c') TargetSignatures('timestamp') The modification time of the &hello_o; target file will be used to decide if it has changed since the last time the "downstream" &hello; target file was built. The &TargetSignatures; function supports two additional argument values: 'source' and 'build'. The 'source' argument specifies that decisions involving whether target files have changed since a previous build should use the same behavior for the decisions configured for source files (using the &SourceSignatures; function). So in the example: Program('hello.c') TargetSignatures('source') SourceSignatures('timestamp') All files, both targets and sources, will use modification times when deciding if an input file has changed since the last time a target was built. Lastly, the 'build' argument specifies that &SCons; should examine the build status of a target file and always rebuild a "downstream" target if the target file was itself rebuilt, without re-examining the contents or timestamp of the newly-built target file. If the target file was not rebuilt during this &scons; invocation, then the target file will be examined the same way as configured by the &SourceSignature; call to decide if it has changed. This mimics the behavior of build signatures in earlier versions of &SCons;. A &buildsignature; re-combined signatures of all the input files that went into making the target file, so that the target file itself did not need to have its contents read to compute an MD5 signature. This can improve performance for some configurations, but is generally not as effective as using Decider('MD5-timestamp').
Implicit Dependencies: The &cv-CPPPATH; Construction Variable Now suppose that our "Hello, World!" program actually has an #include line to include the &hello_h; file in the compilation: Program('hello.c', CPPPATH = '.') #include <hello.h> int main() { printf("Hello, %s!\n", string); } #define string "world" And, for completeness, the &hello_h; file looks like this: In this case, we want &SCons; to recognize that, if the contents of the &hello_h; file change, the &hello; program must be recompiled. To do this, we need to modify the &SConstruct; file like so: The &cv-link-CPPPATH; value tells &SCons; to look in the current directory ('.') for any files included by C source files (.c or .h files). With this assignment in the &SConstruct; file: scons -Q hello scons -Q hello edit hello.h scons -Q hello First, notice that &SCons; added the -I. argument from the &cv-CPPPATH; variable so that the compilation would find the &hello_h; file in the local directory. Second, realize that &SCons; knows that the &hello; program must be rebuilt because it scans the contents of the &hello_c; file for the #include lines that indicate another file is being included in the compilation. &SCons; records these as implicit dependencies of the target file, Consequently, when the &hello_h; file changes, &SCons; realizes that the &hello_c; file includes it, and rebuilds the resulting &hello; program that depends on both the &hello_c; and &hello_h; files. Like the &cv-link-LIBPATH; variable, the &cv-CPPPATH; variable may be a list of directories, or a string separated by the system-specific path separation character (':' on POSIX/Linux, ';' on Windows). Either way, &SCons; creates the right command-line options so that the following example: Program('hello.c', CPPPATH = ['include', '/home/project/inc']) int main() { printf("Hello, world!\n"); } Will look like this on POSIX or Linux: scons -Q hello And like this on Windows: scons -Q hello.exe
Caching Implicit Dependencies Scanning each file for #include lines does take some extra processing time. When you're doing a full build of a large system, the scanning time is usually a very small percentage of the overall time spent on the build. You're most likely to notice the scanning time, however, when you rebuild all or part of a large system: &SCons; will likely take some extra time to "think about" what must be built before it issues the first build command (or decides that everything is up to date and nothing must be rebuilt). In practice, having &SCons; scan files saves time relative to the amount of potential time lost to tracking down subtle problems introduced by incorrect dependencies. Nevertheless, the "waiting time" while &SCons; scans files can annoy individual developers waiting for their builds to finish. Consequently, &SCons; lets you cache the implicit dependencies that its scanners find, for use by later builds. You can do this by specifying the &implicit-cache; option on the command line: scons -Q --implicit-cache hello scons -Q hello If you don't want to specify &implicit-cache; on the command line each time, you can make it the default behavior for your build by setting the &implicit_cache; option in an &SConscript; file: SetOption('implicit_cache', 1) &SCons; does not cache implicit dependencies like this by default because the &implicit-cache; causes &SCons; to simply use the implicit dependencies stored during the last run, without any checking for whether or not those dependencies are still correct. Specifically, this means &implicit-cache; instructs &SCons; to not rebuild "correctly" in the following cases: When &implicit-cache; is used, &SCons; will ignore any changes that may have been made to search paths (like &cv-CPPPATH; or &cv-LIBPATH;,). This can lead to &SCons; not rebuilding a file if a change to &cv-CPPPATH; would normally cause a different, same-named file from a different directory to be used. When &implicit-cache; is used, &SCons; will not detect if a same-named file has been added to a directory that is earlier in the search path than the directory in which the file was found last time.
The &implicit-deps-changed; Option When using cached implicit dependencies, sometimes you want to "start fresh" and have &SCons; re-scan the files for which it previously cached the dependencies. For example, if you have recently installed a new version of external code that you use for compilation, the external header files will have changed and the previously-cached implicit dependencies will be out of date. You can update them by running &SCons; with the &implicit-deps-changed; option: scons -Q --implicit-deps-changed hello scons -Q hello In this case, &SCons; will re-scan all of the implicit dependencies and cache updated copies of the information.
The &implicit-deps-unchanged; Option By default when caching dependencies, &SCons; notices when a file has been modified and re-scans the file for any updated implicit dependency information. Sometimes, however, you may want to force &SCons; to use the cached implicit dependencies, even if the source files changed. This can speed up a build for example, when you have changed your source files but know that you haven't changed any #include lines. In this case, you can use the &implicit-deps-unchanged; option: scons -Q --implicit-deps-unchanged hello scons -Q hello In this case, &SCons; will assume that the cached implicit dependencies are correct and will not bother to re-scan changed files. For typical builds after small, incremental changes to source files, the savings may not be very big, but sometimes every bit of improved performance counts.
Explicit Dependencies: the &Depends; Function Sometimes a file depends on another file that is not detected by an &SCons; scanner. For this situation, &SCons; allows you to specific explicitly that one file depends on another file, and must be rebuilt whenever that file changes. This is specified using the &Depends; method: hello = Program('hello.c') Depends(hello, 'other_file') % scons -Q hello cc -c hello.c -o hello.o cc -o hello hello.o % scons -Q hello scons: `hello' is up to date. % edit other_file [CHANGE THE CONTENTS OF other_file] % scons -Q hello cc -c hello.c -o hello.o cc -o hello hello.o Note that the dependency (the second argument to &Depends;) may also be a list of Node objects (for example, as returned by a call to a Builder): hello = Program('hello.c') goodbye = Program('goodbye.c') Depends(hello, goodbye) in which case the dependency or dependencies will be built before the target(s): % scons -Q hello cc -c goodbye.c -o goodbye.o cc -o goodbye goodbye.o cc -c hello.c -o hello.o cc -o hello hello.o
Dependencies From External Files: the &ParseDepends; Function &SCons; has built-in scanners for a number of languages. Sometimes these scanners fail to extract certain implicit dependencies due to limitations of the scanner implementation. The following example illustrates a case where the built-in C scanner is unable to extract the implicit dependency on a header file. #define FOO_HEADER <foo.h> #include FOO_HEADER int main() { return FOO; } Program('hello', 'hello.c', CPPPATH='.') #define FOO 42 scons -Q edit foo.h scons -Q Apparently, the scanner does not know about the header dependency. Being not a full-fledged C preprocessor, the scanner does not expand the macro. In these cases, you may also use the compiler to extract the implicit dependencies. &ParseDepends; can parse the contents of the compiler output in the style of &Make;, and explicitly establish all of the listed dependencies. The following example uses &ParseDepends; to process a compiler generated dependency file which is generated as a side effect during compilation of the object file: #define FOO_HEADER <foo.h> #include FOO_HEADER int main() { return FOO; } obj = Object('hello.c', CCFLAGS='-MD -MF hello.d', CPPPATH='.') SideEffect('hello.d', obj) ParseDepends('hello.d') Program('hello', obj) #define FOO 42 hello.o: hello.c foo.h scons -Q edit foo.h scons -Q Parsing dependencies from a compiler-generated .d file has a chicken-and-egg problem, that causes unnecessary rebuilds: #define FOO_HEADER <foo.h> #include FOO_HEADER int main() { return FOO; } obj = Object('hello.c', CCFLAGS='-MD -MF hello.d', CPPPATH='.') SideEffect('hello.d', obj) ParseDepends('hello.d') Program('hello', obj) #define FOO 42 % scons -Q cc -o hello.o -c -MD -MF hello.d -I. hello.c cc -o hello hello.o % scons -Q --debug=explain scons: rebuilding `hello.o' because `foo.h' is a new dependency cc -o hello.o -c -MD -MF hello.d -I. hello.c % scons -Q scons: `.' is up to date. In the first pass, the dependency file is generated while the object file is compiled. At that time, &SCons; does not know about the dependency on foo.h. In the second pass, the object file is regenerated because foo.h is detected as a new dependency. &ParseDepends; immediately reads the specified file at invocation time and just returns if the file does not exist. A dependency file generated during the build process is not automatically parsed again. Hence, the compiler-extracted dependencies are not stored in the signature database during the same build pass. This limitation of &ParseDepends; leads to unnecessary recompilations. Therefore, &ParseDepends; should only be used if scanners are not available for the employed language or not powerful enough for the specific task.
Ignoring Dependencies: the &Ignore; Function Sometimes it makes sense to not rebuild a program, even if a dependency file changes. In this case, you would tell &SCons; specifically to ignore a dependency as follows: hello_obj=Object('hello.c') hello = Program(hello_obj) Ignore(hello_obj, 'hello.h') #include "hello.h" int main() { printf("Hello, %s!\n", string); } #define string "world" % scons -Q hello cc -c -o hello.o hello.c cc -o hello hello.o % scons -Q hello scons: `hello' is up to date. % edit hello.h [CHANGE THE CONTENTS OF hello.h] % scons -Q hello scons: `hello' is up to date. Now, the above example is a little contrived, because it's hard to imagine a real-world situation where you wouldn't want to rebuild &hello; if the &hello_h; file changed. A more realistic example might be if the &hello; program is being built in a directory that is shared between multiple systems that have different copies of the &stdio_h; include file. In that case, &SCons; would notice the differences between the different systems' copies of &stdio_h; and would rebuild &hello; each time you change systems. You could avoid these rebuilds as follows: hello = Program('hello.c', CPPPATH=['/usr/include']) Ignore(hello, '/usr/include/stdio.h') &Ignore; can also be used to prevent a generated file from being built by default. This is due to the fact that directories depend on their contents. So to ignore a generated file from the default build, you specify that the directory should ignore the generated file. Note that the file will still be built if the user specifically requests the target on scons command line, or if the file is a dependency of another file which is requested and/or is built by default. hello_obj=Object('hello.c') hello = Program(hello_obj) Ignore('.',[hello,hello_obj]) #include "stdio.h" int main() { printf("Hello!\n"); } scons -Q scons -Q hello scons -Q hello
Order-Only Dependencies: the &Requires; Function Occasionally, it may be useful to specify that a certain file or directory must, if necessary, be built or created before some other target is built, but that changes to that file or directory do not require that the target itself be rebuilt. Such a relationship is called an order-only dependency because it only affects the order in which things must be built--the dependency before the target--but it is not a strict dependency relationship because the target should not change in response to changes in the dependent file. For example, suppose that you want to create a file every time you run a build that identifies the time the build was performed, the version number, etc., and which is included in every program that you build. The version file's contents will change every build. If you specify a normal dependency relationship, then every program that depends on that file would be rebuilt every time you ran &SCons;. For example, we could use some Python code in a &SConstruct; file to create a new version.c file with a string containing the current date every time we run &SCons;, and then link a program with the resulting object file by listing version.c in the sources: import time version_c_text = """ char *date = "%s"; """ % time.ctime(time.time()) open('version.c', 'w').write(version_c_text) hello = Program(['hello.c', 'version.c']) extern char *date; int main() { printf("Hello, %s! I was built: %s\n", date); } If we list version.c as an actual source file, though, then the version.o file will get rebuilt every time we run &SCons; (because the &SConstruct; file itself changes the contents of version.c) and the hello executable will get re-linked every time (because the version.o file changes): scons -Q hello sleep 1 scons -Q hello sleep 1 scons -Q hello (Note that for the above example to work, we &sleep; for one second in between each run, so that the &SConstruct; file will create a version.c file with a time string that's one second later than the previous run.) One solution is to use the &Requires; function to specify that the version.o must be rebuilt before it is used by the link step, but that changes to version.o should not actually cause the hello executable to be re-linked: import time version_c_text = """ char *date = "%s"; """ % time.ctime(time.time()) open('version.c', 'w').write(version_c_text) version_obj = Object('version.c') hello = Program('hello.c', LINKFLAGS = str(version_obj[0])) Requires(hello, version_obj) extern char *date; int main() { printf("Hello, %s! I was built: %s\n", date); } Notice that because we can no longer list version.c as one of the sources for the hello program, we have to find some other way to get it into the link command line. For this example, we're cheating a bit and stuffing the object file name (extracted from version_obj list returned by the &b-Object; call) into the &cv-link-LINKFLAGS; variable, because &cv-LINKFLAGS; is already included in the &cv-link-LINKCOM; command line. With these changes, we get the desired behavior of only re-linking the hello executable when the hello.c has changed, even though the version.o is rebuilt (because the &SConstruct; file still changes the version.c contents directly each run): scons -Q hello sleep 1 scons -Q hello sleep 1 edit hello.c scons -Q hello sleep 1 scons -Q hello
The &AlwaysBuild; Function How &SCons; handles dependencies can also be affected by the &AlwaysBuild; method. When a file is passed to the &AlwaysBuild; method, like so: hello = Program('hello.c') AlwaysBuild(hello) int main() { printf("Hello, %s!\n", string); } Then the specified target file (&hello; in our example) will always be considered out-of-date and rebuilt whenever that target file is evaluated while walking the dependency graph: scons -Q scons -Q The &AlwaysBuild; function has a somewhat misleading name, because it does not actually mean the target file will be rebuilt every single time &SCons; is invoked. Instead, it means that the target will, in fact, be rebuilt whenever the target file is encountered while evaluating the targets specified on the command line (and their dependencies). So specifying some other target on the command line, a target that does not itself depend on the &AlwaysBuild; target, will still be rebuilt only if it's out-of-date with respect to its dependencies: scons -Q scons -Q hello.o
scons-doc-2.3.0/doc/user/libraries.in0000644000175000017500000002634112114661557020257 0ustar dktrkranzdktrkranz It's often useful to organize large software projects by collecting parts of the software into one or more libraries. &SCons; makes it easy to create libraries and to use them in the programs.
Building Libraries You build your own libraries by specifying &b-link-Library; instead of &b-link-Program;: Library('foo', ['f1.c', 'f2.c', 'f3.c']) void f1() { printf("f1.c\n"); } void f2() { printf("f2.c\n"); } void f3() { printf("f3.c\n"); } &SCons; uses the appropriate library prefix and suffix for your system. So on POSIX or Linux systems, the above example would build as follows (although &ranlib; may not be called on all systems): scons -Q On a Windows system, a build of the above example would look like: scons -Q The rules for the target name of the library are similar to those for programs: if you don't explicitly specify a target library name, &SCons; will deduce one from the name of the first source file specified, and &SCons; will add an appropriate file prefix and suffix if you leave them off.
Building Libraries From Source Code or Object Files 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: Library('foo', ['f1.c', 'f2.o', 'f3.c', 'f4.o']) void f1() { printf("f1.c\n"); } object file void f3() { printf("f3.c\n"); } object file And SCons realizes that only the source code files must be compiled into object files before creating the final library: scons -Q Of course, in this example, the object files must already exist for the build to succeed. See , below, for information about how you can build object files explicitly and include the built files in a library.
Building Static Libraries Explicitly: the &b-StaticLibrary; Builder The &b-link-Library; function builds a traditional static library. If you want to be explicit about the type of library being built, you can use the synonym &b-link-StaticLibrary; function instead of &b-Library;: StaticLibrary('foo', ['f1.c', 'f2.c', 'f3.c']) There is no functional difference between the &b-link-StaticLibrary; and &b-Library; functions.
Building Shared (DLL) Libraries: the &b-SharedLibrary; Builder If you want to build a shared library (on POSIX systems) or a DLL file (on Windows systems), you use the &b-link-SharedLibrary; function: SharedLibrary('foo', ['f1.c', 'f2.c', 'f3.c']) void f1() { printf("f1.c\n"); } void f2() { printf("f2.c\n"); } void f3() { printf("f3.c\n"); } The output on POSIX: scons -Q And the output on Windows: scons -Q Notice again that &SCons; takes care of building the output file correctly, adding the -shared option for a POSIX compilation, and the /dll option on Windows.
Linking with Libraries Usually, you build a library because you want to link it with one or more programs. You link libraries with a program by specifying the libraries in the &cv-link-LIBS; construction variable, and by specifying the directory in which the library will be found in the &cv-link-LIBPATH; construction variable: Library('foo', ['f1.c', 'f2.c', 'f3.c']) Program('prog.c', LIBS=['foo', 'bar'], LIBPATH='.') int main() { printf("Hello, world!\n"); } int main() { printf("Hello, world!\n"); } int main() { printf("Hello, world!\n"); } int main() { printf("Hello, world!\n"); } Notice, of course, that you don't need to specify a library prefix (like lib) or suffix (like .a or .lib). &SCons; uses the correct prefix or suffix for the current system. On a POSIX or Linux system, a build of the above example would look like: scons -Q On a Windows system, a build of the above example would look like: scons -Q As usual, notice that &SCons; has taken care of constructing the correct command lines to link with the specified library on each system. Note also that, if you only have a single library to link with, you can specify the library name in single string, instead of a Python list, so that: Program('prog.c', LIBS='foo', LIBPATH='.') is equivalent to: Program('prog.c', LIBS=['foo'], LIBPATH='.') This is similar to the way that &SCons; handles either a string or a list to specify a single source file.
Finding Libraries: the &cv-LIBPATH; Construction Variable By default, the linker will only look in certain system-defined directories for libraries. &SCons; knows how to look for libraries in directories that you specify with the &cv-link-LIBPATH; construction variable. &cv-LIBPATH; consists of a list of directory names, like so: Program('prog.c', LIBS = 'm', LIBPATH = ['/usr/lib', '/usr/local/lib']) int main() { printf("prog.c\n"); } Using a Python list is preferred because it's portable across systems. Alternatively, you could put all of the directory names in a single string, separated by the system-specific path separator character: a colon on POSIX systems: LIBPATH = '/usr/lib:/usr/local/lib' or a semi-colon on Windows systems: LIBPATH = 'C:\\lib;D:\\lib' (Note that Python requires that the backslash separators in a Windows path name be escaped within strings.) When the linker is executed, &SCons; will create appropriate flags so that the linker will look for libraries in the same directories as &SCons;. So on a POSIX or Linux system, a build of the above example would look like: scons -Q On a Windows system, a build of the above example would look like: scons -Q Note again that &SCons; has taken care of the system-specific details of creating the right command-line options.
scons-doc-2.3.0/doc/user/preface.xml0000644000175000017500000002666012114661557020106 0ustar dktrkranzdktrkranz Thank you for taking the time to read about &SCons;. &SCons; is a next-generation software construction tool, or make tool--that is, a software utility for building software (or other files) and keeping built software up-to-date whenever the underlying input files change. The most distinctive thing about &SCons; is that its configuration files are actually scripts, written in the &Python; programming language. This is in contrast to most alternative build tools, which typically invent a new language to configure the build. &SCons; still has a learning curve, of course, because you have to know what functions to call to set up your build properly, but the underlying syntax used should be familiar to anyone who has ever looked at a Python script. Paradoxically, using Python as the configuration file format makes &SCons; easier for non-programmers to learn than the cryptic languages of other build tools, which are usually invented by programmers for other programmers. This is in no small part due to the consistency and readability that are hallmarks of Python. It just so happens that making a real, live scripting language the basis for the configuration files makes it a snap for more accomplished programmers to do more complicated things with builds, as necessary.
&SCons; Principles There are a few overriding principles we try to live up to in designing and implementing &SCons;: Correctness First and foremost, by default, &SCons; guarantees a correct build even if it means sacrificing performance a little. We strive to guarantee the build is correct regardless of how the software being built is structured, how it may have been written, or how unusual the tools are that build it. Performance Given that the build is correct, we try to make &SCons; build software as quickly as possible. In particular, wherever we may have needed to slow down the default &SCons; behavior to guarantee a correct build, we also try to make it easy to speed up &SCons; through optimization options that let you trade off guaranteed correctness in all end cases for a speedier build in the usual cases. Convenience &SCons; tries to do as much for you out of the box as reasonable, including detecting the right tools on your system and using them correctly to build the software. In a nutshell, we try hard to make &SCons; just "do the right thing" and build software correctly, with a minimum of hassles.
A Caveat About This Guide's Completeness One word of warning as you read through this Guide: Like too much Open Source software out there, the &SCons; documentation isn't always kept up-to-date with the available features. In other words, there's a lot that &SCons; can do that isn't yet covered in this User's Guide. (Come to think of it, that also describes a lot of proprietary software, doesn't it?) Although this User's Guide isn't as complete as we'd like it to be, our development process does emphasize making sure that the &SCons; man page is kept up-to-date with new features. So if you're trying to figure out how to do something that &SCons; supports but can't find enough (or any) information here, it would be worth your while to look at the man page to see if the information is covered there. And if you do, maybe you'd even consider contributing a section to the User's Guide so the next person looking for that information won't have to go through the same thing...?
Acknowledgements &SCons; would not exist without a lot of help from a lot of people, many of whom may not even be aware that they helped or served as inspiration. So in no particular order, and at the risk of leaving out someone: First and foremost, &SCons; owes a tremendous debt to Bob Sidebotham, the original author of the classic Perl-based &Cons; tool which Bob first released to the world back around 1996. Bob's work on Cons classic provided the underlying architecture and model of specifying a build configuration using a real scripting language. My real-world experience working on Cons informed many of the design decisions in SCons, including the improved parallel build support, making Builder objects easily definable by users, and separating the build engine from the wrapping interface. Greg Wilson was instrumental in getting &SCons; started as a real project when he initiated the Software Carpentry design competition in February 2000. Without that nudge, marrying the advantages of the Cons classic architecture with the readability of Python might have just stayed no more than a nice idea. The entire &SCons; team have been absolutely wonderful to work with, and &SCons; would be nowhere near as useful a tool without the energy, enthusiasm and time people have contributed over the past few years. The "core team" of Chad Austin, Anthony Roach, Bill Deegan, Charles Crain, Steve Leblanc, Greg Noel, Gary Oberbrunner, Greg Spencer and Christoph Wiedemann have been great about reviewing my (and other) changes and catching problems before they get in the code base. Of particular technical note: Anthony's outstanding and innovative work on the tasking engine has given &SCons; a vastly superior parallel build model; Charles has been the master of the crucial Node infrastructure; Christoph's work on the Configure infrastructure has added crucial Autoconf-like functionality; and Greg has provided excellent support for Microsoft Visual Studio. Special thanks to David Snopek for contributing his underlying "Autoscons" code that formed the basis of Christoph's work with the Configure functionality. David was extremely generous in making this code available to &SCons;, given that he initially released it under the GPL and &SCons; is released under a less-restrictive MIT-style license. Thanks to Peter Miller for his splendid change management system, &Aegis;, which has provided the &SCons; project with a robust development methodology from day one, and which showed me how you could integrate incremental regression tests into a practical development cycle (years before eXtreme Programming arrived on the scene). And last, thanks to Guido van Rossum for his elegant scripting language, which is the basis not only for the &SCons; implementation, but for the interface itself.
Contact The best way to contact people involved with SCons, including the author, is through the SCons mailing lists. If you want to ask general questions about how to use &SCons; send email to &scons-users;. If you want to contact the &SCons; development community directly, send email to &scons-devel;. If you want to receive announcements about &SCons;, join the low-volume &scons-announce; mailing list.
scons-doc-2.3.0/doc/user/separate.in0000644000175000017500000003742612114661557020115 0ustar dktrkranzdktrkranz It's often useful to keep any built files completely separate from the source files. In &SCons;, this is usually done by creating one or more separate variant directory trees that are used to hold the built objects files, libraries, and executable programs, etc. for a specific flavor, or variant, of build. &SCons; provides two ways to do this, one through the &SConscript; function that we've already seen, and the second through a more flexible &VariantDir; function. One historical note: the &VariantDir; function used to be called &BuildDir;. That name is still supported but has been deprecated because the &SCons; functionality differs from the model of a "build directory" implemented by other build systems like the GNU Autotools.
Specifying a Variant Directory Tree as Part of an &SConscript; Call The most straightforward way to establish a variant directory tree uses the fact that the usual way to set up a build hierarchy is to have an &SConscript; file in the source subdirectory. If you then pass a &variant_dir; argument to the &SConscript; function call: SConscript('src/SConscript', variant_dir='build') env = Environment() env.Program('hello.c') int main() { printf("Hello, world!\n"); } &SCons; will then build all of the files in the &build; subdirectory: ls src scons -Q ls build But wait a minute--what's going on here? &SCons; created the object file build/hello.o in the &build; subdirectory, as expected. But even though our &hello_c; file lives in the &src; subdirectory, &SCons; has actually compiled a build/hello.c file to create the object file. What's happened is that &SCons; has duplicated the &hello_c; file from the &src; subdirectory to the &build; subdirectory, and built the program from there. The next section explains why &SCons; does this.
Why &SCons; Duplicates Source Files in a Variant Directory Tree &SCons; duplicates source files in variant directory trees because it's the most straightforward way to guarantee a correct build regardless of include-file directory paths, relative references between files, or tool support for putting files in different locations, and the &SCons; philosophy is to, by default, guarantee a correct build in all cases. The most direct reason to duplicate source files in variant directories is simply that some tools (mostly older versions) are written to only build their output files in the same directory as the source files. In this case, the choices are either to build the output file in the source directory and move it to the variant directory, or to duplicate the source files in the variant directory. Additionally, relative references between files can cause problems if we don't just duplicate the hierarchy of source files in the variant directory. You can see this at work in use of the C preprocessor #include mechanism with double quotes, not angle brackets: #include "file.h" The de facto standard behavior for most C compilers in this case is to first look in the same directory as the source file that contains the #include line, then to look in the directories in the preprocessor search path. Add to this that the &SCons; implementation of support for code repositories (described below) means not all of the files will be found in the same directory hierarchy, and the simplest way to make sure that the right include file is found is to duplicate the source files into the variant directory, which provides a correct build regardless of the original location(s) of the source files. Although source-file duplication guarantees a correct build even in these end-cases, it can usually be safely disabled. The next section describes how you can disable the duplication of source files in the variant directory.
Telling &SCons; to Not Duplicate Source Files in the Variant Directory Tree In most cases and with most tool sets, &SCons; can place its target files in a build subdirectory without duplicating the source files and everything will work just fine. You can disable the default &SCons; behavior by specifying duplicate=0 when you call the &SConscript; function: SConscript('src/SConscript', variant_dir='build', duplicate=0) When this flag is specified, &SCons; uses the variant directory like most people expect--that is, the output files are placed in the variant directory while the source files stay in the source directory: % ls src SConscript hello.c % scons -Q cc -c src/hello.c -o build/hello.o cc -o build/hello build/hello.o % ls build hello hello.o
The &VariantDir; Function Use the &VariantDir; function to establish that target files should be built in a separate directory from the source files: VariantDir('build', 'src') env = Environment() env.Program('build/hello.c') int main() { printf("Hello, world!\n"); } Note that when you're not using an &SConscript; file in the &src; subdirectory, you must actually specify that the program must be built from the build/hello.c file that &SCons; will duplicate in the &build; subdirectory. When using the &VariantDir; function directly, &SCons; still duplicates the source files in the variant directory by default: ls src scons -Q ls build You can specify the same duplicate=0 argument that you can specify for an &SConscript; call: VariantDir('build', 'src', duplicate=0) env = Environment() env.Program('build/hello.c') int main() { printf("Hello, world!\n"); } In which case &SCons; will disable duplication of the source files: ls src scons -Q ls build
Using &VariantDir; With an &SConscript; File Even when using the &VariantDir; function, it's much more natural to use it with a subsidiary &SConscript; file. For example, if the src/SConscript looks like this: VariantDir('build', 'src') SConscript('build/SConscript') env = Environment() env.Program('hello.c') int main() { printf("Hello, world!\n"); } Then our &SConstruct; file could look like: Yielding the following output: ls src scons -Q ls build Notice that this is completely equivalent to the use of &SConscript; that we learned about in the previous section.
Using &Glob; with &VariantDir; The &Glob; file name pattern matching function works just as usual when using &VariantDir;. For example, if the src/SConscript looks like this: VariantDir('build', 'src') SConscript('build/SConscript') env = Environment() env.Program('hello', Glob('*.c')) #include "f2.h" int main() { printf(f2()); } const char * f2() { return("Hello, world!\n"); } const char * f2(); Then with the same &SConstruct; file as in the previous section, and source files f1.c and f2.c in src, we would see the following output: ls src scons -Q ls build The &Glob; function returns Nodes in the build/ tree, as you'd expect.
scons-doc-2.3.0/doc/user/functions.xml0000644000175000017500000000253412114661557020503 0ustar dktrkranzdktrkranz This appendix contains descriptions of all of the function and construction environment methods in this version of &SCons; &functions-gen; scons-doc-2.3.0/doc/user/builders-commands.in0000644000175000017500000001026512114661557021711 0ustar dktrkranzdktrkranz Creating a &Builder; and attaching it to a &consenv; allows for a lot of flexibility when you want to re-use actions to build multiple files of the same type. This can, however, be cumbersome if you only need to execute one specific command to build a single file (or group of files). For these situations, &SCons; supports a &Command; &Builder; that arranges for a specific action to be executed to build a specific file or files. This looks a lot like the other builders (like &b-link-Program;, &b-link-Object;, etc.), but takes as an additional argument the command to be executed to build the file: env = Environment() env.Command('foo.out', 'foo.in', "sed 's/x/y/' < $SOURCE > $TARGET") foo.in When executed, &SCons; runs the specified command, substituting &cv-link-SOURCE; and &cv-link-TARGET; as expected: scons -Q This is often more convenient than creating a &Builder; object and adding it to the &cv-link-BUILDERS; variable of a &consenv; Note that the action you specify to the &Command; &Builder; can be any legal &SCons; &Action;, such as a Python function: env = Environment() def build(target, source, env): # Whatever it takes to build return None env.Command('foo.out', 'foo.in', build) foo.in Which executes as follows: scons -Q Note that &cv-link-SOURCE; and &cv-link-TARGET; are expanded in the source and target as well as of SCons 1.1, so you can write: env.Command('${SOURCE.basename}.out', 'foo.in', build) which does the same thing as the previous example, but allows you to avoid repeating yourself. scons-doc-2.3.0/doc/user/builders-writing.in0000644000175000017500000007576512114661557021613 0ustar dktrkranzdktrkranz Although &SCons; provides many useful methods for building common software products (programs, libraries, documents, etc.), you frequently want to be able to build some other type of file not supported directly by &SCons;. Fortunately, &SCons; makes it very easy to define your own &Builder; objects for any custom file types you want to build. (In fact, the &SCons; interfaces for creating &Builder; objects are flexible enough and easy enough to use that all of the the &SCons; built-in &Builder; objects are created using the mechanisms described in this section.)
Writing Builders That Execute External Commands The simplest &Builder; to create is one that executes an external command. For example, if we want to build an output file by running the contents of the input file through a command named foobuild, creating that &Builder; might look like: bld = Builder(action = 'foobuild < $SOURCE > $TARGET') All the above line does is create a free-standing &Builder; object. The next section will show us how to actually use it.
Attaching a Builder to a &ConsEnv; A &Builder; object isn't useful until it's attached to a &consenv; so that we can call it to arrange for files to be built. This is done through the &cv-link-BUILDERS; &consvar; in an environment. The &cv-BUILDERS; variable is a Python dictionary that maps the names by which you want to call various &Builder; objects to the objects themselves. For example, if we want to call the &Builder; we just defined by the name Foo, our &SConstruct; file might look like: bld = Builder(action = 'foobuild < $SOURCE > $TARGET') env = Environment(BUILDERS = {'Foo' : bld}) import os env['ENV']['PATH'] = env['ENV']['PATH'] + os.pathsep + os.getcwd() env.Foo('file.foo', 'file.input') file.input cat bld = Builder(action = 'foobuild < $SOURCE > $TARGET') env = Environment(BUILDERS = {'Foo' : bld}) With the &Builder; attached to our &consenv; with the name Foo, we can now actually call it like so: env.Foo('file.foo', 'file.input') Then when we run &SCons; it looks like: scons -Q Note, however, that the default &cv-BUILDERS; variable in a &consenv; comes with a default set of &Builder; objects already defined: &b-link-Program;, &b-link-Library;, etc. And when we explicitly set the &cv-BUILDERS; variable when we create the &consenv;, the default &Builder;s are no longer part of the environment: import SCons.Defaults; SCons.Defaults.ConstructionEnvironment['TOOLS'] = {}; bld = Builder(action = 'foobuild < $SOURCE > $TARGET') env = Environment(BUILDERS = {'Foo' : bld}) env.Foo('file.foo', 'file.input') env.Program('hello.c') bld = Builder(action = 'foobuild < $SOURCE > $TARGET') env = Environment(BUILDERS = {'Foo' : bld}) env.Foo('file.foo', 'file.input') env.Program('hello.c') file.input hello.c scons -Q To be able to use both our own defined &Builder; objects and the default &Builder; objects in the same &consenv;, you can either add to the &cv-BUILDERS; variable using the &Append; function: env = Environment() import os env['ENV']['PATH'] = env['ENV']['PATH'] + os.pathsep + os.getcwd() bld = Builder(action = 'foobuild < $SOURCE > $TARGET') env.Append(BUILDERS = {'Foo' : bld}) env.Foo('file.foo', 'file.input') env.Program('hello.c') file.input hello.c cat env = Environment() bld = Builder(action = 'foobuild < $SOURCE > $TARGET') env.Append(BUILDERS = {'Foo' : bld}) env.Foo('file.foo', 'file.input') env.Program('hello.c') Or you can explicitly set the appropriately-named key in the &cv-BUILDERS; dictionary: env = Environment() bld = Builder(action = 'foobuild < $SOURCE > $TARGET') env['BUILDERS']['Foo'] = bld env.Foo('file.foo', 'file.input') env.Program('hello.c') Either way, the same &consenv; can then use both the newly-defined Foo &Builder; and the default &b-link-Program; &Builder;: scons -Q
Letting &SCons; Handle The File Suffixes By supplying additional information when you create a &Builder;, you can let &SCons; add appropriate file suffixes to the target and/or the source file. For example, rather than having to specify explicitly that you want the Foo &Builder; to build the file.foo target file from the file.input source file, you can give the .foo and .input suffixes to the &Builder;, making for more compact and readable calls to the Foo &Builder;: bld = Builder(action = 'foobuild < $SOURCE > $TARGET', suffix = '.foo', src_suffix = '.input') env = Environment(BUILDERS = {'Foo' : bld}) import os env['ENV']['PATH'] = env['ENV']['PATH'] + os.pathsep + os.getcwd() env.Foo('file1') env.Foo('file2') file1.input file2.input cat bld = Builder(action = 'foobuild < $SOURCE > $TARGET', suffix = '.foo', src_suffix = '.input') env = Environment(BUILDERS = {'Foo' : bld}) env.Foo('file1') env.Foo('file2') scons -Q You can also supply a prefix keyword argument if it's appropriate to have &SCons; append a prefix to the beginning of target file names.
Builders That Execute Python Functions In &SCons;, you don't have to call an external command to build a file. You can, instead, define a Python function that a &Builder; object can invoke to build your target file (or files). Such a &buildfunc; definition looks like: def build_function(target, source, env): # Code to build "target" from "source" return None The arguments of a &buildfunc; are: target A list of Node objects representing the target or targets to be built by this builder function. The file names of these target(s) may be extracted using the Python &str; function. source A list of Node objects representing the sources to be used by this builder function to build the targets. The file names of these source(s) may be extracted using the Python &str; function. env The &consenv; used for building the target(s). The builder function may use any of the environment's construction variables in any way to affect how it builds the targets. The builder function must return a 0 or None value if the target(s) are built successfully. The builder function may raise an exception or return any non-zero value to indicate that the build is unsuccessful, Once you've defined the Python function that will build your target file, defining a &Builder; object for it is as simple as specifying the name of the function, instead of an external command, as the &Builder;'s action argument: def build_function(target, source, env): # Code to build "target" from "source" return None bld = Builder(action = build_function, suffix = '.foo', src_suffix = '.input') env = Environment(BUILDERS = {'Foo' : bld}) env.Foo('file') file.input And notice that the output changes slightly, reflecting the fact that a Python function, not an external command, is now called to build the target file: scons -Q
Builders That Create Actions Using a &Generator; &SCons; Builder objects can create an action "on the fly" by using a function called a &generator;. This provides a great deal of flexibility to construct just the right list of commands to build your target. A &generator; looks like: def generate_actions(source, target, env, for_signature): return 'foobuild < %s > %s' % (target[0], source[0]) The arguments of a &generator; are: source A list of Node objects representing the sources to be built by the command or other action generated by this function. The file names of these source(s) may be extracted using the Python &str; function. target A list of Node objects representing the target or targets to be built by the command or other action generated by this function. The file names of these target(s) may be extracted using the Python &str; function. env The &consenv; used for building the target(s). The generator may use any of the environment's construction variables in any way to determine what command or other action to return. for_signature A flag that specifies whether the generator is being called to contribute to a build signature, as opposed to actually executing the command. The &generator; must return a command string or other action that will be used to build the specified target(s) from the specified source(s). Once you've defined a &generator;, you create a &Builder; to use it by specifying the generator keyword argument instead of action. def generate_actions(source, target, env, for_signature): return 'foobuild < %s > %s' % (source[0], target[0]) bld = Builder(generator = generate_actions, suffix = '.foo', src_suffix = '.input') env = Environment(BUILDERS = {'Foo' : bld}) import os env['ENV']['PATH'] = env['ENV']['PATH'] + os.pathsep + os.getcwd() env.Foo('file') file.input cat def generate_actions(source, target, env, for_signature): return 'foobuild < %s > %s' % (source[0], target[0]) bld = Builder(generator = generate_actions, suffix = '.foo', src_suffix = '.input') env = Environment(BUILDERS = {'Foo' : bld}) env.Foo('file') scons -Q Note that it's illegal to specify both an action and a generator for a &Builder;.
Builders That Modify the Target or Source Lists Using an &Emitter; &SCons; supports the ability for a Builder to modify the lists of target(s) from the specified source(s). You do this by defining an &emitter; function that takes as its arguments the list of the targets passed to the builder, the list of the sources passed to the builder, and the construction environment. The emitter function should return the modified lists of targets that should be built and sources from which the targets will be built. For example, suppose you want to define a Builder that always calls a foobuild program, and you want to automatically add a new target file named new_target and a new source file named new_source whenever it's called. The &SConstruct; file might look like this: def modify_targets(target, source, env): target.append('new_target') source.append('new_source') return target, source bld = Builder(action = 'foobuild $TARGETS - $SOURCES', suffix = '.foo', src_suffix = '.input', emitter = modify_targets) env = Environment(BUILDERS = {'Foo' : bld}) import os env['ENV']['PATH'] = env['ENV']['PATH'] + os.pathsep + os.getcwd() env.Foo('file') file.input new_source cat def modify_targets(target, source, env): target.append('new_target') source.append('new_source') return target, source bld = Builder(action = 'foobuild $TARGETS - $SOURCES', suffix = '.foo', src_suffix = '.input', emitter = modify_targets) env = Environment(BUILDERS = {'Foo' : bld}) env.Foo('file') And would yield the following output: scons -Q One very flexible thing that you can do is use a construction variable to specify different emitter functions for different construction variable. To do this, specify a string containing a construction variable expansion as the emitter when you call the &Builder; function, and set that construction variable to the desired emitter function in different construction environments: bld = Builder(action = 'my_command $SOURCES > $TARGET', suffix = '.foo', src_suffix = '.input', emitter = '$MY_EMITTER') def modify1(target, source, env): return target, source + ['modify1.in'] def modify2(target, source, env): return target, source + ['modify2.in'] env1 = Environment(BUILDERS = {'Foo' : bld}, MY_EMITTER = modify1) env2 = Environment(BUILDERS = {'Foo' : bld}, MY_EMITTER = modify2) env1.Foo('file1') env2.Foo('file2') import os env1['ENV']['PATH'] = env2['ENV']['PATH'] + os.pathsep + os.getcwd() env2['ENV']['PATH'] = env2['ENV']['PATH'] + os.pathsep + os.getcwd() file1.input file2.input modify1.input modify2.input cat bld = Builder(action = 'my_command $SOURCES > $TARGET', suffix = '.foo', src_suffix = '.input', emitter = '$MY_EMITTER') def modify1(target, source, env): return target, source + ['modify1.in'] def modify2(target, source, env): return target, source + ['modify2.in'] env1 = Environment(BUILDERS = {'Foo' : bld}, MY_EMITTER = modify1) env2 = Environment(BUILDERS = {'Foo' : bld}, MY_EMITTER = modify2) env1.Foo('file1') env2.Foo('file2') In this example, the modify1.in and modify2.in files get added to the source lists of the different commands: scons -Q
Where To Put Your Custom Builders and Tools The site_scons directories give you a place to put Python modules and packages that you can import into your &SConscript; files (site_scons), add-on tools that can integrate into &SCons; (site_scons/site_tools), and a site_scons/site_init.py file that gets read before any &SConstruct; or &SConscript; file, allowing you to change &SCons;'s default behavior. Each system type (Windows, Mac, Linux, etc.) searches a canonical set of directories for site_scons; see the man page for details. The top-level SConstruct's site_scons dir is always searched last, and its dir is placed first in the tool path so it overrides all others. If you get a tool from somewhere (the &SCons; wiki or a third party, for instance) and you'd like to use it in your project, a site_scons dir is the simplest place to put it. Tools come in two flavors; either a Python function that operates on an &Environment; or a Python module or package containing two functions, exists() and generate(). A single-function Tool can just be included in your site_scons/site_init.py file where it will be parsed and made available for use. For instance, you could have a site_scons/site_init.py file like this: def TOOL_ADD_HEADER(env): """A Tool to add a header from $HEADER to the source file""" add_header = Builder(action=['echo "$HEADER" > $TARGET', 'cat $SOURCE >> $TARGET']) env.Append(BUILDERS = {'AddHeader' : add_header}) env['HEADER'] = '' # set default value env=Environment(tools=['default', TOOL_ADD_HEADER], HEADER="=====") env.AddHeader('tgt', 'src') hi there and a &SConstruct; like this: # Use TOOL_ADD_HEADER from site_scons/site_init.py env=Environment(tools=['default', TOOL_ADD_HEADER], HEADER="=====") env.AddHeader('tgt', 'src') The TOOL_ADD_HEADER tool method will be called to add the AddHeader tool to the environment. A more full-fledged tool with exists() and generate() methods can be installed either as a module in the file site_scons/site_tools/toolname.py or as a package in the directory site_scons/site_tools/toolname. In the case of using a package, the exists() and generate() are in the file site_scons/site_tools/toolname/__init__.py. (In all the above case toolname is replaced by the name of the tool.) Since site_scons/site_tools is automatically added to the head of the tool search path, any tool found there will be available to all environments. Furthermore, a tool found there will override a built-in tool of the same name, so if you need to change the behavior of a built-in tool, site_scons gives you the hook you need. Many people have a library of utility Python functions they'd like to include in &SConscript;s; just put that module in site_scons/my_utils.py or any valid Python module name of your choice. For instance you can do something like this in site_scons/my_utils.py to add build_id and MakeWorkDir functions: from SCons.Script import * # for Execute and Mkdir def build_id(): """Return a build ID (stub version)""" return "100" def MakeWorkDir(workdir): """Create the specified dir immediately""" Execute(Mkdir(workdir)) import my_utils MakeWorkDir('/tmp/work') print "build_id=" + my_utils.build_id() And then in your &SConscript; or any sub-&SConscript; anywhere in your build, you can import my_utils and use it: import my_utils print "build_id=" + my_utils.build_id() my_utils.MakeWorkDir('/tmp/work') Note that although you can put this library in site_scons/site_init.py, it is no better there than site_scons/my_utils.py since you still have to import that module into your &SConscript;. Also note that in order to refer to objects in the SCons namespace such as &Environment; or &Mkdir; or &Execute; in any file other than a &SConstruct; or &SConscript; you always need to do from SCons.Script import * This is true in modules in site_scons such as site_scons/site_init.py as well. You can use any of the user- or machine-wide site dirs such as ~/.scons/site_scons instead of ./site_scons, or use the --site-dir option to point to your own dir. site_init.py and site_tools will be located under that dir. To avoid using a site_scons dir at all, even if it exists, use the --no-site-dir option.
scons-doc-2.3.0/doc/user/make.in0000644000175000017500000001132012114661557017207 0ustar dktrkranzdktrkranz XXX
Differences Between &Make; and &SCons; XXX
Advantages of &SCons; Over &Make; XXX
scons-doc-2.3.0/doc/user/environments.xml0000644000175000017500000014167512114661557021234 0ustar dktrkranzdktrkranz An environment is a collection of values that can affect how a program executes. &SCons; distinguishes between three different types of environments that can affect the behavior of &SCons; itself (subject to the configuration in the &SConscript; files), as well as the compilers and other tools it executes: External Environment The external environment is the set of variables in the user's environment at the time the user runs &SCons;. These variables are available within the &SConscript; files through the Python os.environ dictionary. See , below. &ConsEnv; A &consenv; is a distinct object creating within a &SConscript; file and and which contains values that affect how &SCons; decides what action to use to build a target, and even to define which targets should be built from which sources. One of the most powerful features of &SCons; is the ability to create multiple &consenvs;, including the ability to clone a new, customized &consenv; from an existing &consenv;. See , below. Execution Environment An execution environment is the values that &SCons; sets when executing an external command (such as a compiler or linker) to build one or more targets. Note that this is not the same as the external environment (see above). See , below. Unlike &Make;, &SCons; does not automatically copy or import values between different environments (with the exception of explicit clones of &consenvs;, which inherit values from their parent). This is a deliberate design choice to make sure that builds are, by default, repeatable regardless of the values in the user's external environment. This avoids a whole class of problems with builds where a developer's local build works because a custom variable setting causes a different compiler or build option to be used, but the checked-in change breaks the official build because it uses different environment variable settings. Note that the &SConscript; writer can easily arrange for variables to be copied or imported between environments, and this is often very useful (or even downright necessary) to make it easy for developers to customize the build in appropriate ways. The point is not that copying variables between different environments is evil and must always be avoided. Instead, it should be up to the implementer of the build system to make conscious choices about how and when to import a variable from one environment to another, making informed decisions about striking the right balance between making the build repeatable on the one hand and convenient to use on the other.
Using Values From the External Environment The external environment variable settings that the user has in force when executing &SCons; are available through the normal Python os.environ dictionary. This means that you must add an import os statement to any &SConscript; file in which you want to use values from the user's external environment. import os More usefully, you can use the os.environ dictionary in your &SConscript; files to initialize &consenvs; with values from the user's external environment. See the next section, , for information on how to do this.
Construction Environments It is rare that all of the software in a large, complicated system needs to be built the same way. For example, different source files may need different options enabled on the command line, or different executable programs need to be linked with different libraries. &SCons; accommodates these different build requirements by allowing you to create and configure multiple &consenvs; that control how the software is built. A &consenv; is an object that has a number of associated &consvars;, each with a name and a value. (A construction environment also has an attached set of &Builder; methods, about which we'll learn more later.)
Creating a &ConsEnv;: the &Environment; Function A &consenv; is created by the &Environment; method: env = Environment() By default, &SCons; initializes every new construction environment with a set of &consvars; based on the tools that it finds on your system, plus the default set of builder methods necessary for using those tools. The construction variables are initialized with values describing the C compiler, the Fortran compiler, the linker, etc., as well as the command lines to invoke them. When you initialize a construction environment you can set the values of the environment's &consvars; to control how a program is built. For example: import os env = Environment(CC = 'gcc', CCFLAGS = '-O2') env.Program('foo.c') The construction environment in this example is still initialized with the same default construction variable values, except that the user has explicitly specified use of the GNU C compiler &gcc;, and further specifies that the -O2 (optimization level two) flag should be used when compiling the object file. In other words, the explicit initializations of &cv-link-CC; and &cv-link-CCFLAGS; override the default values in the newly-created construction environment. So a run from this example would look like: % scons -Q gcc -o foo.o -c -O2 foo.c gcc -o foo foo.o
Fetching Values From a &ConsEnv; You can fetch individual construction variables using the normal syntax for accessing individual named items in a Python dictionary: env = Environment() print "CC is:", env['CC'] This example &SConstruct; file doesn't build anything, but because it's actually a Python script, it will print the value of &cv-link-CC; for us: % scons -Q CC is: cc scons: `.' is up to date. A construction environment, however, is actually an object with associated methods, etc. If you want to have direct access to only the dictionary of construction variables, you can fetch this using the &Dictionary; method: env = Environment(FOO = 'foo', BAR = 'bar') dict = env.Dictionary() for key in ['OBJSUFFIX', 'LIBSUFFIX', 'PROGSUFFIX']: print "key = %s, value = %s" % (key, dict[key]) This &SConstruct; file will print the specified dictionary items for us on POSIX systems as follows: % scons -Q key = OBJSUFFIX, value = .o key = LIBSUFFIX, value = .a key = PROGSUFFIX, value = scons: `.' is up to date. And on Windows: C:\>scons -Q key = OBJSUFFIX, value = .obj key = LIBSUFFIX, value = .lib key = PROGSUFFIX, value = .exe scons: `.' is up to date. If you want to loop and print the values of all of the construction variables in a construction environment, the Python code to do that in sorted order might look something like: env = Environment() for item in sorted(env.Dictionary().items()): print "construction variable = '%s', value = '%s'" % item
Expanding Values From a &ConsEnv;: the &subst; Method Another way to get information from a construction environment is to use the &subst; method on a string containing $ expansions of construction variable names. As a simple example, the example from the previous section that used env['CC'] to fetch the value of &cv-link-CC; could also be written as: env = Environment() print "CC is:", env.subst('$CC') One advantage of using &subst; to expand strings is that construction variables in the result get re-expanded until there are no expansions left in the string. So a simple fetch of a value like &cv-link-CCCOM;: env = Environment(CCFLAGS = '-DFOO') print "CCCOM is:", env['CCCOM'] Will print the unexpanded value of &cv-CCCOM;, showing us the construction variables that still need to be expanded: % scons -Q CCCOM is: $CC $CCFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS -c -o $TARGET $SOURCES scons: `.' is up to date. Calling the &subst; method on $CCOM, however: env = Environment(CCFLAGS = '-DFOO') print "CCCOM is:", env.subst('$CCCOM') Will recursively expand all of the construction variables prefixed with $ (dollar signs), showing us the final output: % scons -Q CCCOM is: gcc -DFOO -c -o scons: `.' is up to date. Note that because we're not expanding this in the context of building something there are no target or source files for &cv-link-TARGET; and &cv-link-SOURCES; to expand.
Handling Problems With Value Expansion If a problem occurs when expanding a construction variable, by default it is expanded to '' (a null string), and will not cause scons to fail. env = Environment() print "value is:", env.subst( '->$MISSING<-' ) % scons -Q value is: -><- scons: `.' is up to date. This default behaviour can be changed using the &AllowSubstExceptions; function. When a problem occurs with a variable expansion it generates an exception, and the &AllowSubstExceptions; function controls which of these exceptions are actually fatal and which are allowed to occur safely. By default, &NameError; and &IndexError; are the two exceptions that are allowed to occur: so instead of causing scons to fail, these are caught, the variable expanded to '' and scons execution continues. To require that all construction variable names exist, and that indexes out of range are not allowed, call &AllowSubstExceptions; with no extra arguments. AllowSubstExceptions() env = Environment() print "value is:", env.subst( '->$MISSING<-' ) % scons -Q value is: scons: *** NameError `MISSING' trying to evaluate `$MISSING' File "/home/my/project/SConstruct", line 3, in <module> This can also be used to allow other exceptions that might occur, most usefully with the ${...} construction variable syntax. For example, this would allow zero-division to occur in a variable expansion in addition to the default exceptions allowed AllowSubstExceptions(IndexError, NameError, ZeroDivisionError) env = Environment() print "value is:", env.subst( '->${1 / 0}<-' ) % scons -Q value is: -><- scons: `.' is up to date. If &AllowSubstExceptions; is called multiple times, each call completely overwrites the previous list of allowed exceptions.
Controlling the Default &ConsEnv;: the &DefaultEnvironment; Function All of the &Builder; functions that we've introduced so far, like &Program; and &Library;, actually use a default &consenv; that contains settings for the various compilers and other tools that &SCons; configures by default, or otherwise knows about and has discovered on your system. The goal of the default construction environment is to make many configurations to "just work" to build software using readily available tools with a minimum of configuration changes. You can, however, control the settings in the default construction environment by using the &DefaultEnvironment; function to initialize various settings: DefaultEnvironment(CC = '/usr/local/bin/gcc') When configured as above, all calls to the &Program; or &Object; Builder will build object files with the /usr/local/bin/gcc compiler. Note that the &DefaultEnvironment; function returns the initialized default construction environment object, which can then be manipulated like any other construction environment. So the following would be equivalent to the previous example, setting the &cv-CC; variable to /usr/local/bin/gcc but as a separate step after the default construction environment has been initialized: env = DefaultEnvironment() env['CC'] = '/usr/local/bin/gcc' One very common use of the &DefaultEnvironment; function is to speed up &SCons; initialization. As part of trying to make most default configurations "just work," &SCons; will actually search the local system for installed compilers and other utilities. This search can take time, especially on systems with slow or networked file systems. If you know which compiler(s) and/or other utilities you want to configure, you can control the search that &SCons; performs by specifying some specific tool modules with which to initialize the default construction environment: env = DefaultEnvironment(tools = ['gcc', 'gnulink'], CC = '/usr/local/bin/gcc') So the above example would tell &SCons; to explicitly configure the default environment to use its normal GNU Compiler and GNU Linker settings (without having to search for them, or any other utilities for that matter), and specifically to use the compiler found at /usr/local/bin/gcc.
Multiple &ConsEnvs; The real advantage of construction environments is that you can create as many different construction environments as you need, each tailored to a different way to build some piece of software or other file. If, for example, we need to build one program with the -O2 flag and another with the -g (debug) flag, we would do this like so: opt = Environment(CCFLAGS = '-O2') dbg = Environment(CCFLAGS = '-g') opt.Program('foo', 'foo.c') dbg.Program('bar', 'bar.c') % scons -Q cc -o bar.o -c -g bar.c cc -o bar bar.o cc -o foo.o -c -O2 foo.c cc -o foo foo.o We can even use multiple construction environments to build multiple versions of a single program. If you do this by simply trying to use the &b-link-Program; builder with both environments, though, like this: opt = Environment(CCFLAGS = '-O2') dbg = Environment(CCFLAGS = '-g') opt.Program('foo', 'foo.c') dbg.Program('foo', 'foo.c') Then &SCons; generates the following error: % scons -Q scons: *** Two environments with different actions were specified for the same target: foo.o File "/home/my/project/SConstruct", line 6, in <module> This is because the two &b-Program; calls have each implicitly told &SCons; to generate an object file named foo.o, one with a &cv-link-CCFLAGS; value of -O2 and one with a &cv-link-CCFLAGS; value of -g. &SCons; can't just decide that one of them should take precedence over the other, so it generates the error. To avoid this problem, we must explicitly specify that each environment compile foo.c to a separately-named object file using the &b-link-Object; builder, like so: opt = Environment(CCFLAGS = '-O2') dbg = Environment(CCFLAGS = '-g') o = opt.Object('foo-opt', 'foo.c') opt.Program(o) d = dbg.Object('foo-dbg', 'foo.c') dbg.Program(d) Notice that each call to the &b-Object; builder returns a value, an internal &SCons; object that represents the object file that will be built. We then use that object as input to the &b-Program; builder. This avoids having to specify explicitly the object file name in multiple places, and makes for a compact, readable &SConstruct; file. Our &SCons; output then looks like: % scons -Q cc -o foo-dbg.o -c -g foo.c cc -o foo-dbg foo-dbg.o cc -o foo-opt.o -c -O2 foo.c cc -o foo-opt foo-opt.o
Making Copies of &ConsEnvs;: the &Clone; Method Sometimes you want more than one construction environment to share the same values for one or more variables. Rather than always having to repeat all of the common variables when you create each construction environment, you can use the &Clone; method to create a copy of a construction environment. Like the &Environment; call that creates a construction environment, the &Clone; method takes &consvar; assignments, which will override the values in the copied construction environment. For example, suppose we want to use &gcc; to create three versions of a program, one optimized, one debug, and one with neither. We could do this by creating a "base" construction environment that sets &cv-link-CC; to &gcc;, and then creating two copies, one which sets &cv-link-CCFLAGS; for optimization and the other which sets &cv-CCFLAGS; for debugging: env = Environment(CC = 'gcc') opt = env.Clone(CCFLAGS = '-O2') dbg = env.Clone(CCFLAGS = '-g') env.Program('foo', 'foo.c') o = opt.Object('foo-opt', 'foo.c') opt.Program(o) d = dbg.Object('foo-dbg', 'foo.c') dbg.Program(d) Then our output would look like: % scons -Q gcc -o foo.o -c foo.c gcc -o foo foo.o gcc -o foo-dbg.o -c -g foo.c gcc -o foo-dbg foo-dbg.o gcc -o foo-opt.o -c -O2 foo.c gcc -o foo-opt foo-opt.o
Replacing Values: the &Replace; Method You can replace existing construction variable values using the &Replace; method: env = Environment(CCFLAGS = '-DDEFINE1') env.Replace(CCFLAGS = '-DDEFINE2') env.Program('foo.c') The replacing value (-DDEFINE2 in the above example) completely replaces the value in the construction environment: % scons -Q cc -o foo.o -c -DDEFINE2 foo.c cc -o foo foo.o You can safely call &Replace; for construction variables that don't exist in the construction environment: env = Environment() env.Replace(NEW_VARIABLE = 'xyzzy') print "NEW_VARIABLE =", env['NEW_VARIABLE'] In this case, the construction variable simply gets added to the construction environment: % scons -Q NEW_VARIABLE = xyzzy scons: `.' is up to date. Because the variables aren't expanded until the construction environment is actually used to build the targets, and because &SCons; function and method calls are order-independent, the last replacement "wins" and is used to build all targets, regardless of the order in which the calls to Replace() are interspersed with calls to builder methods: env = Environment(CCFLAGS = '-DDEFINE1') print "CCFLAGS =", env['CCFLAGS'] env.Program('foo.c') env.Replace(CCFLAGS = '-DDEFINE2') print "CCFLAGS =", env['CCFLAGS'] env.Program('bar.c') The timing of when the replacement actually occurs relative to when the targets get built becomes apparent if we run &scons; without the -Q option: % scons scons: Reading SConscript files ... CCFLAGS = -DDEFINE1 CCFLAGS = -DDEFINE2 scons: done reading SConscript files. scons: Building targets ... cc -o bar.o -c -DDEFINE2 bar.c cc -o bar bar.o cc -o foo.o -c -DDEFINE2 foo.c cc -o foo foo.o scons: done building targets. Because the replacement occurs while the &SConscript; files are being read, the &cv-link-CCFLAGS; variable has already been set to -DDEFINE2 by the time the &foo_o; target is built, even though the call to the &Replace; method does not occur until later in the &SConscript; file.
Setting Values Only If They're Not Already Defined: the &SetDefault; Method Sometimes it's useful to be able to specify that a construction variable should be set to a value only if the construction environment does not already have that variable defined You can do this with the &SetDefault; method, which behaves similarly to the set_default method of Python dictionary objects: env.SetDefault(SPECIAL_FLAG = '-extra-option') This is especially useful when writing your own Tool modules to apply variables to construction environments.
Appending to the End of Values: the &Append; Method You can append a value to an existing construction variable using the &Append; method: env = Environment(CCFLAGS = ['-DMY_VALUE']) env.Append(CCFLAGS = ['-DLAST']) env.Program('foo.c') &SCons; then supplies both the -DMY_VALUE and -DLAST flags when compiling the object file: % scons -Q cc -o foo.o -c -DMY_VALUE -DLAST foo.c cc -o foo foo.o If the construction variable doesn't already exist, the &Append; method will create it: env = Environment() env.Append(NEW_VARIABLE = 'added') print "NEW_VARIABLE =", env['NEW_VARIABLE'] Which yields: % scons -Q NEW_VARIABLE = added scons: `.' is up to date. Note that the &Append; function tries to be "smart" about how the new value is appended to the old value. If both are strings, the previous and new strings are simply concatenated. Similarly, if both are lists, the lists are concatenated. If, however, one is a string and the other is a list, the string is added as a new element to the list.
Appending Unique Values: the &AppendUnique; Method Some times it's useful to add a new value only if the existing construction variable doesn't already contain the value. This can be done using the &AppendUnique; method: env.AppendUnique(CCFLAGS=['-g']) In the above example, the -g would be added only if the &cv-CCFLAGS; variable does not already contain a -g value.
Appending to the Beginning of Values: the &Prepend; Method You can append a value to the beginning of an existing construction variable using the &Prepend; method: env = Environment(CCFLAGS = ['-DMY_VALUE']) env.Prepend(CCFLAGS = ['-DFIRST']) env.Program('foo.c') &SCons; then supplies both the -DFIRST and -DMY_VALUE flags when compiling the object file: % scons -Q cc -o foo.o -c -DFIRST -DMY_VALUE foo.c cc -o foo foo.o If the construction variable doesn't already exist, the &Prepend; method will create it: env = Environment() env.Prepend(NEW_VARIABLE = 'added') print "NEW_VARIABLE =", env['NEW_VARIABLE'] Which yields: % scons -Q NEW_VARIABLE = added scons: `.' is up to date. Like the &Append; function, the &Prepend; function tries to be "smart" about how the new value is appended to the old value. If both are strings, the previous and new strings are simply concatenated. Similarly, if both are lists, the lists are concatenated. If, however, one is a string and the other is a list, the string is added as a new element to the list.
Prepending Unique Values: the &PrependUnique; Method Some times it's useful to add a new value to the beginning of a construction variable only if the existing value doesn't already contain the to-be-added value. This can be done using the &PrependUnique; method: env.PrependUnique(CCFLAGS=['-g']) In the above example, the -g would be added only if the &cv-CCFLAGS; variable does not already contain a -g value.
Controlling the Execution Environment for Issued Commands When &SCons; builds a target file, it does not execute the commands with the same external environment that you used to execute &SCons;. Instead, it uses the dictionary stored in the &cv-link-ENV; construction variable as the external environment for executing commands. The most important ramification of this behavior is that the &PATH; environment variable, which controls where the operating system will look for commands and utilities, is not the same as in the external environment from which you called &SCons;. This means that &SCons; will not, by default, necessarily find all of the tools that you can execute from the command line. The default value of the &PATH; environment variable on a POSIX system is /usr/local/bin:/bin:/usr/bin. The default value of the &PATH; environment variable on a Windows system comes from the Windows registry value for the command interpreter. If you want to execute any commands--compilers, linkers, etc.--that are not in these default locations, you need to set the &PATH; value in the &cv-ENV; dictionary in your construction environment. The simplest way to do this is to initialize explicitly the value when you create the construction environment; this is one way to do that: path = ['/usr/local/bin', '/bin', '/usr/bin'] env = Environment(ENV = {'PATH' : path}) Assign a dictionary to the &cv-ENV; construction variable in this way completely resets the external environment so that the only variable that will be set when external commands are executed will be the &PATH; value. If you want to use the rest of the values in &cv-ENV; and only set the value of &PATH;, the most straightforward way is probably: env['ENV']['PATH'] = ['/usr/local/bin', '/bin', '/usr/bin'] Note that &SCons; does allow you to define the directories in the &PATH; in a string, separated by the pathname-separator character for your system (':' on POSIX systems, ';' on Windows): env['ENV']['PATH'] = '/usr/local/bin:/bin:/usr/bin' But doing so makes your &SConscript; file less portable, (although in this case that may not be a huge concern since the directories you list are likley system-specific, anyway).
Propagating &PATH; From the External Environment You may want to propagate the external &PATH; to the execution environment for commands. You do this by initializing the &PATH; variable with the &PATH; value from the os.environ dictionary, which is Python's way of letting you get at the external environment: import os env = Environment(ENV = {'PATH' : os.environ['PATH']}) Alternatively, you may find it easier to just propagate the entire external environment to the execution environment for commands. This is simpler to code than explicity selecting the &PATH; value: import os env = Environment(ENV = os.environ) Either of these will guarantee that &SCons; will be able to execute any command that you can execute from the command line. The drawback is that the build can behave differently if it's run by people with different &PATH; values in their environment--for example, if both the /bin and /usr/local/bin directories have different &cc; commands, then which one will be used to compile programs will depend on which directory is listed first in the user's &PATH; variable.
Adding to <varname>PATH</varname> Values in the Execution Environment One of the most common requirements for manipulating a variable in the execution environment is to add one or more custom directories to a search like the $PATH variable on Linux or POSIX systems, or the %PATH% variable on Windows, so that a locally-installed compiler or other utility can be found when &SCons; tries to execute it to update a target. &SCons; provides &PrependENVPath; and &AppendENVPath; functions to make adding things to execution variables convenient. You call these functions by specifying the variable to which you want the value added, and then value itself. So to add some /usr/local directories to the $PATH and $LIB variables, you might: env = Environment(ENV = os.environ) env.PrependENVPath('PATH', '/usr/local/bin') env.AppendENVPath('LIB', '/usr/local/lib') Note that the added values are strings, and if you want to add multiple directories to a variable like $PATH, you must include the path separate character (: on Linux or POSIX, ; on Windows) in the string.
scons-doc-2.3.0/doc/user/add-method.in0000644000175000017500000001072212114661557020305 0ustar dktrkranzdktrkranz The &AddMethod; function is used to add a method to an environment. It's typically used to add a "pseudo-builder," a function that looks like a &Builder; but wraps up calls to multiple other &Builder;s or otherwise processes its arguments before calling one or more &Builder;s. In the following example, we want to install the program into the standard /usr/bin directory hierarchy, but also copy it into a local install/bin directory from which a package might be built: def install_in_bin_dirs(env, source): """Install source in both bin dirs""" i1 = env.Install("$BIN", source) i2 = env.Install("$LOCALBIN", source) return [i1[0], i2[0]] # Return a list, like a normal builder env = Environment(BIN='__ROOT__/usr/bin', LOCALBIN='#install/bin') env.AddMethod(install_in_bin_dirs, "InstallInBinDirs") env.InstallInBinDirs(Program('hello.c')) # installs hello in both bin dirs int main() { printf("Hello, world!\n"); } This produces the following: scons -Q / As mentioned, a pseudo-builder also provides more flexibility in parsing arguments than you can get with a &Builder;. The next example shows a pseudo-builder with a named argument that modifies the filename, and a separate argument for the resource file (rather than having the builder figure it out by file extension). This example also demonstrates using the global &AddMethod; function to add a method to the global Environment class, so it will be used in all subsequently created environments. def BuildTestProg(env, testfile, resourcefile, testdir="tests"): """Build the test program; prepends "test_" to src and target, and puts target into testdir.""" srcfile = "test_%s.c" % testfile target = "%s/test_%s" % (testdir, testfile) if env['PLATFORM'] == 'win32': resfile = env.RES(resourcefile) p = env.Program(target, [srcfile, resfile]) else: p = env.Program(target, srcfile) return p AddMethod(Environment, BuildTestProg) env = Environment() env.BuildTestProg('stuff', resourcefile='res.rc') int main() { printf("Hello, world!\n"); } res.rc This produces the following on Linux: scons -Q And the following on Windows: scons -Q Using &AddMethod; is better than just adding an instance method to a &consenv; because it gets called as a proper method, and because &AddMethod; provides for copying the method to any clones of the &consenv; instance. scons-doc-2.3.0/doc/user/parseflags.xml0000644000175000017500000001166212114661557020624 0ustar dktrkranzdktrkranz &SCons; has a bewildering array of construction variables for different types of options when building programs. Sometimes you may not know exactly which variable should be used for a particular option. &SCons; construction environments have a &ParseFlags; method that takes a set of typical command-line options and distrbutes them into the appropriate construction variables. Historically, it was created to support the &ParseConfig; method, so it focuses on options used by the GNU Compiler Collection (GCC) for the C and C++ toolchains. &ParseFlags; returns a dictionary containing the options distributed into their respective construction variables. Normally, this dictionary would be passed to &MergeFlags; to merge the options into a &consenv;, but the dictionary can be edited if desired to provide additional functionality. (Note that if the flags are not going to be edited, calling &MergeFlags; with the options directly will avoid an additional step.) env = Environment() d = env.ParseFlags("-I/opt/include -L/opt/lib -lfoo") for k,v in sorted(d.items()): if v: print k, v env.MergeFlags(d) env.Program('f1.c') % scons -Q CPPPATH ['/opt/include'] LIBPATH ['/opt/lib'] LIBS ['foo'] cc -o f1.o -c -I/opt/include f1.c cc -o f1 f1.o -L/opt/lib -lfoo Note that if the options are limited to generic types like those above, they will be correctly translated for other platform types: C:\>scons -Q CPPPATH ['/opt/include'] LIBPATH ['/opt/lib'] LIBS ['foo'] cl /Fof1.obj /c f1.c /nologo /I\opt\include link /nologo /OUT:f1.exe /LIBPATH:\opt\lib foo.lib f1.obj embedManifestExeCheck(target, source, env) Since the assumption is that the flags are used for the GCC toolchain, unrecognized flags are placed in &cv-link-CCFLAGS; so they will be used for both C and C++ compiles: env = Environment() d = env.ParseFlags("-whatever") for k,v in sorted(d.items()): if v: print k, v env.MergeFlags(d) env.Program('f1.c') % scons -Q CCFLAGS -whatever cc -o f1.o -c -whatever f1.c cc -o f1 f1.o &ParseFlags; will also accept a (recursive) list of strings as input; the list is flattened before the strings are processed: env = Environment() d = env.ParseFlags(["-I/opt/include", ["-L/opt/lib", "-lfoo"]]) for k,v in sorted(d.items()): if v: print k, v env.MergeFlags(d) env.Program('f1.c') % scons -Q CPPPATH ['/opt/include'] LIBPATH ['/opt/lib'] LIBS ['foo'] cc -o f1.o -c -I/opt/include f1.c cc -o f1 f1.o -L/opt/lib -lfoo If a string begins with a "!" (an exclamation mark, often called a bang), the string is passed to the shell for execution. The output of the command is then parsed: env = Environment() d = env.ParseFlags(["!echo -I/opt/include", "!echo -L/opt/lib", "-lfoo"]) for k,v in sorted(d.items()): if v: print k, v env.MergeFlags(d) env.Program('f1.c') % scons -Q CPPPATH ['/opt/include'] LIBPATH ['/opt/lib'] LIBS ['foo'] cc -o f1.o -c -I/opt/include f1.c cc -o f1 f1.o -L/opt/lib -lfoo &ParseFlags; is regularly updated for new options; consult the man page for details about those currently recognized. scons-doc-2.3.0/doc/user/alias.in0000644000175000017500000000653412114661557017376 0ustar dktrkranzdktrkranz We've already seen how you can use the &Alias; function to create a target named install: env = Environment() hello = env.Program('hello.c') env.Install('__ROOT__/usr/bin', hello) env.Alias('install', '__ROOT__/usr/bin') int main() { printf("Hello, world!\n"); } You can then use this alias on the command line to tell &SCons; more naturally that you want to install files: scons -Q install Like other &Builder; methods, though, the &Alias; method returns an object representing the alias being built. You can then use this object as input to anothother &Builder;. This is especially useful if you use such an object as input to another call to the &Alias; &Builder;, allowing you to create a hierarchy of nested aliases: env = Environment() p = env.Program('foo.c') l = env.Library('bar.c') env.Install('__ROOT__/usr/bin', p) env.Install('__ROOT__/usr/lib', l) ib = env.Alias('install-bin', '__ROOT__/usr/bin') il = env.Alias('install-lib', '__ROOT__/usr/lib') env.Alias('install', [ib, il]) int main() { printf("foo.c\n"); } void bar() { printf("bar.c\n"); } This example defines separate install, install-bin, and install-lib aliases, allowing you finer control over what gets installed: scons -Q install-bin scons -Q install-lib scons -Q -c __ROOT__/ scons -Q install scons-doc-2.3.0/doc/user/ant.in0000644000175000017500000000261712114661557017065 0ustar dktrkranzdktrkranz XXX
Differences Between &Ant; and &SCons; XXX
Advantages of &SCons; Over &Ant; XXX
scons-doc-2.3.0/doc/user/actions.in0000644000175000017500000002571012114661557017742 0ustar dktrkranzdktrkranz &SCons; supports several types of &build_actions; that can be performed to build one or more target files. Usually, a &build_action; is a command-line string that invokes an external command. A build action can also be an external command specified as a list of arguments, or even a Python function. Build action objects are created by the &Action; function. This function is, in fact, what &SCons; uses to interpret the &action; keyword argument when you call the &Builder; function. So the following line that creates a simple Builder: b = Builder(action = 'build < $SOURCE > $TARGET') Is equivalent to: b = Builder(action = Action('build < $SOURCE > $TARGET')) The advantage of using the &Action; function directly is that it can take a number of additional options to modify the action's behavior in many useful ways.
Command Strings as Actions
Suppressing Command-Line Printing XXX
Ignoring Exit Status XXX
Argument Lists as Actions XXX
Python Functions as Actions XXX
Modifying How an Action is Printed
XXX: the &strfunction; keyword argument XXX
XXX: the &cmdstr; keyword argument XXX
Making an Action Depend on Variable Contents: the &varlist; keyword argument XXX
chdir=1 XXX
Batch Building of Multiple Targets from Separate Sources: the &batch_key; keyword argument XXX
Manipulating the Exit Status of an Action: the &exitstatfunc; keyword argument XXX
scons-doc-2.3.0/doc/user/java.xml0000644000175000017500000003204012114661557017407 0ustar dktrkranzdktrkranz So far, we've been using examples of building C and C++ programs to demonstrate the features of &SCons;. &SCons; also supports building Java programs, but Java builds are handled slightly differently, which reflects the ways in which the Java compiler and tools build programs differently than other languages' tool chains.
Building Java Class Files: the &b-Java; Builder The basic activity when programming in Java, of course, is to take one or more .java files containing Java source code and to call the Java compiler to turn them into one or more .class files. In &SCons;, you do this by giving the &b-link-Java; Builder a target directory in which to put the .class files, and a source directory that contains the .java files: Java('classes', 'src') If the src directory contains three .java source files, then running &SCons; might look like this: % scons -Q javac -d classes -sourcepath src src/Example1.java src/Example2.java src/Example3.java &SCons; will actually search the src directory tree for all of the .java files. The Java compiler will then create the necessary class files in the classes subdirectory, based on the class names found in the .java files.
How &SCons; Handles Java Dependencies In addition to searching the source directory for .java files, &SCons; actually runs the .java files through a stripped-down Java parser that figures out what classes are defined. In other words, &SCons; knows, without you having to tell it, what .class files will be produced by the &javac; call. So our one-liner example from the preceding section: Java('classes', 'src') Will not only tell you reliably that the .class files in the classes subdirectory are up-to-date: % scons -Q javac -d classes -sourcepath src src/Example1.java src/Example2.java src/Example3.java % scons -Q classes scons: `classes' is up to date. But it will also remove all of the generated .class files, even for inner classes, without you having to specify them manually. For example, if our Example1.java and Example3.java files both define additional classes, and the class defined in Example2.java has an inner class, running scons -c will clean up all of those .class files as well: % scons -Q javac -d classes -sourcepath src src/Example1.java src/Example2.java src/Example3.java % scons -Q -c classes Removed classes/Example1.class Removed classes/AdditionalClass1.class Removed classes/Example2$Inner2.class Removed classes/Example2.class Removed classes/Example3.class Removed classes/AdditionalClass3.class To ensure correct handling of .class dependencies in all cases, you need to tell &SCons; which Java version is being used. This is needed because Java 1.5 changed the .class file names for nested anonymous inner classes. Use the JAVAVERSION construction variable to specify the version in use. With Java 1.6, the one-liner example can then be defined like this: Java('classes', 'src', JAVAVERSION='1.6') See JAVAVERSION in the man page for more information.
Building Java Archive (<filename>.jar</filename>) Files: the &b-Jar; Builder After building the class files, it's common to collect them into a Java archive (.jar) file, which you do by calling the &b-link-Jar; Builder method. If you want to just collect all of the class files within a subdirectory, you can just specify that subdirectory as the &b-Jar; source: Java(target = 'classes', source = 'src') Jar(target = 'test.jar', source = 'classes') &SCons; will then pass that directory to the &jar; command, which will collect all of the underlying .class files: % scons -Q javac -d classes -sourcepath src src/Example1.java src/Example2.java src/Example3.java jar cf test.jar classes If you want to keep all of the .class files for multiple programs in one location, and only archive some of them in each .jar file, you can pass the &b-Jar; builder a list of files as its source. It's extremely simple to create multiple .jar files this way, using the lists of target class files created by calls to the &b-link-Java; builder as sources to the various &b-Jar; calls: prog1_class_files = Java(target = 'classes', source = 'prog1') prog2_class_files = Java(target = 'classes', source = 'prog2') Jar(target = 'prog1.jar', source = prog1_class_files) Jar(target = 'prog2.jar', source = prog2_class_files) This will then create prog1.jar and prog2.jar next to the subdirectories that contain their .java files: % scons -Q javac -d classes -sourcepath prog1 prog1/Example1.java prog1/Example2.java javac -d classes -sourcepath prog2 prog2/Example3.java prog2/Example4.java jar cf prog1.jar -C classes Example1.class -C classes Example2.class jar cf prog2.jar -C classes Example3.class -C classes Example4.class
Building C Header and Stub Files: the &b-JavaH; Builder You can generate C header and source files for implementing native methods, by using the &b-link-JavaH; Builder. There are several ways of using the &JavaH; Builder. One typical invocation might look like: classes = Java(target = 'classes', source = 'src/pkg/sub') JavaH(target = 'native', source = classes) The source is a list of class files generated by the call to the &b-link-Java; Builder, and the target is the output directory in which we want the C header files placed. The target gets converted into the when &SCons; runs &javah;: % scons -Q javac -d classes -sourcepath src/pkg/sub src/pkg/sub/Example1.java src/pkg/sub/Example2.java src/pkg/sub/Example3.java javah -d native -classpath classes pkg.sub.Example1 pkg.sub.Example2 pkg.sub.Example3 In this case, the call to &javah; will generate the header files native/pkg_sub_Example1.h, native/pkg_sub_Example2.h and native/pkg_sub_Example3.h. Notice that &SCons; remembered that the class files were generated with a target directory of classes, and that it then specified that target directory as the option to the call to &javah;. Although it's more convenient to use the list of class files returned by the &b-Java; Builder as the source of a call to the &b-JavaH; Builder, you can specify the list of class files by hand, if you prefer. If you do, you need to set the &cv-link-JAVACLASSDIR; construction variable when calling &b-JavaH;: Java(target = 'classes', source = 'src/pkg/sub') class_file_list = ['classes/pkg/sub/Example1.class', 'classes/pkg/sub/Example2.class', 'classes/pkg/sub/Example3.class'] JavaH(target = 'native', source = class_file_list, JAVACLASSDIR = 'classes') The &cv-JAVACLASSDIR; value then gets converted into the when &SCons; runs &javah;: % scons -Q javac -d classes -sourcepath src/pkg/sub src/pkg/sub/Example1.java src/pkg/sub/Example2.java src/pkg/sub/Example3.java javah -d native -classpath classes pkg.sub.Example1 pkg.sub.Example2 pkg.sub.Example3 Lastly, if you don't want a separate header file generated for each source file, you can specify an explicit File Node as the target of the &b-JavaH; Builder: classes = Java(target = 'classes', source = 'src/pkg/sub') JavaH(target = File('native.h'), source = classes) Because &SCons; assumes by default that the target of the &b-JavaH; builder is a directory, you need to use the &File; function to make sure that &SCons; doesn't create a directory named native.h. When a file is used, though, &SCons; correctly converts the file name into the &javah; option: % scons -Q javac -d classes -sourcepath src/pkg/sub src/pkg/sub/Example1.java src/pkg/sub/Example2.java src/pkg/sub/Example3.java javah -o native.h -classpath classes pkg.sub.Example1 pkg.sub.Example2 pkg.sub.Example3
Building RMI Stub and Skeleton Class Files: the &b-RMIC; Builder You can generate Remote Method Invocation stubs by using the &b-link-RMIC; Builder. The source is a list of directories, typically returned by a call to the &b-link-Java; Builder, and the target is an output directory where the _Stub.class and _Skel.class files will be placed: classes = Java(target = 'classes', source = 'src/pkg/sub') RMIC(target = 'outdir', source = classes) As it did with the &b-link-JavaH; Builder, &SCons; remembers the class directory and passes it as the option to &rmic;: % scons -Q javac -d classes -sourcepath src/pkg/sub src/pkg/sub/Example1.java src/pkg/sub/Example2.java rmic -d outdir -classpath classes pkg.sub.Example1 pkg.sub.Example2 This example would generate the files outdir/pkg/sub/Example1_Skel.class, outdir/pkg/sub/Example1_Stub.class, outdir/pkg/sub/Example2_Skel.class and outdir/pkg/sub/Example2_Stub.class.
scons-doc-2.3.0/doc/user/parseconfig.xml0000644000175000017500000001040112114661557020763 0ustar dktrkranzdktrkranz Configuring the right options to build programs to work with libraries--especially shared libraries--that are available on POSIX systems can be very complicated. To help this situation, various utilies with names that end in config return the command-line options for the GNU Compiler Collection (GCC) that are needed to use these libraries; for example, the command-line options to use a library named lib would be found by calling a utility named lib-config. A more recent convention is that these options are available from the generic pkg-config program, which has common framework, error handling, and the like, so that all the package creator has to do is provide the set of strings for his particular package. &SCons; construction environments have a &ParseConfig; method that executes a *config utility (either pkg-config or a more specific utility) and configures the appropriate construction variables in the environment based on the command-line options returned by the specified command. env = Environment() env['CPPPATH'] = ['/lib/compat'] env.ParseConfig("pkg-config x11 --cflags --libs") print env['CPPPATH'] &SCons; will execute the specified command string, parse the resultant flags, and add the flags to the appropriate environment variables. % scons -Q ['/lib/compat', '/usr/X11/include'] scons: `.' is up to date. In the example above, &SCons; has added the include directory to CPPPATH. (Depending upon what other flags are emitted by the pkg-config command, other variables may have been extended as well.) Note that the options are merged with existing options using the &MergeFlags; method, so that each option only occurs once in the construction variable: env = Environment() env.ParseConfig("pkg-config x11 --cflags --libs") env.ParseConfig("pkg-config x11 --cflags --libs") print env['CPPPATH'] % scons -Q ['/usr/X11/include'] scons: `.' is up to date. scons-doc-2.3.0/doc/user/builders-built-in.xml0000644000175000017500000004732412114661557022033 0ustar dktrkranzdktrkranz &SCons; provides the ability to build a lot of different types of files right "out of the box." So far, we've been using &SCons;' ability to build programs, objects and libraries to illustrate much of the underlying functionality of &SCons; This section will describe all of the different types of files that you can build with &SCons;, and the built-in &Builder; objects used to build them. By default, all of the &Builder; objects in this section can be built either with or without an explicit construction environment.
Programs: the &Program; Builder As we've seen, the &b-link-Program; Builder is used to build an executable program. The &source; argument is one or more source-code files or object files, and the ⌖ argument is the name of the executable program name to be created. For example: Program('prog', 'file1.o') Will create the &prog; executable on a POSIX system, the &prog_exe; executable on a Windows system. The target file's prefix and suffix may be omitted, and the values from the &cv-link-PROGPREFIX; and &cv-link-PROGSUFFIX; construction variables will be appended appropriately. For example: env = Environment(PROGPREFIX='my', PROGSUFFIX='.xxx') env.Program('prog', ['file1.o', 'file2.o']) Will create a program named myprog.xxx regardless of the system on which it is run. If you omit the ⌖, the base of the first input file name specified becomes the base of the target program created. For example: Program(['hello.c', 'goodbye.c']) Will create the &hello; executable on a POSIX system, the &hello_exe; executable on a Windows system. Two construction variables control what libraries will be linked with the resulting program. The &cv-link-LIBS; variable is a list of the names of libraries that will be linked into any programs, and the &cv-link-LIBPATH; variables is a list of directories that will be searched for the specified libraries. &SCons; will construct the right command-line options for the running system. For example: env = Environment(LIBS = ['foo1', 'foo2'], LIBPATH = ['/usr/dir1', 'dir2']) env.Program(['hello.c', 'goodbye.c']) Will execute as follows on a POSIX system: % scons -Q cc -o goodbye.o -c goodbye.c cc -o hello.o -c hello.c cc -o hello hello.o goodbye.o -L/usr/dir1 -Ldir2 -lfoo1 -lfoo2 And execute as follows on a Windows system: C:\>scons -Q cl /Fogoodbye.obj /c goodbye.c /nologo cl /Fohello.obj /c hello.c /nologo link /nologo /OUT:hello.exe /LIBPATH:\usr\dir1 /LIBPATH:dir2 foo1.lib foo2.lib hello.obj goodbye.obj embedManifestExeCheck(target, source, env) The &cv-LIBS; construction variable is turned into command line options by appending the &cv-link-LIBLINKPREFIX; and &cv-link-LIBLINKSUFFIX; construction variables to the beginning and end, respectively, of each specified library. The &cv-LIBPATH; construction variable is turned into command line options by appending the &cv-link-LIBDIRPREFIX; and &cv-link-LIBDIRSUFFIX; construction variables to the beginning and end, respectively, of each specified library. Other relevant construction variables include those used by the &b-link-Object; builders to affect how the source files specified as input to the &t-Program; builders are turned into object files; see the next section. The command line used to control how a program is linked is specified by the &cv-link-LINKCOM; construction variable. By default, it uses the &cv-link-LINK; construction variable and the &cv-link-LINKFLAGS; construction variable.
Object-File Builders &SCons; provides separate Builder objects to create static and shared object files. The distinction becomes especially important when archiving object files into different types of libraries.
The &StaticObject; Builder The &b-link-StaticObject; Builder is used to build an object file suitable for static linking into a program, or for inclusion in a static library. The &source; argument is a single source-code file, and the ⌖ argument is the name of the static object file to be created. For example: StaticObject('file', 'file.c') Will create the &file_o; object file on a POSIX system, the &file_obj; executable on a Windows system. The target file's prefix and suffix may be omitted, and the values from the &cv-link-OBJPREFIX; and &cv-link-OBJSUFFIX; construction variables will be appended appropriately. For example: env = Environment(OBJPREFIX='my', OBJSUFFIX='.xxx') env.StaticObject('file', 'file.c') Will create an object file named myfile.xxx regardless of the system on which it is run. If you omit the ⌖, the base of the first input file name specified beomces the base of the name of the static object file to be created. For example: StaticObject('file.c') Will create the &file_o; executable on a POSIX system, the &file_obj; executable on a Windows system.
The &SharedObject; Builder The &b-link-SharedObject; Builder is used to build an object file suitable for shared linking into a program, or for inclusion in a shared library. The &source; argument is a single source-code file, and the ⌖ argument is the name of the shared object file to be created. For example: SharedObject('file', 'file.c') Will create the &file_o; object file on a POSIX system, the &file_obj; executable on a Windows system. The target file's prefix and suffix may be omitted, and the values from the &cv-link-SHOBJPREFIX; and &cv-link-SHOBJSUFFIX; construction variables will be appended appropriately. For example: env = Environment(SHOBJPREFIX='my', SHOBJSUFFIX='.xxx') env.SharedObject('file', 'file.c') Will create an object file named myfile.xxx regardless of the system on which it is run. If you omit the ⌖, the base of the first input file name specified becomes the base of the name of the shared object file to be created. For example: SharedObject('file.c') Will create the &file_o; executable on a POSIX system, the &file_obj; executable on a Windows system.
The &Object; Builder The &b-link-Object; Builder is a synonym for &b-link-StaticObject; and is completely equivalent.
Library Builders &SCons; provides separate Builder objects to create static and shared libraries.
The &StaticLibrary; Builder The &b-link-StaticLibrary; Builder is used to create a library suitable for static linking into a program. The &source; argument is one or more source-code files or object files, and the ⌖ argument is the name of the static library to be created. For example: StaticLibrary('foo', ['file1.c', 'file2.c']) The target file's prefix and suffix may be omitted, and the values from the &cv-link-LIBPREFIX; and &cv-link-LIBSUFFIX; construction variables will be appended appropriately. For example: env = Environment(LIBPREFIX='my', LIBSUFFIX='.xxx') env.StaticLibrary('lib', ['file1.o', 'file2.o']) Will create an object file named mylib.xxx regardless of the system on which it is run. StaticLibrary('foo', ['file1.c', 'file2.c']) If you omit the ⌖, the base of the first input file name specified becomes the base of the name of the static object file to be created. For example: StaticLibrary(['file.c', 'another.c']) Will create the &libfile_a; library on a POSIX system, the &file_lib; library on a Windows system.
The &SharedLibrary; Builder The &b-link-SharedLibrary; Builder is used to create a shared library suitable for linking with a program. The &source; argument is one or more source-code files or object files, and the ⌖ argument is the name of the shared library to be created. For example: SharedLibrary('foo', ['file1.c', 'file2.c']) The target file's prefix and suffix may be omitted, and the values from the &cv-link-SHLIBPREFIX; and &cv-link-SHLIBSUFFIX; construction variables will be appended appropriately. For example: env = Environment(SHLIBPREFIX='my', SHLIBSUFFIX='.xxx') env.SharedLibrary('shared', ['file1.o', 'file2.o']) Will create an object file named myshared.xxx regardless of the system on which it is run. SharedLibrary('foo', ['file1.c', 'file2.c']) If you omit the ⌖, the base of the first input file name specified becomes the base of the name of the shared library to be created. For example: SharedLibrary(['file.c', 'another.c']) Will create the &libfile_so; library on a POSIX system, the &file_dll; library on a Windows system.
The &Library; Builder The &b-link-Library; Builder is a synonym for &b-link-StaticLibrary; and is completely equivalent.
Pre-Compiled Headers: the &PCH; Builder XXX PCH()
Microsoft Visual C++ Resource Files: the &RES; Builder XXX RES()
Source Files By default &SCons; supports two Builder objects that know how to build source files from other input files. These are typically invoked "internally" to turn files that need preprocessing into other source files.
The &CFile; Builder XXX CFile() XXX CFile() programlisting XXX CFile() screen
The &CXXFile; Builder XXX CXXFILE() XXX CXXFILE() programlisting XXX CXXFILE() screen
Documents &SCons; provides a number of Builder objects for creating different types of documents.
The &DVI; Builder XXX DVI() para XXX DVI() programlisting XXX DVI() screen
The &PDF; Builder XXX PDF() para
The &PostScript; Builder XXX PostScript() para XXX PostScript() programlisting XXX PostScript() screen
Archives &SCons; provides Builder objects for creating two different types of archive files.
The &Tar; Builder The &b-link-Tar; Builder object uses the &tar; utility to create archives of files and/or directory trees: env = Environment() env.Tar('out1.tar', ['file1', 'file2']) env.Tar('out2', 'directory') % scons -Q . tar -c -f out1.tar file1 file2 tar -c -f out2.tar directory One common requirement when creating a &tar; archive is to create a compressed archive using the option. This is easily handled by specifying the value of the &cv-link-TARFLAGS; variable when you create the construction environment. Note, however, that the used to to instruct &tar; to create the archive is part of the default value of &cv-TARFLAGS;, so you need to set it both options: env = Environment(TARFLAGS = '-c -z') env.Tar('out.tar.gz', 'directory') % scons -Q . tar -c -z -f out.tar.gz directory you may also wish to set the value of the &cv-link-TARSUFFIX; construction variable to your desired suffix for compress &tar; archives, so that &SCons; can append it to the target file name without your having to specify it explicitly: env = Environment(TARFLAGS = '-c -z', TARSUFFIX = '.tgz') env.Tar('out', 'directory') % scons -Q . tar -c -z -f out.tgz directory
The &Zip; Builder The &b-link-Zip; Builder object creates archives of files and/or directory trees in the ZIP file format. Python versions 1.6 or later contain an internal &zipfile; module that &SCons; will use. In this case, given the following &SConstruct; file: env = Environment() env.Zip('out', ['file1', 'file2']) Your output will reflect the fact that an internal Python function is being used to create the output ZIP archive: % scons -Q . zip(["out.zip"], ["file1", "file2"])
Java &SCons; provides Builder objects for creating various types of Java output files.
Building Class Files: the &Java; Builder The &b-link-Java; builder takes one or more input .java files and turns them into one or more .class files Unlike most builders, however, the &Java; builder takes target and source directories, not files, as input. env = Environment() env.Java(target = 'classes', source = 'src') The &Java; builder will then search the specified source directory tree for all .java files, and pass any out-of-date XXX Java() screen
The &Jar; Builder XXX The &Jar; builder object env = Environment() env.Java(target = 'classes', source = 'src') env.Jar(target = '', source = 'classes') XXX Jar() screen
Building C header and stub files: the &JavaH; Builder XXX JavaH() para XXX JavaH() programlisting XXX JavaH() screen
Building RMI stub and skeleton class files: the &RMIC; Builder XXX RMIC() para XXX RMIC() programlisting XXX RMIC() screen
scons-doc-2.3.0/doc/user/main.in0000644000175000017500000002461012114661557017224 0ustar dktrkranzdktrkranz %version; %scons; %builders-mod; %functions-mod; %tools-mod; %variables-mod; ]> SCons User Guide &buildversion; Steven Knight Revision &buildrevision; (&builddate;) 2004, 2005, 2006, 2007, 2008, 2009, 2010 2004, 2005, 2006, 2007, 2008, 2009, 2010 Steven Knight ©right; version &buildversion; Preface &preface; Building and Installing &SCons; &build-install; Simple Builds &simple; Less Simple Things to Do With Builds &less-simple; Building and Linking with Libraries &libraries; Node Objects &nodes; Dependencies &depends; Environments &environments; Automatically Putting Command-line Options into their Construction Variables This chapter describes the &MergeFlags;, &ParseFlags;, and &ParseConfig; methods of a &consenv;.
Merging Options into the Environment: the &MergeFlags; Function &mergeflags;
Separating Compile Arguments into their Variables: the &ParseFlags; Function &parseflags;
Finding Installed Library Information: the &ParseConfig; Function &parseconfig;
Controlling Build Output &output; Controlling a Build From the Command Line &command-line; Installing Files in Other Directories: the &Install; Builder &install; Platform-Independent File System Manipulation &factories; Controlling Removal of Targets &file-removal; Hierarchical Builds &hierarchy; Separating Source and Build Directories &separate; Variant Builds &variants; Internationalization and localization with gettext &gettext; Writing Your Own Builders &builders-writing; Not Writing a Builder: the &Command; Builder &builders-commands; Pseudo-Builders: the AddMethod function &add-method; Writing Scanners &scanners; Building From Code Repositories &repositories; Multi-Platform Configuration (&Autoconf; Functionality) &sconf; Caching Built Files &caching; Alias Targets &alias; Java Builds &java; Miscellaneous Functionality &misc; Troubleshooting &troubleshoot; Construction Variables &variables-xml; Builders &builders; Tools &tools; Functions and Environment Methods &functions; Handling Common Tasks &tasks;
scons-doc-2.3.0/doc/user/simple.xml0000644000175000017500000003574012114661557017771 0ustar dktrkranzdktrkranz In this chapter, you will see several examples of very simple build configurations using &SCons;, which will demonstrate how easy it is to use &SCons; to build programs from several different programming languages on different types of systems.
Building Simple C / C++ Programs Here's the famous "Hello, World!" program in C: int main() { printf("Hello, world!\n"); } And here's how to build it using &SCons;. Enter the following into a file named &SConstruct;: Program('hello.c') This minimal configuration file gives &SCons; two pieces of information: what you want to build (an executable program), and the input file from which you want it built (the hello.c file). &b-link-Program; is a builder_method, a Python call that tells &SCons; that you want to build an executable program. That's it. Now run the &scons; command to build the program. On a POSIX-compliant system like Linux or UNIX, you'll see something like: % scons scons: Reading SConscript files ... scons: done reading SConscript files. scons: Building targets ... cc -o hello.o -c hello.c cc -o hello hello.o scons: done building targets. On a Windows system with the Microsoft Visual C++ compiler, you'll see something like: C:\>scons scons: Reading SConscript files ... scons: done reading SConscript files. scons: Building targets ... cl /Fohello.obj /c hello.c /nologo link /nologo /OUT:hello.exe hello.obj embedManifestExeCheck(target, source, env) scons: done building targets. First, notice that you only need to specify the name of the source file, and that &SCons; correctly deduces the names of the object and executable files to be built from the base of the source file name. Second, notice that the same input &SConstruct; file, without any changes, generates the correct output file names on both systems: hello.o and hello on POSIX systems, hello.obj and hello.exe on Windows systems. This is a simple example of how &SCons; makes it extremely easy to write portable software builds. (Note that we won't provide duplicate side-by-side POSIX and Windows output for all of the examples in this guide; just keep in mind that, unless otherwise specified, any of the examples should work equally well on both types of systems.)
Building Object Files The &b-link-Program; builder method is only one of many builder methods that &SCons; provides to build different types of files. Another is the &b-link-Object; builder method, which tells &SCons; to build an object file from the specified source file: Object('hello.c') Now when you run the &scons; command to build the program, it will build just the &hello_o; object file on a POSIX system: % scons scons: Reading SConscript files ... scons: done reading SConscript files. scons: Building targets ... cc -o hello.o -c hello.c scons: done building targets. And just the &hello_obj; object file on a Windows system (with the Microsoft Visual C++ compiler): C:\>scons scons: Reading SConscript files ... scons: done reading SConscript files. scons: Building targets ... cl /Fohello.obj /c hello.c /nologo scons: done building targets.
Simple Java Builds &SCons; also makes building with Java extremely easy. Unlike the &b-link-Program; and &b-link-Object; builder methods, however, the &b-link-Java; builder method requires that you specify the name of a destination directory in which you want the class files placed, followed by the source directory in which the .java files live: Java('classes', 'src') If the src directory contains a single hello.java file, then the output from running the &scons; command would look something like this (on a POSIX system): % scons scons: Reading SConscript files ... scons: done reading SConscript files. scons: Building targets ... javac -d classes -sourcepath src src/hello.java scons: done building targets. We'll cover Java builds in more detail, including building Java archive (.jar) and other types of file, in .
Cleaning Up After a Build When using &SCons;, it is unnecessary to add special commands or target names to clean up after a build. Instead, you simply use the -c or --clean option when you invoke &SCons;, and &SCons; removes the appropriate built files. So if we build our example above and then invoke scons -c afterwards, the output on POSIX looks like: % scons scons: Reading SConscript files ... scons: done reading SConscript files. scons: Building targets ... cc -o hello.o -c hello.c cc -o hello hello.o scons: done building targets. % scons -c scons: Reading SConscript files ... scons: done reading SConscript files. scons: Cleaning targets ... Removed hello.o Removed hello scons: done cleaning targets. And the output on Windows looks like: C:\>scons scons: Reading SConscript files ... scons: done reading SConscript files. scons: Building targets ... cl /Fohello.obj /c hello.c /nologo link /nologo /OUT:hello.exe hello.obj embedManifestExeCheck(target, source, env) scons: done building targets. C:\>scons -c scons: Reading SConscript files ... scons: done reading SConscript files. scons: Cleaning targets ... Removed hello.obj Removed hello.exe scons: done cleaning targets. Notice that &SCons; changes its output to tell you that it is Cleaning targets ... and done cleaning targets.
The &SConstruct; File If you're used to build systems like &Make; you've already figured out that the &SConstruct; file is the &SCons; equivalent of a &Makefile;. That is, the &SConstruct; file is the input file that &SCons; reads to control the build.
&SConstruct; Files Are Python Scripts There is, however, an important difference between an &SConstruct; file and a &Makefile;: the &SConstruct; file is actually a Python script. If you're not already familiar with Python, don't worry. This User's Guide will introduce you step-by-step to the relatively small amount of Python you'll need to know to be able to use &SCons; effectively. And Python is very easy to learn. One aspect of using Python as the scripting language is that you can put comments in your &SConstruct; file using Python's commenting convention; that is, everything between a '#' and the end of the line will be ignored: # Arrange to build the "hello" program. Program('hello.c') # "hello.c" is the source file. You'll see throughout the remainder of this Guide that being able to use the power of a real scripting language can greatly simplify the solutions to complex requirements of real-world builds.
&SCons; Functions Are Order-Independent One important way in which the &SConstruct; file is not exactly like a normal Python script, and is more like a &Makefile;, is that the order in which the &SCons; functions are called in the &SConstruct; file does not affect the order in which &SCons; actually builds the programs and object files you want it to build. In programming parlance, the &SConstruct; file is declarative, meaning you tell &SCons; what you want done and let it figure out the order in which to do it, rather than strictly imperative, where you specify explicitly the order in which to do things. In other words, when you call the &b-link-Program; builder (or any other builder method), you're not telling &SCons; to build the program at the instant the builder method is called. Instead, you're telling &SCons; to build the program that you want, for example, a program built from a file named &hello_c;, and it's up to &SCons; to build that program (and any other files) whenever it's necessary. (We'll learn more about how &SCons; decides when building or rebuilding a file is necessary in , below.) &SCons; reflects this distinction between calling a builder method like &b-Program; and actually building the program by printing the status messages that indicate when it's "just reading" the &SConstruct; file, and when it's actually building the target files. This is to make it clear when &SCons; is executing the Python statements that make up the &SConstruct; file, and when &SCons; is actually executing the commands or other actions to build the necessary files. Let's clarify this with an example. Python has a print statement that prints a string of characters to the screen. If we put print statements around our calls to the &b-Program; builder method: print "Calling Program('hello.c')" Program('hello.c') print "Calling Program('goodbye.c')" Program('goodbye.c') print "Finished calling Program()" Then when we execute &SCons;, we see the output from the print statements in between the messages about reading the &SConscript; files, indicating that that is when the Python statements are being executed: % scons scons: Reading SConscript files ... Calling Program('hello.c') Calling Program('goodbye.c') Finished calling Program() scons: done reading SConscript files. scons: Building targets ... cc -o goodbye.o -c goodbye.c cc -o goodbye goodbye.o cc -o hello.o -c hello.c cc -o hello hello.o scons: done building targets. Notice also that &SCons; built the &goodbye; program first, even though the "reading &SConscript;" output shows that we called Program('hello.c') first in the &SConstruct; file.
Making the &SCons; Output Less Verbose You've already seen how &SCons; prints some messages about what it's doing, surrounding the actual commands used to build the software: C:\>scons scons: Reading SConscript files ... scons: done reading SConscript files. scons: Building targets ... cl /Fohello.obj /c hello.c /nologo link /nologo /OUT:hello.exe hello.obj embedManifestExeCheck(target, source, env) scons: done building targets. These messages emphasize the order in which &SCons; does its work: all of the configuration files (generically referred to as &SConscript; files) are read and executed first, and only then are the target files built. Among other benefits, these messages help to distinguish between errors that occur while the configuration files are read, and errors that occur while targets are being built. One drawback, of course, is that these messages clutter the output. Fortunately, they're easily disabled by using the &Q; option when invoking &SCons;: C:\>scons -Q cl /Fohello.obj /c hello.c /nologo link /nologo /OUT:hello.exe hello.obj embedManifestExeCheck(target, source, env) Because we want this User's Guide to focus on what &SCons; is actually doing, we're going to use the &Q; option to remove these messages from the output of all the remaining examples in this Guide.
scons-doc-2.3.0/doc/user/builders-commands.xml0000644000175000017500000000760312114661557022105 0ustar dktrkranzdktrkranz Creating a &Builder; and attaching it to a &consenv; allows for a lot of flexibility when you want to re-use actions to build multiple files of the same type. This can, however, be cumbersome if you only need to execute one specific command to build a single file (or group of files). For these situations, &SCons; supports a &Command; &Builder; that arranges for a specific action to be executed to build a specific file or files. This looks a lot like the other builders (like &b-link-Program;, &b-link-Object;, etc.), but takes as an additional argument the command to be executed to build the file: env = Environment() env.Command('foo.out', 'foo.in', "sed 's/x/y/' < $SOURCE > $TARGET") When executed, &SCons; runs the specified command, substituting &cv-link-SOURCE; and &cv-link-TARGET; as expected: % scons -Q sed 's/x/y/' < foo.in > foo.out This is often more convenient than creating a &Builder; object and adding it to the &cv-link-BUILDERS; variable of a &consenv; Note that the action you specify to the &Command; &Builder; can be any legal &SCons; &Action;, such as a Python function: env = Environment() def build(target, source, env): # Whatever it takes to build return None env.Command('foo.out', 'foo.in', build) Which executes as follows: % scons -Q build(["foo.out"], ["foo.in"]) Note that &cv-link-SOURCE; and &cv-link-TARGET; are expanded in the source and target as well as of SCons 1.1, so you can write: env.Command('${SOURCE.basename}.out', 'foo.in', build) which does the same thing as the previous example, but allows you to avoid repeating yourself. scons-doc-2.3.0/doc/user/builders-built-in.in0000644000175000017500000005071412114661557021636 0ustar dktrkranzdktrkranz &SCons; provides the ability to build a lot of different types of files right "out of the box." So far, we've been using &SCons;' ability to build programs, objects and libraries to illustrate much of the underlying functionality of &SCons; This section will describe all of the different types of files that you can build with &SCons;, and the built-in &Builder; objects used to build them. By default, all of the &Builder; objects in this section can be built either with or without an explicit construction environment.
Programs: the &Program; Builder As we've seen, the &b-link-Program; Builder is used to build an executable program. The &source; argument is one or more source-code files or object files, and the ⌖ argument is the name of the executable program name to be created. For example: Program('prog', 'file1.o') Will create the &prog; executable on a POSIX system, the &prog_exe; executable on a Windows system. The target file's prefix and suffix may be omitted, and the values from the &cv-link-PROGPREFIX; and &cv-link-PROGSUFFIX; construction variables will be appended appropriately. For example: env = Environment(PROGPREFIX='my', PROGSUFFIX='.xxx') env.Program('prog', ['file1.o', 'file2.o']) Will create a program named myprog.xxx regardless of the system on which it is run. If you omit the ⌖, the base of the first input file name specified becomes the base of the target program created. For example: Program(['hello.c', 'goodbye.c']) Will create the &hello; executable on a POSIX system, the &hello_exe; executable on a Windows system. Two construction variables control what libraries will be linked with the resulting program. The &cv-link-LIBS; variable is a list of the names of libraries that will be linked into any programs, and the &cv-link-LIBPATH; variables is a list of directories that will be searched for the specified libraries. &SCons; will construct the right command-line options for the running system. For example: env = Environment(LIBS = ['foo1', 'foo2'], LIBPATH = ['/usr/dir1', 'dir2']) env.Program(['hello.c', 'goodbye.c']) int hello() { printf("Hello, world!\n"); } int goodbye() { printf("Goodbye, world!\n"); } Will execute as follows on a POSIX system: scons -Q And execute as follows on a Windows system: scons -Q The &cv-LIBS; construction variable is turned into command line options by appending the &cv-link-LIBLINKPREFIX; and &cv-link-LIBLINKSUFFIX; construction variables to the beginning and end, respectively, of each specified library. The &cv-LIBPATH; construction variable is turned into command line options by appending the &cv-link-LIBDIRPREFIX; and &cv-link-LIBDIRSUFFIX; construction variables to the beginning and end, respectively, of each specified library. Other relevant construction variables include those used by the &b-link-Object; builders to affect how the source files specified as input to the &t-Program; builders are turned into object files; see the next section. The command line used to control how a program is linked is specified by the &cv-link-LINKCOM; construction variable. By default, it uses the &cv-link-LINK; construction variable and the &cv-link-LINKFLAGS; construction variable.
Object-File Builders &SCons; provides separate Builder objects to create static and shared object files. The distinction becomes especially important when archiving object files into different types of libraries.
The &StaticObject; Builder The &b-link-StaticObject; Builder is used to build an object file suitable for static linking into a program, or for inclusion in a static library. The &source; argument is a single source-code file, and the ⌖ argument is the name of the static object file to be created. For example: StaticObject('file', 'file.c') Will create the &file_o; object file on a POSIX system, the &file_obj; executable on a Windows system. The target file's prefix and suffix may be omitted, and the values from the &cv-link-OBJPREFIX; and &cv-link-OBJSUFFIX; construction variables will be appended appropriately. For example: env = Environment(OBJPREFIX='my', OBJSUFFIX='.xxx') env.StaticObject('file', 'file.c') Will create an object file named myfile.xxx regardless of the system on which it is run. If you omit the ⌖, the base of the first input file name specified beomces the base of the name of the static object file to be created. For example: StaticObject('file.c') Will create the &file_o; executable on a POSIX system, the &file_obj; executable on a Windows system.
The &SharedObject; Builder The &b-link-SharedObject; Builder is used to build an object file suitable for shared linking into a program, or for inclusion in a shared library. The &source; argument is a single source-code file, and the ⌖ argument is the name of the shared object file to be created. For example: SharedObject('file', 'file.c') Will create the &file_o; object file on a POSIX system, the &file_obj; executable on a Windows system. The target file's prefix and suffix may be omitted, and the values from the &cv-link-SHOBJPREFIX; and &cv-link-SHOBJSUFFIX; construction variables will be appended appropriately. For example: env = Environment(SHOBJPREFIX='my', SHOBJSUFFIX='.xxx') env.SharedObject('file', 'file.c') Will create an object file named myfile.xxx regardless of the system on which it is run. If you omit the ⌖, the base of the first input file name specified becomes the base of the name of the shared object file to be created. For example: SharedObject('file.c') Will create the &file_o; executable on a POSIX system, the &file_obj; executable on a Windows system.
The &Object; Builder The &b-link-Object; Builder is a synonym for &b-link-StaticObject; and is completely equivalent.
Library Builders &SCons; provides separate Builder objects to create static and shared libraries.
The &StaticLibrary; Builder The &b-link-StaticLibrary; Builder is used to create a library suitable for static linking into a program. The &source; argument is one or more source-code files or object files, and the ⌖ argument is the name of the static library to be created. For example: StaticLibrary('foo', ['file1.c', 'file2.c']) The target file's prefix and suffix may be omitted, and the values from the &cv-link-LIBPREFIX; and &cv-link-LIBSUFFIX; construction variables will be appended appropriately. For example: env = Environment(LIBPREFIX='my', LIBSUFFIX='.xxx') env.StaticLibrary('lib', ['file1.o', 'file2.o']) Will create an object file named mylib.xxx regardless of the system on which it is run. StaticLibrary('foo', ['file1.c', 'file2.c']) If you omit the ⌖, the base of the first input file name specified becomes the base of the name of the static object file to be created. For example: StaticLibrary(['file.c', 'another.c']) Will create the &libfile_a; library on a POSIX system, the &file_lib; library on a Windows system.
The &SharedLibrary; Builder The &b-link-SharedLibrary; Builder is used to create a shared library suitable for linking with a program. The &source; argument is one or more source-code files or object files, and the ⌖ argument is the name of the shared library to be created. For example: SharedLibrary('foo', ['file1.c', 'file2.c']) The target file's prefix and suffix may be omitted, and the values from the &cv-link-SHLIBPREFIX; and &cv-link-SHLIBSUFFIX; construction variables will be appended appropriately. For example: env = Environment(SHLIBPREFIX='my', SHLIBSUFFIX='.xxx') env.SharedLibrary('shared', ['file1.o', 'file2.o']) Will create an object file named myshared.xxx regardless of the system on which it is run. SharedLibrary('foo', ['file1.c', 'file2.c']) If you omit the ⌖, the base of the first input file name specified becomes the base of the name of the shared library to be created. For example: SharedLibrary(['file.c', 'another.c']) Will create the &libfile_so; library on a POSIX system, the &file_dll; library on a Windows system.
The &Library; Builder The &b-link-Library; Builder is a synonym for &b-link-StaticLibrary; and is completely equivalent.
Pre-Compiled Headers: the &PCH; Builder XXX PCH()
Microsoft Visual C++ Resource Files: the &RES; Builder XXX RES()
Source Files By default &SCons; supports two Builder objects that know how to build source files from other input files. These are typically invoked "internally" to turn files that need preprocessing into other source files.
The &CFile; Builder XXX CFile() XXX CFile() programlisting XXX CFile() screen
The &CXXFile; Builder XXX CXXFILE() XXX CXXFILE() programlisting XXX CXXFILE() screen
Documents &SCons; provides a number of Builder objects for creating different types of documents.
The &DVI; Builder XXX DVI() para XXX DVI() programlisting XXX DVI() screen
The &PDF; Builder XXX PDF() para
The &PostScript; Builder XXX PostScript() para XXX PostScript() programlisting XXX PostScript() screen
Archives &SCons; provides Builder objects for creating two different types of archive files.
The &Tar; Builder The &b-link-Tar; Builder object uses the &tar; utility to create archives of files and/or directory trees: env = Environment() env.Tar('out1.tar', ['file1', 'file2']) env.Tar('out2', 'directory') file1 file2 directory/file3 scons -Q . One common requirement when creating a &tar; archive is to create a compressed archive using the option. This is easily handled by specifying the value of the &cv-link-TARFLAGS; variable when you create the construction environment. Note, however, that the used to to instruct &tar; to create the archive is part of the default value of &cv-TARFLAGS;, so you need to set it both options: env = Environment(TARFLAGS = '-c -z') env.Tar('out.tar.gz', 'directory') directory/file scons -Q . you may also wish to set the value of the &cv-link-TARSUFFIX; construction variable to your desired suffix for compress &tar; archives, so that &SCons; can append it to the target file name without your having to specify it explicitly: env = Environment(TARFLAGS = '-c -z', TARSUFFIX = '.tgz') env.Tar('out', 'directory') directory/file scons -Q .
The &Zip; Builder The &b-link-Zip; Builder object creates archives of files and/or directory trees in the ZIP file format. Python versions 1.6 or later contain an internal &zipfile; module that &SCons; will use. In this case, given the following &SConstruct; file: env = Environment() env.Zip('out', ['file1', 'file2']) file1 file2 Your output will reflect the fact that an internal Python function is being used to create the output ZIP archive: scons -Q .
Java &SCons; provides Builder objects for creating various types of Java output files.
Building Class Files: the &Java; Builder The &b-link-Java; builder takes one or more input .java files and turns them into one or more .class files Unlike most builders, however, the &Java; builder takes target and source directories, not files, as input. env = Environment() env.Java(target = 'classes', source = 'src') The &Java; builder will then search the specified source directory tree for all .java files, and pass any out-of-date XXX Java() screen
The &Jar; Builder XXX The &Jar; builder object env = Environment() env.Java(target = 'classes', source = 'src') env.Jar(target = '', source = 'classes') XXX Jar() screen
Building C header and stub files: the &JavaH; Builder XXX JavaH() para XXX JavaH() programlisting XXX JavaH() screen
Building RMI stub and skeleton class files: the &RMIC; Builder XXX RMIC() para XXX RMIC() programlisting XXX RMIC() screen
scons-doc-2.3.0/doc/user/factories.xml0000644000175000017500000002665312114661557020462 0ustar dktrkranzdktrkranz &SCons; provides a number of platform-independent functions, called factories, that perform common file system manipulations like copying, moving or deleting files and directories, or making directories. These functions are factories because they don't perform the action at the time they're called, they each return an &Action; object that can be executed at the appropriate time.
Copying Files or Directories: The &Copy; Factory Suppose you want to arrange to make a copy of a file, and don't have a suitable pre-existing builder. Unfortunately, in the early days of SCons design, we used the name &Copy; for the function that returns a copy of the environment, otherwise that would be the logical choice for a Builder that copies a file or directory tree to a target location. One way would be to use the &Copy; action factory in conjunction with the &Command; builder: Command("file.out", "file.in", Copy("$TARGET", "$SOURCE")) Notice that the action returned by the &Copy; factory will expand the &cv-link-TARGET; and &cv-link-SOURCE; strings at the time &file_out; is built, and that the order of the arguments is the same as that of a builder itself--that is, target first, followed by source: % scons -Q Copy("file.out", "file.in") You can, of course, name a file explicitly instead of using &cv-TARGET; or &cv-SOURCE;: Command("file.out", [], Copy("$TARGET", "file.in")) Which executes as: % scons -Q Copy("file.out", "file.in") The usefulness of the &Copy; factory becomes more apparent when you use it in a list of actions passed to the &Command; builder. For example, suppose you needed to run a file through a utility that only modifies files in-place, and can't "pipe" input to output. One solution is to copy the source file to a temporary file name, run the utility, and then copy the modified temporary file to the target, which the &Copy; factory makes extremely easy: Command("file.out", "file.in", [ Copy("tempfile", "$SOURCE"), "modify tempfile", Copy("$TARGET", "tempfile"), ]) The output then looks like: % scons -Q Copy("tempfile", "file.in") modify tempfile Copy("file.out", "tempfile")
Deleting Files or Directories: The &Delete; Factory If you need to delete a file, then the &Delete; factory can be used in much the same way as the &Copy; factory. For example, if we want to make sure that the temporary file in our last example doesn't exist before we copy to it, we could add &Delete; to the beginning of the command list: Command("file.out", "file.in", [ Delete("tempfile"), Copy("tempfile", "$SOURCE"), "modify tempfile", Copy("$TARGET", "tempfile"), ]) Which then executes as follows: % scons -Q Delete("tempfile") Copy("tempfile", "file.in") modify tempfile Copy("file.out", "tempfile") Of course, like all of these &Action; factories, the &Delete; factory also expands &cv-link-TARGET; and &cv-link-SOURCE; variables appropriately. For example: Command("file.out", "file.in", [ Delete("$TARGET"), Copy("$TARGET", "$SOURCE") ]) Executes as: % scons -Q Delete("file.out") Copy("file.out", "file.in") Note, however, that you typically don't need to call the &Delete; factory explicitly in this way; by default, &SCons; deletes its target(s) for you before executing any action. One word of caution about using the &Delete; factory: it has the same variable expansions available as any other factory, including the &cv-SOURCE; variable. Specifying Delete("$SOURCE") is not something you usually want to do!
Moving (Renaming) Files or Directories: The &Move; Factory The &Move; factory allows you to rename a file or directory. For example, if we don't want to copy the temporary file, we could use: Command("file.out", "file.in", [ Copy("tempfile", "$SOURCE"), "modify tempfile", Move("$TARGET", "tempfile"), ]) Which would execute as: % scons -Q Copy("tempfile", "file.in") modify tempfile Move("file.out", "tempfile")
Updating the Modification Time of a File: The &Touch; Factory If you just need to update the recorded modification time for a file, use the &Touch; factory: Command("file.out", "file.in", [ Copy("$TARGET", "$SOURCE"), Touch("$TARGET"), ]) Which executes as: % scons -Q Copy("file.out", "file.in") Touch("file.out")
Creating a Directory: The &Mkdir; Factory If you need to create a directory, use the &Mkdir; factory. For example, if we need to process a file in a temporary directory in which the processing tool will create other files that we don't care about, you could use: Command("file.out", "file.in", [ Delete("tempdir"), Mkdir("tempdir"), Copy("tempdir/${SOURCE.file}", "$SOURCE"), "process tempdir", Move("$TARGET", "tempdir/output_file"), Delete("tempdir"), ]) Which executes as: % scons -Q Delete("tempdir") Mkdir("tempdir") Copy("tempdir/file.in", "file.in") process tempdir Move("file.out", "tempdir/output_file") scons: *** [file.out] tempdir/output_file: No such file or directory
Changing File or Directory Permissions: The &Chmod; Factory To change permissions on a file or directory, use the &Chmod; factory. The permission argument uses POSIX-style permission bits and should typically be expressed as an octal, not decimal, number: Command("file.out", "file.in", [ Copy("$TARGET", "$SOURCE"), Chmod("$TARGET", 0755), ]) Which executes: % scons -Q Copy("file.out", "file.in") Chmod("file.out", 0755)
Executing an action immediately: the &Execute; Function We've been showing you how to use &Action; factories in the &Command; function. You can also execute an &Action; returned by a factory (or actually, any &Action;) at the time the &SConscript; file is read by using the &Execute; function. For example, if we need to make sure that a directory exists before we build any targets, Execute(Mkdir('/tmp/my_temp_directory')) Notice that this will create the directory while the &SConscript; file is being read: % scons scons: Reading SConscript files ... Mkdir("/tmp/my_temp_directory") scons: done reading SConscript files. scons: Building targets ... scons: `.' is up to date. scons: done building targets. If you're familiar with Python, you may wonder why you would want to use this instead of just calling the native Python os.mkdir() function. The advantage here is that the &Mkdir; action will behave appropriately if the user specifies the &SCons; or options--that is, it will print the action but not actually make the directory when is specified, or make the directory but not print the action when is specified. The &Execute; function returns the exit status or return value of the underlying action being executed. It will also print an error message if the action fails and returns a non-zero value. &SCons; will not, however, actually stop the build if the action fails. If you want the build to stop in response to a failure in an action called by &Execute;, you must do so by explicitly checking the return value and calling the &Exit; function (or a Python equivalent): if Execute(Mkdir('/tmp/my_temp_directory')): # A problem occurred while making the temp directory. Exit(1)
scons-doc-2.3.0/doc/user/SCons-win32-install-2.jpg0000644000175000017500000006477312114661557022260 0ustar dktrkranzdktrkranzÿØÿàJFIF,,ÿÛC    $.' ",#(7),01444'9=82<.342ÿÛC  2!!22222222222222222222222222222222222222222222222222ÿÀU"ÿÄ ÿĵ}!1AQa"q2‘¡#B±ÁRÑð$3br‚ %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyzƒ„…†‡ˆ‰Š’“”•–—˜™š¢£¤¥¦§¨©ª²³´µ¶·¸¹ºÂÃÄÅÆÇÈÉÊÒÓÔÕÖרÙÚáâãäåæçèéêñòóôõö÷øùúÿÄ ÿĵw!1AQaq"2B‘¡±Á #3RðbrÑ $4á%ñ&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz‚ƒ„…†‡ˆ‰Š’“”•–—˜™š¢£¤¥¦§¨©ª²³´µ¶·¸¹ºÂÃÄÅÆÇÈÉÊÒÓÔÕÖרÙÚâãäåæçèéêòóôõö÷øùúÿÚ ?/¯¼9á?xNyü¤êSjrÉ$²ÆªÛ‚®I;NIÝYð°ð¿ý}ÿÿâ*Õ®•á x²ê̾­i {‡»šÞF••Ñ%x‡î21žj¸ð&—ÿ=ï?äSþÛûëþ¿û¿wî{u÷ªµ>·ûßù“Í>âÂyáú''þ;ÿÄRÿÂwáú''þ;ÿÄT¾(ð.¥Áâq§É~³h-hK\J’-ÂÎ:`"ì ‘Î[8è3Çž ¸S§5u½ÿ™2œÖìïÿá9ð¿ýÿÿâ)á8ð¿ýÿÿâ+áUì!ç÷¿óµŸs½ÿ„ßÂÿôNtOÉøŠ_øM¼/ÿDçDü—ÿˆ® S…Â{ÿ0ö³îwŸðšx_þ‰Ö‰ù/ÿKÿ Ÿ…ÿèhŸ’ÿñŠp£ØÃÏïæÖ}Îçþ/ ÿÑ;Ñ?%ÿâ)á0ð¿ý½ò_þ"¸qO{yýïüÃÚϹÛÂ_áú'š'ýò¿üE/ü%¾ÿ¢y¢ß+ÿÄW)—±‡ŸÞÿÌ=¬û§ü%žÿ¢y¢ß+ÿÄS¿á+ð¿ýÝþù_þ"¸±O{ùýïüÃÚϹÙÂSáú'º'ýò¿üE/ü%ÿ¢}¢ß+ÿÄW)Âc?½ÿ˜{Y÷;â ÿÑ>Ñ?ï•ÿâ)á&ð¿ýíþø_þ&¹N½Œ|þ÷þaígÜë¿á$ð¿ýýþø_þ&—þ? ÿÑ?Ñ?ï…ÿâk“áG±ŸÞÿÌ=¬û_ü$>ÿ¡Dÿ¾ÿ‰§ÂAáú4OûáøšåçR5ï ÿЃ¢ß ÿÄÒÿnø_þ„þý¯ÿ\¸§ =”|þ÷þaígÜéÿ·çD5 ÿЇ¢ßµÿâiµ|/ÿB‰ÿ~—ÿ‰®xS…Ê>{ÿ1ûY÷:íO ÿЉ¢ߥÿâiF¥áú4Oûô¿üM` p£ÙGÏïæÖ}Íá¨ø_þ„]þý/ÿKý¡áútOûô¿üMa p¥ì£ç÷¿óµŸssíÞÿ¡Dÿ¿KÿÄÒý·Âÿô#hŸ÷åøšÅáG²ŸÞÿÌ=¬û›?lð¿ýÚ'ýù_þ&”]x_þ„}þü¯ÿXâœ(öQóûßù‡µŸs_í>ÿ¡Dÿ¿+ÿÄÓ¾ÑáúôOûò¿áYž(öQóûßù‡µŸsTMáú4Oûò¿áJ%ð¿ý 'ýù_ð¬±N½œ|þ÷þaígÜÓßáútOûð¿áJÂÿô$èŸ÷á³…8Qìãç÷¿ók>æ€>ÿ¡'Dÿ¿ þ¸ð¿ý Z'ýø_ðªœ(öqóûßù‡µŸrþß ÿЕ¢ß…ÿ 6x_þ„½þü/øU1J(ökÏïæÖ}Ë¢? Ÿù’ôOü_ð¥ø_þ„½ÿ×ü* § ^Íyýì=¬û–„ÿ¡3DÿÀuÿ Qmásÿ2f‰ÿ€ëþXSÅÍyýì=´û“‹_ ÿЛ¢à:ÿ…(´ð¿ý º'þ¯øT"œ(ökÏïaí§Ü—ì^ÿ¡;DÿÀeÿ wØ|/ÿBv‰ÿ€Ëþ§Š=šóûØ{i÷ö ÿТà2ÿ…/öw…ÿèOÑ?ðš)—"óûØ{i÷i¾ÿ¡?DÿÀeÿ _ìÏ ÿП¢à2ÿ…œ(ä^{m>âex_þ… ÿ—ü)F“áú4OüZx§ 9ŸÞÃÛO¹ÒfãÜú×®øgþD Ø.ý´«W‹98¤·˜%-nx¨ðïį·}»ÍÔ¾×åù?hþÓ_3ËÎvîó3·<ã¦i°ø_â,qÓ¡7ñØ•d6ɨ¨Œ«gpÚ99òkÛ(¥õéÿ*þ¾böîÏ"ñ.“ñÅW²O¨Ú8…Ù\ZGx¾DlnU ‡üIõ¬1ðëÅô ÿɈ¿øª÷š(Ž>¤U’_×̽[g„‡~*ÿ _þLEÿÅS¿á^x§þù1ÿ^ëE?í ½—õóÕâx_ü+ßÿÐ/ÿ&"ÿâ©GÃïÐ/ÿ&"ÿâ«Üè£ûB¯eý|Ãêñ<8|?ñGý?òb/þ*œ<âúäx¿øªöú(þЫÙ_0ú¼Oñ7ý?ò<üU(ð‰¿èÿ‘ãÿâ«Û(£ûB¯eý|Ãêñ¿S²þ¾aõxž><â/úÿähÿøªpðOˆèÿ‘£ÿâ«×¨£ëõ;/ëæW‰äCÁ^!ÿ þFÿЧøƒþÿù?þ*½nŠ>¿S²þ¾aõxžL<¯ÿχþFÿЧëÿóáÿ‘£ÿâ«Õè£ëõ;/ëæW‰å#ÁÚ÷üøädÿâ©ÃÁú÷üøÿädÿâ«Õ(£ëõ;/ëæV‰å£ÂïüøÿäTÿâ©ÃÂ:çüøÿäTÿâ«Ô(£ëõ;/ëæV‰æÂZçüøÿäTÿpðž·ÿ>_ù?ƽ6Š_^©Ù_0ú´3ÖÿçËÿ"§øÓ‡…u¯ùòÿÈ©þ5éTQõê—õó«Dóqámgþ|ÿò*(ð¾³ÿ>ù?ƽŠ>½S²þ¾aõxžt<1¬ÏŸþEOñ§ ëóçÿ‘ükÐè£ëÕ; ú´=ÕÿçÓÿ"'øÓ‡†õùôÿȉþ5èQõê}Z<9«ϧþDOñ¥Õç×ÿ"/ø×{E]©ÙÕ pƒÃº¯üúÿäEÿpðö©ÿ>¿ùÆ»š)}v§dVÃêŸóëÿ‘üiÃ@ÔÿçÛÿ"/ø×mE]©ÙÕ qcAÔ¿çÛÿ_ñ§ RÿŸoü}Æ»*(úíNÈ>­Ž£ÿ>ÿøúÿ8hšüûÿãëþ5×ÑG×jvAõh—ö.¡ÿ>ÿøúÿ(ѵù÷ÿÇ×ük¬¢®ÔìƒêÐ9Q£ßÿÏü}Æ”ißóÃÿ_ñ®¦Š>»S²«@æ“}ÿ¹>È>­0ZOýÏÔS…¬ßÜýEhÑG×'ÙÕ PÒÿsõáo/÷QWh£ësìƒêÐ)ˆ$þïëN¿÷ZµE[ŸdV‡5¯‰fžI¶­“{Ú¶#' Ëæª_hzî£c5ε C*í`,€8úﮞŠÇ*üÌב÷‡ùWñ<Ô´ðzù ÿ¡QNø§ÿ![úàô*(o™Ý‚VV=Ã?ò#økþÁpè5¥Y¾ÿ‘Ã_ö ƒÿA­*+^¥-‚Š*¯ö‡üþÛßÕÿÌeª*¯ö‡üþÛßÕÿ?´ì?çöÛþþ¯øÐª*¯ö‡üþÛßÕÿ?´ì?çöÛþþ¯øÐª*¯ö‡üþÛßÕÿ?´ì?çöÛþþ¯øÐª*¯ö‡üþÛßÕÿ?´ì?çöÛþþ¯øÐª*¯ö‡üþÛßÕÿ?´ì?çöÛþþ¯øÐª*¯ö‡üþÛßÕÿ?´ì?çöÛþþ¯øÐª*¯ö‡üþÛßÕÿ?´ì?çöÛþþ¯øÐª*¯ö‡üþÛßÕÿ?´ì?çöÛþþ¯øÐª*¯ö‡üþÛßÕÿ?´ì?çöÛþþ¯øÐª)‘MñùÈ’&JîFgÓ#ê?:}QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEyoÅ?ù Ø×ÿ¡QGÅ?ù Ø×ÿ¡QV¶%žáŸùü5ÿ`¸?ôÒ¬ß ÿÈá¯ûÁÿ Ö•:ßÄ—¨ÖÁ^tªon Pzý¿Ú5ïõà7?ñùqÿ]_ÿB5Û–$ê»öýQææ’jгëú2 ‹ýÑùQ±º?*uîû8vGƒí'ÝØ¿Ý•û£ò§QG³‡dÒ}ØÝ‹ýÑùQ±º?*u{8vAí'ÝØ¿Ý•û£ò§QG³‡dÒ}ØÝ‹ýÑùQ±º?*u{8vAí'ÝØ¿Ý•û£ò§QG³‡dÒ}ØÝ‹ýÑùQ±º?*u{8vAí'ÝØ¿Ý•û£ò§QG³‡dÒ}ØÝ‹ýÑùQ±º?*u{8vAí'ÝžÑà€´°?y?þ‡[u‰àŸù´Ïúé?þ‡[uòø+OÕŸU†w£EùfjZ•Õ½õµ……¤7sÇ$ÀO9†0ˆP7ÌÎìȸÆ3ÈÀN²5í>]Bcf—©B¬Y­õ … Ž[cà¸co;ºŒa±7-IªÙ[}/®!²¸»À†ÞædY¸ùÉ À>Ry5VÓÄVÇ»¸¶³šk¹í`ŠYÔ4ÍÍÊ2IàtÜ5…àë»%ãíþfŸs,÷·ùÙ¿22F}»Ì$£2ôÆï˜‘ þ »’k¢ÞLÉ{çG*›ëˆ4k‰å¤xó²³à£i¾b@#§¿ñ™a¡¾în,-žæ{X¤V™QWq;3‘ÁÎ#Ö¦¹Ö4Ë;¯nµ8-&Ç•<³ªÆùXœ‘ŽÕ…?‡/î´ûíAb¶Sµä‘Þ6é&V¸óOà+ç»yÈR07ü¶µÕÔÝü?5­”2ÝÅz^XÌÌ"LÛL§2$.[•ä03@Ë©X:+­í³+,n¬%R ÈvÆG=ð¾§¥déž0Ó5I!)<0ÛÜ[[M’Ì Èó4ª"Æq¼ˆ s‘Ú²,ô †ÖþÎÅÖ6au¨¿’ÛEºk¨c…Ø(aºi0 Ä`a SãðmÒiö¬lÚáôÑí§9ÏÉç ÄíÊ«‰ˆÁs´Ó¾±¦GuqjúšÜ[Ffž#:‡‰³ åWž9=µå­ç›ö[˜gòd0Ëå8mŽ:«c£ ò5ÈêžÔïôÛ1ÍmÄ—·Nemî÷ :„dÛ…U7æ Äì(ÝÇCg¥}[¹º‰!ŽÑì­­¡Š1žSLqŒ`.$P1èzPí5› k¿±7W Å Êɹ¶+‡‚À++gå àf™?ˆtÈ&¶ípÉçÞ› ÑȤG>ÆmŽsÃ|¸Ç\²Œs\ö‘àÛ­?DkY ŸÚÚçN‘¥LüÉl¶à©;ryŠB£§Ì:dâÕ—‡/ิŊýŽí(â݃ŽHÈ FQGœÎ‘|áT>Ö4îüEarK‹kÉ¡»‚Öx¢KBÒ̱|ÀgN]¤qF•â+ FÓK2\[[Þê‘ÝGdÓ©“k.î€ÁçsŸð‰k3j–×73Bæ/Ìïæ”ÎEͼ¬â&]äBß"2Àt¢/ k1Úèö²M Égö¸¿š4ˆ@c.‚]’ä£0wÁùÀãh ‚ÇÅeö˜š§ÛláÓÞÚ)üÉnT4{Ù—‹‚»s¸åƒ6ótëbùÔlÇŸåù9yægËÛÏ;¶¶1×+˜µð®§a6qÙÍ.›mm1¼¬‚).bË6Ó³):¶nA^Ÿ5Á·FÓ].lÍÞ¥§Koò|§–[‰]wmÎÌ̃ e¶d¨àPO>«km«Ùé’¾Û‹Èå’y{7(ç%°ùÀj}†¥aª@Óé÷¶×p«l2[ʲ(lŒ‚yÁfxƒA—Yr##Yl.ì$, (³ùÀþ" `mã!‰ÈÆ Úeÿö¥æ§¨GmÓÁ ¸†Þf•BÆÒ6íÅS’e#ãnrs€têV Nom„/\,†UÚÑ ò 2äôäzÓ$Ö4È¾Çæj6iöÜ}—tê<üã9ù³¹zg¨õ®b êË ¼ßÙÒ[éÚsXÀ¥ä )Y lDòH‘ʧƒÉ泄ä¢e%vzUâÚ®©â0ê§Š-¯'Ó˜­Ü6æ`Ñâ2rèª@vUùI?6qŒ‘KIñŒu‰àŽÛRp²ÝÃgæ»áVIKlÏ||ŒrÆ>•аu¹Ÿ¶ŽÇ»Q_;ÿÂ{â_ú MùÑÿ ï‰è)7çUõ ¢öð>ˆ¢¼ø»ÅCJMDjÎЙÚUbZ6 ô mõØÿÝ£QñwŠ´ËÖ´ŸVs2*XüŒÊ£ge'kÌíGÔjÛÄ÷Ú+çøO|KÿAI¿:’_xŽ7 ºËÊ «nBØ€HäA8=² $ú…Q{xBQ_;ÿÂ{â_ú MùÖ­§ˆ¼QybÓÅâ(MÇ•$Ëgæ1•£@K·¢à+¬Áˆ^Êäx«qûxžçExf“â/jûR/CÄÒˆm­å‘¼Ë‰0ª¹, 3•RO^ Ó§æÇ÷À'Ôj‡·‰ïTWÎÿðžø—þ‚“~tÂ{â_ú MùÑõ ¡íà}E|÷޼G,ñÆúË®ÁL®Xªz œ¼}>ÓÆ^,¾™¢¶Ô&wX¤”øù ±äöU'ð£ê5CÛÀúŠùßþßÿÐRoÎ­Þø»ÅVIk#êÎñÝ@'…ÑŽrTŽpA ®§ŽªHÈ “ê5CÛÀ÷Ú+çøO|KÿAI¿:?á=ñ/ý&üèú…Pöð>ˆ¢¾wÿ„÷Ä¿ô›ó£þßÿÐRoΨUoèŠ+çøO|KÿAI¿:?á=ñ/ý&üèú…Pöð>ˆ¢¾wÿ„÷Ä¿ô›ó£þßÿÐRoΨUoèŠ+çøO|KÿAI¿:?á=ñ/ý&üèú…Pöð>ˆ¢¾wÿ„÷Ä¿ô›ó£þßÿÐRoΨUoèŠ+çøO|KÿAI¿:?á=ñ/ý&üèú…Pöð>ˆ¢¾wÿ„÷Ä¿ô›ó£þßÿÐRoΨUoèŠ+çøO|KÿAI¿:?á=ñ/ý&üèú…Pöð>ˆ¢¾wÿ„÷Ä¿ô›ó£þßÿÐRoΨUoèŠ+çøO|KÿAI¿:?á=ñ/ý&üèú…Pöð>ˆ¢¾wÿ„÷Ä¿ô›ó¯HøQ­ê:êë©\¼æÞ8|­Äáw1Ï€ÿ&¢¦¥8¹Kb£Z2vG QEÌjyoÅ?ù Ø×ÿ¡QGÅ?ù Ø×ÿ¡QV¶%žáŸùü5ÿ`¸?ôÒ¬ß ÿÈá¯ûÁÿ Ö•:ßÄ—¨ÖÁ^sÿ—õÕÿô#^ý^sÿ—õÕÿô#]ÙWñ_§ê36þ õýQ^ñóáEPEPEPEPEPEPEPEP´x'þD-3þºOÿ¡ÖÝbx'þD-3þºOÿ¡ÖÝ|®'øÓõ™õ¸oàÃÑ~A^Eñþ?´ïú景]ªºGü”tÿ°D¿ú::š3äš—cI®hØñy¼W¨ßü@’}"Õ/ þÕ7PÃeb‘MuK檳$~c1Ýž@b2*õ—ˆô½úÛFSsg¥êa¯—‘Cs<ŽB¡n3:²ŒD[²ª×ÒtWcÅE«rþ?ð }“î|·§øš9ôµûV«ö]hý¢+{⎿cŒµ³ VIDÚ—«Âù„`+Wá/±ŽóG…/¿Ñ´ƒj®"oô˜¼›T•˜íÜé#G32ž_‚ëœWÒôQõ¨ÿ/ãÿ=“î|áíFÆßí6z´^v.Ûƒæ]ÒÅ–A•ùÔÉz/›¸çh®Ãþ/Š©o'³[Û‹én¯šæ;ô´p˜@2¤œ‰~IpŸ¼«éÚ*¥ŒRÞ?ü*-u>^‡Ä¶Í!maát«;eÌ,ÇÀ¡ã^#ÌÎbqåH ne!­[x›LWÓÚ=WìÐ[ý™µ6J?´aKkt0aT‡ÃE2âB©ûÎ Ä}/EOÖ£ü¿üû'ÜùSRñÖ7¶/w¾Õt‹(­!òÈ_µ ·Wlcï…Y—yçhÚ0*¢iöÞimu›8u)â\$±Ïæªr<˜ÊÆPoe‹ †Ùòûþ¹¢©cVQüà ØùŸ%hðé¶pOq¿aoª$íÍÆØÐ‰£Û|ÌI¶Ò˜ÈŠ”µàÛÈ,­EÌòmŠÓ[Ónç ) ƒ9'h.ƒ8êÊ:‘_UÑCÆ]5müÿà£n§Êöþ …ôEûN¬åM¥Ìwº{ùŒ×—RJNF lT¼'s°aåpÕά~$Ñ-.õíF-I$ŸS»šò(Ñ™cmx¨¹v³o™—9f¶ý'EKÅEýŸÇþý“î|½ˆÒçL€ÇâÓõÙ í:¤†`ï¶[Ñ´‘«;n{®#T “Iñ“mñ\Õf»Ûeu|²Ã/–çr øfÎÈù#¶:ñ_NÑOëQ×Ýüà²}Ïõm_ûWB°ûTþv£ÍÀl¦<¸ Ãå¢àaP0—j/ “€3Ïh¯æ|IE}·EÚÝüàÕüω(¯¶è£ûCû¿üú¿™ñ%öÝhwñÿ€Wó>$¢¾Û¢íîþ?ðêþgÄ”WÛtQý¡ýßÇþ}_Ìø’ŠûnŠ?´?»øÿÀ«ùŸQ_mÑGö‡÷øõ3âJõÏ‚?ê¼Iÿ\íÿô&¯¬Ý[ýQÿ®gÿBJʾ3ÚSpå*ye{˜TQEyçIå¿ÿä+aÿ\þ…Eÿä+aÿ\þ…EZØ–z†äGð×ý‚àÿÐkJ³|3ÿ"?†¿ìþƒZTë^£[x Ïü~\×WÿÐ{õx Ïü~\×WÿÐwe_Å~Ÿª<ÌÛø+×ôdTQE{ÇÏ…Q@Q@Q@Q@Q@Q@Q@Q@ÑàŸù´Ïúé?þ‡[u‰àŸù´Ïúé?þ‡[uò¸ŸãOÕþgÖῃEùUÒ?䣧ý‚%ÿÑÑÕª«¤ÉGOûKÿ££¬£¹³;š(¢¬¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¬Ý[ýQÿ®gÿBJÒ¬Ý[ýQÿ®gÿBJO`FQPYå¿ÿä+aÿ\þ…Eÿä+aÿ\þ…EZØ–z†äGð×ý‚àÿÐkJ³|3ÿ"?†¿ìþƒZTë^£[x Ïü~\×WÿÐ{õx Ïü~\×WÿÐwe_Å~Ÿª<ÌÛø+×ôdTQE{ÇÏ…Q@Q@Q@Q@Q@Q@Q@Q@ÑàŸù´Ïúé?þ‡[u‰àŸù´Ïúé?þ‡[uò¸ŸãOÕþgÖῃEùUÒ?䣧ý‚%ÿÑÑÕª«¤ÉGOûKÿ££¬£¹³;š(¢¬¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¬Ý[ýQÿ®gÿBJÒ¬Ý[ýQÿ®gÿBJO`FQPYå¿ÿä+aÿ\þ…Eÿä+aÿ\þ…EZØ–z†äGð×ý‚àÿÐkJ³|3ÿ"?†¿ìþƒZTë^£[x Ïü~\×WÿÐ{õx Ïü~\×WÿÐwe_Å~Ÿª<ÌÛø+×ôdTQE{ÇÏ…Q@Q@Q@Q@Q@Q@Q@Q@ÑàŸù´Ïúé?þ‡[u‰àŸù´Ïúé?þ‡[uò¸ŸãOÕþgÖῃEùUÒ?䣧ý‚%ÿÑÑÕª«¤ÉGOûKÿ££¬£¹³;š(¢¬¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¬Ý[ýQÿ®gÿBJÒ¬Ý[ýQÿ®gÿBJO`FQPYå¿ÿä+aÿ\þ…Eÿä+aÿ\þ…EZØ–z†äGð×ý‚àÿÐkJ³|3ÿ"?†¿ìþƒZTë^£[x Ïü~\×WÿÐ{õx Ïü~\×WÿÐwe_Å~Ÿª<ÌÛø+×ôdTQE{ÇÏ…Q@Q@Q@Q@Q@Q@Q@Q@ÑàŸù´Ïúé?þ‡[u‰àŸù´Ïúé?þ‡[uò¸ŸãOÕþgÖῃEùUÒ?䣧ý‚%ÿÑÑÕª«¤ÉGOûKÿ££¬£¹³;š(¢¬¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¬Ý[ýQÿ®gÿBJÒ¬Ý[ýQÿ®gÿBJO`FQPYå¿ÿä+aÿ\þ…Eÿä+aÿ\þ…EZØ–z†äGð×ý‚àÿÐkJ³|3ÿ"?†¿ìþƒZTë^£[x Ïü~\×WÿÐ{õx Ïü~\×WÿÐwe_Å~Ÿª<ÌÛø+×ôdTQE{ÇÏ…A{#Ãaq,g‘3)ô žªê_ò »ÿ®/ÿ š™é]5y£±ÿ„3ÁzG…´mKÄ^(Õí¦Ôm„ª#—p'i'gÔ~CÖŸoðž üP5ñ Ö—©ÝC ¤²#Àê¬äÇ<õ—ÔŸ€Òñþ®Ää}ŠRFî¿0ÿkü~½ëºøwãÍÄ~"Õl´ÿ Ûis-¼’5ÄL»¤›®1ýàx'¿= /k5Õƒ¥MëÊŽ.ËÁ^Ñ<=iã_ßÛK¨’Õmf,6qœg'§¶p+F×á<7^(T‹Ä²è—–2]X¼wÌ8ÀÉä|Àç\T4O^ðwƒÛJ³¸¿X­›Ì6àÉ´ípÍýÓëÓ¯jô­Ͳ>µ™ŒWPhr "gù•†ÁÈó=G¡úŠ=¤û±û8vGˆx#Â÷^%ñeöƒ­ÝßY6Ÿk+aŽF`p dóÔzôú“¡_Ët·ó6ónÁD„òÜgߎµí¿|a£xÆþêöXRÛÄ–öÒC6ÂGs»;¹ã¿µàz Ô^j&yãtƒÜ òÞ§ŸÌ×^´ýºæ–Ÿð,u{ËWù5ŠÊèX2°È äK^ñóÁEP´x'þD-3þºOÿ¡ÖÝbx'þD-3þºOÿ¡ÖÝ|®'øÓõ™õ¸oàÃÑ~AUtù(éÿ`‰ôtujªéòQÓþÁÿèèë(îlÎæŠ(«$(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š+7VÿTë™ÿÐ’´«7VÿTë™ÿÐ’“Ø…ETyoÅ?ù Ø×ÿ¡QGÅ?ù Ø×ÿ¡QV¶%žáŸùü5ÿ`¸?ôÒ¬ß ÿÈá¯ûÁÿ Ö•:ßÄ—¨ÖÁ^sÿ—õÕÿô#^ý^sÿ—õÕÿô#]ÙWñ_§ê36þ õýQ^ñóáK§èz·‹5™4(Ú#›GšI.\—•ÀÇÿ_­%u? î"¶øxòȪ¿Ù pÎ8|÷#úþÉŽœ¡E¸³·κRF_Œ~xñü9noµK]RÛI‹d6öònuLœœcœøúT:/‚Ø’lnbNäüÙêy'Ú·u=Š^ðÛh:ͲI§@b»[‰Yýù>‡ð=qÅxP«8&¢ísß©FrW±Éê¾ñÞ»­Aá]Nò#ÒíKId|Fñ¯÷N9==;ÕIâmkÅð^Cc}Ÿ"Ü3¸ÚÊ ÎÞ¹äŒÕìQx¯FÐüM¡øj÷VW¿¶Ó^ÎYbžiÛ·-¿‚vž¼ûгmmá¯ÀÚ ¹°€éÖog|ð}ù$l0È’r'ž¢¥M¨¸­™n ÉIî&øà_ù3ëºF¡o¥´êñn¹“e 凌ŽãÚ› xÇžñÅÝÅmzÖ’¹¹/û¹ãï´‘ÉÎ>†½{Ãú¶â¿i§Ûiú™¶‹Ê•/å1˜Ü#‡qÿëÍA¦øÊK߉·Z=ͽµ‘Ò¬&¢ºÞ¯’¤wQÀ¶G=*J8+|SðO„¦¹ÓµH¼¿õòY# eLî @ ôëÇôªÞÐþ$øŽâãÅðë0Ù\^FÑ»}ždeOÝ\`.GŸjã¢ñ§Œfñ§­Yj5ÔŠÑÌTŒl9À žÜã¯{ðΫcâŸi&ÎßOÕ$¶€E:_JPÇ ë¸óü‰¦âÒM­Ä¤›i=²ð׎üã¶Ò´·eÔ/#t1üÑÉûÏ“ØuÏc]Ï‚>\è~$º›ÄPØjv²Ù͇Sæ*Iœs’0HÏøŠÞ_ˆ–éñCûTŠßOû5´¶ÑIÁ’7w ¨$€:ŒçÒ¬ü?ð¿ˆü&ªš®»gq¥Ê²H‘¬Å™\–ç$Œp2yþY¤3Áü7+—¾ƒwîbykÙr[8ëéë[ÕÏxlæóS9Îd^sžíît5ôXÝßúÔù¬zK+y~AEWQÆ{G‚äBÓ?ë¤ÿúmÖ'‚äBÓ?ë¤ÿúm×Êâ?WùŸ[†þ =äWHÿ’ŽŸö—ÿGGVª®‘ÿ%?ì/þŽŽ²ŽæÌîh¢Š²BŠ( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( ³uoõGþ¹Ÿý +J³uoõGþ¹Ÿý )=TQEAg–üSÿ­‡ýp?ú|Sÿ­‡ýp?úkbYèÿ‘Ã_ö ƒÿA­*ÍðÏüˆþÿ°\ú iS­üIzlà7?ñùqÿ]_ÿB5ïÕà7?ñùqÿ]_ÿB5Ý•ú~¨ó3oà¯_Ñ‘QEï>CRÒ£Ô0s ê0²¯\wÔŸZ¿ELáÇ–Jè¸T•9sEÙ™ºFºRJÆVŒ¸œ~&¢¿Ð–ês5½ËÛæ@£!®29äÿœÖ½›ÃÒpTÚÑ,MUQÔOVsñxZ?9¤¹»’lƒÑvœúç&£o HÁƒjNw͘úŸSóWIEgõ*·/ækõüEïÍù á•X£[{Ùb bCŒ‡>¸ÈÇR>ŸŽbÿ„ZO0Éý¤ûÛ9o/“ž¼î®’Š ƒû?˜,~!}¯ÈÍÒ4…Ò’P&2´„díÀtãñ5þ„·S™­î^Ø¿2 }q‘Ï'üæµè­“‚¦ÖˆÉbjªŽ¢z³Ÿ‹ÂÑùÍ%ÍÜ“d‹´ç×94‡Ãw9Õe9ëòö½Ïç] ŸÔ¨ZÜ¿™¯×ñ¿7äféBéI(ZB2và:qøšÒ¢Šè„#N<±Øæ©RU$å'¨QEDÑàŸù´Ïúé?þ‡[u‰àŸù´Ïúé?þ‡[uò¸ŸãOÕþgÖῃEùUÒ?䣧ý‚%ÿÑÑÕª«¤ÉGOûKÿ££¬£¹³;š(¢¬¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¬Ý[ýQÿ®gÿBJÒ¬Ý[ýQÿ®gÿBJO`FQPYå¿ÿä+aÿ\þ…Eÿä+aÿ\þ…EZØ–z†äGð×ý‚àÿÐkJ³|3ÿ"?†¿ìþƒZTë^£[x Ïü~\×WÿÐ{õx Ïü~\×WÿÐwe_Å~Ÿª<ÌÛø+×ôdTQE{ÇÏ…Q@Q@Q@Q@Q@Q@Q@Q@ÑàŸù´Ïúé?þ‡[u‰àŸù´Ïúé?þ‡[uò¸ŸãOÕþgÖῃEùUÒ?䣧ý‚%ÿÑÑÕª«¤ÉGOûKÿ££¬£¹³;š(¢¬¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¬Ý[ýQÿ®gÿBJÒ¬Ý[ýQÿ®gÿBJO`FQPYå¿ÿä+aÿ\þ…Eÿä+aÿ\þ…EZØ–z†äGð×ý‚àÿÐkJ³|3ÿ"?†¿ìþƒZTë^£[x Ïü~\×WÿÐ{õx Ïü~\×WÿÐwe_Å~Ÿª<ÌÛø+×ôdTQE{ÇÏ…Q@Q@Q@Q@Q@Q@Q@Q@ÑàŸù´Ïúé?þ‡[u‰àŸù´Ïúé?þ‡[uò¸ŸãOÕþgÖῃEùUÒ?䣧ý‚%ÿÑÑÕª«¤ÉGOûKÿ££¬£¹³;š(¢¬¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¬Ý[ýQÿ®gÿBJÒ¬Ý[ýQÿ®gÿBJO`FQPYå¿ÿä+aÿ\þ…Eÿä+aÿ\þ…EZØ–z†äGð×ý‚àÿÐkJ³|3ÿ"?†¿ìþƒZTë^£[x Ïü~\×WÿÐ{õx Ïü~\×WÿÐwe_Å~Ÿª<ÌÛø+×ôdTQE{ÇÏ…Q@Q@Q@Q@Q@Q@Q@Q@ÑàŸù´Ïúé?þ‡[u‰àŸù´Ïúé?þ‡[uò¸ŸãOÕþgÖῃEù%¼6Úçö±•ÄÂØÛ*y`®ÒÁ‰<ƒœ¨ýzç…¢±66?¶?ÚÿÈ?ýÛíäþαè§v6?¶?ÚÿÈ?ýÛíäþαè¢ì,llµÿû:?¶?ÚÿÈ?ýcÑEØXØþØÿkÿ ÿötlµÿû:Ç¢‹°±±ý±þ×þAÿìèþØÿkÿ ÿöuEaccûcý¯üƒÿÙÑý±þ×þAÿìë›$±Ã’WTAÕ˜àÆ‹°±µý±þ×þAÿìèþØÿkÿ ÿöuÎÿiØÏí·ýý_ñ£ûNÃþm¿ïêÿac¢þØÿkÿ ÿötlµÿû:ç´ì?çöÛþþ¯øÑý§aÿ?¶ß÷õÆ‹°±Ñlµÿû:?¶?ÚÿÈ?ýs¿ÚvóûmÿWühþÓ°ÿŸÛoûú¿ãEØXè¿¶?ÚÿÈ?ýÛíäþιßí;ùý¶ÿ¿«þ4iØÏí·ýý_ñ¢ì,t_ÛíäþÎíö¿òÿg\ïö‡üþÛßÕÿ?´ì?çöÛþþ¯øÑv:/íö¿òÿgGöÇû_ùÿ³®wûNÃþm¿ïêÿÚvóûmÿWüh» öÇû_ùÿ³£ûcý¯üƒÿÙ×;ý§aÿ?¶ß÷õÆí;ùý¶ÿ¿«þ4]…Ž‹ûcý¯üƒÿÙÑý±þ×þAÿìëþÓ°ÿŸÛoûú¿ãGö‡üþÛßÕÿ.ÂÇEý±þ×þAÿìèþØÿkÿ ÿöuÎÿiØÏí·ýý_ñ£ûNÃþm¿ïêÿac¢þØÿkÿ ÿötlµÿû:ç´ì?çöÛþþ¯øÑý§aÿ?¶ß÷õÆ‹°±Ñlµÿû:?¶?ÚÿÈ?ýs¿ÚvóûmÿWühþÓ°ÿŸÛoûú¿ãEØXè¿¶?ÚÿÈ?ýÛíäþιßí;ùý¶ÿ¿«þ4iØÏí·ýý_ñ¢ì,t_ÛíäþÎíö¿òÿg\ïö‡üþÛßÕÿ?´ì?çöÛþþ¯øÑv:/íö¿òÿgGöÇû_ùÿ³®wûNÃþm¿ïêÿÚvóûmÿWüh» öÇû_ùÿ³£ûcý¯üƒÿÙ×;ý§aÿ?¶ß÷õÆí;ùý¶ÿ¿«þ4]…Ž‹ûcý¯üƒÿÙÑý±þ×þAÿìëþÓ°ÿŸÛoûú¿ãGö‡üþÛßÕÿ.ÂÇEý±þ×þAÿìê½ÕúÜDÀ’[nшöŽ ÿxúV/ö‡üþÛßÕÿ’«{–e‚x¥eˆG @Î3ÇÔ~t]5QHg–üSÿ­‡ýp?ú|Sÿ­‡ýp?úkbYèÿ‘Ã_ö ƒÿA­*ÍðÏüˆþÿ°\ú iS­üIzlà7?ñùqÿ]_ÿB5ïÕà7?ñùqÿ]_ÿB5Ý•ú~¨ó3oà¯_Ñ‘QEï>QEQEQEQEQEQEQEQE{G‚äBÓ?ë¤ÿúmÖ'‚äBÓ?ë¤ÿúm×Êâ?WùŸ[†þ =äÍø]—KÕ4ûAªizd7O#\jYK#D¯ï’$cÔýÚé*”º™­Úê^n<‹i òöýï1¢lçÉúuµâ!ŽBé½¥8â%Ø¿¼ /Ì 8aWn´™5¬òA46QGœÅÓo‰ Æ `±±Ÿ¸I*>eÍA„<½"ÿNÙK%4¨ÜÃÌhžpF?7ÌÛeè RF3€h#BiÏr&¹t[u‘žSžYòÁ.ªûv»(VÊ©,6·)Å­?V±Õ<Ï±Ïæyx'(˹Nvºä ÈØ8q•lƒXZ‡ƒå¿´¹±mEÉšêh[“$rβ«møe|„(U?wæàçv-?ËÖîµ/7>}´0y{~ï–Ò¶sžsæôÇ÷àCÖõ9¬ôK­Q¬åMf41 X#˜L»[s¶åÚ¬20Aƒ¸•µgâ­.k7ÚÞá•a ÐYÌ<×’1 ¦ 1Øw•НÞéšf‹áÛ« }2 Bþ¸´¸Õ,ÖcŒÇ½òí¹¶3 Û·:/ʼn„—×M ðÏÚ­–ŽÕ-þx÷|À…,>a‚G\r´þ&Ò#HœÜ¹W]ìËŒ!\•&\/î@*Àù›pQ³÷[ÛøªÊKy$š;”‘.î-„1[Ë3¿•!Bꨄ•û¹` ‚ÁIÍAká›4©Óoí­ ,; <Ž<•,d_„ŸÃÄ1i‡n­nçN¿†)ã’äÆn-ŒªâE–E!]Io1x9)ÁüÔvéWWbÚ ¯1ÚD‰Y#c³Df_[÷cwàê3fÃP¶ÔíêÑà‘UÑÚ6@êʸ Œ0éß#¨ sÂÓi–Öö[Ü–û\3C{û¿ô4Ž­Ûvìïf‰dFçä ®žÂÍtûD³‡bÚÀ«´jcT(RI;ŽA玲@-VŒäQÔ¿ë—õ¹X~1ÿ‘GRÿ®_ÔP·Ìõ™âÓµ)£¹¿µ³ûDÓ›8ÓC·–$‰f’%ó‡4mªçn$â±WQÖ?²¯.Ýl’kf€›vÒm÷¥V"_¹ÂƒåŒãÍ^yÝñ’ú®©+^i^ Îkˆ£KM?z\¡¸–P|ÒFÌù…s±À79Ú0´VÓµE2hzÌÚtñoì£uö¯ ²·Ì„ñ¹ˆÀö¡\º¥DqIÊæ¬®à‹HuSþÏ[óè6«ß³‹†_0Ù >NH€r3­¦ñ=Ì:Œ‹cjŸ`‰ä™_H„7îÝEÊûÉæ)`q’jÖ«Œµ–¶Ó-­5ôÓµ¥¨´™dŽñÅA;ïRÙ8õ8­Muug“TÓ†©²^Å$×Ãnòy+A,¡8Äå¸%Ã?…jÊËð ËÌÄÖ%ñ•­K§­¤©»–ÒÖa£@ÓG!C°yg'8àœTçÅzöCFíQ $[Õ™K€WÊÈ%U˜@OA]ÞjÓêÞ ¹ŸA×äMVù¬Ýž(˜y«*Θ\àìÆáÁªZ„z±ð»h:~¯˜—ìñ‰d±xüøÑîemè cç02ÀìÝp•=¹Wà—vQÓõ)µ GX﬿´„\,E·ò‚DŒìB †*Œ@UaÊ‚Ã'oCáA©¬ÛbæËP´I¾Ïv£Á—h¥t1°™srÁÝùy RÖ¬N¤º¬öº_‰mn5 ™î&C¤oó³+Ÿÿ€Éþ¥EyGY›ÿö‰ÿ@}?ÿ“ü(ÿ„{Dÿ >Ÿÿ€Éþ¥EfÿÂ=¢ÐOÿÀdÿ ?áÑ?è§ÿà2…iQ@¿ðhŸôÓÿð?ÂøG´Oúéÿø ŸáZTPoü#Ú'ýôÿüOð£þíþ€úþ'øV•›ÿö‰ÿ@}?ÿ“ü(ÿ„{Dÿ >Ÿÿ€Éþ¥EfÿÂ=¢ÐOÿÀdÿ ?áÑ?è§ÿà2…iQ@¿ðhŸôÓÿð?ÂøG´Oúéÿø ŸáZTPoü#Ú'ýôÿüOð£þíþ€úþ'øV•›ÿö‰ÿ@}?ÿ“ü(ÿ„{Dÿ >Ÿÿ€Éþ¥EfÿÂ=¢ÐOÿÀdÿ ?áÑ?è§ÿà2…iQ@¿ðhŸôÓÿð?ÂøG´Oúéÿø ŸáZTPoü#Ú'ýôÿüOð«úveaߨìíí·Æ7y1*nà gž¦ŸSCþªãþ¹ý h Q@Ï-ø§ÿ![úàô*(ø§ÿ![úàô**ÖijÐ<3ÿ"?†¿ìþƒZU›áŸùü5ÿ`¸?ôÒ§[ø’õØ+Ànãòãþº¿þ„kß«Ànãòãþº¿þ„k»*þ+ôýQæfßÁ^¿£"¢Š+Þ>|(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢ŠöÿÈ…¦×Iÿô:Û¬OÿÈ…¦×Iÿô:Û¯•Äÿ~¯ó>· üz/È(¢ŠÄÜ(¢Š(¢Š(¢Š(¢Š(¢Š*ޱ¦cI¸ÓÄËž»LŒ 22p:Õê(o&?ùù‹òoþ&&?ùù‹òoþ&¡¢€&òcÿŸ˜¿&ÿâhòcÿŸ˜¿&ÿâj(o&?ùù‹òoþ&&?ùù‹òoþ&¡¢€&òcÿŸ˜¿&ÿâhòcÿŸ˜¿&ÿâj(o&?ùù‹òoþ&&?ùù‹òoþ&¡¢€&òcÿŸ˜¿&ÿâhòcÿŸ˜¿&ÿâj(o&?ùù‹òoþ&&?ùù‹òoþ&¡¢€&òcÿŸ˜¿&ÿâhòcÿŸ˜¿&ÿâj(o&?ùù‹òoþ&&?ùù‹òoþ&¡¢€&òcÿŸ˜¿&ÿâhòcÿŸ˜¿&ÿâj(o&?ùù‹òoþ&&?ùù‹òoþ&¡¢€&òcÿŸ˜¿&ÿâhòcÿŸ˜¿&ÿâj(o&?ùù‹òoþ&&?ùù‹òoþ&¡¢€&òcÿŸ˜¿&ÿâhòcÿŸ˜¿&ÿâj(o&?ùù‹òoþ&&?ùù‹òoþ&¡¢€&òcÿŸ˜¿&ÿâhòcÿŸ˜¿&ÿâj(o&?ùù‹òoþ&œqE7ïÑË(P7÷î=ª½(¢ŠyoÅ?ù Ø×ÿ¡QGÅ?ù Ø×ÿ¡QV¶%žáŸùü5ÿ`¸?ôÒ¬ß ÿÈá¯ûÁÿ Ö•:ßÄ—¨ÖÁ^sÿ—õÕÿô#^ý^sÿ—õÕÿô#]ÙWñ_§ê36þ õýQ^ñóáEPEPEPEPEPEPEPEP´x'þD-3þºOÿ¡ÖÝbx'þD-3þºOÿ¡ÖÝ|®'øÓõ™õ¸oàÃÑ~AUd¿Š=RßO*þtðK:°hXÙçß2.>†­W1â½&ûRûGÙ ó7è·ö«ó¨Ì²ù;“ßcsÐcœV&çOPÁwÌ×1DûžÚAÃmbŠø÷ù]OµÈêž’)/¢²ÒüûŽÚAôq,êÓowŽF 3ø»)`7Þ‹†\øzõmµHΚ“-ôöÒ\Ú+‰^$PF­p0ì²Ç»2 rGÎp$úí¬ì:CG1¸—ËÚÀ ƒzÌÃ'9éný»¯¾.Ïw´ÖÑJû^æC#î`ŒøöùQ>•çÚo‡¼Emuis®Ù`Ïodj0â#€ OÂpÂð¼?Ið½Ü´ÿeÜǦÛÝÛÜ-½ÒZ!2¸F‘R &TÉ pÄ'*¢‹è0»KrÔ­môØ®4¿ø˜GöPßû$QG– Û·Æ9†>o'æljx_ÃÓè¿Øxµò é&-E¼ÀÅçNÀÇ$¾Ð% Ô(È Óµñ$77 ¯cyomö™-Ròo/ÊyRCѵË.YH•A8YA׉ÚD,Ñ` Ç$€­–ÞÔ¤Ó›o.Yð±È‘âû}ÄÒF»²>x™>WùI`€pè5 p\ÍsO¹í¤L0FÖ(¯•Ôñë\=¯†äµ‚Ûí:Σ¦«NF™pm£vl,lÙ/Ý$þ÷=]ÂÏ'‡.cšâYô”¼Š[¸..mÖU˜Ü;Hˆ´¥|Â’'ï00C[ võ‘⛉­<3qo#G,qîWS‚Eq÷:LšMµÞ¡}0Ê#·:tLèdsä×)f˜' PCÕÈÈw©ªiͦø3WI­Ð\Ê<³¦ “$òKåû̹ä€ì€ 0$»[+§‚kùlˆ…â‡#?¼‘b) w€A8ºÚB¥ôVmâ=d\Kʉæ§*…CìÇ×óúÓa½:F±¬¬öWÒÉyv“Û {W‘d_"(þøîF;.:œ OªBRÎ=RÂ;™.4ùä—Ëes$Ñ–"h€o™( …,‘ò@1=„×fÝ|Gâ Díoæ¼,°ùŠóLA3¸m7'd‘Z¡yOIâMYVÙµÄ`»`¶ÉÉ“ôÒªhÚÓC#ß^ß©sp¶‘¬yNñ¶v 1®>l:©ÅdiÖºôÚ%ÕÛ^Ko§ÜÁi3 ™‹Ï\Ã,À)ÉF•ãRN K€£4¿g¤-ý½å¯ˆõ™-î#Yb5åapS#ƒÞ¦ÿ„r_ú5ŸûüŸüEröVÆ™áýT+.—Ô—-I-ƒ"(ÿVœ€;mÏÌà»cc{¨kQBgÖ¢ÐöÜ´!åš'uÅ®Ñ#¶%ÌóÊä†À ‡ܰZC¨ ñ.¸&ܨÌ9±µAÅc•Â’ ܸ0ÌɦÊp¾)×,‹8Ú³ouEhÉ‹.çA¹r£r’pA¤°ÔîuHô›-nÛQ‰ãŽÒYó§Mþ‘sµ$º¦ÈÑòå íU!ç´û?Šäï ÔmnÙã·G²–h…”î:3nXÛ ¥Üíg` k¤+ßKf¾#ÖMÄQ¤®žjp®X)ÎÌrQ¿/¥G›ÍÜÖ±ø‹\óáPîŽÁR΀óÈ&7Æ:€B ©a«Ïk®\]êÖÑÍ-…¼ÖÚ|òÆÒÅ-ÂÉ·b¶’sÉVSÞ±e‡P]GWãÖ†¦ðHšd‘­Ç–Ò‹»³r¿»*C3åÚGð“@M®·°´¶þ#Ö]I"'ÍAó#”aÊve#ð¡´…K¥¶ë)+ã`yP$1Â’˜fÂ1 d€2p¨µ.¬dÕ-m…âÚ^Æn£’Ò2æ *Ì0 ¬ ¾Ô>c<© ¶²"µÔnµ­"kˆuAenÖ³bY$c"ñ:€†^w û ™@ù°ÑÂ9/ý ÏýþOþ"øG%ÿ¡ƒYÿ¿ÉÿÄV†`ñ’C6¥0¼kaö¡wa9….7.p^m­ÿ-ò§9<=:îôÝËq¦Çª^ßÇ~.bžY–ÞHU§¢4ŸºS¼@¿'#žÁ¨¢ÿ„r_ú5ŸûüŸüEBºB½ô¶kâ=dÜEJéæ§ 傜ìÇ%òúW5bÚ¢M,\jóèË$pñÚ^Ã. N'˜Ï9ùÖß;€'Boâ×ù#ÕLKKc"ʬ.$E–ä¯#æ` ¡1ŸÞ2•Ý–Ü’mÿÂ9/ý ÏýþOþ"øG%ÿ¡ƒYÿ¿ÉÿÄW5ªÍâ?íë‡ÒþÙÜŸh‰mþÏ;Ä¡`“É5ØÛα6T‚ÛIáóVêK‰u ËmëZŠÁVÑî¾Ô—ÒÈŠ~Õ’¨Ì³`ºÂ–GNr ëÿá—þ† gþÿ'ÿPÙé coykâ=fK{ˆÖXŸÍA¹XdÈà÷¬Û5Ôã¸Ñójóž@aD»<Æ!˜aÄaAåH(D¡”îèÐ˧Ïy¥˜ßì°0–ÒM§hŠBO•ž™F…Cõ¤_ðŽKÿC³ÿ“ÿˆ£þÉè`Öïòñ¹EþÉè`ÖïòñÂ9/ý ÏýþOþ"·(¢àaÿÂ9/ý ÏýþOþ"øG%ÿ¡ƒYÿ¿ÉÿÄVå\ ?øG%ÿ¡ƒYÿ¿ÉÿÄQÿä¿ô0k?÷ù?øŠÜ¢‹‡ÿä¿ô0k?÷ù?øŠ?á—þ† gþÿ'ÿ[”Qp0ÿá—þ† gþÿ'ÿGü#’ÿÐÁ¬ÿßäÿâ+rŠ.ü#’ÿÐÁ¬ÿßäÿâ+KIÒŠÝ»ßÞÞÛö™ù‡@ëþ{ÕªšõWõÌèKEÀ†Š( g–üSÿ­‡ýp?ú|Sÿ­‡ýp?úkbYèÿ‘Ã_ö ƒÿA­*ÍðÏüˆþÿ°\ú iS­üIzlä²øÅ3M$©£M±Ý™rÊ $pNEzÕx|D¨IÊ&Œòÿß²\ÿϼ¿÷Á¨h ͧIqOfÒÃ"”xÞ-ÊÊF ŽAC§IopAfÑC„HÒ-ªªÊ(7Ù.çÞ_ûàÑöKŸù÷—þø5  ›ì—?óï/ýðhû%ÏüûËÿ|†Š›ì—?óï/ýðhû%ÏüûËÿ|†Š›ì—?óï/ýðhû%ÏüûËÿ|†Š›ì—?óï/ýði‘iÒ@…!³hÔ³9T‹³ÌxI$ŸRM2ŠMöKŸù÷—þø4}’çþ}åÿ¾ CE6%ÄA=›K ŠQãx·+) ‚9T:\ Ÿ¥%¤,ÛÌvöâ5-€3€8ò©( Dßd¹ÿŸyïƒGÙ.çÞ_ûàÔ4P2o²\ÿϼ¿÷Á£ì—?óï/ýðj(o²\ÿϼ¿÷Á£ì—?óï/ýðj(o²\ÿϼ¿÷Á£ì—?óï/ýðj(o²\ÿϼ¿÷Á£ì—?óï/ýðj(o²\ÿϼ¿÷Á£ì—?óï/ýðj(o²\ÿϼ¿÷Á£ì—?óï/ýðj(o²\ÿϼ¿÷Á© b‚ᤉÐl,¤ÕZ(QE òߊò°ÿ®ÿB¢Šò°ÿ®ÿB¢­lK=Ã?ò#økþÁpè5¥Y¾ÿ‘Ã_ö ƒÿA­eD.ìTd’p§[ø’õØZ*¯ö‡üþÛßÕÿ?´ì?çöÛþþ¯øÖc-QU´ì?çöÛþþ¯øÑý§aÿ?¶ß÷õÆ€-QU´ì?çöÛþþ¯øÑý§aÿ?¶ß÷õÆ€-QU´ì?çöÛþþ¯øÑý§aÿ?¶ß÷õÆ€-QU´ì?çöÛþþ¯øÑý§aÿ?¶ß÷õÆ€-QU´ì?çöÛþþ¯øÑý§aÿ?¶ß÷õÆ€-QU´ì?çöÛþþ¯øÑý§aÿ?¶ß÷õÆ€-QU´ì?çöÛþþ¯øÑý§aÿ?¶ß÷õÆ€-QU´ì?çöÛþþ¯øÑý§aÿ?¶ß÷õÆ€-QU´ì?çöÛþþ¯øÑý§aÿ?¶ß÷õÆ€-QU´ì?çöÛþþ¯øÑý§aÿ?¶ß÷õÆ€-QU´ì?çöÛþþ¯øÑý§aÿ?¶ß÷õÆ€-QU´ì?çöÛþþ¯øÑý§aÿ?¶ß÷õÆ€-QU´ì?çöÛþþ¯øÑý§aÿ?¶ß÷õÆ€-QU´ì?çöÛþþ¯øÑý§aÿ?¶ß÷õÆ€-QU´ì?çöÛþþ¯øÑý§aÿ?¶ß÷õÆ€-QU´ì?çöÛþþ¯øÑý§aÿ?¶ß÷õÆ€-QU´ì?çöÛþþ¯øÑý§aÿ?¶ß÷õÆ€-QU´ì?çöÛþþ¯øÑý§aÿ?¶ß÷õÆ€-QU´ì?çöÛþþ¯øÑý§aÿ?¶ß÷õÆ€-QU´ì?çöÛþþ¯øÑý§aÿ?¶ß÷õÆ€-QU´ì?çöÛþþ¯øÑý§aÿ?¶ß÷õÆ€-QU´ì?çöÛþþ¯øÑý§aÿ?¶ß÷õÆ€-QU´ì?çöÛþþ¯øÑý§aÿ?¶ß÷õÆ€-QU´ì?çöÛþþ¯øÑý§aÿ?¶ß÷õÆ€-QU´ì?çöÛþþ¯øÑý§aÿ?¶ß÷õÆ€-QU´ì?çöÛþþ¯øÑý§aÿ?¶ß÷õÆ€-QU´ì?çöÛþþ¯øÑý§aÿ?¶ß÷õÆ€-QU´ì?çöÛþþ¯øÑý§aÿ?¶ß÷õÆ€-QU´ì?çöÛþþ¯øÑý§aÿ?¶ß÷õÆ€-QU´ì?çöÛþþ¯øÑý§aÿ?¶ß÷õÆ€-QU´ì?çöÛþþ¯øÑý§aÿ?¶ß÷õÆ€-QU´ì?çöÛþþ¯øÑý§aÿ?¶ß÷õÆ€-QU´ì?çöÛþþ¯øÑý§aÿ?¶ß÷õÆ€-QU´ì?çöÛþþ¯øÑý§aÿ?¶ß÷õÆ€-QU㿳–EŽ;¸Ü…UY$ž€ ±@[ñOþB¶õÀÿèTQñOþB¶õÀÿèTU­‰g xgþD Ø.ý´Á*Á”G ŽÕ™áŸùü5ÿ`¸?ôÒ§[ø’õØ›íw?óñ/ýöhû]ÏüüKÿ}š†ŠÌdßk¹ÿŸ‰ï³GÚîçâ_ûìÔ4Pßk¹ÿŸ‰ï³GÚîçâ_ûìÔ4Pßk¹ÿŸ‰ï³GÚîçâ_ûìÔ4Pßk¹ÿŸ‰ï³GÚîçâ_ûìÔ4Pßk¹ÿŸ‰ï³GÚîçâ_ûìÔ4Pßk¹ÿŸ‰ï³GÚîçâ_ûìÔ4Pßk¹ÿŸ‰ï³GÚîçâ_ûìÔ4Pßk¹ÿŸ‰ï³GÚîçâ_ûìÔ4Pßk¹ÿŸ‰ï³GÚîçâ_ûìÔ4Pßk¹ÿŸ‰ï³GÚîçâ_ûìÔ4Pßk¹ÿŸ‰ï³GÚîçâ_ûìÔ4Pßk¹ÿŸ‰ï³GÚîçâ_ûìÔ4Pßk¹ÿŸ‰ï³GÚîçâ_ûìÔ4Pßk¹ÿŸ‰ï³GÚîçâ_ûìÔ4Pßk¹ÿŸ‰ï³GÚîçâ_ûìÔ4Pßk¹ÿŸ‰ï³GÚîçâ_ûìÔ4Pßk¹ÿŸ‰ï³GÚîçâ_ûìÔ4Pßk¹ÿŸ‰ï³GÚîçâ_ûìÔ4Pßk¹ÿŸ‰ï³GÚîçâ_ûìÔ4Pßk¹ÿŸ‰ï³GÚîçâ_ûìÔ5$Qy»¾u@£q-ŸP;}hïµÜÿÏÄ¿÷Ù£íw?óñ/ýöhòcÿŸ˜¿&ÿâhòcÿŸ˜¿&ÿâhû]ÏüüKÿ}š>×sÿ?ÿßf&?ùù‹òoþ&&?ùù‹òoþ&€µÜÿÏÄ¿÷Ù£íw?óñ/ýöhòcÿŸ˜¿&ÿâhòcÿŸ˜¿&ÿâhû]ÏüüKÿ}š>×sÿ?ÿßf&?ùù‹òoþ&&?ùù‹òoþ&€µÜÿÏÄ¿÷Ù£íw?óñ/ýöj®¥sm¥é·ÓÜ!Š.ÊŠÅŽ;:žœÖEž»}cm{má­M­îbI¢cqf¥‘”2œÁEÐý®çþ~%ÿ¾Ík¹ÿŸ‰ï³X¿ÚZ§ý —þYòEÚZ§ý —þYòEµö»Ÿùø—þû4}®çþ~%ÿ¾ÍbÿijŸô,j_øeÿÉijŸô,j_øeÿÉj×Úîçâ_ûìÑö»Ÿùø—þû5‹ý¥ªб©àU—ÿ$Qý¥ªб©àU—ÿ$Q¨_k¹ÿŸ‰ï³GÚîçâ_ûìÖ/ö–©ÿBÆ¥ÿV_ü‘Gö–©ÿBÆ¥ÿV_ü‘F m}®çþ~%ÿ¾Ík¹ÿŸ‰ï³Xš½¿ ÜÛÏZ\µ¬©1Bw¨RpQ™Hù‡ ÖÌQy»¾u@£q-ŸP;}hßk¹ÿŸ‰ï³GÚîçâ_ûìÑäÇÿ?1~MÿÄÑäÇÿ?1~MÿÄÐö»Ÿùø—þû4}®çþ~%ÿ¾ÍLóóäßüMLóóäßüMk¹ÿŸ‰ï³GÚîçâ_ûìÑäÇÿ?1~MÿÄÑäÇÿ?1~MÿÄÐö»Ÿùø—þû4}®çþ~%ÿ¾ÍLóóäßüMLóóäßüMk¹ÿŸ‰ï³PÔz•Ͷ—¦Ü_Op†(»*+8ì8êzsRP–üSÿ­‡ýp?ú|Sÿ­‡ýp?úka3Ð<3ÿ"?†¿ìþƒZU›áŸùü5ÿ`¸?ôÒ§[ø’õØ(¢ŠÌaEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPSCþªãþ¹ý jšõWõÌèK@ˆh¢ŠU{ø—PŠÅUä—Ìp€s†sØ0Rs…b9ÍÄ÷Z^·qiÞœžE”w0Ù΄Ï|ìÒ*#¼a– a\åÇ¡è?³ü½Oí¶Òù^oQmÊͅ·_•ÆÝÝF8R¹š¿‡nµ+ëçŽþm5 $²º‰­‹ÈPrQ÷€¬D¬9V€pzP#NçU´´¾ŠÒs2<¸Û!‚O+$à&݉àA$Üg2ïÅV†:|žd­smù°È‰,rOlñ±H 8;”‘ó)èF`Õ<#ý£­Ç¨yö-̧³ógÊdm‘K¼yhvr6ž]Ïñ`ðŠO.u¨C6™oöt†h>xâ‘’Ç{Œ.@Qó1*x¡´»‚úÕ.mŸÌó±À 8Î23ÕN2àŒA©ª­…¬¶p4r÷«~å¤ÉuL +1?9?1äŒg',mP3Æ?ò(ê_õËúбáù¼?ÿ`«Oý•_Æ?ò(ê_õËúбáù¼?ÿ`«Oý”ú ©©Yúõüº_‡u=BFšÖÒYÑ\¥• ãdV…RÖ4ÿímÿMó|¯µÛI™·vÍêW8ÈÎ3Ó4†fZø“ϸ°3¬6°Ieu5ߘÿñï,DŽ»øT´€¶0v‚:ݶñ›u4P¤³G<²yI öÒC!;ÁÚêR#|1%H#KQ𕮣«Ýß=ÄÑ¥æ5„ðÇ€™°G£í@¤r?»Í%ð-¬¶2Ù]®œ¶—2!º¶Óôñk芩*ÅÃoelîÆXF¶‰â mvKÑj®a·h¼¹J°YRHRU`H8|äŒq¸UÛ ø¯àgExäFòæ†@ÂàU€Ï8 ä ‚AÖÒ4¹ôÖ¸yï~Õ-Ï•$Ò‚™cXÙ€XF‡n8;¹ €&Óôÿ²y“M/Ú/gÁžr»wc8U;Qrp¹8É$–,ÄíQ@Î'á¯üxë¿ö“ÿEE]ä?ê®?ë˜ÿЖ¸?†¿ñã®ÿØ^Oýwÿª¸ÿ®cÿBZoqÑE†bÝÝêw:ÜÚn›=·Ùí¢žI.mÚ}þcH ®›qårs»¶9´ú¼6ÓÛZß+Ás2®JÅ#À®Ç|ír[€ Èã$ ‚óL¿þÔ“PÓ/m­æš‚e¹¶i”ª3²íÛ"`æGÎsž:`ç2ûÁÍ{ªAx÷–Ó4sÛÌg»²\©‰íŽ@Ê#VÙ’¡q—süX oh—–¦æÞ÷|>X•Êqæ‚@Äy¼`̪UrC0R)çÄÚX$\³32ù g3N¤Nè‚ïP!ÉP>uþðÎ{xC6=¸¾ÃévKo˜xwG‚Dr7}ÝÖã+œÇ :Ñ©xV}[ì×÷uåäbw¦‰m•_fBż0oݯÌ]¾óö ) šF«ý©>§±á’ÞÞå#‚HŽDˆÐE läƒÌ‡qŒ}kN³4]4eºHæóiÔyj›Ã@a@_ùež8ŠÓ fŒäQÔ¿ë—õ¹X~1ÿ‘GRÿ®_ÔVåyoÅ?ù Ø×ÿ¡QGÅ?ù Ø×ÿ¡QV¶=Ã?ò#økþÁpè5¥X¾¿³‹ÁžŽK¸EÒà ­ |¹ägЊÐþÓ°ÿŸÛoûú¿ãN·ñ%ê5±jŠ«ý§aÿ?¶ß÷õÆí;ùý¶ÿ¿«þ5˜ËTU_í;ùý¶ÿ¿«þ4iØÏí·ýý_ñ  TU_í;ùý¶ÿ¿«þ4iØÏí·ýý_ñ  TU_í;ùý¶ÿ¿«þ4iØÏí·ýý_ñ  TU_í;ùý¶ÿ¿«þ4iØÏí·ýý_ñ  TU_í;ùý¶ÿ¿«þ4iØÏí·ýý_ñ  TU_í;ùý¶ÿ¿«þ4iØÏí·ýý_ñ  TU_í;ùý¶ÿ¿«þ4iØÏí·ýý_ñ  TU_í;ùý¶ÿ¿«þ4iØÏí·ýý_ñ  TU_í;ùý¶ÿ¿«þ4iØÏí·ýý_ñ  TU_í;ùý¶ÿ¿«þ4iØÏí·ýý_ñ  TU_í;ùý¶ÿ¿«þ4iØÏí·ýý_ñ  TU_í;ùý¶ÿ¿«þ4iØÏí·ýý_ñ  TU_í;ùý¶ÿ¿«þ4iØÏí·ýý_ñ  TU_í;ùý¶ÿ¿«þ4iØÏí·ýý_ñ  TU_í;ùý¶ÿ¿«þ4iØÏí·ýý_ñ  TU_í;ùý¶ÿ¿«þ4iØÏí·ýý_ñ  TU_í;ùý¶ÿ¿«þ4iØÏí·ýý_ñ  TU_í;ùý¶ÿ¿«þ4iØÏí·ýý_ñ  TU_í;ùý¶ÿ¿«þ4iØÏí·ýý_ñ  UâÞÉjñXÝGm+à$‡ÌÂç< ÜÏ=øîý§aÿ?¶ß÷õÆí;ùý¶ÿ¿«þ4ÍþÎñý à¼ñtgx‡þ†?ð^?øºÒþÓ°ÿŸÛoûú¿ãGö‡üþÛßÕÿ57û;Ä?ô0Aÿ‚ñÿÅÑýâú ÿÁxÿâëKûNÃþm¿ïêÿÚvóûmÿWühÔ ßìïÿÐÁþ ÇÿGöwˆè`ƒÿãÿ‹­/í;ùý¶ÿ¿«þ4iØÏí·ýý_ñ£P3³¼CÿCø/ü]ÙÞ!ÿ¡‚üþ.´¿´ì?çöÛþþ¯øÑý§aÿ?¶ß÷õÆ@žÐu­FÆk;z†UÚÀXqõßY0x7Å6Ö–öÑx×lVð¤¨Óñ„E £‰}®ÃûNÃþm¿ïêÿÚvóûmÿWüiÝÉÿÂ'âÏúÛÿOÿ£þ?ÐîßøøíuŸÚvóûmÿWühþÓ°ÿŸÛoûú¿ãEØŸü"~,ÿ¡Ý¿ðÿñÚ?áñgýíÿ€'ÿŽ×Yý§aÿ?¶ß÷õÆí;ùý¶ÿ¿«þ4]ÉÿÂ'âÏúÛÿOÿ£þ?ÐîßøøíuŸÚvóûmÿWühþÓ°ÿŸÛoûú¿ãEØŸü"~,ÿ¡Ý¿ðÿñÚ?áñgýíÿ€'ÿŽ×Yý§aÿ?¶ß÷õÆí;ùý¶ÿ¿«þ4]•á?KỸ'¾[Énnšå¤X|  E\css={ÖÅâÞÉjñXÝGm+à$‡ÌÂç< ÜÏ=øîý§aÿ?¶ß÷õÆí;ùý¶ÿ¿«þ4€ÍþÎñý à¼ñtgx‡þ†?ð^?øºÒþÓ°ÿŸÛoûú¿ãGö‡üþÛßÕÿ57û;Ä?ô0Aÿ‚ñÿÅÑýâú ÿÁxÿâëKûNÃþm¿ïêÿÚvóûmÿWühÔ ßìïÿÐÁþ ÇÿGöwˆè`ƒÿãÿ‹­/í;ùý¶ÿ¿«þ4iØÏí·ýý_ñ£P3³¼CÿCø/ü]ÙÞ!ÿ¡‚üþ.´¿´ì?çöÛþþ¯øÑý§aÿ?¶ß÷õÆ@žÐu­FÆk;z†UÚÀXqõß]%UþÓ°ÿŸÛoûú¿ãGö‡üþÛßÕÿóŠò°ÿ®ÿB¢£ø<7•‹Ã*H¢£3Ÿj*ÖÂgŸÚÇ”õÍ•ME­_Ž^¬•°QE˜ÂŠ( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( ÿÙscons-doc-2.3.0/doc/user/sourcecode.xml0000644000175000017500000000624212114661557020626 0ustar dktrkranzdktrkranz XXX
Fetching Source Code From BitKeeper XXX env = Environment() env.SourceCode('.', env.BitKeeper()) env.Program('hello.c') % scons -Q bk get hello.c cc -o hello.o -c hello.c cc -o hello hello.o
Fetching Source Code From CVS XXX env = Environment() env.SourceCode('.', env.CVS('/usr/local/CVS')) env.Program('hello.c') % scons -Q cvs -d /usr/local/CVS co hello.c cc -o hello.o -c hello.c cc -o hello hello.o
Fetching Source Code From RCS XXX env = Environment() env.SourceCode('.', env.RCS()) env.Program('hello.c') % scons -Q co hello.c cc -o hello.o -c hello.c cc -o hello hello.o
Fetching Source Code From SCCS XXX env = Environment() env.SourceCode('.', env.SCCS()) env.Program('hello.c') % scons -Q sccs get hello.c cc -o hello.o -c hello.c cc -o hello hello.o
scons-doc-2.3.0/doc/user/sideeffect.in0000644000175000017500000001530212114661557020377 0ustar dktrkranzdktrkranz Sometimes a program the you need to call to build a target file will also update another file, such as a log file describing what the program does while building the target. For example, we the folowing configuration would have &SCons; invoke a hypothetical script named build (in the local directory) with command-line arguments that write log information to a common logfile.txt file: env = Environment() env.Command('file1.out', 'file.in', './build --log logfile.txt $SOURCE $TARGET') env.Command('file2.out', 'file.in', './build --log logfile.txt $SOURCE $TARGET') This can cause problems when running the build in parallel if &SCons; decides to update both targets by running both program invocations at the same time. The multiple program invocations may interfere with each other writing to the common log file, leading at best to intermixed output in the log file, and at worst to an actual failed build (on a system like Windows, for example, where only one process at a time can open the log file for writing). We can make sure that &SCons; does not run these build commands at the same time by using the &SideEffect; function to specify that updating the logfile.txt file is a side effect of building the specified file1 and file2 target files: env = Environment() f1 = env.Command('file1.out', 'file1.in', './build --log logfile.txt $SOURCE $TARGET') f2 = env.Command('file2.out', 'file2.in', './build --log logfile.txt $SOURCE $TARGET') env.SideEffect('logfile.txt', f1 + f2) file1.in file2.in cat This makes sure the the two ./build steps are run sequentially, even withthe --jobs=2 in the command line: scons -Q --jobs=2 The &SideEffect; function can be called multiple times for the same side-effect file. Additionally, the name used as a &SideEffect; does not even need to actually exist as a file on disk. &SCons; will still make sure that the relevant targets will be executed sequentially, not in parallel: env = Environment() f1 = env.Command('file1.out', [], 'echo >$TARGET data1') env.SideEffect('not_really_updated', f1) f2 = env.Command('file2.out', [], 'echo >$TARGET data2') env.SideEffect('not_really_updated', f2) scons -Q --jobs=2 Note that it might be tempting to use &SideEffect; for additional target files that a command produces. For example, versions the Microsoft Visual C/C++ compiler produce a foo.ilk alongside compiling foo.obj file. Specifying foo.ilk as a side-effect of foo.obj is not a recommended use of &SideEffect;, because &SCons; handle side-effect files slightly differently in its analysis of the dependency graph. When a command produces multiple output files, they should be specified as multiple targets of the call to the relevant builder function, and the &SideEffect; function itself should really only be used when it's important to ensure that commands are not executed in parallel, such as when a "peripheral" file (such as a log file) may actually updated by more than one command invocation. scons-doc-2.3.0/doc/user/cons.pl0000644000175000017500000006402012114661557017246 0ustar dktrkranzdktrkranz=head1 Introduction B is a system for constructing, primarily, software, but is quite different from previous software construction systems. Cons was designed from the ground up to deal easily with the construction of software spread over multiple source directories. Cons makes it easy to create build scripts that are simple, understandable and maintainable. Cons ensures that complex software is easily and accurately reproducible. Cons uses a number of techniques to accomplish all of this. Construction scripts are just Perl scripts, making them both easy to comprehend and very flexible. Global scoping of variables is replaced with an import/export mechanism for sharing information between scripts, significantly improving the readability and maintainability of each script. B are introduced: these are Perl objects that capture the information required for controlling the build process. Multiple environments are used when different semantics are required for generating products in the build tree. Cons implements automatic dependency analysis and uses this to globally sequence the entire build. Variant builds are easily produced from a single source tree. Intelligent build subsetting is possible, when working on localized changes. Overrides can be setup to easily override build instructions without modifying any scripts. MD5 cryptographic B are associated with derived files, and are used to accurately determine whether a given file needs to be rebuilt. While offering all of the above, and more, Cons remains simple and easy to use. This will, hopefully, become clear as you read the remainder of this document. =head2 Automatic global build sequencing Because Cons does full and accurate dependency analysis, and does this globally, for the entire build, Cons is able to use this information to take full control of the B of the build. This sequencing is evident in the above examples, and is equivalent to what you would expect for make, given a full set of dependencies. With Cons, this extends trivially to larger, multi-directory builds. As a result, all of the complexity involved in making sure that a build is organized correctly--including multi-pass hierarchical builds--is eliminated. We'll discuss this further in the next sections. =head1 A Model for sharing files =head2 Some simple conventions In any complex software system, a method for sharing build products needs to be established. We propose a simple set of conventions which are trivial to implement with Cons, but very effective. The basic rule is to require that all build products which need to be shared between directories are shared via an intermediate directory. We have typically called this F, and, in a C environment, provided conventional sub-directories of this directory, such as F, F, F, etc. These directories are defined by the top-level F file. A simple F file for a B application, organized using multiple directories, might look like this: # Construct file for Hello, World! # Where to put all our shared products. $EXPORT = '#export'; Export qw( CONS INCLUDE LIB BIN ); # Standard directories for sharing products. $INCLUDE = "$EXPORT/include"; $LIB = "$EXPORT/lib"; $BIN = "$EXPORT/bin"; # A standard construction environment. $CONS = new cons ( CPPPATH => $INCLUDE, # Include path for C Compilations LIBPATH => $LIB, # Library path for linking programs LIBS => '-lworld', # List of standard libraries ); Build qw( hello/Conscript world/Conscript ); The F directory's F file looks like this: # Conscript file for directory world Import qw( CONS INCLUDE LIB ); # Install the products of this directory Install $CONS $LIB, 'libworld.a'; Install $CONS $INCLUDE, 'world.h'; # Internal products Library $CONS 'libworld.a', 'world.c'; and the F directory's F file looks like this: # Conscript file for directory hello Import qw( CONS BIN ); # Exported products Install $CONS $BIN, 'hello'; # Internal products Program $CONS 'hello', 'hello.c'; To construct a B program with this directory structure, go to the top-level directory, and invoke C with the appropriate arguments. In the following example, we tell Cons to build the directory F. To build a directory, Cons recursively builds all known products within that directory (only if they need rebuilding, of course). If any of those products depend upon other products in other directories, then those will be built, too. % cons export Install world/world.h as export/include/world.h cc -Iexport/include -c hello/hello.c -o hello/hello.o cc -Iexport/include -c world/world.c -o world/world.o ar r world/libworld.a world/world.o ar: creating world/libworld.a ranlib world/libworld.a Install world/libworld.a as export/lib/libworld.a cc -o hello/hello hello/hello.o -Lexport/lib -lworld Install hello/hello as export/bin/hello =head2 Clean, understandable, location-independent scripts You'll note that the two F files are very clean and to-the-point. They simply specify products of the directory and how to build those products. The build instructions are minimal: they specify which construction environment to use, the name of the product, and the name of the inputs. Note also that the scripts are location-independent: if you wish to reorganize your source tree, you are free to do so: you only have to change the F file (in this example), to specify the new locations of the F files. The use of an export tree makes this goal easy. Note, too, how Cons takes care of little details for you. All the F directories, for example, were made automatically. And the installed files were really hard-linked into the respective export directories, to save space and time. This attention to detail saves considerable work, and makes it even easier to produce simple, maintainable scripts. =head1 Signatures Cons uses file B to decide if a derived file is out-of-date and needs rebuilding. In essence, if the contents of a file change, or the manner in which the file is built changes, the file's signature changes as well. This allows Cons to decide with certainty when a file needs rebuilding, because Cons can detect, quickly and reliably, whether any of its dependency files have been changed. =head2 MD5 content and build signatures Cons uses the B (B) algorithm to compute file signatures. The MD5 algorithm computes a strong cryptographic checksum for any given input string. Cons can, based on configuration, use two different MD5 signatures for a given file: The B of a file is an MD5 checksum of the file's contents. Consequently, when the contents of a file change, its content signature changes as well. The B of a file is a combined MD5 checksum of: =over 4 the signatures of all the input files used to build the file the signatures of all dependency files discovered by source scanners (for example, C<.h> files) the signatures of all dependency files specified explicitly via the C method) the command-line string used to build the file =back The build signature is, in effect, a digest of all the dependency information for the specified file. Consequently, a file's build signature changes whenever any part of its dependency information changes: a new file is added, the contents of a file on which it depends change, there's a change to the command line used to build the file (or any of its dependency files), etc. For example, in the previous section, the build signature of the F file will include: =over 4 the signature of the F file the signatures of any header files that Cons detects are included, directly or indirectly, by F the text of the actual command line was used to generate F =back Similarly, the build signature of the F file will include all the signatures of its constituents (and hence, transitively, the signatures of B constituents), as well as the command line that created the file. Note that there is no need for a derived file to depend upon any particular F or F file. If changes to these files affect a file, then this will be automatically reflected in its build signature, since relevant parts of the command line are included in the signature. Unrelated F or F changes will have no effect. =head2 Storing signatures in .consign files Before Cons exits, it stores the calculated signatures for all of the files it built or examined in F<.consign> files, one per directory. Cons uses this stored information on later invocations to decide if derived files need to be rebuilt. After the previous example was compiled, the F<.consign> file in the F directory looked like this: world.h:985533370 - d181712f2fdc07c1f05d97b16bfad904 world.o:985533372 2a0f71e0766927c0532977b0d2158981 world.c:985533370 - c712f77189307907f4189b5a7ab62ff3 libworld.a:985533374 69e568fc5241d7d25be86d581e1fb6aa After the file name and colon, the first number is a timestamp of the file's modification time (on UNIX systems, this is typically the number of seconds since January 1st, 1970). The second value is the build signature of the file (or ``-'' in the case of files with no build signature--that is, source files). The third value, if any, is the content signature of the file. =head2 Using build signatures to decide when to rebuild files When Cons is deciding whether to build or rebuild a derived file, it first computes the file's current build signature. If the file doesn't exist, it must obviously be built. If, however, the file already exists, Cons next compares the modification timestamp of the file against the timestamp value in the F<.consign> file. If the timestamps match, Cons compares the newly-computed build signature against the build signature in the F<.consign> file. If the timestamps do not match or the build signatures do not match, the derived file is rebuilt. After the file is built or rebuilt, Cons arranges to store the newly-computed build signature in the F<.consign> file when it exits. =head2 Signature example The use of these signatures is an extremely simple, efficient, and effective method of improving--dramatically--the reproducibility of a system. We'll demonstrate this with a simple example: # Simple "Hello, World!" Construct file $CFLAGS = '-g' if $ARG{DEBUG} eq 'on'; $CONS = new cons(CFLAGS => $CFLAGS); Program $CONS 'hello', 'hello.c'; Notice how Cons recompiles at the appropriate times: % cons hello cc -c hello.c -o hello.o cc -o hello hello.o % cons hello cons: "hello" is up-to-date. % cons DEBUG=on hello cc -g -c hello.c -o hello.o cc -o hello hello.o % cons DEBUG=on hello cons: "hello" is up-to-date. % cons hello cc -c hello.c -o hello.o cc -o hello hello.o =head2 Source-file signature configuration Cons provides a C method that allows you to configure how the signature should be calculated for any source file when its signature is being used to decide if a dependent file is up-to-date. The arguments to the C method consist of one or more pairs of strings: SourceSignature 'auto/*.c' => 'content', '*' => 'stored-content'; The first string in each pair is a pattern to match against derived file path names. The pattern is a file-globbing pattern, not a Perl regular expression; the pattern <*.l> will match all Lex source files. The C<*> wildcard will match across directory separators; the pattern C would match all C source files in any subdirectory underneath the C subdirectory. The second string in each pair contains one of the following keywords to specify how signatures should be calculated for source files that match the pattern. The available keywords are: =over 4 =item content Use the content signature of the source file when calculating signatures of files that depend on it. This guarantees correct calculation of the file's signature for all builds, by telling Cons to read the contents of a source file to calculate its content signature each time it is run. =item stored-content Use the source file's content signature as stored in the F<.consign> file, provided the file's timestamp matches the cached timestamp value in the F<.consign> file. This optimizes performance, with the slight risk of an incorrect build if a source file's contents have been changed so quickly after its previous update that the timestamp still matches the stored timestamp in the F<.consign> file even though the contents have changed. =back The Cons default behavior of always calculating a source file's signature from the file's contents is equivalent to specifying: SourceSignature '*' => 'content'; The C<*> will match all source files. The C keyword specifies that Cons will read the contents of a source file to calculate its signature each time it is run. A useful global performance optimization is: SourceSignature '*' => 'stored-content'; This specifies that Cons will use pre-computed content signatures from F<.consign> files, when available, rather than re-calculating a signature from the the source file's contents each time Cons is run. In practice, this is safe for most build situations, and only a problem when source files are changed automatically (by scripts, for example). The Cons default, however, errs on the side of guaranteeing a correct build in all situations. Cons tries to match source file path names against the patterns in the order they are specified in the C arguments: SourceSignature '/usr/repository/objects/*' => 'stored-content', '/usr/repository/*' => 'content', '*.y' => 'content', '*' => 'stored-content'; In this example, all source files under the F directory will use F<.consign> file content signatures, source files anywhere else underneath F will not use F<.consign> signature values, all Yacc source files (C<*.y>) anywhere else will not use F<.consign> signature values, and any other source file will use F<.consign> signature values. =head2 Derived-file signature configuration Cons provides a C construction variable that allows you to configure how signatures are calculated for any derived file when its signature is being used to decide if a dependent file is up-to-date. The value of the C construction variable is a Perl array reference that holds one or more pairs of strings, like the arguments to the C method. The first string in each pair is a pattern to match against derived file path names. The pattern is a file-globbing pattern, not a Perl regular expression; the pattern `*.obj' will match all (Win32) object files. The C<*> wildcard will match across directory separators; the pattern `foo/*.a' would match all (UNIX) library archives in any subdirectory underneath the foo subdirectory. The second string in each pair contains one of the following keywords to specify how signatures should be calculated for derived files that match the pattern. The available keywords are the same as for the C method, with an additional keyword: =over 4 =item build Use the build signature of the derived file when calculating signatures of files that depend on it. This guarantees correct builds by forcing Cons to rebuild any and all files that depend on the derived file. =item content Use the content signature of the derived file when calculating signatures of files that depend on it. This guarantees correct calculation of the file's signature for all builds, by telling Cons to read the contents of a derived file to calculate its content signature each time it is run. =item stored-content Use the derived file's content signature as stored in the F<.consign> file, provided the file's timestamp matches the cached timestamp value in the F<.consign> file. This optimizes performance, with the slight risk of an incorrect build if a derived file's contents have been changed so quickly after a Cons build that the file's timestamp still matches the stored timestamp in the F<.consign> file. =back The Cons default behavior (as previously described) for using derived-file signatures is equivalent to: $env = new cons(SIGNATURE => ['*' => 'build']); The C<*> will match all derived files. The C keyword specifies that all derived files' build signatures will be used when calculating whether a dependent file is up-to-date. A useful alternative default C configuration for many sites: $env = new cons(SIGNATURE => ['*' => 'content']); In this configuration, derived files have their signatures calculated from the file contents. This adds slightly to Cons' workload, but has the useful effect of "stopping" further rebuilds if a derived file is rebuilt to exactly the same file contents as before, which usually outweighs the additional computation Cons must perform. For example, changing a comment in a C file and recompiling should generate the exact same object file (assuming the compiler doesn't insert a timestamp in the object file's header). In that case, specifying C or C for the signature calculation will cause Cons to recognize that the object file did not actually change as a result of being rebuilt, and libraries or programs that include the object file will not be rebuilt. When C is specified, however, Cons will only "know" that the object file was rebuilt, and proceed to rebuild any additional files that include the object file. Note that Cons tries to match derived file path names against the patterns in the order they are specified in the C array reference: $env = new cons(SIGNATURE => ['foo/*.o' => 'build', '*.o' => 'content', '*.a' => 'stored-content', '*' => 'content']); In this example, all object files underneath the F subdirectory will use build signatures, all other object files (including object files underneath other subdirectories!) will use F<.consign> file content signatures, libraries will use F<.consign> file build signatures, and all other derived files will use content signatures. =head2 Debugging signature calculation Cons provides a C<-S> option that can be used to specify what internal Perl package Cons should use to calculate signatures. The default Cons behavior is equivalent to specifying C<-S md5> on the command line. The only other package (currently) available is an C package that prints out detailed information about the MD5 signature calculations performed by Cons: % cons -S md5::debug hello sig::md5::srcsig(hello.c) => |52d891204c62fe93ecb95281e1571938| sig::md5::collect(52d891204c62fe93ecb95281e1571938) => |fb0660af4002c40461a2f01fbb5ffd03| sig::md5::collect(52d891204c62fe93ecb95281e1571938, fb0660af4002c40461a2f01fbb5ffd03, cc -c %< -o %>) => |f7128da6c3fe3c377dc22ade70647b39| sig::md5::current(|| eq |f7128da6c3fe3c377dc22ade70647b39|) cc -c hello.c -o hello.o sig::md5::collect() => |d41d8cd98f00b204e9800998ecf8427e| sig::md5::collect(f7128da6c3fe3c377dc22ade70647b39, d41d8cd98f00b204e9800998ecf8427e, cc -o %> %< ) => |a0bdce7fd09e0350e7efbbdb043a00b0| sig::md5::current(|| eq |a0bdce7fd09e0350e7efbbdb043a00b0|) cc -o hello, hello.o =head1 Temporary overrides Cons provides a very simple mechanism for overriding aspects of a build. The essence is that you write an override file containing one or more C commands, and you specify this on the command line, when you run C: % cons -o over export will build the F directory, with all derived files subject to the overrides present in the F file. If you leave out the C<-o> option, then everything necessary to remove all overrides will be rebuilt. =head2 Overriding environment variables The override file can contain two types of overrides. The first is incoming environment variables. These are normally accessible by the F file from the C<%ENV> hash variable. These can trivially be overridden in the override file by setting the appropriate elements of C<%ENV> (these could also be overridden in the user's environment, of course). =head2 The Override command The second type of override is accomplished with the C command, which looks like this: Override , => , => , ...; The regular expression I is matched against every derived file that is a candidate for the build. If the derived file matches, then the variable/value pairs are used to override the values in the construction environment associated with the derived file. Let's suppose that we have a construction environment like this: $CONS = new cons( COPT => '', CDBG => '-g', CFLAGS => '%COPT %CDBG', ); Then if we have an override file F containing this command: Override '\.o$', COPT => '-O', CDBG => ''; then any C invocation with C<-o over> that creates F<.o> files via this environment will cause them to be compiled with C<-O >and no C<-g>. The override could, of course, be restricted to a single directory by the appropriate selection of a regular expression. Here's the original version of the Hello, World! program, built with this environment. Note that Cons rebuilds the appropriate pieces when the override is applied or removed: % cons hello cc -g -c hello.c -o hello.o cc -o hello hello.o % cons -o over hello cc -O -c hello.c -o hello.o cc -o hello hello.o % cons -o over hello cons: "hello" is up-to-date. % cons hello cc -g -c hello.c -o hello.o cc -o hello hello.o It's important that the C command only be used for temporary, on-the-fly overrides necessary for development because the overrides are not platform independent and because they rely too much on intimate knowledge of the workings of the scripts. For temporary use, however, they are exactly what you want. Note that it is still useful to provide, say, the ability to create a fully optimized version of a system for production use--from the F and F files. This way you can tailor the optimized system to the platform. Where optimizer trade-offs need to be made (particular files may not be compiled with full optimization, for example), then these can be recorded for posterity (and reproducibility) directly in the scripts. =head2 The C method The C method is a combination of the C and C methods. Rather than generating an executable program directly, this command allows you to specify your own command to actually generate a module. The method is invoked as follows: Module $env , , ; This command is useful in instances where you wish to create, for example, dynamically loaded modules, or statically linked code libraries. =head2 The C method The C method returns the construction variables for building various components with one of the rule sets supported by Cons. The currently supported rule sets are: =over 4 =item msvc Rules for the Microsoft Visual C++ compiler suite. =item unix Generic rules for most UNIX-like compiler suites. =back On systems with more than one available compiler suite, this allows you to easily create side-by-side environments for building software with multiple tools: $msvcenv = new cons(RuleSet("msvc")); $cygnusenv = new cons(RuleSet("unix")); In the future, this could also be extended to other platforms that have different default rule sets. =head2 The C method The C method sets the default construction variables that will be returned by the C method to the specified arguments: DefaultRules(CC => 'gcc', CFLAGS => '', CCCOM => '%CC %CFLAGS %_IFLAGS -c %< -o %>'); $env = new cons(); # $env now contains *only* the CC, CFLAGS, # and CCCOM construction variables Combined with the C method, this also provides an easy way to set explicitly the default build environment to use some supported toolset other than the Cons defaults: # use a UNIX-like tool suite (like cygwin) on Win32 DefaultRules(RuleSet('unix')); $env = new cons(); Note that the C method completely replaces the default construction environment with the specified arguments, it does not simply override the existing defaults. To override one or more variables in a supported C, append the variables and values: DefaultRules(RuleSet('unix'), CFLAGS => '-O3'); $env1 = new cons(); $env2 = new cons(); # both $env1 and $env2 have 'unix' defaults # with CFLAGS set to '-O3' =head2 The C method The C mathod returns the real source path name of a file, as opposed to the path name within a build directory. It is invoked as follows: $path = SourcePath ; =head2 The C method The C method returns true if the supplied path is a derivable file, and returns undef (false) otherwise. It is invoked as follows: $result = ConsPath ; =head2 The C method The C method looks up multiple path names in a string separated by the default path separator for the operating system (':' on UNIX systems, ';' on Windows NT), and returns the fully-qualified names. It is invoked as follows: @paths = SplitPath ; The C method will convert names prefixed '#' to the appropriate top-level build name (without the '#') and will convert relative names to top-level names. =head2 The C method The C method returns the build path name(s) of a directory or list of directories. It is invoked as follows: $cwd = DirPath ; The most common use for the C method is: $cwd = DirPath '.'; to fetch the path to the current directory of a subsidiary F file. =head2 The C method The C method returns the build path name(s) of a file or list of files. It is invoked as follows: $file = FilePath ; scons-doc-2.3.0/doc/user/preface.in0000644000175000017500000002666012114661557017714 0ustar dktrkranzdktrkranz Thank you for taking the time to read about &SCons;. &SCons; is a next-generation software construction tool, or make tool--that is, a software utility for building software (or other files) and keeping built software up-to-date whenever the underlying input files change. The most distinctive thing about &SCons; is that its configuration files are actually scripts, written in the &Python; programming language. This is in contrast to most alternative build tools, which typically invent a new language to configure the build. &SCons; still has a learning curve, of course, because you have to know what functions to call to set up your build properly, but the underlying syntax used should be familiar to anyone who has ever looked at a Python script. Paradoxically, using Python as the configuration file format makes &SCons; easier for non-programmers to learn than the cryptic languages of other build tools, which are usually invented by programmers for other programmers. This is in no small part due to the consistency and readability that are hallmarks of Python. It just so happens that making a real, live scripting language the basis for the configuration files makes it a snap for more accomplished programmers to do more complicated things with builds, as necessary.
&SCons; Principles There are a few overriding principles we try to live up to in designing and implementing &SCons;: Correctness First and foremost, by default, &SCons; guarantees a correct build even if it means sacrificing performance a little. We strive to guarantee the build is correct regardless of how the software being built is structured, how it may have been written, or how unusual the tools are that build it. Performance Given that the build is correct, we try to make &SCons; build software as quickly as possible. In particular, wherever we may have needed to slow down the default &SCons; behavior to guarantee a correct build, we also try to make it easy to speed up &SCons; through optimization options that let you trade off guaranteed correctness in all end cases for a speedier build in the usual cases. Convenience &SCons; tries to do as much for you out of the box as reasonable, including detecting the right tools on your system and using them correctly to build the software. In a nutshell, we try hard to make &SCons; just "do the right thing" and build software correctly, with a minimum of hassles.
A Caveat About This Guide's Completeness One word of warning as you read through this Guide: Like too much Open Source software out there, the &SCons; documentation isn't always kept up-to-date with the available features. In other words, there's a lot that &SCons; can do that isn't yet covered in this User's Guide. (Come to think of it, that also describes a lot of proprietary software, doesn't it?) Although this User's Guide isn't as complete as we'd like it to be, our development process does emphasize making sure that the &SCons; man page is kept up-to-date with new features. So if you're trying to figure out how to do something that &SCons; supports but can't find enough (or any) information here, it would be worth your while to look at the man page to see if the information is covered there. And if you do, maybe you'd even consider contributing a section to the User's Guide so the next person looking for that information won't have to go through the same thing...?
Acknowledgements &SCons; would not exist without a lot of help from a lot of people, many of whom may not even be aware that they helped or served as inspiration. So in no particular order, and at the risk of leaving out someone: First and foremost, &SCons; owes a tremendous debt to Bob Sidebotham, the original author of the classic Perl-based &Cons; tool which Bob first released to the world back around 1996. Bob's work on Cons classic provided the underlying architecture and model of specifying a build configuration using a real scripting language. My real-world experience working on Cons informed many of the design decisions in SCons, including the improved parallel build support, making Builder objects easily definable by users, and separating the build engine from the wrapping interface. Greg Wilson was instrumental in getting &SCons; started as a real project when he initiated the Software Carpentry design competition in February 2000. Without that nudge, marrying the advantages of the Cons classic architecture with the readability of Python might have just stayed no more than a nice idea. The entire &SCons; team have been absolutely wonderful to work with, and &SCons; would be nowhere near as useful a tool without the energy, enthusiasm and time people have contributed over the past few years. The "core team" of Chad Austin, Anthony Roach, Bill Deegan, Charles Crain, Steve Leblanc, Greg Noel, Gary Oberbrunner, Greg Spencer and Christoph Wiedemann have been great about reviewing my (and other) changes and catching problems before they get in the code base. Of particular technical note: Anthony's outstanding and innovative work on the tasking engine has given &SCons; a vastly superior parallel build model; Charles has been the master of the crucial Node infrastructure; Christoph's work on the Configure infrastructure has added crucial Autoconf-like functionality; and Greg has provided excellent support for Microsoft Visual Studio. Special thanks to David Snopek for contributing his underlying "Autoscons" code that formed the basis of Christoph's work with the Configure functionality. David was extremely generous in making this code available to &SCons;, given that he initially released it under the GPL and &SCons; is released under a less-restrictive MIT-style license. Thanks to Peter Miller for his splendid change management system, &Aegis;, which has provided the &SCons; project with a robust development methodology from day one, and which showed me how you could integrate incremental regression tests into a practical development cycle (years before eXtreme Programming arrived on the scene). And last, thanks to Guido van Rossum for his elegant scripting language, which is the basis not only for the &SCons; implementation, but for the interface itself.
Contact The best way to contact people involved with SCons, including the author, is through the SCons mailing lists. If you want to ask general questions about how to use &SCons; send email to &scons-users;. If you want to contact the &SCons; development community directly, send email to &scons-devel;. If you want to receive announcements about &SCons;, join the low-volume &scons-announce; mailing list.
scons-doc-2.3.0/doc/user/misc.in0000644000175000017500000004020512114661557017231 0ustar dktrkranzdktrkranz &SCons; supports a lot of additional functionality that doesn't readily fit into the other chapters.
Verifying the Python Version: the &EnsurePythonVersion; Function Although the &SCons; code itself will run on any 2.x Python version 2.4 or later, you are perfectly free to make use of Python syntax and modules from more modern versions (for example, Python 2.5 or 2.6) when writing your &SConscript; files or your own local modules. If you do this, it's usually helpful to configure &SCons; to exit gracefully with an error message if it's being run with a version of Python that simply won't work with your code. This is especially true if you're going to use &SCons; to build source code that you plan to distribute publicly, where you can't be sure of the Python version that an anonymous remote user might use to try to build your software. &SCons; provides an &EnsurePythonVersion; function for this. You simply pass it the major and minor versions numbers of the version of Python you require: EnsurePythonVersion(2, 5) And then &SCons; will exit with the following error message when a user runs it with an unsupported earlier version of Python: % scons -Q Python 2.5 or greater required, but you have Python 2.3.6
Verifying the SCons Version: the &EnsureSConsVersion; Function You may, of course, write your &SConscript; files to use features that were only added in recent versions of &SCons;. When you publicly distribute software that is built using &SCons;, it's helpful to have &SCons; verify the version being used and exit gracefully with an error message if the user's version of &SCons; won't work with your &SConscript; files. &SCons; provides an &EnsureSConsVersion; function that verifies the version of &SCons; in the same the &EnsurePythonVersion; function verifies the version of Python, by passing in the major and minor versions numbers of the version of SCons you require: EnsureSConsVersion(1, 0) And then &SCons; will exit with the following error message when a user runs it with an unsupported earlier version of &SCons;: % scons -Q SCons 1.0 or greater required, but you have SCons 0.98.5
Explicitly Terminating &SCons; While Reading &SConscript; Files: the &Exit; Function &SCons; supports an &Exit; function which can be used to terminate &SCons; while reading the &SConscript; files, usually because you've detected a condition under which it doesn't make sense to proceed: if ARGUMENTS.get('FUTURE'): print "The FUTURE option is not supported yet!" Exit(2) env = Environment() env.Program('hello.c') hello.c scons -Q FUTURE=1 scons -Q The &Exit; function takes as an argument the (numeric) exit status that you want &SCons; to exit with. If you don't specify a value, the default is to exit with 0, which indicates successful execution. Note that the &Exit; function is equivalent to calling the Python sys.exit function (which the it actually calls), but because &Exit; is a &SCons; function, you don't have to import the Python sys module to use it.
Searching for Files: the &FindFile; Function The &FindFile; function searches for a file in a list of directories. If there is only one directory, it can be given as a simple string. The function returns a File node if a matching file exists, or None if no file is found. (See the documentation for the &Glob; function for an alternative way of searching for entries in a directory.) # one directory print FindFile('missing', '.') t = FindFile('exists', '.') print t.__class__, t exists scons -Q # several directories includes = [ '.', 'include', 'src/include'] headers = [ 'nonesuch.h', 'config.h', 'private.h', 'dist.h'] for hdr in headers: print '%-12s' % ('%s:' % hdr), FindFile(hdr, includes) exists exists exists scons -Q If the file exists in more than one directory, only the first occurrence is returned. print FindFile('multiple', ['sub1', 'sub2', 'sub3']) print FindFile('multiple', ['sub2', 'sub3', 'sub1']) print FindFile('multiple', ['sub3', 'sub1', 'sub2']) exists exists exists scons -Q In addition to existing files, &FindFile; will also find derived files (that is, non-leaf files) that haven't been built yet. (Leaf files should already exist, or the build will fail!) # Neither file exists, so build will fail Command('derived', 'leaf', 'cat >$TARGET $SOURCE') print FindFile('leaf', '.') print FindFile('derived', '.') scons -Q # Only 'leaf' exists Command('derived', 'leaf', 'cat >$TARGET $SOURCE') print FindFile('leaf', '.') print FindFile('derived', '.') leaf scons -Q If a source file exists, &FindFile; will correctly return the name in the build directory. # Only 'src/leaf' exists VariantDir('build', 'src') print FindFile('leaf', 'build') leaf scons -Q
Handling Nested Lists: the &Flatten; Function &SCons; supports a &Flatten; function which takes an input Python sequence (list or tuple) and returns a flattened list containing just the individual elements of the sequence. This can be handy when trying to examine a list composed of the lists returned by calls to various Builders. For example, you might collect object files built in different ways into one call to the &Program; Builder by just enclosing them in a list, as follows: objects = [ Object('prog1.c'), Object('prog2.c', CCFLAGS='-DFOO'), ] Program(objects) prog1.c prog2.c Because the Builder calls in &SCons; flatten their input lists, this works just fine to build the program: scons -Q But if you were debugging your build and wanted to print the absolute path of each object file in the objects list, you might try the following simple approach, trying to print each Node's abspath attribute: objects = [ Object('prog1.c'), Object('prog2.c', CCFLAGS='-DFOO'), ] Program(objects) for object_file in objects: print object_file.abspath prog1.c prog2.c This does not work as expected because each call to str is operating an embedded list returned by each &Object; call, not on the underlying Nodes within those lists: scons -Q The solution is to use the &Flatten; function so that you can pass each Node to the str separately: objects = [ Object('prog1.c'), Object('prog2.c', CCFLAGS='-DFOO'), ] Program(objects) for object_file in Flatten(objects): print object_file.abspath prog1.c prog2.c % scons -Q /home/me/project/prog1.o /home/me/project/prog2.o cc -o prog1.o -c prog1.c cc -o prog2.o -c -DFOO prog2.c cc -o prog1 prog1.o prog2.o
Finding the Invocation Directory: the &GetLaunchDir; Function If you need to find the directory from which the user invoked the &scons; command, you can use the &GetLaunchDir; function: env = Environment( LAUNCHDIR = GetLaunchDir(), ) env.Command('directory_build_info', '$LAUNCHDIR/build_info' Copy('$TARGET', '$SOURCE')) Because &SCons; is usually invoked from the top-level directory in which the &SConstruct; file lives, the Python os.getcwd() is often equivalent. However, the &SCons; -u, -U and -D command-line options, when invoked from a subdirectory, will cause &SCons; to change to the directory in which the &SConstruct; file is found. When those options are used, &GetLaunchDir; will still return the path to the user's invoking subdirectory, allowing the &SConscript; configuration to still get at configuration (or other) files from the originating directory.
scons-doc-2.3.0/doc/user/file-removal.in0000644000175000017500000001464512114661557020671 0ustar dktrkranzdktrkranz There are two occasions when &SCons; will, by default, remove target files. The first is when &SCons; determines that an target file needs to be rebuilt and removes the existing version of the target before executing The second is when &SCons; is invoked with the -c option to "clean" a tree of its built targets. These behaviours can be suppressed with the &Precious; and &NoClean; functions, respectively.
Preventing target removal during build: the &Precious; Function By default, &SCons; removes targets before building them. Sometimes, however, this is not what you want. For example, you may want to update a library incrementally, not by having it deleted and then rebuilt from all of the constituent object files. In such cases, you can use the &Precious; method to prevent &SCons; from removing the target before it is built: env = Environment(RANLIBCOM='') lib = env.Library('foo', ['f1.c', 'f2.c', 'f3.c']) env.Precious(lib) int f1() { } int f2() { } int f3() { } Although the output doesn't look any different, &SCons; does not, in fact, delete the target library before rebuilding it: scons -Q &SCons; will, however, still delete files marked as &Precious; when the -c option is used.
Preventing target removal during clean: the &NoClean; Function By default, &SCons; removes all built targets when invoked with the -c option to clean a source tree of built targets. Sometimes, however, this is not what you want. For example, you may want to remove only intermediate generated files (such as object files), but leave the final targets (the libraries) untouched. In such cases, you can use the &NoClean; method to prevent &SCons; from removing a target during a clean: env = Environment(RANLIBCOM='') lib = env.Library('foo', ['f1.c', 'f2.c', 'f3.c']) env.NoClean(lib) int f1() { } int f2() { } int f3() { } Notice that the libfoo.a is not listed as a removed file: scons -Q scons -c
Removing additional files during clean: the &Clean; Function There may be additional files that you want removed when the -c option is used, but which &SCons; doesn't know about because they're not normal target files. For example, perhaps a command you invoke creates a log file as part of building the target file you want. You would like the log file cleaned, but you don't want to have to teach SCons that the command "builds" two files. You can use the &Clean; function to arrange for additional files to be removed when the -c option is used. Notice, however, that the &Clean; function takes two arguments, and the second argument is the name of the additional file you want cleaned (foo.log in this example): t = Command('foo.out', 'foo.in', 'build -o $TARGET $SOURCE') Clean(t, 'foo.log') env = DefaultEnvironment() import os env['ENV']['PATH'] = env['ENV']['PATH'] + os.pathsep + os.getcwd() SConscript('S') foo.in foo.log cat $3 > $2 The first argument is the target with which you want the cleaning of this additional file associated. In the above example, we've used the return value from the &Command; function, which represents the foo.out target. Now whenever the foo.out target is cleaned by the -c option, the foo.log file will be removed as well: scons -Q scons -Q -c
scons-doc-2.3.0/doc/user/SCons-win32-install-3.jpg0000644000175000017500000006106112114661557022244 0ustar dktrkranzdktrkranzÿØÿàJFIF,,ÿÛC    $.' ",#(7),01444'9=82<.342ÿÛC  2!!22222222222222222222222222222222222222222222222222ÿÀU"ÿÄ ÿĵ}!1AQa"q2‘¡#B±ÁRÑð$3br‚ %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyzƒ„…†‡ˆ‰Š’“”•–—˜™š¢£¤¥¦§¨©ª²³´µ¶·¸¹ºÂÃÄÅÆÇÈÉÊÒÓÔÕÖרÙÚáâãäåæçèéêñòóôõö÷øùúÿÄ ÿĵw!1AQaq"2B‘¡±Á #3RðbrÑ $4á%ñ&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz‚ƒ„…†‡ˆ‰Š’“”•–—˜™š¢£¤¥¦§¨©ª²³´µ¶·¸¹ºÂÃÄÅÆÇÈÉÊÒÓÔÕÖרÙÚâãäåæçèéêòóôõö÷øùúÿÚ ?·ãM[ÂþÖ Ó‡€ôkÏ2Ö9üáËdcO§ë\ïü, ÿÑ6Ñ?5ÿâ+§ñ΃kñ:#¯”Ž4½Ä–ßhKyÎí²4ÆÝÁdŽFUï¬ê¶¯¬[K§[iö—6²ÙÚ-‚Ê“»gXâ}¤oàbr¹+‚§K“‘9ßïg<¥+»3;þÿ ÿÑ7Ñ?ñßþ"—þß ÿÑ7Ñ?ñßþ"¤Õ4ß Y|8Ô.,êPÿn˜lïˆ&ÚmƒrÈK*¶AQ·q«íð×F_øƒN7þNw¦A y‰¹–åÕ_wËÉŽ1|ÖŸºë½ùyù“ÍS¹›ÿ ç…ÿèœhŸøïÿKÿ ß…ÿèœhŸøïÿUç{ÿ ¿…ÿèœèŸ’ÿñ¿ð›x_þ‰Ö‰ù/ÿ\§ =„<þ÷þaígÜîÿá4ð¿ý­ò_þ"—þ? ÿÑ:Ñ?%ÿâ+…áG±‡ŸÞÿÌ=¬ûÏü&>ÿ¢w¢~KÿÄRx_þ‰Þ‰ù/ÿ\@§ =Œ<þ÷þaígÜí¿á/ð¿ýÍþù_þ"ÿ o…ÿèžhŸ÷ÊÿñÄŠx¥ìaç÷¿ók>çiÿ g…ÿèžhŸ÷Êÿñ£Å~ÿ¢{¢ß+ÿÄW)Âc?½ÿ˜{Y÷;/øJ|/ÿD÷Dÿ¾Wÿˆ¥ÿ„£ÂÿôO´OûåøŠã…8Qìcç÷¿ók>çaÿ ?…ÿèŸhŸ÷Âÿñ¿ð’ø_þ‰ö‰ÿ|/ÿ\€§Š^Æ>{ÿ0ö³îu£Äžÿ¢¢ß ÿÄÒÿÂEáú'ú'ýð¿üMrbœ(ö1óûßù‡µŸs¬ÿ„‡Âÿô hŸ÷Âÿñ4£Äÿ¡Dÿ¾ÿ‰®TS…Æ>{ÿ0ö³îu#^ð¿ý:'ýð¿üM/öï…ÿèAÑ?ïÚÿñ5ËŠx£ÙGÏïæÖ}Θk~ÿ¡Dÿ¿kÿÄÒkÂÿô!hŸ÷íøšæ…8Qì£ç÷¿ók>çIý³áú´Oûö¿üM/öÇ…ÿèCÑ?ïÚÿñ5Î p¥ì£ç÷¿óµŸs¢·…ÿèCÑ?ïÚÿñ4ïí_ ÿЇ¢ߥÿâkñG²ŸÞÿÌ~Ö}΀jžÿ¡Dÿ¿KÿÄÒKÂÿô"hŸ÷éøšÀáG²ŸÞÿÌ=¬û›ßÚ>ÿ¡Dÿ¿KÿÄÒý¿Âÿô"èŸ÷éøšÂñKÙGÏïæ/k>æØ¾ð¿ýº'ýú_þ&—í¾ÿ¡Dÿ¿+ÿÄÖ(§ =”|þ÷þaígÜÙûg…ÿèFÑ?ïÊÿñ4¿jð¿ýú'ýù_þ&±Å8Qì£ç÷¿ók>æÇÚ|/ÿB>‰ÿ~Wÿ‰¥ÿ¡Dÿ¿+þ’)Âe?½ÿ˜{Y÷5DÞÿ¡#Dÿ¿+þ¢_ Ÿù’4Oûò¿áYbž){8ùýïüÃÚϹ¤Âÿô$èŸ÷á”7…ÿèIÑ?ïÂÿ…g p£ÙÇÏïæÖ}Í ø_þ„­þü/øS±áú´Oûð¿áYâž(öqóûßù‡µŸröß ÿЕ¢ß…ÿ ]žÿ¡/Dÿ¿ þHS…ÍyýïüÃÚϹpEáúôOü_ð¥ø_þ„½ÿ×ü* § ^Íyýì=¬û–¼ ÿЙ¢à:ÿ…(¶ð¿ý š'þ¯øUaO{5ç÷°öÓîN-|/ÿBn‰ÿ€ëþ¿cð¿ý º'þ¯øT"œ(ökÏïaí§Ü›ì^ÿ¡;DÿÀeÿ _°ø_þ„íÿ—ü*1N½šóûØ{i÷ýŸáú´Oü_ð¥þÎð¿ý ú'þ/øSE8QȼþöÚ}ÅþÍð¿ý ú'þ/øRÿeø_þ… ÿ—ü(ñG"óûØ{i÷ý•áú4Oü_ð¥O…ÿèPÑ?ðiâœ(ä^{m>äØþÿ¡GDÿÀU§cx_þ…ÿV¤ñK‘w{m>ä#Eð¿ý Z'þ­/ö…ÿèRÑ?ðjqNr.ïïaí§Ü¯ý…áú´OüZ|~ð̲,iá-³û*òMXjÃþ?í¿ëªÿ1IÅ%»ûØÕY·¹WþЧáÿûõøÕ{ÏxZ–çÂþDi5Ä1±,Ä(“Éþ½+ Æ7Úž¯cÿäó\kícL'}¼°w’PYDx8Úà‚OËÈéVÖx.|)¥Ë£y}pÚ´?k{̬±Ï¼o£Î"Úx8Îrxc^M¥ú¿ó:ÜZ[þ_äv~´²²ñGˆ£±°µ²‡É³ÄVÑפ§·SÏSEKàÿùüEÿ\lÿ””TI¶õØá~!hž,ºñu¾£á¿´Ä5½ÚÂÝKûÀ‘÷O¥rø[â4ZŒºŒfý/¥]’\®¢¢W^8-¿$|«Ç°ô¯u¹ÿZ¿õÍ?ôPÖ°ÆNåIè¦ïsÄ…¾"³Ý»ò׊鎢¹@Úüÿ0Á#¢VI_0xx¾§„‡~*ÿ _þLEÿÅS‡ÃÏÿÐ/ÿ&"ÿâ«Ýh§ý¡W²þ¾bú¼O ÿ…{âŸúÿäÄ_üU8|>ñGýÿòb/þ*½ÊŠ?´*ö_×Ì>¯ÇÃÿÐ3ÿ&"ÿâ©ÃÀ'ÿ gþG‹ÿНo¢í ½—õó«Äñ!àÐ3ÿ#ÇÿÅS‡€¼Mÿ@ßüÿ^×EÚ{/ëæW‰â£À~%ÿ oþGÿЧx—þ¿ù?þ*½¢Š__«Ù_0ú¼Oñ'ýò<üU(ð7ˆÿèÿ‘ãÿâ«Ùh£ëõ;/ëæW‰ãƒÀþ#ÿ wþGÿЧx‹þßù?þ*½†Š>¿S²þ¾aõxž><â/úÿähÿøªpðOˆèÿ‘£ÿâ«×¨£ëõ;/ëæW‰äcÁ^!ÿ þFÿŠ¥ ñýÿò4üUzÝ}~§eý|Ãêñ<˜x3_ÿŸüÿN ×ÿçÃÿ#GÿÅW«ÑG×êv_×Ì>¯ʇƒµïùðÿÈÉÿÅRë¿óãÿ‘“ÿНT¢¯Ô쿯˜}^'–k¿óãÿ‘SÿЧëŸóãÿ‘SÿНP¢¯Ô쿯˜}Z'˜ kŸóãÿ‘SüiÃÂzßüùäTÿôÚ)}z§eý|ÃêÐ<ÌxS[ÿŸ/üŠŸãNÖ¿çËÿ"§ø×¥QGתv_×Ì>­ÍÇ…µŸùóÿÈ©þ4áá}gþ|ÿò*z5}z§eý|ÃêÑ<èxcXÿŸ?üŠŸãNÖ?çÓÿ"'ø×¡ÑGתvAõh|<5«ÿϧþDOñ¥Õ¿çÓÿ"'ø× QGתvAõhðæ­ÿ>Ÿù?Æœ<;ªÿϯþD_ñ®òŠ>»S²«@ᇵ_ùõÿÈ‹þ4ááíSþ}ò"ÿw4QõÚ}ZßùÆ»j)}v§dVÅRÿŸoü}Æœ4-Kþ}¿ñõÿ쨣ëµ; ú´»S²«@俱uù÷ÿÇ×üi±¯ÿçßÿ_ñ®²Š>»S²«@åFÿ¿ã]E}v§dVÌ &ûþxãëþ4á¥^ÿÏüyƺZ(úåNÈ>­œ]çüñÿLJøÓ†™yÿ<ñáþ5ÐÑG×*vAõhøÓnÿç—þÅ´IV®úé(¬•Dµå_ùšr7ÕþäCàñxˆùãgü¤¢áù¼Iÿ\¬ÿ”´Q{ê;[BÕÏúÕÿ®iÿ Š†¦¹ÿZ¿õÍ?ôPÖeŒÊˆ]Ø*¨É$àU¿´ì?çöÛþþ¯øÐ2ÕWûNÃþm¿ïêÿÚvóûmÿWühÕWûNÃþm¿ïêÿÚvóûmÿWühÕWûNÃþm¿ïêÿÚvóûmÿWühÕWûNÃþm¿ïêÿÚvóûmÿWühÕWûNÃþm¿ïêÿÚvóûmÿWühÕWûNÃþm¿ïêÿÚvóûmÿWühÕWûNÃþm¿ïêÿÚvóûmÿWühÕWûNÃþm¿ïêÿÚvóûmÿWühÕWûNÃþm¿ïêÿÚvóûmÿWühÕØÝ%d•ãnU”äqÁúƒN Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( ü#ÿ#w‰?땟ò–Š<#ÿ#w‰?땟ò–屫Ÿõ«ÿ\ÓÿA Msþµëšè"¡¨(«©ÿÈ*óþ¸?þ‚kÀÑ¢ü£§¥{æ§ÿ «Ïúàÿú ¯O¸¿A^¦Wå+£ÊÍdãÙ†Åþèü¨Ø¿Ý•:Šö½œ;#Äö“îÆì_îÊ‹ýÑùS¨£Ùòi>ìnÅþèü¨Ø¿Ý•:Š=œ; ö“îÆì_îÊ‹ýÑùS¨£Ùòi>ìnÅþèü¨Ø¿Ý•:Š=œ; ö“îÆì_îÊ‹ýÑùS¨£Ùòi>ìnÅþèü¨Ø¿Ý•:Š=œ; ö“îÆì_îÊ‹ýÑùS¨£Ùòi>ìnÅþèü¨Ø¿Ý•:Š=œ; ö“îÏxѸð¾‡ÿ^Z·Utù4Oúð‹úÕªùJŸ>ºŸÀ½²/5;ÿíI4ý2ÊÚâh`Iæk›–…B»:®Ý±¾Nc|çã®N5ëîÓS¶ÖæÔ´Ø,î~ÑmGspÐlòÚFGÝŸ4ñ½óÄž IîïbºŽ$³Ýæý¢áDŠPÈIQ•Ú¾v† :îáÿð‘X=ݰ†âÚ[)­..êN¦5XZ5nGýáÉÏOá…sá-NãW›Pk‹7s @ÜÝ~ÄÀólVkGË §æäU«_êZ¥¶¥r,lç‰ep¶û¥_4´2ù ç Bs¤|›pÈ4õíz-iï<$7WbÝÝHJ”w26…BeŽF'¶ ‰®Z£j­y$6vú}ÊÀóÍ(T;£Á$à/2…Ç·¾*=GL—UþÈ7Q[m‚v’îc"2µ¼±²Œ¨Ü3 êF~•„þ¿Xn7^}©×V7ÖàÝKníÙÄ L™ueÅóÚ3÷Ž:¥Ô¬ÖöÙ•–7V©d;c#žŒx_SÒ³×Å:cxfÛÄ>n4ùüŸœ²þïÌuOœçio›ž0}+" ]Åsc:hÅ«$†4Ž“3NòIçËJP6èØ‘ûÂÍ…E4+¨üc¤ !7gòØ’_³ÈŒ™8ÊïŒðÛwnÇ É©XK¨K§Ç{l÷±.ù-–U2"ñÉ\ä˜~cÖ©ZøŠÂã\¾Ñ丶†öÞqp4ëæL¦$“pN¸ùˆï÷IúRµÐïâÔ-RCmö+Kû‹øæYÉ#Kç|…6á@óÏ͸ç`ànùY>‰©Í©ßG¶Ìi÷ZµÿŸç·š¾RÁòy{0r`Æwð88ÁÚXÓ%ûg—¨Ù¿Ø³ö­³©ò1œïçåÆÖ뎇ҡƒ_Ó.äŒÚjztöï¹L‘Ý«û£P  ƒÌŠ9`îã‘_ ϤiQ¼«¼éÑÀH·Wo?—42Ý`ˆŒ*E|g9Âa™‡uÝÜjW–vÐAqv­çó’0Ö$ü¯¶µ”a•y#ªÔÚO­ØE ÜëPΗvPA$æKgYª[iü¤uëPXxŠÂúú{U¸¶Vˆí±:“t¾DsAÜ(éž{Ô3è2ÝYxšÒIÒ5ÕÙ„n ±ZÚ8²G‚„ã=1Ï¥SáÛ©¿´n$ŽÎ+»ÝFÊíŠ1l$>A([h'96ñ˜tÉÀí®¥a{=Ä—¶×[6ÉãŠUv‰²Fòœƒ×ÐÔ)¬Ø) qcÍ9…QnTîbãæ&6q÷•€ÎÜÖ/…ü/>‰<pŒ-->ÉÆúyÚUÊe‚9Ù>X%wP~j¿ð†ÝÒK©,–umUÝÈä¨9㸬˟_Ïsvè,R1:\Û»æu&#¡òCÕ_ip͉6©Ü­Ÿ¯xKYÕçœyиoµlž[ù€ÄK'Ù˜Óo˜ ¸9! êÄPGiâ+ cŒÝÜ[YÍ5Üö°E,êfŠf‹å$€p:nš-|Eavft¸¶Žv¹K:uW p¬Jóòàî$‘€ÉÇÍÇ9«xKY»Ò¯l¢šRçí{ßÍl™f•ÑŸ;+"ÀØqÆ¬ÜøNýî#hå¶+m=ÅÄ[™‘žî ¥VàíБ»­ƒ’ ¡:ö޶ixukjêγ›”ØÊ)!³‚2©÷ w«Ww–¶¯uys µºc|³8D\œ “Àäø×=aáÛ¡âe×/ã³·žæ(ØÉ以¼k±ŠŒü6NøÁ4ø´;ûOøvM´·º:ÆLo#$r°¡a¼)*>rÀí9ÀÈу\µºÖÆ›o$2fÉ/D‚P|Äv*¥ûËòœ¶@N»¸²u+{´kÛ`ÖjåL«˜À¿?(À'žÕ“áý ëJÔ/n®$…¾Õ%c$ìs=Ä̹ eGž ÚNJ«sáËùînÝŠF'K›`7|γ¤Ät>Hc«í.±&Õ;•€6Ÿ^ÑâÓâÔ$Õ¬RÊVÙË\ Ûžgü§ò>•1Ô¬ñ,Íí°ºvdX «½˜(bç$…ecìAïX§NÖP‡XŽÓKûnÙ£’ÙgtB¯äüÆ_,—aä÷†ø2Ô´ïK¦érÚ$–ÒLnô÷J³Ål¶à†ààæ) nëÉ ®ÚòÖóÍû-Ì3ù2eòœ6ÇU±Ñ†yšÈÕüK‘-úMµ‚ÚA,’„BÓÊñ.âGÊ  %¹àž8Á4=2ÿLy±XùlËù,À¤J¹\…Üß,D‘fѵC5M ê÷T¸ºŽHBIý€Äçýåå~ÝÕ€ýqÖ€6§¼µµÏÚ.a‡¼ÇÌp¸EÆæçøFFOA‘P>±¦GuqjúšÜ[Ffž#:‡‰³ åWž9‹â]9õ}NÒÆ2™­§µ»ÀÆ4´•~r¯Âù»¢@É 1bŒo¼'¨ÚK¥I-´V^}åÄwJÌò3\,ë´Ç€_´wœì ß(\&‰§xˆfEWhà ʤ *ØúJu+Eu½¶eeÕ„ªAYØÈç£Ôô¬ý*ËS]nûSÔ…œh¶‚á¶v/ËiIË0³æœ¸Ç›"ÏÁrÛ%‚Çe‚tIÍ$xJ5¸/ÞÉì ·q¸`¤‹XÓ&’xâÔlä{y•'Rcvmª¬3ÃàÉkØ£¼†ÔMmç;ѼÁ\)W ªàî$ÆÜq±ÏËŠç-<"Ñêî×pÃqgö››€ÒÞO ;ÌÊ}˜þé0%e-ódò‚ùX.üu{¥­¬×ûîIR[©˜ÊþGÙ¦·‹’-‰Ø÷”‚3Šéí52þ4’ÏQ³¹G êáœ.â ƒËmã®jx/-n±ö{˜fÌi0òÜ6Q³µ¸þƒƒÐà×#ÿ…Üñ¼å¾ÅxdŒ+Ç©Ü]: Y#vÊ2$ò”@%ÀÚÑ4/ì›ëù¼ÌÅ&Èm#Kº®01µå‘@XÇPrhëÚ:¥Û¶­bÍ‚\±¹L@ÄíùùNA÷©¯õ+ .ŸP½¶´…›`’âUK`œd‘Îü«•O êm“o+Ù¢iÁo ¤¬Æá{yØm[m·áAa—ûÃ;ºÿö¥ž§§Çm<ÐA5¹†âf‰JÈѶíÁ_bçvr1‚%¶¹k6«w§K$0\EsäCÊ7ψc•Н^œž™ïSE¬i“I½£Å§Å¨I«X¥”­²;–¹A·<Î ùOä})é¬i’][Ú¦£f×1‰ ˆN¥åB £9eÀ'#Ž s”7zˆ§Ö¢‡íos$¢8†+U,ÍRm`Öü0Cg<Ut?^Úŧ}ª$#mœÓ‡¿™D2CK³ÈCåÈs!ËpHá‚€ÀõQ@Š( ü#ÿ#w‰?땟ò–Š<#ÿ#w‰?땟ò–屫Ÿõ«ÿ\ÓÿA Msþµëšè"¡¨(«©ÿÈ*óþ¸?þ‚kÁî/ÐW½êò ¼ÿ®ÿ šðDû‹ôëe_'7ø#ê:Š(¯hð‚Š( Š( Š( Š( Š( Š( Š( Š( yÑÿäXÑ?ëÂ/ëVª®ÿ"Ɖÿ^Zµ_%WãgØRø W?âOmamö¤Ž6žécóZH£ (ÎÙ*yà‘ÐVf«áý7YÝöئ;ã0ÉäÜÉšœü¯±†õå° nlu9‚Ì[¯Ogcu}.“‹X伂ûHÝ,–âfé·åB°·ÌNCq´˜ëI¯Eom¯\ψt†`ûf‘D 1 `áñŒöëÏ\hÑ‹XÖÀCÄ2]@ó+ʉ,…÷±PêND²qœ ÞÀSôíÃLÐ`ÑaÊ(<ƒˆ¤H¸Ãn`–É'ŽI>´…{¬êrk:^“5´6÷&ö »fàhî|û··$®0@ N±§¨kÿaÔ'µû6ÿ+ì_7™Œý¢v‡¦?‡nï|㎵=·‡ôÛY¢™"šIâ“ÍI§¹’iØèçbJ#áIÀ,H9¡´?Ä1j·O ¿eã³A ^fÝìX“–;Àf9ÍdGã |» ¥Ó‘aÔ–¬Ê\cÍ dÈ6 ŒÂ °8a¸`v/Ë?Š_FŽÍ6ÄÒ‰'iˆ!R;wÈ]¼’nÆF““Ò¦O 館-œ«®ÅVžF®C[÷ R<½¸(¸û«‰4ÿiZ]ѹ³µòç;÷9‘˜±qrrNY¼¨É'’A=I$¯Šu¥Ðl ½kwœ«LÊ‹9Œe-¦“œgp"20F!º¨¨.+{6Êæ-?nž>Éo3ÉvÏ2I:DPTùŠ<èòìÀý・ZßÃ:]´æuŽæIËFÂYï&•Ô¡m¸gbT|îÈ$Q†t‹g¶0Û:-ºÆ©žO,ù`fMÛ]”*á˜WŸ”`Ð Ú‰'ÕþÀntß±BËí–ÃÏ«å†Ý€ó*•Á9$!ùj;ï˧Dͦ>ÛV# !gvwXв$+3eJy™ à€ë²µít›?±}ž Ÿb¶6¶ÿ;‘™^O?êÓ““ǹ¨_ÃÚSÉq#Zå§ÎïÞ7ÈK&1ŸÝ±p”ÚKÇ rš—‹u‡´š[{W±h´½FiduâXZ9#óbVp ›pÊ£;ºíÔÄ7ÿÛ“éVÚJKzÌæB÷íä£$VÌy(H_ßàm^J‚Tnbºñø{JŽ5Ckæc~æšF•å¡XHÌI‘H 0Ä‘?º¸}®‰§Ù^}²_í;J™^gv`V%9,NIÅÉçåÏRr‹Œ%¼‚ë9Nií •ç¸1Ê­p"+„Á€YÓ9aÈaد£ê“êjóì¾Ìa“fß49ì¸À*à`2¤2•w5þ C¨Z˜nRÛM¶h-a+7“·fóæìs”Q¹£-´œ€Ã^ ÆÓÐMn¾b;åo™S;"ÉÉX”œ„Rª9Ã0 Þj׉ªIca§¥ÓA\\o¸òÛc³…¤3~íøbƒîüÜ’9ý/ÄWÞÜX¤u-ܱB×l±‘ö›ÒðÛ¥»B’rŠpÓßèš~¥:Íu ³mØá&tYS$ì‘T"òß+‚>fãæ9¼3¤1•…³¤’¶ó$sÈŽ­¾Gʰ`Tîš_ºGGN(2jñ<`ÖWï÷–ÁmÚÝFD—>c«t$ª£Ø¡X¡âS,ž"Ò­…¾©u Z]HÐi÷¦Ý‹+À1ó#Șc'ït­uðö”ËÚàKFçÌmÇc³«nÎw‡v}ùݸç9Á§ê:-–©<Üý¥f]#’ÞêX+,3.A(½}(ž›Åòi>Ò®æ ©³X%Õì°,»¶lÌ ±aÈó cå<Œ²kzæ£?†µÛ›[O*Ò+kØ¢»ŽçG$BE,SjïC‚¬Ç•8;u¯ü3¤j0,[Îc¶žKuh€ FÂ6]Ê8 n8ÆMøgH¹{“5³ºÜ,ŠñäòǘvTݵƒ6Y@c¹¹ùŽ@-iz‡ö«],^\&FH²ß9 v’Ë‘·O#vÖÊ®eͰռSwgs=âÛÛY[Ë[]Ëo†‘æ I”·§\ãc':ÃO¶[Ç»Et™Ù]öHÊ®ÁJ‚Êí8äsµ¸¸†ÿE²ÔgYæûLsØd¶º–Ý™A$1²î“ŒçŽ1“@Âk²é> Ô-f77Ó ü›w–àªî1ØF* Ÿq`¿/ϵNâ «ÝsQ²×ôÄ»´ò§šÚæ%·KÖòÈd¶±“ 7•Ë(9%T1e¬|1£ov[BÊW÷lÈb 6€FÂà® ”`äÔø{J‡þ]|Òc–'iäiŒ‹&ÍáË’_"46pLj É>eæÍ—¦)ü™Z7eK Ô†_™ðFqŽ„ƒ¡vÞ±Ž?:þYd&8.fžiˆ)²2†#–øÜÕ­;*ÒîÆ+I„ÌãËO •È6ðØ$œH9ɪ§Ã:Y#Ü«+3yéy2ÎÄ€éCo`B Áb>EþèÀe—ŒÚü ¨tÇ]7ϵ„Ï$À97 G„äƒ2†ÉAc•>*ºB̺WúÄqIfb˜Èíæ²,k"ܬ|Å8A'FêB†Ô¶ðö•icö8-v[ù˳ÌcóB#œ“žQývóœœÆžÒ#IP[9W]Š­<Œ!\†,·î@*¤y{pQq÷W€fiZÝÞ«â+5­Í—‚H]dT‘•튺‰€FJŒÀdrM?^ÕfͪZÛ\ÞIwä™n Iöx.<¿˜¬m†Õ@ÁȖϵg¢iöÇ<¿žŠëæÉ3Èì¦íÌÄ–?»Œd’@PÉü?¦Üd˜¦‰Œ.û{™!`_Àd`B±ŠŽ |ÄÍR¾ñTphf§kg4çSòþÍ W$nŒÉóÕØaTýÕnqÐdˆ"ñs½Æk.—4Z†´SC!Yg 2‚ªˆ€°RညڹÒlnlb³h<¸!Ç’ v„ñ‚œ¿):*ðö•ámqŸ'$ÈıŠC*s’Þc3ybNìÐ_‚õ{­bÖîYe¢ý›ìÊó$@ö°ÈQ˜Œ¶ çy$’Ç8ÀÎ.“â}^+Q©ÞÛý¥A³¿»"ãj"ærî«·™]B ùH.0¤öšv“c¤Æ#±ƒÉO.8¶‡bEÚ¤‚ym  ÝHU£iKkul-qÕ·Ù%A#`×!?*1À€Ö*’ÿ^{Òî~Ê'–ÝnDràÛöˆ7ý¶ØZÜ|ì7Ä7áx<¬~F>€9‹O^Ýim¨iúL2Ë<‘³M¨8y$’ÚU"[ŽAûª68ˆŸVñ¸Ò®ïì?h·Š9ŒSDeÃÉO##1ˆF?Õº®ÄÊýíº×^Òîïñ㹎åÙ™¥·¼š%–5<£Š>:|¹ëšeׄôkÙ¤’âÞfy›¢R¬Y‘ˆÃl Áß$ å‰êsF€fjúΧ*ˆ`¶†¶Ôlmï&[¶®ò@̨|èVERIRrß/¯[Y—^Óo/–îh¦óD‘ÌV;™7t «:+vW–áTt´!‰`‚8P¹XÔ(.娀1ËI>ääÐ袊QEGáù¼Iÿ\¬ÿ”´Qáù¼Iÿ\¬ÿ”´U­ˆ-\ÿ­_úæŸú¨jkŸõ«ÿ\ÓÿA AE]OþAWŸõÁÿô^Ÿq~‚½ïSÿUçýpýׂ'Ü_ ¯[*ø¤y9¿ÁQÔQE{G„QEQEQEQEQEQEQEQE{Îÿ"Ɖÿ^ZµUtù4Oúð‹úÕªù*¿>—À½Š(¨4 (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€#ðüÞ$ÿ®VÊZ(ðüÞ$ÿ®VÊZ*ÖĮ֯ýsOýT55ÏúÕÿ®iÿ Š† ¢®§ÿ «Ïúàÿú ¯O¸¿A^÷©ÿÈ*óþ¸?þ‚kÁî/ÐW­•|R<œßà¨ê(¢½£Â (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€=çGÿ‘cDÿ¯¿­Zªº?ü‹'ýxEýjÕ|•_ŸaKà^ETQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@øGþFï×+?å-xGþFï×+?å-kb W?ëWþ¹§þ‚*šçýjÿ×4ÿÐECPQWSÿUçýpýׂ'Ü_ ¯{Ôÿäyÿ\ÿA5à‰÷è+Öʾ)NoðGÔuQ^ÑáQ@Q@Q@Q@Q@Q@Q@Q@ó£ÿȱ¢ׄ_Ö­U]þEþ¼"þµj¾J¯ÆÏ°¥ð/@¢Š* Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( ü#ÿ#w‰?땟ò–Š<#ÿ#w‰?땟ò–屫Ÿõ«ÿ\ÓÿA Msþµëšè"¡¨(«©ÿÈ*óþ¸?þ‚kÁî/ÐW½êò ¼ÿ®ÿ šðDû‹ôëe_'7ø#ê:Š(¯hð‚Š( Š( Š( Š( Š( Š( Š( Š( yÑÿäXÑ?ëÂ/ëVª®ÿ"Ɖÿ^Zµ_%WãgØRø QEEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEP~ÿ‘»ÄŸõÊÏùKEÿ‘»ÄŸõÊÏùKEZØ‚ÕÏúÕÿ®iÿ Š†¦¹ÿZ¿õÍ?ôPÔUÔÿäyÿ\ÿA5à‰÷è+Þõ?ù^×ÿÐMx"}Åú õ²¯ŠG“›üõEW´xAEPEPEPEPEPEPEPEP¼èÿò,hŸõáõ«UWGÿ‘cDÿ¯¿­Z¯’«ñ³ì)| Ð(¢Šƒ@¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(?ÿÈÝâOúågü¥¢ÿÈÝâOúågü¥¢­lAjçýjÿ×4ÿÐECS\ÿ­_úæŸú¨j *êò ¼ÿ®ÿ šðDû‹ôïzŸü‚¯?ëƒÿè&¼>âýzÙWÅ#ÉÍþúŽ¢Š+Ú< ªZ«¼zt†9 lJ®ñŒ€XרÕÚ£«iÌsÞGÎqükî?e]µJMvfØtX§Ý~g jÞð‡.­,µŸë]Ï8Q6A¨Ïàg=ûŸJÌ›ádšzx¯5Û¶[ !wdÑÜ™NHÞ ³Ozë>$øÿFðÆ¹§Zßøb×VŸì©*Ï+.Pn©þ.¼×œi_¼A'‹®5k¸þӥ̋߻X·a@'¿=úóž+æÔæölúw ktŠþ ÐÇ‹ôjwú¥Ý³évªñ vURpI$dg¡ôë× ®×AðWÃý{Kº¾´ñ^±$v1¬—dϳËRItá¿_J©ñG\·ð&Ÿÿ¯†¬b±]J/´ÝOeàŸCøt¬ÿ…ÉkàE.cq` RØ#(ÄÇ_þ¿jn¥G«l:kD°øGÃ:͈ïtê×úe‚Mw ûÌs’ z½kÁz xËBñ£w©ÜÛϦګ@°È¨…¹å²G\~¿A]À»ø´ÏøÂöx„ðÛÀ’¼%€bG'ý?ÕÔx;Æ:W‰¼â¦èÚ/Ù­Aq¨”ÆN ž6ŸNÜÒö³µ®ÃÙS½ùQÍøKÃXÙøÃź„½Â Z;y‰@¬ËŽpxëÉ÷=¨O„wQÍâ($Ö/®%µ´[9`˜6v†ºü¸ôç­?ã¬ë~1Ñ®ô½:êöÜYľl ]AÝêãïN½OZõ GU“L¸ñí¼¨g´Ñ¡”)p@a¼ò<Ïè>¦i>ì~ΑàÞðü¾&Ñìu]ŽŽŠ(¯lðŠ( yÑÿäXÑ?ëÂ/ëVª®ÿ"Ɖÿ^Zµ_%WãgØRø QEEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEP~ÿ‘»ÄŸõÊÏùKEÿ‘»ÄŸõÊÏùKEZØ‚ÕÏúÕÿ®iÿ Š†¦¹ÿZ¿õÍ?ôPÔUÔÿäyÿ\ÿA5à‰÷è+Þõ?ù^×ÿÐMx"}Åú õ²¯ŠG“›üõEW´xARèþÖüsw¨ØiRÙ[Ågå%Èl’#áÛÞ¢®Çán­o£/¯nÈC ¦.ð§,>?ÕØNQ£î½Ý¿3ÐËaÖ÷–ÊÿŠ9¿ø/ƶæÓÄzõô²ZyhæÕ·4Q¨8Àõëø÷«6 <_âM;–}7NŽà‡H§‘–ePÊTþ¿ç5KZøËö½éº‰Š“²‹™!}Å1éŒúWkâ¿ ]üL¿ÑüEáírÑ,Þ8È–fV ¬3Æzóìr±¯5g¸ÅèÏrTa9)ÉjŽUð׎¯¥øªóÃ÷©id-ÞÃ$€3Œ`…ü›ž+Ù›Åz»â­kÃPjhn®tøìÔ¶ï-¤·wà‘»¶>¦£Ön¼/á/kZ¥Å½­í¢Ù$vy%ç@Aá_¯>ÇŽMO;ååè_"æçë±ç~øqãÄðåÃXj–º]¾­É­î$Úì™8Ç }ÿ:Îð׃<d¾&Ñ´¹’Ô¤*·P³ö…'‡Ügž=+Ý¢¿²ñ=–ªi–Z^¥EÛ5ÔûdÈ AüÀõ®[KñýÆ¢þ5¾Ùocw¥Ù,QªÜ Tºoäe€#'Ð}{T”r7–¿¾øJ¶Ö!žÂßïE%hTí<ägñøûÔ^ð?ÄmGJ¾¿Ü_Û‰þ“ܸ‘Ô‘‚F8ÈcøqÞ¸=7ž,†-NîBå­¯›ý9·n-È$œŽ;ŽÕôÄWö^'²ÓµM2ËKÔ`1®Ù®§ØèC&@' ÌZn-n„¤žÌð xkÇv:ö©á 9ÚÏíQ„½vÿUåö}Þ‡ #®k°Ð¾·†¼-âæ×­loû_’¯+w (¢·9yÑÿäXÑ?ëÂ/ëVª®ÿ"Ɖÿ^Zµ_%WãgØRø QEEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEP~ÿ‘»ÄŸõÊÏùKEÿ‘»ÄŸõÊÏùKEZØ‚ÕÏúÕÿ®iÿ Š†¦¹ÿZ¿õÍ?ôPÔUÔÿäyÿ\ÿA5à‰÷è+Þõ?ù^×ÿÐMx"}Åú õ²¯ŠG“›üõEW´xAYzž‹ ÞbJ`”®Ê2z‘íþqZ”TT§‘嚺4§RtåÍfRÒôåÓ- C!,X±ç§OÀUŸoœ½­ä–Ñ“Ÿ-A nF:ʶ訖”¢ Öˆ¸âjÆnqz³ŸƒÂÑÄ$g»‘¥#÷n«·aõëÏÿ¯ðˆøQÊ…:ƒ ¸Ïýõ]-ŸÔ¨/æj±øæüŒ<7†e¾šÀ/'ž9ê:à€xQÔ0ƒÃ ˆºý~jéh¡à¨7~_Ì?•¹¿"–—§.™h` bňÇ=:~¨Üø{|åío$¶Œœùj ûr1Ð~U·Ei,=)EA­”q5c78½YÏÁáhâ3ÝÈÒ‘û·UÛ°úõçÿ×ø4øfvR­ªHTŒPã·û^Ãò®ŠŠÏêT?—ó5XüGó~E-/N]2ÐÀ$2Å‹Žztü]¢Šé„T"£‘Ë9Êrr–ì(¢Šdžó£ÿȱ¢ׄ_Ö­U]þEþ¼"þµj¾J¯ÆÏ°¥ð/@¢Š* Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( ü#ÿ#w‰?땟ò–Š<#ÿ#w‰?땟ò–屫Ÿõ«ÿ\ÓÿA Msþµëšè"¡¨(«©ÿÈ*óþ¸?þ‚kÁî/ÐW½êò ¼ÿ®ÿ šðDû‹ôëe_'7ø#ê:Š(¯hð‚Š( Š( Š( Š( Š( Š( Š( Š( yÑÿäXÑ?ëÂ/ëVª®ÿ"Ɖÿ^Zµ_%WãgØRø QEEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEP~ÿ‘»ÄŸõÊÏùKEÿ‘»ÄŸõÊÏùKEZØ‚ÕÏúÕÿ®iÿ Š†¦¹ÿZ¿õÍ?ôPÔUÔÿäyÿ\ÿA5à‰÷è+Þõ?ù^×ÿÐMx"}Åú õ²¯ŠG“›üõEW´xAEPEPEPEPEPEPEPEP¼èÿò,hŸõáõ«UWGÿ‘cDÿ¯¿­Z¯’«ñ³ì)| Ð(¢Šƒ@¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(?ÿÈÝâOúågü¥¢ÿÈÝâOúågü¥¢­lAjçýjÿ×4ÿÐECS\ÿ­_úæŸú¨j *êò ¼ÿ®ÿ šðDû‹ôïzŸü‚¯?ëƒÿè&¼>âýzÙWÅ#ÉÍþúŽ¢Š+Ú< ¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(Þtù4Oúð‹úÕª«£ÿȱ¢ׄ_Ö­WÉUøÙö¾èQEA QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE„änñ'ýr³þRÑG„änñ'ýr³þRÑV¶ µsþµëšè"¡©®Ö¯ýsOýT5u?ù^×ÿÐMx"}Åú ÷½OþAWŸõÁÿô^Ÿq~‚½l«â‘äæÿ}GQEíQEQEQEQEQEQEQEQEï:?ü‹'ýxEýjÕUÑÿäXÑ?ëÂ/ëV«äªülû _ô (¢ Ð(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢ŠÅºñv­¡46ö6ÖÖ²yÏ‘†šYcù@¹DçŒd’qÑçÄÚBÀ’½Ë ,ÊÉ$+Å´Í"Ý€ÊK8 )' 2ÍC@ûv¡=×Úvy¿bù|¼ãìó´Ýsü[¶ûc<ô¨nü7<×w7j^Ažõn¿Ô1â(ãÊ6AˆÎ Ê‘#+#ƒ@‰­¼Io-„÷rÛ^(Šök2°ÚË9&7eÜ!%H\ä vç"©Zx¡î|Euh<™tø£šXå·F‘ÝR+GR»IÝŸ´?AÎåï„~щg³š$¹žt‚þÏíþùüÆ,›×.®[kq…r¸$–ªVžû-ºÅý§¿m°ƒ•ñªaÆï™Ù>eãrÈW#&€h^x¾Ê7XâwZÒêwžkiI¶hDg÷±`0dÝÎÜ }õ«³ø›H¶{‘5Ë¢Û¬Œò˜$òÏ– uWÛµÙB¶UIaµ¸ùN0¢ð—gÝÙÀ÷‘ÜÂËicåCM HvǼáƒBœàå†9ZÔ<-ý¥Í‹j(–L×S@¢Ü™#–u•X³oÃ(óä!B©û¿7&€néúµŽ©æ}Ž3ËÁ9F]Êsµ× nFÁÃŒ«`àœÏÆ:$¶kYîD%Qãße2´Ê쪦5)™çAòƒËž¢´"Óü½nëRósçÛC—·îùm+g9ç>oLq~9'ÂZŒš6Šºµä)q§Û[GPÁ,$Jêç{lÀªv–89t'Ä:RÝKÝy~^ýÓI$$ %À”€ŒÊ²6¶~éÄx¯I–é-CÞ%ÃÉK¶Æä¸r§ €íÄRÝÓ’+?Pð›\Ú\ÛMróiÁ®®"¶‚ ·IÖPãÌfØGï¤Ú6®>\±ÁÌV¬ê ]kXÙnÐ4^\BFp‘Ü©ÈY¤¤‚ìü¤m´ž&Ò$I\\¸T]êÍŠ&\…,¯ï, y{²]q÷—2'ˆt§’Þ5ºËO¿»o–*‡»bà ´–G ŠÅÓ|—k4í§F>ÍöhÞ=20ò&W"á‰&\…±°Ìx%JèG Þ$úd­«;µ¢ívh²ì3’ªää+p?˜HD9<µ «<ÌÂÈ¡€t(ÀžT€Aö#"ŸL„J°F'ty‚€ìˆUY±É“žÙ?SO aEPEPEPEPEPEPEPEPEP~ÿ‘»ÄŸõÊÏùKEÿ‘»ÄŸõÊÏùKEZØ‚ÕÏúÕÿ®iÿ Š†¦¹ÿZ¿õÍ?ôPÔUÔÿäyÿ\ÿA5à‰÷è+Þõ?ù^×ÿÐMx"}Åú õ²¯ŠG“›üõEW´xAEPEPEPEPEPEPEPEP¼èÿò,hŸõáõ«UWGÿ‘cDÿ¯¿­Z¯’«ñ³ì)| Ð(¢Šƒ@¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(?ÿÈÝâOúågü¥¢ÿÈÝâOúågü¥¢­lAjçýjÿ×4ÿÐECS\ÿ­_úæŸú¨j *êò ¼ÿ®ÿ šðDû‹ôïzŸü‚¯?ëƒÿè&¼>âýzÙWÅ#ÉÍþúŽ¢Š+Ú< ¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(Þtù4Oúð‹úÕª«£ÿȱ¢ׄ_Ö­WÉUøÙö¾èB8âêðÉ$ÿ$¡T,Π ŠzRjýUµÿ›ïúî?ôZTö|?ß¹ÿÀ™?øª?³áþýÏþÉÿÅUª(¯ö|?ß¹ÿÀ™?øª?³áþýÏþÉÿÅUª(¯ö|?ß¹ÿÀ™?øª?³áþýÏþÉÿÅUª(¯ö|?ß¹ÿÀ™?øª?³áþýÏþÉÿÅUª(¯ö|?ß¹ÿÀ™?øª?³áþýÏþÉÿÅUª(¯ö|?ß¹ÿÀ™?øª?³áþýÏþÉÿÅUª(¯ö|?ß¹ÿÀ™?øª?³áþýÏþÉÿÅUª(¯ö|?ß¹ÿÀ™?øª?³áþýÏþÉÿÅUª(¯ö|?ß¹ÿÀ™?øª?³áþýÏþÉÿÅUª(¯ö|?ß¹ÿÀ™?øª?³áþýÏþÉÿÅUª(¯ö|?ß¹ÿÀ™?øª?³áþýÏþÉÿÅUª(¯ö|?ß¹ÿÀ™?øª?³áþýÏþÉÿÅUª(¯ö|?ß¹ÿÀ™?øª?³áþýÏþÉÿÅUª(¯ö|?ß¹ÿÀ™?øª?³áþýÏþÉÿÅUª(¯ö|?ß¹ÿÀ™?øª?³áþýÏþÉÿÅUª(¯ö|?ß¹ÿÀ™?øª?³áþýÏþÉÿÅUª(¯ö|?ß¹ÿÀ™?øª?³áþýÏþÉÿÅUª(¯ö|?ß¹ÿÀ™?øª?³áþýÏþÉÿÅUª(¯ö|?ß¹ÿÀ™?øª?³áþýÏþÉÿÅUª(¯ö|?ß¹ÿÀ™?øª?³áþýÏþÉÿÅUª(¯ö|?ß¹ÿÀ™?øª?³áþýÏþÉÿÅUª(¯ö|?ß¹ÿÀ™?øª?³áþýÏþÉÿÅUª(¯ö|?ß¹ÿÀ™?øªºbHmíÕ7ca9f,OÌÝIäÓ*i¿Õ[ÿ×3ÿ¡5!¢Š(„änñ'ýr³þRÑG„änñ'ýr³þRÑV¶ µsþµëšè"¡©®Ö¯ýsOýT5ïãytë˜ãFwxUTd’AÀ¼¥~ø° F—§üôOþ*½zŠèÃâgA·¹Ï‰ÂÃ’“Øò?øWž+ÿ 4¿÷Úð¯gsÒŠåIGgkpÒݸ·”£Ì °C†qø‚? ’ŠE}’çþ}åÿ¾ d¹ÿŸyïƒPÑ@}’çþ}åÿ¾ d¹ÿŸyïƒPÑ@}’çþ}åÿ¾ d¹ÿŸyïƒPÑ@}’çþ}åÿ¾ d¹ÿŸyïƒPÑ@}’çþ}åÿ¾ d¹ÿŸyïƒPÑ@}’çþ}åÿ¾ d¹ÿŸyïƒPÑ@}’çþ}åÿ¾ d¹ÿŸyïƒPÑ@}’çþ}åÿ¾ d¹ÿŸyïƒPÑ@}’çþ}åÿ¾ d¹ÿŸyïƒPÑ@}’çþ}åÿ¾ d¹ÿŸyïƒPÑ@}’çþ}åÿ¾ d¹ÿŸyïƒPÑ@}’çþ}åÿ¾ d¹ÿŸyïƒPÑ@}’çþ}åÿ¾ d¹ÿŸyïƒPÑ@}’çþ}åÿ¾ d¹ÿŸyïƒPÑ@}’çþ}åÿ¾ d¹ÿŸyïƒPÑ@}’çþ}åÿ¾ d¹ÿŸyïƒPÑ@}’çþ}åÿ¾ d¹ÿŸyïƒPÑ@}’çþ}åÿ¾ d¹ÿŸyïƒPÑ@}’çþ}åÿ¾ d¹ÿŸyïƒPÑ@}’çþ}åÿ¾ d¹ÿŸyïƒPÑ@}’çþ}åÿ¾ d¹ÿŸyïƒPÑ@}’çþ}åÿ¾ d¹ÿŸyïƒPÑ@}’çþ}åÿ¾ -Ê2%ºº•a Œ¼ÕQE„änñ'ýr³þRÑG„änñ'ýr³þRÑV¶ µsþµëšè"¡©®Ö¯ýsOýUy%ŽÌ’º¢¬Ç~5¢ªÿiØÏí·ýý_ñ£ûNÃþm¿ïêÿ-QU´ì?çöÛþþ¯øÑý§aÿ?¶ß÷õÆ€-QU´ì?çöÛþþ¯øÑý§aÿ?¶ß÷õÆ€-QU´ì?çöÛþþ¯øÑý§aÿ?¶ß÷õÆ€-QU´ì?çöÛþþ¯øÑý§aÿ?¶ß÷õÆ€-QU´ì?çöÛþþ¯øÑý§aÿ?¶ß÷õÆ€-QU´ì?çöÛþþ¯øÑý§aÿ?¶ß÷õÆ€-QU´ì?çöÛþþ¯øÑý§aÿ?¶ß÷õÆ€-QU´ì?çöÛþþ¯øÑý§aÿ?¶ß÷õÆ€-QU´ì?çöÛþþ¯øÑý§aÿ?¶ß÷õÆ€-QU´ì?çöÛþþ¯øÑý§aÿ?¶ß÷õÆ€-QU´ì?çöÛþþ¯øÑý§aÿ?¶ß÷õÆ€-QU´ì?çöÛþþ¯øÑý§aÿ?¶ß÷õÆ€-QU´ì?çöÛþþ¯øÑý§aÿ?¶ß÷õÆ€-QU´ì?çöÛþþ¯øÑý§aÿ?¶ß÷õÆ€-QU´ì?çöÛþþ¯øÑý§aÿ?¶ß÷õÆ€-QU´ì?çöÛþþ¯øÑý§aÿ?¶ß÷õÆ€-QU´ì?çöÛþþ¯øÑý§aÿ?¶ß÷õÆ€-QU´ì?çöÛþþ¯øÑý§aÿ?¶ß÷õÆ€-QU´ì?çöÛþþ¯øÑý§aÿ?¶ß÷õÆ€-QU´ì?çöÛþþ¯øÑý§aÿ?¶ß÷õÆ€-QU´ì?çöÛþþ¯øÑý§aÿ?¶ß÷õÆ€-QU´ì?çöÛþþ¯øÑý§aÿ?¶ß÷õÆ€-QU´ì?çöÛþþ¯øÑý§aÿ?¶ß÷õÆ€-QU´ì?çöÛþþ¯øÑý§aÿ?¶ß÷õÆ€-QU´ì?çöÛþþ¯øÑý§aÿ?¶ß÷õÆ€-QU´ì?çöÛþþ¯øÑý§aÿ?¶ß÷õÆ€-QU´ì?çöÛþþ¯øÑý§aÿ?¶ß÷õÆ€-QU´ì?çöÛþþ¯øÑý§aÿ?¶ß÷õÆ€-QU´ì?çöÛþþ¯øÑý§aÿ?¶ß÷õÆ€-QU´ì?çöÛþþ¯øÑý§aÿ?¶ß÷õÆ€-QU´ì?çöÛþþ¯øÑý§aÿ?¶ß÷õÆ€-QU´ì?çöÛþþ¯øÑý§aÿ?¶ß÷õÆ€-QU´ì?çöÛþþ¯øÑý§aÿ?¶ß÷õÆ€-QU´ì?çöÛþþ¯øÑý§aÿ?¶ß÷õÆ€-QPE{kq Ž˜dä…I''íSÐ~ÿ‘»ÄŸõÊÏùKEÿ‘»ÄŸõÊÏùKEZØ‚ÕÏúÕÿ®iÿ Š‰‘ƒ#aЃƒRÜÿ­_úæŸú¨j &û]ÏüüKÿ}š>×sÿ?ÿßf¡¢“}®çþ~%ÿ¾Ík¹ÿŸ‰ï³PÑ@}®çþ~%ÿ¾Ík¹ÿŸ‰ï³PÑ@}®çþ~%ÿ¾Ík¹ÿŸ‰ï³PÑ@}®çþ~%ÿ¾Ík¹ÿŸ‰ï³PÑ@}®çþ~%ÿ¾Ík¹ÿŸ‰ï³PÑ@}®çþ~%ÿ¾Ík¹ÿŸ‰ï³PÑ@}®çþ~%ÿ¾Ík¹ÿŸ‰ï³PÑ@}®çþ~%ÿ¾Ík¹ÿŸ‰ï³PÑ@}®çþ~%ÿ¾Ík¹ÿŸ‰ï³PÑ@}®çþ~%ÿ¾Ík¹ÿŸ‰ï³PÑ@}®çþ~%ÿ¾Ík¹ÿŸ‰ï³PÑ@}®çþ~%ÿ¾Ík¹ÿŸ‰ï³PÑ@}®çþ~%ÿ¾Ík¹ÿŸ‰ï³PÑ@}®çþ~%ÿ¾Ík¹ÿŸ‰ï³PÑ@}®çþ~%ÿ¾Ík¹ÿŸ‰ï³PÑ@}®çþ~%ÿ¾Ík¹ÿŸ‰ï³PÑ@}®çþ~%ÿ¾Ík¹ÿŸ‰ï³PÑ@}®çþ~%ÿ¾Ík¹ÿŸ‰ï³PÑ@}®çþ~%ÿ¾Ík¹ÿŸ‰ï³PÑ@}®çþ~%ÿ¾Ík¹ÿŸ‰ï³PԑýÌˆŠ¤ ¶zœúé@‡}®çþ~%ÿ¾Ík¹ÿŸ‰ï³G“üüÅù7ÿG“üüÅù7ÿ@Úîçâ_ûìÑö»Ÿùø—þû4y1ÿÏÌ_“ñ4y1ÿÏÌ_“ñ4}®çþ~%ÿ¾Ík¹ÿŸ‰ï³G“üüÅù7ÿG“üüÅù7ÿ@Úîçâ_ûìÑö»Ÿùø—þû4y1ÿÏÌ_“ñ4y1ÿÏÌ_“ñ4}®çþ~%ÿ¾Ík¹ÿŸ‰ï³Y^"Ö,|5¢Ïª^JeŠ,~î%ÛéœSÉ*3¨êªÅ[Âú˜#‚ Õ—ù1F lý®çþ~%ÿ¾Ík¹ÿŸ‰ï³X¿ÚZ§ý —þYòEÚZ§ý —þYòEµö»Ÿùø—þû4}®çþ~%ÿ¾ÍbÿijŸô,j_øeÿÉijŸô,j_øeÿÉj×Úîçâ_ûìÑö»Ÿùø—þû5‹ý¥ªб©àU—ÿ$Qý¥ªб©àU—ÿ$Q¨_k¹ÿŸ‰ï³GÚîçâ_ûìÖ/ö–©ÿBÆ¥ÿV_ü‘HÚ¦¤ˆ]ü3¨ª¨É&îÄ?ð"@Ûû]ÏüüKÿ}š>×sÿ?ÿßf²4jßÄ4:¬SE¥Ô$À\¡Î T÷­XáÞŒæDER[=N}ô }®çþ~%ÿ¾Ík¹ÿŸ‰ï³G“üüÅù7ÿG“üüÅù7ÿ@Úîçâ_ûìÑö»Ÿùø—þû4y1ÿÏÌ_“ñ4y1ÿÏÌ_“ñ4}®çþ~%ÿ¾Ík¹ÿŸ‰ï³G“üüÅù7ÿG“üüÅù7ÿ@Úîçâ_ûìÑö»Ÿùø—þû4y1ÿÏÌ_“ñ4y1ÿÏÌ_“ñ4†êᔫO)‚ žj*‹Pº¶Ó¡ŠIgWófŽTVÉg`£¨äûÂ¥ ü#ÿ#w‰?땟ò–Š<#ÿ#w‰?땟ò–е±%«Ÿõ«ÿ\ÓÿA Msþµëšè"¡¨((¢ŠQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE2ÿÇœ¿õÑ?“T52ÿÇœ¿õÑ?“P"(¢•Rþ)u lâW‘¡\Í"²68! þñvp0N7.îr_ÝGâk»»ÓŸÉ½†Ù4à‡íR£¤E¥Ý_1˜ü„mŒò9# ¶ÓþÇ},¶òì¶›/%¶Ü)9.§?.yÜ9á¸%‹fKáÛ©µ;¹þ§Ý^Ãzð c應bÚ›ñ·0©?&pHÈà€F„ÚÞŸmy%­ÌÏnÈ¥Œ“ÂñÄ@]ǰÄ($€IXÿ Æ|þ*´Ø¤ò¢’壺ûd2[¼Qˆ&>Ù¹‹ˆÇ :ƒŒëÿŨêRO=Å·“+N^Qh>ØË,RFPÎ[•Q'Ê6p8ÍhÜøvëVkC¬ßÃr-®|õŽÞØÂƒÈ€©Þί™n ÆÅÀS–&€nÃ*Ïs p²(` 0g• }ˆÈ§Ô6‘O ªGsqö‰W ÊP!až ØÆHÀ'$8P3‰ø¯ÿ"çÔ#]åßü~Oÿ]ù×ñ_þD+ϨþF»Ë¿øüŸþº7ó§Ð]Hk#ÄÚæ‘¡½å…ª]] àŠ8¶‰ ʉ·wbCpOàœÖ½RÕ4ÿí+Hàó|½—0O»nsåJ’c¯}˜ÏlçšC3àñ-´·wù©ýš¶·pÊ·Éç4 ½I;*¸–Ç$SÂM¤$•®^1O"Iˆñ¤AK–B¡”"’5Ÿwà»[›fO?1j_g³Ëx£xdi>ïGvË/’ç9n!o$–1AçÙÛ˜d{¨VÎÅ`Š+¼(Še@Ù;¶U™ƒ9à(‚6´ÝcûJÂòæ;I‹Û\Ü[ù `ÈbvQ´¶Õ;¶Žs€Iðjí¥ÜÖ©slûâ|àAAåXAAÁVËN—O³»·¶¸LÉ<ÓÂÒF[Ëi¹Ü À;1ãoËÔn3XXEa"3É#·™4Ò^g ÌF9ÀÕUÔÿäyÿ\ÿA5jªêò ¼ÿ®ÿ šs¿ äC±ÿ®·?ú>Jì—þ<åÿ®‰üš¸ß†¿ò!Øÿ×[Ÿý%vKÿrÿ×DþMCÜD4QE0æ±{©j §Éc6¬ˆ]švò’Oõ‡–/Ü|c<çhµyâ 7Ošâ;ÉfƒÈ¥g’ÚAMä#íÚìµI8Vト$Ò5ïï&Óõ8m ½‘eœ=¯™*°EŒ˜ÛxUùQq¹$ä£Uðö•ÝÄ¿k³O7íÏk×GÍŠHö´ÛÆä_7å]£ ˆ¹ã4Ý&Ò•.]ÀeUD‚FywU£@»¤RˆdHV áNñ6‘DæåʺïfX$a ä©2árVÌÛ‚ŸºØ‡Qðô·Z×öµµâCuÂ!BdEd©,)`Vá¸`¨9=*–£àѪ]Çwy5ÕËÀ°O-Þœ“PÎÃÈâ2<Æ0“…Lî –ÚѯåÔldžeEe»¹€ ±Îñ¯^øQŸ|Ö…RÒôÿìÛI ó|Í÷3Ï»n1æÊòc¯møÏ|gŠ»@Ì?ÿ¨Ó?ì'mÿ¡ŠÜ¬?ÿ¨Ó?ì'mÿ¡ŠÜ D~ÿ‘»ÄŸõÊÏùKEÿ‘»ÄŸõÊÏùKEZØ’ÕÏúÕÿ®iÿ Š†£ºÔ¬V}¦öÜ2¢©UÈ!@#­Cý§aÿ?¶ß÷õÆ ¢ÕWûNÃþm¿ïêÿÚvóûmÿWühjŠ«ý§aÿ?¶ß÷õÆí;ùý¶ÿ¿«þ4jŠ«ý§aÿ?¶ß÷õÆí;ùý¶ÿ¿«þ4jŠ«ý§aÿ?¶ß÷õÆí;ùý¶ÿ¿«þ4jŠ«ý§aÿ?¶ß÷õÆí;ùý¶ÿ¿«þ4jŠ«ý§aÿ?¶ß÷õÆí;ùý¶ÿ¿«þ4jŠ«ý§aÿ?¶ß÷õÆí;ùý¶ÿ¿«þ4jŠ«ý§aÿ?¶ß÷õÆí;ùý¶ÿ¿«þ4jŠ«ý§aÿ?¶ß÷õÆí;ùý¶ÿ¿«þ4jŠ«ý§aÿ?¶ß÷õÆí;ùý¶ÿ¿«þ4jŠ«ý§aÿ?¶ß÷õÆí;ùý¶ÿ¿«þ4jŠ«ý§aÿ?¶ß÷õÆí;ùý¶ÿ¿«þ4jŠ«ý§aÿ?¶ß÷õÆí;ùý¶ÿ¿«þ4jŠ«ý§aÿ?¶ß÷õÆí;ùý¶ÿ¿«þ4jŠ«ý§aÿ?¶ß÷õÆí;ùý¶ÿ¿«þ4jŠ«ý§aÿ?¶ß÷õÆí;ùý¶ÿ¿«þ4jŠ«ý§aÿ?¶ß÷õÆí;ùý¶ÿ¿«þ4jŠ«ý§aÿ?¶ß÷õÆí;ùý¶ÿ¿«þ4jŠ«ý§aÿ?¶ß÷õÆí;ùý¶ÿ¿«þ4jŠ«ý§aÿ?¶ß÷õÆí;ùý¶ÿ¿«þ4jªjê3@±X_Ei–Üìöþil€>`SëÛ§uþÓ°ÿŸÛoûú¿ãGö‡üþÛßÕÿfÿgx‡þ†?ð^?øº?³¼CÿCø/ü]iiØÏí·ýý_ñ£ûNÃþm¿ïêÿ›ýâú ÿÁxÿâèþÎñý à¼ñu¥ý§aÿ?¶ß÷õÆí;ùý¶ÿ¿«þ4joöwˆè`ƒÿãÿ‹£û;Ä?ô0Aÿ‚ñÿÅÖ—ö‡üþÛßÕÿ?´ì?çöÛþþ¯øÑ¨¿ÙÞ!ÿ¡‚üþ.ìïÿÐÁþ ÇÿZ_ÚvóûmÿWühþÓ°ÿŸÛoûú¿ãF sšï„õoi3i׺üF)GU°Á×ïÔ2ø_ÅÓLò¿Î÷bÍ?'ØK]Oö‡üþÛßÕÿ?´ì?çöÛþþ¯øÓ»“ÿ„OÅŸô;·þŸþ;Gü"~,ÿ¡Ý¿ðÿñÚë?´ì?çöÛþþ¯øÑý§aÿ?¶ß÷õÆ‹°9?øDüYÿC»à ÿã´Â'âÏúÛÿOÿ®³ûNÃþm¿ïêÿÚvóûmÿWüh»“ÿ„OÅŸô;·þŸþ;Gü"~,ÿ¡Ý¿ðÿñÚë?´ì?çöÛþþ¯øÑý§aÿ?¶ß÷õÆ‹°9?øDüYÿC»à ÿã´á:–V Øÿ¿µÖÿiØÏí·ýý_ñ£ûNÃþm¿ïêÿ`RðƈÞðý¾–×"å¢iÊ#ؼŒÿw'ÝŽ½ªæ¡£4 …ôV™mÎÏoæ–À8æu>½ºw_í;ùý¶ÿ¿«þ4iØÏí·ýý_ñ¤oöwˆè`ƒÿãÿ‹£û;Ä?ô0Aÿ‚ñÿÅÖ—ö‡üþÛßÕÿ?´ì?çöÛþþ¯øÑ¨¿ÙÞ!ÿ¡‚üþ.ìïÿÐÁþ ÇÿZ_ÚvóûmÿWühþÓ°ÿŸÛoûú¿ãF fÿgx‡þ†?ð^?øº?³¼CÿCø/ü]iiØÏí·ýý_ñ£ûNÃþm¿ïêÿ›ýâú ÿÁxÿâèþÎñý à¼ñu¥ý§aÿ?¶ß÷õÆí;ùý¶ÿ¿«þ4jLº­w-·Ûu¨¦Š ˆçض[ (Àã;øéŠè*¯ö‡üþÛßÕÿ?´ì?çöÛþþ¯øÐÞÿ‘»ÄŸõÊÏùKE3Á²Ç7ŠüE$N®†+L2œƒÄ½è«[|íES¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(Ø>Ì{þÝÿö¥Q@ÿÙscons-doc-2.3.0/doc/user/less-simple.xml0000644000175000017500000003650212114661557020732 0ustar dktrkranzdktrkranz In this chapter, you will see several examples of very simple build configurations using &SCons;, which will demonstrate how easy it is to use &SCons; to build programs from several different programming languages on different types of systems.
Specifying the Name of the Target (Output) File You've seen that when you call the &b-link-Program; builder method, it builds the resulting program with the same base name as the source file. That is, the following call to build an executable program from the &hello_c; source file will build an executable program named &hello; on POSIX systems, and an executable program named &hello_exe; on Windows systems: Program('hello.c') If you want to build a program with a different name than the base of the source file name, you simply put the target file name to the left of the source file name: Program('new_hello', 'hello.c') (&SCons; requires the target file name first, followed by the source file name, so that the order mimics that of an assignment statement in most programming languages, including Python: "program = source files".) Now &SCons; will build an executable program named &new_hello; when run on a POSIX system: % scons -Q cc -o hello.o -c hello.c cc -o new_hello hello.o And &SCons; will build an executable program named &new_hello_exe; when run on a Windows system: C:\>scons -Q cl /Fohello.obj /c hello.c /nologo link /nologo /OUT:new_hello.exe hello.obj embedManifestExeCheck(target, source, env)
Compiling Multiple Source Files You've just seen how to configure &SCons; to compile a program from a single source file. It's more common, of course, that you'll need to build a program from many input source files, not just one. To do this, you need to put the source files in a Python list (enclosed in square brackets), like so: Program(['prog.c', 'file1.c', 'file2.c']) A build of the above example would look like: % scons -Q cc -o file1.o -c file1.c cc -o file2.o -c file2.c cc -o prog.o -c prog.c cc -o prog prog.o file1.o file2.o Notice that &SCons; deduces the output program name from the first source file specified in the list--that is, because the first source file was &prog_c;, &SCons; will name the resulting program &prog; (or &prog_exe; on a Windows system). If you want to specify a different program name, then (as we've seen in the previous section) you slide the list of source files over to the right to make room for the output program file name. (&SCons; puts the output file name to the left of the source file names so that the order mimics that of an assignment statement: "program = source files".) This makes our example: Program('program', ['prog.c', 'file1.c', 'file2.c']) On Linux, a build of this example would look like: % scons -Q cc -o file1.o -c file1.c cc -o file2.o -c file2.c cc -o prog.o -c prog.c cc -o program prog.o file1.o file2.o Or on Windows: C:\>scons -Q cl /Fofile1.obj /c file1.c /nologo cl /Fofile2.obj /c file2.c /nologo cl /Foprog.obj /c prog.c /nologo link /nologo /OUT:program.exe prog.obj file1.obj file2.obj embedManifestExeCheck(target, source, env)
Making a list of files with &Glob; You can also use the &Glob; function to find all files matching a certain template, using the standard shell pattern matching characters *, ? and [abc] to match any of a, b or c. [!abc] is also supported, to match any character except a, b or c. This makes many multi-source-file builds quite easy: Program('program', Glob('*.c')) The SCons man page has more details on using &Glob; with variant directories (see , below) and repositories (see , below), and returning strings rather than Nodes.
Specifying Single Files Vs. Lists of Files We've now shown you two ways to specify the source for a program, one with a list of files: Program('hello', ['file1.c', 'file2.c']) And one with a single file: Program('hello', 'hello.c') You could actually put a single file name in a list, too, which you might prefer just for the sake of consistency: Program('hello', ['hello.c']) &SCons; functions will accept a single file name in either form. In fact, internally, &SCons; treats all input as lists of files, but allows you to omit the square brackets to cut down a little on the typing when there's only a single file name. Although &SCons; functions are forgiving about whether or not you use a string vs. a list for a single file name, Python itself is more strict about treating lists and strings differently. So where &SCons; allows either a string or list: # The following two calls both work correctly: Program('program1', 'program1.c') Program('program2', ['program2.c']) Trying to do "Python things" that mix strings and lists will cause errors or lead to incorrect results: common_sources = ['file1.c', 'file2.c'] # THE FOLLOWING IS INCORRECT AND GENERATES A PYTHON ERROR # BECAUSE IT TRIES TO ADD A STRING TO A LIST: Program('program1', common_sources + 'program1.c') # The following works correctly, because it's adding two # lists together to make another list. Program('program2', common_sources + ['program2.c'])
Making Lists of Files Easier to Read One drawback to the use of a Python list for source files is that each file name must be enclosed in quotes (either single quotes or double quotes). This can get cumbersome and difficult to read when the list of file names is long. Fortunately, &SCons; and Python provide a number of ways to make sure that the &SConstruct; file stays easy to read. To make long lists of file names easier to deal with, &SCons; provides a &Split; function that takes a quoted list of file names, with the names separated by spaces or other white-space characters, and turns it into a list of separate file names. Using the &Split; function turns the previous example into: Program('program', Split('main.c file1.c file2.c')) (If you're already familiar with Python, you'll have realized that this is similar to the split() method in the Python standard string module. Unlike the split() member function of strings, however, the &Split; function does not require a string as input and will wrap up a single non-string object in a list, or return its argument untouched if it's already a list. This comes in handy as a way to make sure arbitrary values can be passed to &SCons; functions without having to check the type of the variable by hand.) Putting the call to the &Split; function inside the &b-Program; call can also be a little unwieldy. A more readable alternative is to assign the output from the &Split; call to a variable name, and then use the variable when calling the &b-Program; function: src_files = Split('main.c file1.c file2.c') Program('program', src_files) Lastly, the &Split; function doesn't care how much white space separates the file names in the quoted string. This allows you to create lists of file names that span multiple lines, which often makes for easier editing: src_files = Split("""main.c file1.c file2.c""") Program('program', src_files) (Note in this example that we used the Python "triple-quote" syntax, which allows a string to contain multiple lines. The three quotes can be either single or double quotes.)
Keyword Arguments &SCons; also allows you to identify the output file and input source files using Python keyword arguments. The output file is known as the target, and the source file(s) are known (logically enough) as the source. The Python syntax for this is: src_files = Split('main.c file1.c file2.c') Program(target = 'program', source = src_files) Because the keywords explicitly identify what each argument is, you can actually reverse the order if you prefer: src_files = Split('main.c file1.c file2.c') Program(source = src_files, target = 'program') Whether or not you choose to use keyword arguments to identify the target and source files, and the order in which you specify them when using keywords, are purely personal choices; &SCons; functions the same regardless.
Compiling Multiple Programs In order to compile multiple programs within the same &SConstruct; file, simply call the &Program; method multiple times, once for each program you need to build: Program('foo.c') Program('bar', ['bar1.c', 'bar2.c']) &SCons; would then build the programs as follows: % scons -Q cc -o bar1.o -c bar1.c cc -o bar2.o -c bar2.c cc -o bar bar1.o bar2.o cc -o foo.o -c foo.c cc -o foo foo.o Notice that &SCons; does not necessarily build the programs in the same order in which you specify them in the &SConstruct; file. &SCons; does, however, recognize that the individual object files must be built before the resulting program can be built. We'll discuss this in greater detail in the "Dependencies" section, below.
Sharing Source Files Between Multiple Programs It's common to re-use code by sharing source files between multiple programs. One way to do this is to create a library from the common source files, which can then be linked into resulting programs. (Creating libraries is discussed in , below.) A more straightforward, but perhaps less convenient, way to share source files between multiple programs is simply to include the common files in the lists of source files for each program: Program(Split('foo.c common1.c common2.c')) Program('bar', Split('bar1.c bar2.c common1.c common2.c')) &SCons; recognizes that the object files for the &common1_c; and &common2_c; source files each need to be built only once, even though the resulting object files are each linked in to both of the resulting executable programs: % scons -Q cc -o bar1.o -c bar1.c cc -o bar2.o -c bar2.c cc -o common1.o -c common1.c cc -o common2.o -c common2.c cc -o bar bar1.o bar2.o common1.o common2.o cc -o foo.o -c foo.c cc -o foo foo.o common1.o common2.o If two or more programs share a lot of common source files, repeating the common files in the list for each program can be a maintenance problem when you need to change the list of common files. You can simplify this by creating a separate Python list to hold the common file names, and concatenating it with other lists using the Python + operator: common = ['common1.c', 'common2.c'] foo_files = ['foo.c'] + common bar_files = ['bar1.c', 'bar2.c'] + common Program('foo', foo_files) Program('bar', bar_files) This is functionally equivalent to the previous example.
scons-doc-2.3.0/doc/user/example.in0000644000175000017500000000240412114661557017730 0ustar dktrkranzdktrkranz XXX
XXX XXX
scons-doc-2.3.0/doc/user/nodes.xml0000644000175000017500000002527412114661557017611 0ustar dktrkranzdktrkranz Internally, &SCons; represents all of the files and directories it knows about as &Nodes;. These internal objects (not object files) can be used in a variety of ways to make your &SConscript; files portable and easy to read.
Builder Methods Return Lists of Target Nodes All builder methods return a list of &Node; objects that identify the target file or files that will be built. These returned &Nodes; can be passed as arguments to other builder methods. For example, suppose that we want to build the two object files that make up a program with different options. This would mean calling the &b-link-Object; builder once for each object file, specifying the desired options: Object('hello.c', CCFLAGS='-DHELLO') Object('goodbye.c', CCFLAGS='-DGOODBYE') One way to combine these object files into the resulting program would be to call the &b-link-Program; builder with the names of the object files listed as sources: Object('hello.c', CCFLAGS='-DHELLO') Object('goodbye.c', CCFLAGS='-DGOODBYE') Program(['hello.o', 'goodbye.o']) The problem with specifying the names as strings is that our &SConstruct; file is no longer portable across operating systems. It won't, for example, work on Windows because the object files there would be named &hello_obj; and &goodbye_obj;, not &hello_o; and &goodbye_o;. A better solution is to assign the lists of targets returned by the calls to the &b-Object; builder to variables, which we can then concatenate in our call to the &b-Program; builder: hello_list = Object('hello.c', CCFLAGS='-DHELLO') goodbye_list = Object('goodbye.c', CCFLAGS='-DGOODBYE') Program(hello_list + goodbye_list) This makes our &SConstruct; file portable again, the build output on Linux looking like: % scons -Q cc -o goodbye.o -c -DGOODBYE goodbye.c cc -o hello.o -c -DHELLO hello.c cc -o hello hello.o goodbye.o And on Windows: C:\>scons -Q cl /Fogoodbye.obj /c goodbye.c -DGOODBYE cl /Fohello.obj /c hello.c -DHELLO link /nologo /OUT:hello.exe hello.obj goodbye.obj embedManifestExeCheck(target, source, env) We'll see examples of using the list of nodes returned by builder methods throughout the rest of this guide.
Explicitly Creating File and Directory Nodes It's worth mentioning here that &SCons; maintains a clear distinction between Nodes that represent files and Nodes that represent directories. &SCons; supports &File; and &Dir; functions that, respectively, return a file or directory Node: hello_c = File('hello.c') Program(hello_c) classes = Dir('classes') Java(classes, 'src') Normally, you don't need to call &File; or &Dir; directly, because calling a builder method automatically treats strings as the names of files or directories, and translates them into the Node objects for you. The &File; and &Dir; functions can come in handy in situations where you need to explicitly instruct &SCons; about the type of Node being passed to a builder or other function, or unambiguously refer to a specific file in a directory tree. There are also times when you may need to refer to an entry in a file system without knowing in advance whether it's a file or a directory. For those situations, &SCons; also supports an &Entry; function, which returns a Node that can represent either a file or a directory. xyzzy = Entry('xyzzy') The returned xyzzy Node will be turned into a file or directory Node the first time it is used by a builder method or other function that requires one vs. the other.
Printing &Node; File Names One of the most common things you can do with a Node is use it to print the file name that the node represents. Keep in mind, though, that because the object returned by a builder call is a list of Nodes, you must use Python subscripts to fetch individual Nodes from the list. For example, the following &SConstruct; file: hello_c = File('hello.c') Program(hello_c) classes = Dir('classes') Java(classes, 'src') object_list = Object('hello.c') program_list = Program(object_list) print "The object file is:", object_list[0] print "The program file is:", program_list[0] Would print the following file names on a POSIX system: % scons -Q The object file is: hello.o The program file is: hello cc -o hello.o -c hello.c cc -o hello hello.o And the following file names on a Windows system: C:\>scons -Q The object file is: hello.obj The program file is: hello.exe cl /Fohello.obj /c hello.c /nologo link /nologo /OUT:hello.exe hello.obj embedManifestExeCheck(target, source, env) Note that in the above example, the object_list[0] extracts an actual Node object from the list, and the Python print statement converts the object to a string for printing.
Using a &Node;'s File Name as a String Printing a &Node;'s name as described in the previous section works because the string representation of a &Node; object is the name of the file. If you want to do something other than print the name of the file, you can fetch it by using the builtin Python &str; function. For example, if you want to use the Python os.path.exists to figure out whether a file exists while the &SConstruct; file is being read and executed, you can fetch the string as follows: import os.path program_list = Program('hello.c') program_name = str(program_list[0]) if not os.path.exists(program_name): print program_name, "does not exist!" Which executes as follows on a POSIX system: % scons -Q hello does not exist! cc -o hello.o -c hello.c cc -o hello hello.o
&GetBuildPath;: Getting the Path From a &Node; or String env.GetBuildPath(file_or_list) returns the path of a &Node; or a string representing a path. It can also take a list of &Node;s and/or strings, and returns the list of paths. If passed a single &Node;, the result is the same as calling str(node) (see above). The string(s) can have embedded construction variables, which are expanded as usual, using the calling environment's set of variables. The paths can be files or directories, and do not have to exist. env=Environment(VAR="value") n=File("foo.c") print env.GetBuildPath([n, "sub/dir/$VAR"]) Would print the following file names: % scons -Q ['foo.c', 'sub/dir/value'] scons: `.' is up to date. There is also a function version of &GetBuildPath; which can be called without an &Environment;; that uses the default SCons &Environment; to do substitution on any string arguments.
scons-doc-2.3.0/doc/user/install.in0000644000175000017500000001562312114661557017752 0ustar dktrkranzdktrkranz Once a program is built, it is often appropriate to install it in another directory for public use. You use the &Install; method to arrange for a program, or any other file, to be copied into a destination directory: env = Environment() hello = env.Program('hello.c') env.Install('__ROOT__/usr/bin', hello) int main() { printf("Hello, world!\n"); } Note, however, that installing a file is still considered a type of file "build." This is important when you remember that the default behavior of &SCons; is to build files in or below the current directory. If, as in the example above, you are installing files in a directory outside of the top-level &SConstruct; file's directory tree, you must specify that directory (or a higher directory, such as /) for it to install anything there: scons -Q scons -Q __ROOT__/usr/bin It can, however, be cumbersome to remember (and type) the specific destination directory in which the program (or any other file) should be installed. This is an area where the &Alias; function comes in handy, allowing you, for example, to create a pseudo-target named install that can expand to the specified destination directory: env = Environment() hello = env.Program('hello.c') env.Install('__ROOT__/usr/bin', hello) env.Alias('install', '__ROOT__/usr/bin') int main() { printf("Hello, world!\n"); } This then yields the more natural ability to install the program in its destination as follows: scons -Q scons -Q install
Installing Multiple Files in a Directory You can install multiple files into a directory simply by calling the &Install; function multiple times: env = Environment() hello = env.Program('hello.c') goodbye = env.Program('goodbye.c') env.Install('__ROOT__/usr/bin', hello) env.Install('__ROOT__/usr/bin', goodbye) env.Alias('install', '__ROOT__/usr/bin') int main() { printf("Hello, world!\n"); } int main() { printf("Goodbye, world!\n"); } Or, more succinctly, listing the multiple input files in a list (just like you can do with any other builder): env = Environment() hello = env.Program('hello.c') goodbye = env.Program('goodbye.c') env.Install('__ROOT__/usr/bin', [hello, goodbye]) env.Alias('install', '__ROOT__/usr/bin') Either of these two examples yields: scons -Q install
Installing a File Under a Different Name The &Install; method preserves the name of the file when it is copied into the destination directory. If you need to change the name of the file when you copy it, use the &InstallAs; function: env = Environment() hello = env.Program('hello.c') env.InstallAs('__ROOT__/usr/bin/hello-new', hello) env.Alias('install', '__ROOT__/usr/bin') int main() { printf("Hello, world!\n"); } This installs the hello program with the name hello-new as follows: scons -Q install
Installing Multiple Files Under Different Names Lastly, if you have multiple files that all need to be installed with different file names, you can either call the &InstallAs; function multiple times, or as a shorthand, you can supply same-length lists for both the target and source arguments: env = Environment() hello = env.Program('hello.c') goodbye = env.Program('goodbye.c') env.InstallAs(['__ROOT__/usr/bin/hello-new', '__ROOT__/usr/bin/goodbye-new'], [hello, goodbye]) env.Alias('install', '__ROOT__/usr/bin') int main() { printf("Hello, world!\n"); } int main() { printf("Goodbye, world!\n"); } In this case, the &InstallAs; function loops through both lists simultaneously, and copies each source file into its corresponding target file name: scons -Q install
scons-doc-2.3.0/doc/user/errors.xml0000644000175000017500000000240412114661557020003 0ustar dktrkranzdktrkranz XXX
XXX XXX
scons-doc-2.3.0/doc/user/run.xml0000644000175000017500000002645312114661557017305 0ustar dktrkranzdktrkranz XXX
Command-Line Options XXX
Getting at Command-Line Arguments XXX
Selective Builds XXX
Overriding Construction Variables XXX
scons-doc-2.3.0/doc/user/repositories.in0000644000175000017500000004550612114661557021036 0ustar dktrkranzdktrkranz Often, a software project will have one or more central repositories, directory trees that contain source code, or derived files, or both. You can eliminate additional unnecessary rebuilds of files by having &SCons; use files from one or more code repositories to build files in your local build tree.
The &Repository; Method It's often useful to allow multiple programmers working on a project to build software from source files and/or derived files that are stored in a centrally-accessible repository, a directory copy of the source code tree. (Note that this is not the sort of repository maintained by a source code management system like BitKeeper, CVS, or Subversion.) You use the &Repository; method to tell &SCons; to search one or more central code repositories (in order) for any source files and derived files that are not present in the local build tree: env = Environment() env.Program('hello.c') Repository('__ROOT__/usr/repository1', '__ROOT__/usr/repository2') int main() { printf("Hello, world!\n"); } Multiple calls to the &Repository; method will simply add repositories to the global list that &SCons; maintains, with the exception that &SCons; will automatically eliminate the current directory and any non-existent directories from the list.
Finding source files in repositories The above example specifies that &SCons; will first search for files under the /usr/repository1 tree and next under the /usr/repository2 tree. &SCons; expects that any files it searches for will be found in the same position relative to the top-level directory. In the above example, if the &hello_c; file is not found in the local build tree, &SCons; will search first for a /usr/repository1/hello.c file and then for a /usr/repository2/hello.c file to use in its place. So given the &SConstruct; file above, if the &hello_c; file exists in the local build directory, &SCons; will rebuild the &hello; program as normal: scons -Q If, however, there is no local &hello_c; file, but one exists in /usr/repository1, &SCons; will recompile the &hello; program from the source file it finds in the repository: env = Environment() env.Program('hello.c') Repository('__ROOT__/usr/repository1', '__ROOT__/usr/repository2') int main() { printf("Hello, world!\n"); } scons -Q And similarly, if there is no local &hello_c; file and no /usr/repository1/hello.c, but one exists in /usr/repository2: env = Environment() env.Program('hello.c') Repository('__ROOT__/usr/repository1', '__ROOT__/usr/repository2') int main() { printf("Hello, world!\n"); } scons -Q
Finding <literal>#include</literal> files in repositories We've already seen that SCons will scan the contents of a source file for #include file names and realize that targets built from that source file also depend on the #include file(s). For each directory in the &cv-link-CPPPATH; list, &SCons; will actually search the corresponding directories in any repository trees and establish the correct dependencies on any #include files that it finds in repository directory. Unless the C compiler also knows about these directories in the repository trees, though, it will be unable to find the #include files. If, for example, the &hello_c; file in our previous example includes the &hello_h; in its current directory, and the &hello_h; only exists in the repository: % scons -Q cc -o hello.o -c hello.c hello.c:1: hello.h: No such file or directory In order to inform the C compiler about the repositories, &SCons; will add appropriate -I flags to the compilation commands for each directory in the &cv-CPPPATH; list. So if we add the current directory to the construction environment &cv-CPPPATH; like so: env = Environment(CPPPATH = ['.']) env.Program('hello.c') Repository('__ROOT__/usr/repository1') int main() { printf("Hello, world!\n"); } Then re-executing &SCons; yields: scons -Q The order of the -I options replicates, for the C preprocessor, the same repository-directory search path that &SCons; uses for its own dependency analysis. If there are multiple repositories and multiple &cv-CPPPATH; directories, &SCons; will add the repository directories to the beginning of each &cv-CPPPATH; directory, rapidly multiplying the number of -I flags. If, for example, the &cv-CPPPATH; contains three directories (and shorter repository path names!): env = Environment(CPPPATH = ['dir1', 'dir2', 'dir3']) env.Program('hello.c') Repository('__ROOT__/r1', '__ROOT__/r2') int main() { printf("Hello, world!\n"); } Then we'll end up with nine -I options on the command line, three (for each of the &cv-CPPPATH; directories) times three (for the local directory plus the two repositories): scons -Q
Limitations on <literal>#include</literal> files in repositories &SCons; relies on the C compiler's -I options to control the order in which the preprocessor will search the repository directories for #include files. This causes a problem, however, with how the C preprocessor handles #include lines with the file name included in double-quotes. As we've seen, &SCons; will compile the &hello_c; file from the repository if it doesn't exist in the local directory. If, however, the &hello_c; file in the repository contains a #include line with the file name in double quotes: #include "hello.h" int main(int argc, char *argv[]) { printf(HELLO_MESSAGE); return (0); } Then the C preprocessor will always use a &hello_h; file from the repository directory first, even if there is a &hello_h; file in the local directory, despite the fact that the command line specifies -I as the first option: env = Environment(CPPPATH = ['.']) env.Program('hello.c') Repository('__ROOT__/usr/repository1') int main() { printf("Hello, world!\n"); } scons -Q This behavior of the C preprocessor--always search for a #include file in double-quotes first in the same directory as the source file, and only then search the -I--can not, in general, be changed. In other words, it's a limitation that must be lived with if you want to use code repositories in this way. There are three ways you can possibly work around this C preprocessor behavior: Some modern versions of C compilers do have an option to disable or control this behavior. If so, add that option to &cv-link-CFLAGS; (or &cv-link-CXXFLAGS; or both) in your construction environment(s). Make sure the option is used for all construction environments that use C preprocessing! Change all occurrences of #include "file.h" to #include &lt;file.h&gt;. Use of #include with angle brackets does not have the same behavior--the -I directories are searched first for #include files--which gives &SCons; direct control over the list of directories the C preprocessor will search. Require that everyone working with compilation from repositories check out and work on entire directories of files, not individual files. (If you use local wrapper scripts around your source code control system's command, you could add logic to enforce this restriction there.
Finding the &SConstruct; file in repositories &SCons; will also search in repositories for the &SConstruct; file and any specified &SConscript; files. This poses a problem, though: how can &SCons; search a repository tree for an &SConstruct; file if the &SConstruct; file itself contains the information about the pathname of the repository? To solve this problem, &SCons; allows you to specify repository directories on the command line using the -Y option: % scons -Q -Y /usr/repository1 -Y /usr/repository2 When looking for source or derived files, &SCons; will first search the repositories specified on the command line, and then search the repositories specified in the &SConstruct; or &SConscript; files.
Finding derived files in repositories If a repository contains not only source files, but also derived files (such as object files, libraries, or executables), &SCons; will perform its normal MD5 signature calculation to decide if a derived file in a repository is up-to-date, or the derived file must be rebuilt in the local build directory. For the &SCons; signature calculation to work correctly, a repository tree must contain the &sconsign; files that &SCons; uses to keep track of signature information. Usually, this would be done by a build integrator who would run &SCons; in the repository to create all of its derived files and &sconsign; files, or who would run &SCons; in a separate build directory and copy the resulting tree to the desired repository: env = Environment() env.Program(['hello.c', 'file1.c', 'file2.c']) Repository('/usr/repository1', '/usr/repository2') int main() { printf("Hello, world!\n"); } int f1() { printf("file1\n"); } int f2() { printf("file2.c\n"); } cd /usr/repository1 scons -Q (Note that this is safe even if the &SConstruct; file lists /usr/repository1 as a repository, because &SCons; will remove the current build directory from its repository list for that invocation.) Now, with the repository populated, we only need to create the one local source file we're interested in working with at the moment, and use the -Y option to tell &SCons; to fetch any other files it needs from the repository: % cd $HOME/build % edit hello.c % scons -Q -Y /usr/repository1 cc -c -o hello.o hello.c cc -o hello hello.o /usr/repository1/file1.o /usr/repository1/file2.o Notice that &SCons; realizes that it does not need to rebuild local copies file1.o and file2.o files, but instead uses the already-compiled files from the repository.
Guaranteeing local copies of files If the repository tree contains the complete results of a build, and we try to build from the repository without any files in our local tree, something moderately surprising happens: % mkdir $HOME/build2 % cd $HOME/build2 % scons -Q -Y /usr/all/repository hello scons: `hello' is up-to-date. Why does &SCons; say that the &hello; program is up-to-date when there is no &hello; program in the local build directory? Because the repository (not the local directory) contains the up-to-date &hello; program, and &SCons; correctly determines that nothing needs to be done to rebuild that up-to-date copy of the file. There are, however, many times when you want to ensure that a local copy of a file always exists. A packaging or testing script, for example, may assume that certain generated files exist locally. To tell &SCons; to make a copy of any up-to-date repository file in the local build directory, use the &Local; function: env = Environment() hello = env.Program('hello.c') Local(hello) int main() { printf("Hello, world!\n"); } If we then run the same command, &SCons; will make a local copy of the program from the repository copy, and tell you that it is doing so: % scons -Y /usr/all/repository hello Local copy of hello from /usr/all/repository/hello scons: `hello' is up-to-date. (Notice that, because the act of making the local copy is not considered a "build" of the &hello; file, &SCons; still reports that it is up-to-date.)
scons-doc-2.3.0/doc/user/gettext.xml0000644000175000017500000003025512114661557020160 0ustar dktrkranzdktrkranz The &t-link-gettext; toolset supports internationalization and localization of SCons-based projects. Builders provided by &t-link-gettext; automatize generation and updates of translation files. You can manage translations and translation templates similarly to how it's done with autotools.
Prerequisites To follow examples provided in this chapter set up your operating system to support two or more languages. In following examples we use locales en_US, de_DE, and pl_PL. Ensure, that you have GNU gettext utilities installed on your system. To edit translation files you may wish to install poedit editor.
Simple project Let's start with a very simple project, the "Hello world" program for example /* hello.c */ #include <stdio.h> int main(int argc, char* argv[]) { printf("Hello world\n"); return 0; } Prepare a SConstruct to compile the program as usual. # SConstruct env = Environment() hello = Program(["hello.c"]) Now we'll convert the project to a multi-lingual one. If you don't already have GNU gettext utilities installed, install them from your preffered package repository, or download from http://ftp.gnu.org/gnu/gettext/. For the purpose of this example, you should have following three locales installed on your system: en_US, de_DE and pl_PL. On debian, for example, you may enable certain locales through dpkg-reconfigure locales. First prepare the hello.c program for internationalization. Change the previous code so it reads as follows: /* hello.c */ #include <stdio.h> #include <libintl.h> #include <locale.h> int main(int argc, char* argv[]) { bindtextdomain("hello", "locale"); setlocale(LC_ALL, ""); textdomain("hello"); printf(gettext("Hello world\n")); return 0; } Detailed recipes for such conversion can be found at http://www.gnu.org/software/gettext/manual/gettext.html#Sources. The gettext("...") has two purposes. First, it marks messages for the xgettext(1) program, which we will use to extract from the sources the messages for localization. Second, it calls the gettext library internals to translate the message at runtime. Now we shall instruct SCons how to generate and maintain translation files. For that, use the &b-link-Translate; builder and &b-link-MOFiles; builder. The first one takes source files, extracts internationalized messages from them, creates so-called POT file (translation template), and then creates PO translation files, one for each requested language. Later, during the development lifecycle, the builder keeps all these files up-to date. The &b-link-MOFiles; builder compiles the PO files to binary form. Then install the MO files under directory called locale. The completed SConstruct is as follows: # SConstruct env = Environment( tools = ['default', 'gettext'] ) hello = env.Program(["hello.c"]) env['XGETTEXTFLAGS'] = [ '--package-name=%s' % 'hello', '--package-version=%s' % '1.0', ] po = env.Translate(["pl","en", "de"], ["hello.c"], POAUTOINIT = 1) mo = env.MOFiles(po) InstallAs(["locale/en/LC_MESSAGES/hello.mo"], ["en.mo"]) InstallAs(["locale/pl/LC_MESSAGES/hello.mo"], ["pl.mo"]) InstallAs(["locale/de/LC_MESSAGES/hello.mo"], ["de.mo"]) Generate the translation files with scons po-update. You should see the output from SCons simillar to this: user@host:$ scons po-update scons: Reading SConscript files ... scons: done reading SConscript files. scons: Building targets ... Entering '/home/ptomulik/projects/tmp' xgettext --package-name=hello --package-version=1.0 -o - hello.c Leaving '/home/ptomulik/projects/tmp' Writting 'messages.pot' (new file) msginit --no-translator -l pl -i messages.pot -o pl.po Created pl.po. msginit --no-translator -l en -i messages.pot -o en.po Created en.po. msginit --no-translator -l de -i messages.pot -o de.po Created de.po. scons: done building targets. If everything is right, you should see following new files. user@host:$ ls *.po* de.po en.po messages.pot pl.po Open en.po in poedit and provide the English translation to message "Hello world\n". Do the same for de.po (deutsch) and pl.po (polish). Let the translations be, for example: en: "Welcome to beautiful world!\n" de: "Hallo Welt!\n" pl: "Witaj swiecie!\n" Now compile the project by executing scons. The output should be similar to this: user@host:$ scons scons: Reading SConscript files ... scons: done reading SConscript files. scons: Building targets ... msgfmt -c -o de.mo de.po msgfmt -c -o en.mo en.po gcc -o hello.o -c hello.c gcc -o hello hello.o Install file: "de.mo" as "locale/de/LC_MESSAGES/hello.mo" Install file: "en.mo" as "locale/en/LC_MESSAGES/hello.mo" msgfmt -c -o pl.mo pl.po Install file: "pl.mo" as "locale/pl/LC_MESSAGES/hello.mo" scons: done building targets. SCons automatically compiled the PO files to binary format MO, and the InstallAs lines installed these files under locale folder. Your program should be now ready. You may try it as follows (linux): user@host:$ LANG=en_US.UTF-8 ./hello Welcome to beautiful world user@host:$ LANG=de_DE.UTF-8 ./hello Hallo Welt user@host:$ LANG=pl_PL.UTF-8 ./hello Witaj swiecie To demonstrate the further life of translation files, let's change Polish translation (poedit pl.po) to "Witaj drogi swiecie\n". Run scons to see how scons reacts to this user@host:$scons scons: Reading SConscript files ... scons: done reading SConscript files. scons: Building targets ... msgfmt -c -o pl.mo pl.po Install file: "pl.mo" as "locale/pl/LC_MESSAGES/hello.mo" scons: done building targets. Now, open hello.c and add another one printf line with new message. /* hello.c */ #include <stdio.h> #include <libintl.h> #include <locale.h> int main(int argc, char* argv[]) { bindtextdomain("hello", "locale"); setlocale(LC_ALL, ""); textdomain("hello"); printf(gettext("Hello world\n")); printf(gettext("and good bye\n")); return 0; } Compile project with scons. This time, the msgmerge(1) program is used by SCons to update PO file. The output from compilation is like: user@host:$scons scons: Reading SConscript files ... scons: done reading SConscript files. scons: Building targets ... Entering '/home/ptomulik/projects/tmp' xgettext --package-name=hello --package-version=1.0 -o - hello.c Leaving '/home/ptomulik/projects/tmp' Writting 'messages.pot' (messages in file were outdated) msgmerge --update de.po messages.pot ... done. msgfmt -c -o de.mo de.po msgmerge --update en.po messages.pot ... done. msgfmt -c -o en.mo en.po gcc -o hello.o -c hello.c gcc -o hello hello.o Install file: "de.mo" as "locale/de/LC_MESSAGES/hello.mo" Install file: "en.mo" as "locale/en/LC_MESSAGES/hello.mo" msgmerge --update pl.po messages.pot ... done. msgfmt -c -o pl.mo pl.po Install file: "pl.mo" as "locale/pl/LC_MESSAGES/hello.mo" scons: done building targets. The next example demonstrates what happens if we change the source code in such way that the internationalized messages do not change. The answer is that none of translation files (POT, PO) are touched (i.e. no content changes, no creation/modification time changed and so on). Let's append another line to the program (after the last printf), so its code becomes: /* hello.c */ #include <stdio.h> #include <libintl.h> #include <locale.h> int main(int argc, char* argv[]) { bindtextdomain("hello", "locale"); setlocale(LC_ALL, ""); textdomain("hello"); printf(gettext("Hello world\n")); printf(gettext("and good bye\n")); printf("----------------\n"); return a; } Compile the project. You'll see on your screen user@host:$scons scons: Reading SConscript files ... scons: done reading SConscript files. scons: Building targets ... Entering '/home/ptomulik/projects/tmp' xgettext --package-name=hello --package-version=1.0 -o - hello.c Leaving '/home/ptomulik/projects/tmp' Not writting 'messages.pot' (messages in file found to be up-to-date) gcc -o hello.o -c hello.c gcc -o hello hello.o scons: done building targets. As you see, the internationalized messages ditn't change, so the POT and the rest of translation files have not even been touched.
scons-doc-2.3.0/doc/user/sconf.xml0000644000175000017500000002731212114661557017604 0ustar dktrkranzdktrkranz &SCons; has integrated support for multi-platform build configuration similar to that offered by GNU &Autoconf;, such as figuring out what libraries or header files are available on the local system. This section describes how to use this &SCons; feature. This chapter is still under development, so not everything is explained as well as it should be. See the &SCons; man page for additional information.
&Configure_Contexts; The basic framework for multi-platform build configuration in &SCons; is to attach a &configure_context; to a construction environment by calling the &Configure; function, perform a number of checks for libraries, functions, header files, etc., and to then call the configure context's &Finish; method to finish off the configuration: env = Environment() conf = Configure(env) # Checks for libraries, header files, etc. go here! env = conf.Finish() &SCons; provides a number of basic checks, as well as a mechanism for adding your own custom checks. Note that &SCons; uses its own dependency mechanism to determine when a check needs to be run--that is, &SCons; does not run the checks every time it is invoked, but caches the values returned by previous checks and uses the cached values unless something has changed. This saves a tremendous amount of developer time while working on cross-platform build issues. The next sections describe the basic checks that &SCons; supports, as well as how to add your own custom checks.
Checking for the Existence of Header Files Testing the existence of a header file requires knowing what language the header file is. A configure context has a &CheckCHeader; method that checks for the existence of a C header file: env = Environment() conf = Configure(env) if not conf.CheckCHeader('math.h'): print 'Math.h must be installed!' Exit(1) if conf.CheckCHeader('foo.h'): conf.env.Append('-DHAS_FOO_H') env = conf.Finish() Note that you can choose to terminate the build if a given header file doesn't exist, or you can modify the construction environment based on the existence of a header file. If you need to check for the existence a C++ header file, use the &CheckCXXHeader; method: env = Environment() conf = Configure(env) if not conf.CheckCXXHeader('vector.h'): print 'vector.h must be installed!' Exit(1) env = conf.Finish()
Checking for the Availability of a Function Check for the availability of a specific function using the &CheckFunc; method: env = Environment() conf = Configure(env) if not conf.CheckFunc('strcpy'): print 'Did not find strcpy(), using local version' conf.env.Append(CPPDEFINES = '-Dstrcpy=my_local_strcpy') env = conf.Finish()
Checking for the Availability of a Library Check for the availability of a library using the &CheckLib; method. You only specify the basename of the library, you don't need to add a lib prefix or a .a or .lib suffix: env = Environment() conf = Configure(env) if not conf.CheckLib('m'): print 'Did not find libm.a or m.lib, exiting!' Exit(1) env = conf.Finish() Because the ability to use a library successfully often depends on having access to a header file that describes the library's interface, you can check for a library and a header file at the same time by using the &CheckLibWithHeader; method: env = Environment() conf = Configure(env) if not conf.CheckLibWithHeader('m', 'math.h', 'c'): print 'Did not find libm.a or m.lib, exiting!' Exit(1) env = conf.Finish() This is essentially shorthand for separate calls to the &CheckHeader; and &CheckLib; functions.
Checking for the Availability of a &typedef; Check for the availability of a &typedef; by using the &CheckType; method: env = Environment() conf = Configure(env) if not conf.CheckType('off_t'): print 'Did not find off_t typedef, assuming int' conf.env.Append(CCFLAGS = '-Doff_t=int') env = conf.Finish() You can also add a string that will be placed at the beginning of the test file that will be used to check for the &typedef;. This provide a way to specify files that must be included to find the &typedef;: env = Environment() conf = Configure(env) if not conf.CheckType('off_t', '#include <sys/types.h>\n'): print 'Did not find off_t typedef, assuming int' conf.env.Append(CCFLAGS = '-Doff_t=int') env = conf.Finish()
Adding Your Own Custom Checks A custom check is a Python function that checks for a certain condition to exist on the running system, usually using methods that &SCons; supplies to take care of the details of checking whether a compilation succeeds, a link succeeds, a program is runnable, etc. A simple custom check for the existence of a specific library might look as follows: mylib_test_source_file = """ #include <mylib.h> int main(int argc, char **argv) { MyLibrary mylib(argc, argv); return 0; } """ def CheckMyLibrary(context): context.Message('Checking for MyLibrary...') result = context.TryLink(mylib_test_source_file, '.c') context.Result(result) return result The &Message; and &Result; methods should typically begin and end a custom check to let the user know what's going on: the &Message; call prints the specified message (with no trailing newline) and the &Result; call prints yes if the check succeeds and no if it doesn't. The &TryLink; method actually tests for whether the specified program text will successfully link. (Note that a custom check can modify its check based on any arguments you choose to pass it, or by using or modifying the configure context environment in the context.env attribute.) This custom check function is then attached to the &configure_context; by passing a dictionary to the &Configure; call that maps a name of the check to the underlying function: env = Environment() conf = Configure(env, custom_tests = {'CheckMyLibrary' : CheckMyLibrary}) You'll typically want to make the check and the function name the same, as we've done here, to avoid potential confusion. We can then put these pieces together and actually call the CheckMyLibrary check as follows: mylib_test_source_file = """ #include <mylib.h> int main(int argc, char **argv) { MyLibrary mylib(argc, argv); return 0; } """ def CheckMyLibrary(context): context.Message('Checking for MyLibrary... ') result = context.TryLink(mylib_test_source_file, '.c') context.Result(result) return result env = Environment() conf = Configure(env, custom_tests = {'CheckMyLibrary' : CheckMyLibrary}) if not conf.CheckMyLibrary(): print 'MyLibrary is not installed!' Exit(1) env = conf.Finish() # We would then add actual calls like Program() to build # something using the "env" construction environment. If MyLibrary is not installed on the system, the output will look like: % scons scons: Reading SConscript file ... Checking for MyLibrary... failed MyLibrary is not installed! If MyLibrary is installed, the output will look like: % scons scons: Reading SConscript file ... Checking for MyLibrary... failed scons: done reading SConscript scons: Building targets ... . . .
Not Configuring When Cleaning Targets Using multi-platform configuration as described in the previous sections will run the configuration commands even when invoking scons -c to clean targets: % scons -Q -c Checking for MyLibrary... yes Removed foo.o Removed foo Although running the platform checks when removing targets doesn't hurt anything, it's usually unnecessary. You can avoid this by using the &GetOption; method to check whether the (clean) option has been invoked on the command line: env = Environment() if not env.GetOption('clean'): conf = Configure(env, custom_tests = {'CheckMyLibrary' : CheckMyLibrary}) if not conf.CheckMyLibrary(): print 'MyLibrary is not installed!' Exit(1) env = conf.Finish() % scons -Q -c Removed foo.o Removed foo
scons-doc-2.3.0/doc/user/make.xml0000644000175000017500000001132012114661557017401 0ustar dktrkranzdktrkranz XXX
Differences Between &Make; and &SCons; XXX
Advantages of &SCons; Over &Make; XXX
scons-doc-2.3.0/doc/user/build-install.xml0000644000175000017500000004311212114661557021233 0ustar dktrkranzdktrkranz This chapter will take you through the basic steps of installing &SCons; on your system, and building &SCons; if you don't have a pre-built package available (or simply prefer the flexibility of building it yourself). Before that, however, this chapter will also describe the basic steps involved in installing Python on your system, in case that is necessary. Fortunately, both &SCons; and Python are very easy to install on almost any system, and Python already comes installed on many systems.
Installing Python Because &SCons; is written in Python, you must obviously have Python installed on your system to use &SCons;. Before you try to install Python, you should check to see if Python is already available on your system by typing python -V (capital 'V') or python --version at your system's command-line prompt. $ python -V Python 2.5.1 And on a Windows system with Python installed: C:\>python -V Python 2.5.1 If Python is not installed on your system, you will see an error message stating something like "command not found" (on UNIX or Linux) or "'python' is not recognized as an internal or external command, operable progam or batch file" (on Windows). In that case, you need to install Python before you can install &SCons;. The standard location for information about downloading and installing Python is http://www.python.org/download/. See that page for information about how to download and install Python on your system. &SCons; will work with any 2.x version of Python from 2.4 on; 3.0 and later are not yet supported. If you need to install Python and have a choice, we recommend using the most recent 2.x Python version available. Newer Pythons have significant improvements that help speed up the performance of &SCons;.
Installing &SCons; From Pre-Built Packages &SCons; comes pre-packaged for installation on a number of systems, including Linux and Windows systems. You do not need to read this entire section, you should need to read only the section appropriate to the type of system you're running on.
Installing &SCons; on Red Hat (and Other RPM-based) Linux Systems &SCons; comes in RPM (Red Hat Package Manager) format, pre-built and ready to install on Red Hat Linux, Fedora, or any other Linux distribution that uses RPM. Your distribution may already have an &SCons; RPM built specifically for it; many do, including SUSE, Mandrake and Fedora. You can check for the availability of an &SCons; RPM on your distribution's download servers, or by consulting an RPM search site like http://www.rpmfind.net/ or http://rpm.pbone.net/. If your distribution supports installation via yum, you should be able to install &SCons; by running: # yum install scons If your Linux distribution does not already have a specific &SCons; RPM file, you can download and install from the generic RPM provided by the &SCons; project. This will install the SCons script(s) in /usr/bin, and the SCons library modules in /usr/lib/scons. To install from the command line, simply download the appropriate .rpm file, and then run: # rpm -Uvh scons-2.3.0-1.noarch.rpm Or, you can use a graphical RPM package manager. See your package manager application's documention for specific instructions about how to use it to install a downloaded RPM.
Installing &SCons; on Debian Linux Systems Debian Linux systems use a different package management format that also makes it very easy to install &SCons;. If your system is connected to the Internet, you can install the latest official Debian package by running: # apt-get install scons
Installing &SCons; on Windows Systems &SCons; provides a Windows installer that makes installation extremely easy. Download the scons-2.3.0.win32.exe file from the &SCons; download page at http://www.scons.org/download.php. Then all you need to do is execute the file (usually by clicking on its icon in Windows Explorer). These will take you through a small sequence of windows that will install &SCons; on your system.
Building and Installing &SCons; on Any System If a pre-built &SCons; package is not available for your system, then you can still easily build and install &SCons; using the native Python distutils package. The first step is to download either the scons-2.3.0.tar.gz or scons-2.3.0.zip, which are available from the SCons download page at http://www.scons.org/download.html. Unpack the archive you downloaded, using a utility like tar on Linux or UNIX, or WinZip on Windows. This will create a directory called scons-2.3.0, usually in your local directory. Then change your working directory to that directory and install &SCons; by executing the following commands: # cd scons-2.3.0 # python setup.py install This will build &SCons;, install the &scons; script in the python which is used to run the setup.py's scripts directory (/usr/local/bin or C:\Python25\Scripts), and will install the &SCons; build engine in the corresponding library directory for the python used (/usr/local/lib/scons or C:\Python25\scons). Because these are system directories, you may need root (on Linux or UNIX) or Administrator (on Windows) privileges to install &SCons; like this.
Building and Installing Multiple Versions of &SCons; Side-by-Side The &SCons; setup.py script has some extensions that support easy installation of multiple versions of &SCons; in side-by-side locations. This makes it easier to download and experiment with different versions of &SCons; before moving your official build process to a new version, for example. To install &SCons; in a version-specific location, add the option when you call setup.py: # python setup.py install --version-lib This will install the &SCons; build engine in the /usr/lib/scons-2.3.0 or C:\Python25\scons-2.3.0 directory, for example. If you use the option the first time you install &SCons;, you do not need to specify it each time you install a new version. The &SCons; setup.py script will detect the version-specific directory name(s) and assume you want to install all versions in version-specific directories. You can override that assumption in the future by explicitly specifying the option.
Installing &SCons; in Other Locations You can install &SCons; in locations other than the default by specifying the option: # python setup.py install --prefix=/opt/scons This would install the scons script in /opt/scons/bin and the build engine in /opt/scons/lib/scons, Note that you can specify both the and the options at the same type, in which case setup.py will install the build engine in a version-specific directory relative to the specified prefix. Adding to the above example would install the build engine in /opt/scons/lib/scons-2.3.0.
Building and Installing &SCons; Without Administrative Privileges If you don't have the right privileges to install &SCons; in a system location, simply use the --prefix= option to install it in a location of your choosing. For example, to install &SCons; in appropriate locations relative to the user's $HOME directory, the &scons; script in $HOME/bin and the build engine in $HOME/lib/scons, simply type: $ python setup.py install --prefix=$HOME You may, of course, specify any other location you prefer, and may use the option if you would like to install version-specific directories relative to the specified prefix. This can also be used to experiment with a newer version of &SCons; than the one installed in your system locations. Of course, the location in which you install the newer version of the &scons; script ($HOME/bin in the above example) must be configured in your &PATH; variable before the directory containing the system-installed version of the &scons; script.
scons-doc-2.3.0/doc/user/python.xml0000644000175000017500000000632512114661557020016 0ustar dktrkranzdktrkranz scons-doc-2.3.0/doc/user/output.xml0000644000175000017500000004741412114661557020041 0ustar dktrkranzdktrkranz A key aspect of creating a usable build configuration is providing good output from the build so its users can readily understand what the build is doing and get information about how to control the build. &SCons; provides several ways of controlling output from the build configuration to help make the build more useful and understandable.
Providing Build Help: the &Help; Function It's often very useful to be able to give users some help that describes the specific targets, build options, etc., that can be used for your build. &SCons; provides the &Help; function to allow you to specify this help text: Help(""" Type: 'scons program' to build the production program, 'scons debug' to build the debug version. """) (Note the above use of the Python triple-quote syntax, which comes in very handy for specifying multi-line strings like help text.) When the &SConstruct; or &SConscript; files contain such a call to the &Help; function, the specified help text will be displayed in response to the &SCons; -h option: % scons -h scons: Reading SConscript files ... scons: done reading SConscript files. Type: 'scons program' to build the production program, 'scons debug' to build the debug version. Use scons -H for help about command-line options. The &SConscript; files may contain multiple calls to the &Help; function, in which case the specified text(s) will be concatenated when displayed. This allows you to split up the help text across multiple &SConscript; files. In this situation, the order in which the &SConscript; files are called will determine the order in which the &Help; functions are called, which will determine the order in which the various bits of text will get concatenated. Another use would be to make the help text conditional on some variable. For example, suppose you only want to display a line about building a Windows-only version of a program when actually run on Windows. The following &SConstruct; file: env = Environment() Help("\nType: 'scons program' to build the production program.\n") if env['PLATFORM'] == 'win32': Help("\nType: 'scons windebug' to build the Windows debug version.\n") Will display the complete help text on Windows: C:\>scons -h scons: Reading SConscript files ... scons: done reading SConscript files. Type: 'scons program' to build the production program. Type: 'scons windebug' to build the Windows debug version. Use scons -H for help about command-line options. But only show the relevant option on a Linux or UNIX system: % scons -h scons: Reading SConscript files ... scons: done reading SConscript files. Type: 'scons program' to build the production program. Use scons -H for help about command-line options. If there is no &Help; text in the &SConstruct; or &SConscript; files, &SCons; will revert to displaying its standard list that describes the &SCons; command-line options. This list is also always displayed whenever the -H option is used.
Controlling How &SCons; Prints Build Commands: the <envar>$*COMSTR</envar> Variables Sometimes the commands executed to compile object files or link programs (or build other targets) can get very long, long enough to make it difficult for users to distinguish error messages or other important build output from the commands themselves. All of the default $*COM variables that specify the command lines used to build various types of target files have a corresponding $*COMSTR variable that can be set to an alternative string that will be displayed when the target is built. For example, suppose you want to have &SCons; display a "Compiling" message whenever it's compiling an object file, and a "Linking" when it's linking an executable. You could write a &SConstruct; file that looks like: env = Environment(CCCOMSTR = "Compiling $TARGET", LINKCOMSTR = "Linking $TARGET") env.Program('foo.c') Which would then yield the output: % scons -Q Compiling foo.o Linking foo &SCons; performs complete variable substitution on $*COMSTR variables, so they have access to all of the standard variables like &cv-TARGET; &cv-SOURCES;, etc., as well as any construction variables that happen to be configured in the construction environment used to build a specific target. Of course, sometimes it's still important to be able to see the exact command that &SCons; will execute to build a target. For example, you may simply need to verify that &SCons; is configured to supply the right options to the compiler, or a developer may want to cut-and-paste a compile command to add a few options for a custom test. One common way to give users control over whether or not &SCons; should print the actual command line or a short, configured summary is to add support for a VERBOSE command-line variable to your &SConstruct; file. A simple configuration for this might look like: env = Environment() if ARGUMENTS.get('VERBOSE') != "1': env['CCCOMSTR'] = "Compiling $TARGET" env['LINKCOMSTR'] = "Linking $TARGET" env.Program('foo.c') By only setting the appropriate $*COMSTR variables if the user specifies VERBOSE=1 on the command line, the user has control over how &SCons; displays these particular command lines: % scons -Q Compiling foo.o Linking foo % scons -Q -c Removed foo.o Removed foo % scons -Q VERBOSE=1 cc -o foo.o -c foo.c cc -o foo foo.o
Providing Build Progress Output: the &Progress; Function Another aspect of providing good build output is to give the user feedback about what &SCons; is doing even when nothing is being built at the moment. This can be especially true for large builds when most of the targets are already up-to-date. Because &SCons; can take a long time making absolutely sure that every target is, in fact, up-to-date with respect to a lot of dependency files, it can be easy for users to mistakenly conclude that &SCons; is hung or that there is some other problem with the build. One way to deal with this perception is to configure &SCons; to print something to let the user know what it's "thinking about." The &Progress; function allows you to specify a string that will be printed for every file that &SCons; is "considering" while it is traversing the dependency graph to decide what targets are or are not up-to-date. Progress('Evaluating $TARGET\n') Program('f1.c') Program('f2.c') Note that the &Progress; function does not arrange for a newline to be printed automatically at the end of the string (as does the Python print statement), and we must specify the \n that we want printed at the end of the configured string. This configuration, then, will have &SCons; print that it is Evaluating each file that it encounters in turn as it traverses the dependency graph: % scons -Q Evaluating SConstruct Evaluating f1.c Evaluating f1.o cc -o f1.o -c f1.c Evaluating f1 cc -o f1 f1.o Evaluating f2.c Evaluating f2.o cc -o f2.o -c f2.c Evaluating f2 cc -o f2 f2.o Evaluating . Of course, normally you don't want to add all of these additional lines to your build output, as that can make it difficult for the user to find errors or other important messages. A more useful way to display this progress might be to have the file names printed directly to the user's screen, not to the same standard output stream where build output is printed, and to use a carriage return character (\r) so that each file name gets re-printed on the same line. Such a configuration would look like: Progress('$TARGET\r', file=open('/dev/tty', 'w'), overwrite=True) Program('f1.c') Program('f2.c') Note that we also specified the overwrite=True argument to the &Progress; function, which causes &SCons; to "wipe out" the previous string with space characters before printing the next &Progress; string. Without the overwrite=True argument, a shorter file name would not overwrite all of the charactes in a longer file name that precedes it, making it difficult to tell what the actual file name is on the output. Also note that we opened up the /dev/tty file for direct access (on POSIX) to the user's screen. On Windows, the equivalent would be to open the con: file name. Also, it's important to know that although you can use $TARGET to substitute the name of the node in the string, the &Progress; function does not perform general variable substitution (because there's not necessarily a construction environment involved in evaluating a node like a source file, for example). You can also specify a list of strings to the &Progress; function, in which case &SCons; will display each string in turn. This can be used to implement a "spinner" by having &SCons; cycle through a sequence of strings: Progress(['-\r', '\\\r', '|\r', '/\r'], interval=5) Program('f1.c') Program('f2.c') Note that here we have also used the interval= keyword argument to have &SCons; only print a new "spinner" string once every five evaluated nodes. Using an interval= count, even with strings that use $TARGET like our examples above, can be a good way to lessen the work that &SCons; expends printing &Progress; strings, while still giving the user feedback that indicates &SCons; is still working on evaluating the build. Lastly, you can have direct control over how to print each evaluated node by passing a Python function (or other Python callable) to the &Progress; function. Your function will be called for each evaluated node, allowing you to implement more sophisticated logic like adding a counter: screen = open('/dev/tty', 'w') count = 0 def progress_function(node) count += 1 screen.write('Node %4d: %s\r' % (count, node)) Progress(progress_function) Of course, if you choose, you could completely ignore the node argument to the function, and just print a count, or anything else you wish. (Note that there's an obvious follow-on question here: how would you find the total number of nodes that will be evaluated so you can tell the user how close the build is to finishing? Unfortunately, in the general case, there isn't a good way to do that, short of having &SCons; evaluate its dependency graph twice, first to count the total and the second time to actually build the targets. This would be necessary because you can't know in advance which target(s) the user actually requested to be built. The entire build may consist of thousands of Nodes, for example, but maybe the user specifically requested that only a single object file be built.)
Printing Detailed Build Status: the &GetBuildFailures; Function SCons, like most build tools, returns zero status to the shell on success and nonzero status on failure. Sometimes it's useful to give more information about the build status at the end of the run, for instance to print an informative message, send an email, or page the poor slob who broke the build. SCons provides a &GetBuildFailures; method that you can use in a python atexit function to get a list of objects describing the actions that failed while attempting to build targets. There can be more than one if you're using -j. Here's a simple example: import atexit def print_build_failures(): from SCons.Script import GetBuildFailures for bf in GetBuildFailures(): print "%s failed: %s" % (bf.node, bf.errstr) atexit.register(print_build_failures) The atexit.register call registers print_build_failures as an atexit callback, to be called before &SCons; exits. When that function is called, it calls &GetBuildFailures; to fetch the list of failed objects. See the man page for the detailed contents of the returned objects; some of the more useful attributes are .node, .errstr, .filename, and .command. The filename is not necessarily the same file as the node; the node is the target that was being built when the error occurred, while the filenameis the file or dir that actually caused the error. Note: only call &GetBuildFailures; at the end of the build; calling it at any other time is undefined. Here is a more complete example showing how to turn each element of &GetBuildFailures; into a string: # Make the build fail if we pass fail=1 on the command line if ARGUMENTS.get('fail', 0): Command('target', 'source', ['/bin/false']) def bf_to_str(bf): """Convert an element of GetBuildFailures() to a string in a useful way.""" import SCons.Errors if bf is None: # unknown targets product None in list return '(unknown tgt)' elif isinstance(bf, SCons.Errors.StopError): return str(bf) elif bf.node: return str(bf.node) + ': ' + bf.errstr elif bf.filename: return bf.filename + ': ' + bf.errstr return 'unknown failure: ' + bf.errstr import atexit def build_status(): """Convert the build status to a 2-tuple, (status, msg).""" from SCons.Script import GetBuildFailures bf = GetBuildFailures() if bf: # bf is normally a list of build failures; if an element is None, # it's because of a target that scons doesn't know anything about. status = 'failed' failures_message = "\n".join(["Failed building %s" % bf_to_str(x) for x in bf if x is not None]) else: # if bf is None, the build completed successfully. status = 'ok' failures_message = '' return (status, failures_message) def display_build_status(): """Display the build status. Called by atexit. Here you could do all kinds of complicated things.""" status, failures_message = build_status() if status == 'failed': print "FAILED!!!!" # could display alert, ring bell, etc. elif status == 'ok': print "Build succeeded." print failures_message atexit.register(display_build_status) When this runs, you'll see the appropriate output: % scons -Q scons: `.' is up to date. Build succeeded. % scons -Q fail=1 scons: *** [target] Source `source' not found, needed by target `target'. FAILED!!!! Failed building target: Source `source' not found, needed by target `target'.
scons-doc-2.3.0/doc/user/ant.xml0000644000175000017500000000261712114661557017257 0ustar dktrkranzdktrkranz XXX
Differences Between &Ant; and &SCons; XXX
Advantages of &SCons; Over &Ant; XXX
scons-doc-2.3.0/doc/user/depends.xml0000644000175000017500000014771512114661557020130 0ustar dktrkranzdktrkranz So far we've seen how &SCons; handles one-time builds. But one of the main functions of a build tool like &SCons; is to rebuild only what is necessary when source files change--or, put another way, &SCons; should not waste time rebuilding things that don't need to be rebuilt. You can see this at work simply by re-invoking &SCons; after building our simple &hello; example: % scons -Q cc -o hello.o -c hello.c cc -o hello hello.o % scons -Q scons: `.' is up to date. The second time it is executed, &SCons; realizes that the &hello; program is up-to-date with respect to the current &hello_c; source file, and avoids rebuilding it. You can see this more clearly by naming the &hello; program explicitly on the command line: % scons -Q hello cc -o hello.o -c hello.c cc -o hello hello.o % scons -Q hello scons: `hello' is up to date. Note that &SCons; reports "...is up to date" only for target files named explicitly on the command line, to avoid cluttering the output.
Deciding When an Input File Has Changed: the &Decider; Function Another aspect of avoiding unnecessary rebuilds is the fundamental build tool behavior of rebuilding things when an input file changes, so that the built software is up to date. By default, &SCons; keeps track of this through an MD5 &signature;, or checksum, of the contents of each file, although you can easily configure &SCons; to use the modification times (or time stamps) instead. You can even specify your own Python function for deciding if an input file has changed.
Using MD5 Signatures to Decide if a File Has Changed By default, &SCons; keeps track of whether a file has changed based on an MD5 checksum of the file's contents, not the file's modification time. This means that you may be surprised by the default &SCons; behavior if you are used to the &Make; convention of forcing a rebuild by updating the file's modification time (using the &touch; command, for example): % scons -Q hello cc -o hello.o -c hello.c cc -o hello hello.o % touch hello.c % scons -Q hello scons: `hello' is up to date. Even though the file's modification time has changed, &SCons; realizes that the contents of the &hello_c; file have not changed, and therefore that the &hello; program need not be rebuilt. This avoids unnecessary rebuilds when, for example, someone rewrites the contents of a file without making a change. But if the contents of the file really do change, then &SCons; detects the change and rebuilds the program as required: % scons -Q hello cc -o hello.o -c hello.c cc -o hello hello.o % edit hello.c [CHANGE THE CONTENTS OF hello.c] % scons -Q hello cc -o hello.o -c hello.c cc -o hello hello.o Note that you can, if you wish, specify this default behavior (MD5 signatures) explicitly using the &Decider; function as follows: Program('hello.c') Decider('MD5') You can also use the string 'content' as a synonym for 'MD5' when calling the &Decider; function.
Ramifications of Using MD5 Signatures Using MD5 signatures to decide if an input file has changed has one surprising benefit: if a source file has been changed in such a way that the contents of the rebuilt target file(s) will be exactly the same as the last time the file was built, then any "downstream" target files that depend on the rebuilt-but-not-changed target file actually need not be rebuilt. So if, for example, a user were to only change a comment in a &hello_c; file, then the rebuilt &hello_o; file would be exactly the same as the one previously built (assuming the compiler doesn't put any build-specific information in the object file). &SCons; would then realize that it would not need to rebuild the &hello; program as follows: % scons -Q hello cc -o hello.o -c hello.c cc -o hello hello.o % edit hello.c [CHANGE A COMMENT IN hello.c] % scons -Q hello cc -o hello.o -c hello.c scons: `hello' is up to date. In essence, &SCons; "short-circuits" any dependent builds when it realizes that a target file has been rebuilt to exactly the same file as the last build. This does take some extra processing time to read the contents of the target (&hello_o;) file, but often saves time when the rebuild that was avoided would have been time-consuming and expensive.
Using Time Stamps to Decide If a File Has Changed If you prefer, you can configure &SCons; to use the modification time of a file, not the file contents, when deciding if a target needs to be rebuilt. &SCons; gives you two ways to use time stamps to decide if an input file has changed since the last time a target has been built. The most familiar way to use time stamps is the way &Make; does: that is, have &SCons; decide that a target must be rebuilt if a source file's modification time is newer than the target file. To do this, call the &Decider; function as follows: Object('hello.c') Decider('timestamp-newer') This makes &SCons; act like &Make; when a file's modification time is updated (using the &touch; command, for example): % scons -Q hello.o cc -o hello.o -c hello.c % touch hello.c % scons -Q hello.o cc -o hello.o -c hello.c And, in fact, because this behavior is the same as the behavior of &Make;, you can also use the string 'make' as a synonym for 'timestamp-newer' when calling the &Decider; function: Object('hello.c') Decider('make') One drawback to using times stamps exactly like &Make; is that if an input file's modification time suddenly becomes older than a target file, the target file will not be rebuilt. This can happen if an old copy of a source file is restored from a backup archive, for example. The contents of the restored file will likely be different than they were the last time a dependent target was built, but the target won't be rebuilt because the modification time of the source file is not newer than the target. Because &SCons; actually stores information about the source files' time stamps whenever a target is built, it can handle this situation by checking for an exact match of the source file time stamp, instead of just whether or not the source file is newer than the target file. To do this, specify the argument 'timestamp-match' when calling the &Decider; function: Object('hello.c') Decider('timestamp-match') When configured this way, &SCons; will rebuild a target whenever a source file's modification time has changed. So if we use the touch -t option to change the modification time of &hello_c; to an old date (January 1, 1989), &SCons; will still rebuild the target file: % scons -Q hello.o cc -o hello.o -c hello.c % touch -t 198901010000 hello.c % scons -Q hello.o cc -o hello.o -c hello.c In general, the only reason to prefer timestamp-newer instead of timestamp-match, would be if you have some specific reason to require this &Make;-like behavior of not rebuilding a target when an otherwise-modified source file is older.
Deciding If a File Has Changed Using Both MD Signatures and Time Stamps As a performance enhancement, &SCons; provides a way to use MD5 checksums of file contents but to read those contents only when the file's timestamp has changed. To do this, call the &Decider; function with 'MD5-timestamp' argument as follows: Program('hello.c') Decider('MD5-timestamp') So configured, &SCons; will still behave like it does when using Decider('MD5'): % scons -Q hello cc -o hello.o -c hello.c cc -o hello hello.o % touch hello.c % scons -Q hello scons: `hello' is up to date. % edit hello.c [CHANGE THE CONTENTS OF hello.c] % scons -Q hello cc -o hello.o -c hello.c cc -o hello hello.o However, the second call to &SCons; in the above output, when the build is up-to-date, will have been performed by simply looking at the modification time of the &hello_c; file, not by opening it and performing an MD5 checksum calcuation on its contents. This can significantly speed up many up-to-date builds. The only drawback to using Decider('MD5-timestamp') is that &SCons; will not rebuild a target file if a source file was modified within one second of the last time &SCons; built the file. While most developers are programming, this isn't a problem in practice, since it's unlikely that someone will have built and then thought quickly enough to make a substantive change to a source file within one second. Certain build scripts or continuous integration tools may, however, rely on the ability to apply changes to files automatically and then rebuild as quickly as possible, in which case use of Decider('MD5-timestamp') may not be appropriate.
Writing Your Own Custom &Decider; Function The different string values that we've passed to the &Decider; function are essentially used by &SCons; to pick one of several specific internal functions that implement various ways of deciding if a dependency (usually a source file) has changed since a target file has been built. As it turns out, you can also supply your own function to decide if a dependency has changed. For example, suppose we have an input file that contains a lot of data, in some specific regular format, that is used to rebuild a lot of different target files, but each target file really only depends on one particular section of the input file. We'd like to have each target file depend on only its section of the input file. However, since the input file may contain a lot of data, we want to open the input file only if its timestamp has changed. This could be done with a custom &Decider; function that might look something like this: Program('hello.c') def decide_if_changed(dependency, target, prev_ni): if self.get_timestamp() != prev_ni.timestamp: dep = str(dependency) tgt = str(target) if specific_part_of_file_has_changed(dep, tgt): return True return False Decider(decide_if_changed) Note that in the function definition, the dependency (input file) is the first argument, and then the ⌖. Both of these are passed to the functions as SCons &Node; objects, which we convert to strings using the Python str(). The third argument, prev_ni, is an object that holds the signature or timestamp information that was recorded about the dependency the last time the target was built. A prev_ni object can hold different information, depending on the type of thing that the dependency argument represents. For normal files, the prev_ni object has the following attributes: .csig The content signature, or MD5 checksum, of the contents of the dependency file the list time the ⌖ was built. .size The size in bytes of the dependency file the list time the target was built. .timestamp The modification time of the dependency file the list time the ⌖ was built. Note that ignoring some of the arguments in your custom &Decider; function is a perfectly normal thing to do, if they don't impact the way you want to decide if the dependency file has changed. Another thing to look out for is the fact that the three attributes above may not be present at the time of the first run. Without any prior build, no targets have been created and no .sconsign DB file exists yet. So, you should always check whether the prev_ni attribute in question is available. We finally present a small example for a csig-based decider function. Note how the signature information for the dependency file has to get initialized via get_csig during each function call (this is mandatory!). env = Environment() def config_file_decider(dependency, target, prev_ni): import os.path # We always have to init the .csig value... dep_csig = dependency.get_csig() # .csig may not exist, because no target was built yet... if 'csig' not in dir(prev_ni): return True # Target file may not exist yet if not os.path.exists(str(target.abspath)): return True if dep_csig != prev_ni.csig: # Some change on source file => update installed one return True return False def update_file(): f = open("test.txt","a") f.write("some line\n") f.close() update_file() # Activate our own decider function env.Decider(config_file_decider) env.Install("install","test.txt")
Mixing Different Ways of Deciding If a File Has Changed The previous examples have all demonstrated calling the global &Decider; function to configure all dependency decisions that &SCons; makes. Sometimes, however, you want to be able to configure different decision-making for different targets. When that's necessary, you can use the env.Decider method to affect only the configuration decisions for targets built with a specific construction environment. For example, if we arbitrarily want to build one program using MD5 checkums and another using file modification times from the same source we might configure it this way: env1 = Environment(CPPPATH = ['.']) env2 = env1.Clone() env2.Decider('timestamp-match') env1.Program('prog-MD5', 'program1.c') env2.Program('prog-timestamp', 'program2.c') If both of the programs include the same inc.h file, then updating the modification time of inc.h (using the &touch; command) will cause only prog-timestamp to be rebuilt: % scons -Q cc -o program1.o -c -I. program1.c cc -o prog-MD5 program1.o cc -o program2.o -c -I. program2.c cc -o prog-timestamp program2.o % touch inc.h % scons -Q cc -o program2.o -c -I. program2.c cc -o prog-timestamp program2.o
Older Functions for Deciding When an Input File Has Changed &SCons; still supports two functions that used to be the primary methods for configuring the decision about whether or not an input file has changed. These functions have been officially deprecated as &SCons; version 2.0, and their use is discouraged, mainly because they rely on a somewhat confusing distinction between how source files and target files are handled. These functions are documented here mainly in case you encounter them in older &SConscript; files.
The &SourceSignatures; Function The &SourceSignatures; function is fairly straightforward, and supports two different argument values to configure whether source file changes should be decided using MD5 signatures: Program('hello.c') SourceSignatures('MD5') Or using time stamps: Program('hello.c') SourceSignatures('timestamp') These are roughly equivalent to specifying Decider('MD5') or Decider('timestamp-match'), respectively, although it only affects how SCons makes decisions about dependencies on source files--that is, files that are not built from any other files.
The &TargetSignatures; Function The &TargetSignatures; function specifies how &SCons; decides when a target file has changed when it is used as a dependency of (input to) another target--that is, the &TargetSignatures; function configures how the signatures of "intermediate" target files are used when deciding if a "downstream" target file must be rebuilt. This easily-overlooked distinction between how &SCons; decides if the target itself must be rebuilt and how the target is then used to decide if a different target must be rebuilt is one of the confusing things that has led to the &TargetSignatures; and &SourceSignatures; functions being replaced by the simpler &Decider; function. The &TargetSignatures; function supports the same 'MD5' and 'timestamp' argument values that are supported by the &SourceSignatures;, with the same meanings, but applied to target files. That is, in the example: Program('hello.c') TargetSignatures('MD5') The MD5 checksum of the &hello_o; target file will be used to decide if it has changed since the last time the "downstream" &hello; target file was built. And in the example: Program('hello.c') TargetSignatures('timestamp') The modification time of the &hello_o; target file will be used to decide if it has changed since the last time the "downstream" &hello; target file was built. The &TargetSignatures; function supports two additional argument values: 'source' and 'build'. The 'source' argument specifies that decisions involving whether target files have changed since a previous build should use the same behavior for the decisions configured for source files (using the &SourceSignatures; function). So in the example: Program('hello.c') TargetSignatures('source') SourceSignatures('timestamp') All files, both targets and sources, will use modification times when deciding if an input file has changed since the last time a target was built. Lastly, the 'build' argument specifies that &SCons; should examine the build status of a target file and always rebuild a "downstream" target if the target file was itself rebuilt, without re-examining the contents or timestamp of the newly-built target file. If the target file was not rebuilt during this &scons; invocation, then the target file will be examined the same way as configured by the &SourceSignature; call to decide if it has changed. This mimics the behavior of build signatures in earlier versions of &SCons;. A &buildsignature; re-combined signatures of all the input files that went into making the target file, so that the target file itself did not need to have its contents read to compute an MD5 signature. This can improve performance for some configurations, but is generally not as effective as using Decider('MD5-timestamp').
Implicit Dependencies: The &cv-CPPPATH; Construction Variable Now suppose that our "Hello, World!" program actually has an #include line to include the &hello_h; file in the compilation: #include <hello.h> int main() { printf("Hello, %s!\n", string); } And, for completeness, the &hello_h; file looks like this: #define string "world" In this case, we want &SCons; to recognize that, if the contents of the &hello_h; file change, the &hello; program must be recompiled. To do this, we need to modify the &SConstruct; file like so: Program('hello.c', CPPPATH = '.') The &cv-link-CPPPATH; value tells &SCons; to look in the current directory ('.') for any files included by C source files (.c or .h files). With this assignment in the &SConstruct; file: % scons -Q hello cc -o hello.o -c -I. hello.c cc -o hello hello.o % scons -Q hello scons: `hello' is up to date. % edit hello.h [CHANGE THE CONTENTS OF hello.h] % scons -Q hello cc -o hello.o -c -I. hello.c cc -o hello hello.o First, notice that &SCons; added the -I. argument from the &cv-CPPPATH; variable so that the compilation would find the &hello_h; file in the local directory. Second, realize that &SCons; knows that the &hello; program must be rebuilt because it scans the contents of the &hello_c; file for the #include lines that indicate another file is being included in the compilation. &SCons; records these as implicit dependencies of the target file, Consequently, when the &hello_h; file changes, &SCons; realizes that the &hello_c; file includes it, and rebuilds the resulting &hello; program that depends on both the &hello_c; and &hello_h; files. Like the &cv-link-LIBPATH; variable, the &cv-CPPPATH; variable may be a list of directories, or a string separated by the system-specific path separation character (':' on POSIX/Linux, ';' on Windows). Either way, &SCons; creates the right command-line options so that the following example: Program('hello.c', CPPPATH = ['include', '/home/project/inc']) Will look like this on POSIX or Linux: % scons -Q hello cc -o hello.o -c -Iinclude -I/home/project/inc hello.c cc -o hello hello.o And like this on Windows: C:\>scons -Q hello.exe cl /Fohello.obj /c hello.c /nologo /Iinclude /I\home\project\inc link /nologo /OUT:hello.exe hello.obj embedManifestExeCheck(target, source, env)
Caching Implicit Dependencies Scanning each file for #include lines does take some extra processing time. When you're doing a full build of a large system, the scanning time is usually a very small percentage of the overall time spent on the build. You're most likely to notice the scanning time, however, when you rebuild all or part of a large system: &SCons; will likely take some extra time to "think about" what must be built before it issues the first build command (or decides that everything is up to date and nothing must be rebuilt). In practice, having &SCons; scan files saves time relative to the amount of potential time lost to tracking down subtle problems introduced by incorrect dependencies. Nevertheless, the "waiting time" while &SCons; scans files can annoy individual developers waiting for their builds to finish. Consequently, &SCons; lets you cache the implicit dependencies that its scanners find, for use by later builds. You can do this by specifying the &implicit-cache; option on the command line: % scons -Q --implicit-cache hello cc -o hello.o -c hello.c cc -o hello hello.o % scons -Q hello scons: `hello' is up to date. If you don't want to specify &implicit-cache; on the command line each time, you can make it the default behavior for your build by setting the &implicit_cache; option in an &SConscript; file: SetOption('implicit_cache', 1) &SCons; does not cache implicit dependencies like this by default because the &implicit-cache; causes &SCons; to simply use the implicit dependencies stored during the last run, without any checking for whether or not those dependencies are still correct. Specifically, this means &implicit-cache; instructs &SCons; to not rebuild "correctly" in the following cases: When &implicit-cache; is used, &SCons; will ignore any changes that may have been made to search paths (like &cv-CPPPATH; or &cv-LIBPATH;,). This can lead to &SCons; not rebuilding a file if a change to &cv-CPPPATH; would normally cause a different, same-named file from a different directory to be used. When &implicit-cache; is used, &SCons; will not detect if a same-named file has been added to a directory that is earlier in the search path than the directory in which the file was found last time.
The &implicit-deps-changed; Option When using cached implicit dependencies, sometimes you want to "start fresh" and have &SCons; re-scan the files for which it previously cached the dependencies. For example, if you have recently installed a new version of external code that you use for compilation, the external header files will have changed and the previously-cached implicit dependencies will be out of date. You can update them by running &SCons; with the &implicit-deps-changed; option: % scons -Q --implicit-deps-changed hello cc -o hello.o -c hello.c cc -o hello hello.o % scons -Q hello scons: `hello' is up to date. In this case, &SCons; will re-scan all of the implicit dependencies and cache updated copies of the information.
The &implicit-deps-unchanged; Option By default when caching dependencies, &SCons; notices when a file has been modified and re-scans the file for any updated implicit dependency information. Sometimes, however, you may want to force &SCons; to use the cached implicit dependencies, even if the source files changed. This can speed up a build for example, when you have changed your source files but know that you haven't changed any #include lines. In this case, you can use the &implicit-deps-unchanged; option: % scons -Q --implicit-deps-unchanged hello cc -o hello.o -c hello.c cc -o hello hello.o % scons -Q hello scons: `hello' is up to date. In this case, &SCons; will assume that the cached implicit dependencies are correct and will not bother to re-scan changed files. For typical builds after small, incremental changes to source files, the savings may not be very big, but sometimes every bit of improved performance counts.
Explicit Dependencies: the &Depends; Function Sometimes a file depends on another file that is not detected by an &SCons; scanner. For this situation, &SCons; allows you to specific explicitly that one file depends on another file, and must be rebuilt whenever that file changes. This is specified using the &Depends; method: hello = Program('hello.c') Depends(hello, 'other_file') % scons -Q hello cc -c hello.c -o hello.o cc -o hello hello.o % scons -Q hello scons: `hello' is up to date. % edit other_file [CHANGE THE CONTENTS OF other_file] % scons -Q hello cc -c hello.c -o hello.o cc -o hello hello.o Note that the dependency (the second argument to &Depends;) may also be a list of Node objects (for example, as returned by a call to a Builder): hello = Program('hello.c') goodbye = Program('goodbye.c') Depends(hello, goodbye) in which case the dependency or dependencies will be built before the target(s): % scons -Q hello cc -c goodbye.c -o goodbye.o cc -o goodbye goodbye.o cc -c hello.c -o hello.o cc -o hello hello.o
Dependencies From External Files: the &ParseDepends; Function &SCons; has built-in scanners for a number of languages. Sometimes these scanners fail to extract certain implicit dependencies due to limitations of the scanner implementation. The following example illustrates a case where the built-in C scanner is unable to extract the implicit dependency on a header file. #define FOO_HEADER <foo.h> #include FOO_HEADER int main() { return FOO; } % scons -Q cc -o hello.o -c -I. hello.c cc -o hello hello.o % edit foo.h [CHANGE CONTENTS OF foo.h] % scons -Q scons: `.' is up to date. Apparently, the scanner does not know about the header dependency. Being not a full-fledged C preprocessor, the scanner does not expand the macro. In these cases, you may also use the compiler to extract the implicit dependencies. &ParseDepends; can parse the contents of the compiler output in the style of &Make;, and explicitly establish all of the listed dependencies. The following example uses &ParseDepends; to process a compiler generated dependency file which is generated as a side effect during compilation of the object file: obj = Object('hello.c', CCFLAGS='-MD -MF hello.d', CPPPATH='.') SideEffect('hello.d', obj) ParseDepends('hello.d') Program('hello', obj) % scons -Q cc -o hello.o -c -MD -MF hello.d -I. hello.c cc -o hello hello.o % edit foo.h [CHANGE CONTENTS OF foo.h] % scons -Q cc -o hello.o -c -MD -MF hello.d -I. hello.c Parsing dependencies from a compiler-generated .d file has a chicken-and-egg problem, that causes unnecessary rebuilds: % scons -Q cc -o hello.o -c -MD -MF hello.d -I. hello.c cc -o hello hello.o % scons -Q --debug=explain scons: rebuilding `hello.o' because `foo.h' is a new dependency cc -o hello.o -c -MD -MF hello.d -I. hello.c % scons -Q scons: `.' is up to date. In the first pass, the dependency file is generated while the object file is compiled. At that time, &SCons; does not know about the dependency on foo.h. In the second pass, the object file is regenerated because foo.h is detected as a new dependency. &ParseDepends; immediately reads the specified file at invocation time and just returns if the file does not exist. A dependency file generated during the build process is not automatically parsed again. Hence, the compiler-extracted dependencies are not stored in the signature database during the same build pass. This limitation of &ParseDepends; leads to unnecessary recompilations. Therefore, &ParseDepends; should only be used if scanners are not available for the employed language or not powerful enough for the specific task.
Ignoring Dependencies: the &Ignore; Function Sometimes it makes sense to not rebuild a program, even if a dependency file changes. In this case, you would tell &SCons; specifically to ignore a dependency as follows: hello_obj=Object('hello.c') hello = Program(hello_obj) Ignore(hello_obj, 'hello.h') % scons -Q hello cc -c -o hello.o hello.c cc -o hello hello.o % scons -Q hello scons: `hello' is up to date. % edit hello.h [CHANGE THE CONTENTS OF hello.h] % scons -Q hello scons: `hello' is up to date. Now, the above example is a little contrived, because it's hard to imagine a real-world situation where you wouldn't want to rebuild &hello; if the &hello_h; file changed. A more realistic example might be if the &hello; program is being built in a directory that is shared between multiple systems that have different copies of the &stdio_h; include file. In that case, &SCons; would notice the differences between the different systems' copies of &stdio_h; and would rebuild &hello; each time you change systems. You could avoid these rebuilds as follows: hello = Program('hello.c', CPPPATH=['/usr/include']) Ignore(hello, '/usr/include/stdio.h') &Ignore; can also be used to prevent a generated file from being built by default. This is due to the fact that directories depend on their contents. So to ignore a generated file from the default build, you specify that the directory should ignore the generated file. Note that the file will still be built if the user specifically requests the target on scons command line, or if the file is a dependency of another file which is requested and/or is built by default. hello_obj=Object('hello.c') hello = Program(hello_obj) Ignore('.',[hello,hello_obj]) % scons -Q scons: `.' is up to date. % scons -Q hello cc -o hello.o -c hello.c cc -o hello hello.o % scons -Q hello scons: `hello' is up to date.
Order-Only Dependencies: the &Requires; Function Occasionally, it may be useful to specify that a certain file or directory must, if necessary, be built or created before some other target is built, but that changes to that file or directory do not require that the target itself be rebuilt. Such a relationship is called an order-only dependency because it only affects the order in which things must be built--the dependency before the target--but it is not a strict dependency relationship because the target should not change in response to changes in the dependent file. For example, suppose that you want to create a file every time you run a build that identifies the time the build was performed, the version number, etc., and which is included in every program that you build. The version file's contents will change every build. If you specify a normal dependency relationship, then every program that depends on that file would be rebuilt every time you ran &SCons;. For example, we could use some Python code in a &SConstruct; file to create a new version.c file with a string containing the current date every time we run &SCons;, and then link a program with the resulting object file by listing version.c in the sources: import time version_c_text = """ char *date = "%s"; """ % time.ctime(time.time()) open('version.c', 'w').write(version_c_text) hello = Program(['hello.c', 'version.c']) If we list version.c as an actual source file, though, then the version.o file will get rebuilt every time we run &SCons; (because the &SConstruct; file itself changes the contents of version.c) and the hello executable will get re-linked every time (because the version.o file changes): % scons -Q hello cc -o hello.o -c hello.c cc -o version.o -c version.c cc -o hello hello.o version.o % sleep 1 % scons -Q hello cc -o version.o -c version.c cc -o hello hello.o version.o % sleep 1 % scons -Q hello cc -o version.o -c version.c cc -o hello hello.o version.o (Note that for the above example to work, we &sleep; for one second in between each run, so that the &SConstruct; file will create a version.c file with a time string that's one second later than the previous run.) One solution is to use the &Requires; function to specify that the version.o must be rebuilt before it is used by the link step, but that changes to version.o should not actually cause the hello executable to be re-linked: import time version_c_text = """ char *date = "%s"; """ % time.ctime(time.time()) open('version.c', 'w').write(version_c_text) version_obj = Object('version.c') hello = Program('hello.c', LINKFLAGS = str(version_obj[0])) Requires(hello, version_obj) Notice that because we can no longer list version.c as one of the sources for the hello program, we have to find some other way to get it into the link command line. For this example, we're cheating a bit and stuffing the object file name (extracted from version_obj list returned by the &b-Object; call) into the &cv-link-LINKFLAGS; variable, because &cv-LINKFLAGS; is already included in the &cv-link-LINKCOM; command line. With these changes, we get the desired behavior of only re-linking the hello executable when the hello.c has changed, even though the version.o is rebuilt (because the &SConstruct; file still changes the version.c contents directly each run): % scons -Q hello cc -o version.o -c version.c cc -o hello.o -c hello.c cc -o hello version.o hello.o % sleep 1 % scons -Q hello cc -o version.o -c version.c scons: `hello' is up to date. % sleep 1 % edit hello.c [CHANGE THE CONTENTS OF hello.c] % scons -Q hello cc -o version.o -c version.c cc -o hello.o -c hello.c cc -o hello version.o hello.o % sleep 1 % scons -Q hello cc -o version.o -c version.c scons: `hello' is up to date.
The &AlwaysBuild; Function How &SCons; handles dependencies can also be affected by the &AlwaysBuild; method. When a file is passed to the &AlwaysBuild; method, like so: hello = Program('hello.c') AlwaysBuild(hello) Then the specified target file (&hello; in our example) will always be considered out-of-date and rebuilt whenever that target file is evaluated while walking the dependency graph: % scons -Q cc -o hello.o -c hello.c cc -o hello hello.o % scons -Q cc -o hello hello.o The &AlwaysBuild; function has a somewhat misleading name, because it does not actually mean the target file will be rebuilt every single time &SCons; is invoked. Instead, it means that the target will, in fact, be rebuilt whenever the target file is encountered while evaluating the targets specified on the command line (and their dependencies). So specifying some other target on the command line, a target that does not itself depend on the &AlwaysBuild; target, will still be rebuilt only if it's out-of-date with respect to its dependencies: % scons -Q cc -o hello.o -c hello.c cc -o hello hello.o % scons -Q hello.o scons: `hello.o' is up to date.
scons-doc-2.3.0/doc/user/simple.in0000644000175000017500000003355312114661557017577 0ustar dktrkranzdktrkranz In this chapter, you will see several examples of very simple build configurations using &SCons;, which will demonstrate how easy it is to use &SCons; to build programs from several different programming languages on different types of systems.
Building Simple C / C++ Programs Here's the famous "Hello, World!" program in C: int main() { printf("Hello, world!\n"); } And here's how to build it using &SCons;. Enter the following into a file named &SConstruct;: Program('hello.c') int main() { printf("Hello, world!\n"); } This minimal configuration file gives &SCons; two pieces of information: what you want to build (an executable program), and the input file from which you want it built (the hello.c file). &b-link-Program; is a builder_method, a Python call that tells &SCons; that you want to build an executable program. That's it. Now run the &scons; command to build the program. On a POSIX-compliant system like Linux or UNIX, you'll see something like: scons On a Windows system with the Microsoft Visual C++ compiler, you'll see something like: scons First, notice that you only need to specify the name of the source file, and that &SCons; correctly deduces the names of the object and executable files to be built from the base of the source file name. Second, notice that the same input &SConstruct; file, without any changes, generates the correct output file names on both systems: hello.o and hello on POSIX systems, hello.obj and hello.exe on Windows systems. This is a simple example of how &SCons; makes it extremely easy to write portable software builds. (Note that we won't provide duplicate side-by-side POSIX and Windows output for all of the examples in this guide; just keep in mind that, unless otherwise specified, any of the examples should work equally well on both types of systems.)
Building Object Files The &b-link-Program; builder method is only one of many builder methods that &SCons; provides to build different types of files. Another is the &b-link-Object; builder method, which tells &SCons; to build an object file from the specified source file: Object('hello.c') int main() { printf("Hello, world!\n"); } Now when you run the &scons; command to build the program, it will build just the &hello_o; object file on a POSIX system: scons And just the &hello_obj; object file on a Windows system (with the Microsoft Visual C++ compiler): scons
Simple Java Builds &SCons; also makes building with Java extremely easy. Unlike the &b-link-Program; and &b-link-Object; builder methods, however, the &b-link-Java; builder method requires that you specify the name of a destination directory in which you want the class files placed, followed by the source directory in which the .java files live: Java('classes', 'src') public class Example1 { public static void main(String[] args) { System.out.println("Hello Java world!\n"); } } If the src directory contains a single hello.java file, then the output from running the &scons; command would look something like this (on a POSIX system): scons We'll cover Java builds in more detail, including building Java archive (.jar) and other types of file, in .
Cleaning Up After a Build When using &SCons;, it is unnecessary to add special commands or target names to clean up after a build. Instead, you simply use the -c or --clean option when you invoke &SCons;, and &SCons; removes the appropriate built files. So if we build our example above and then invoke scons -c afterwards, the output on POSIX looks like: Program('hello.c') int main() { printf("Hello, world!\n"); } scons scons -c And the output on Windows looks like: scons scons -c Notice that &SCons; changes its output to tell you that it is Cleaning targets ... and done cleaning targets.
The &SConstruct; File If you're used to build systems like &Make; you've already figured out that the &SConstruct; file is the &SCons; equivalent of a &Makefile;. That is, the &SConstruct; file is the input file that &SCons; reads to control the build.
&SConstruct; Files Are Python Scripts There is, however, an important difference between an &SConstruct; file and a &Makefile;: the &SConstruct; file is actually a Python script. If you're not already familiar with Python, don't worry. This User's Guide will introduce you step-by-step to the relatively small amount of Python you'll need to know to be able to use &SCons; effectively. And Python is very easy to learn. One aspect of using Python as the scripting language is that you can put comments in your &SConstruct; file using Python's commenting convention; that is, everything between a '#' and the end of the line will be ignored: # Arrange to build the "hello" program. Program('hello.c') # "hello.c" is the source file. You'll see throughout the remainder of this Guide that being able to use the power of a real scripting language can greatly simplify the solutions to complex requirements of real-world builds.
&SCons; Functions Are Order-Independent One important way in which the &SConstruct; file is not exactly like a normal Python script, and is more like a &Makefile;, is that the order in which the &SCons; functions are called in the &SConstruct; file does not affect the order in which &SCons; actually builds the programs and object files you want it to build. In programming parlance, the &SConstruct; file is declarative, meaning you tell &SCons; what you want done and let it figure out the order in which to do it, rather than strictly imperative, where you specify explicitly the order in which to do things. In other words, when you call the &b-link-Program; builder (or any other builder method), you're not telling &SCons; to build the program at the instant the builder method is called. Instead, you're telling &SCons; to build the program that you want, for example, a program built from a file named &hello_c;, and it's up to &SCons; to build that program (and any other files) whenever it's necessary. (We'll learn more about how &SCons; decides when building or rebuilding a file is necessary in , below.) &SCons; reflects this distinction between calling a builder method like &b-Program; and actually building the program by printing the status messages that indicate when it's "just reading" the &SConstruct; file, and when it's actually building the target files. This is to make it clear when &SCons; is executing the Python statements that make up the &SConstruct; file, and when &SCons; is actually executing the commands or other actions to build the necessary files. Let's clarify this with an example. Python has a print statement that prints a string of characters to the screen. If we put print statements around our calls to the &b-Program; builder method: print "Calling Program('hello.c')" Program('hello.c') print "Calling Program('goodbye.c')" Program('goodbye.c') print "Finished calling Program()" int main() { printf("Hello, world!\n"); } int main() { printf("Goodbye, world!\n"); } Then when we execute &SCons;, we see the output from the print statements in between the messages about reading the &SConscript; files, indicating that that is when the Python statements are being executed: scons Notice also that &SCons; built the &goodbye; program first, even though the "reading &SConscript;" output shows that we called Program('hello.c') first in the &SConstruct; file.
Making the &SCons; Output Less Verbose You've already seen how &SCons; prints some messages about what it's doing, surrounding the actual commands used to build the software: scons These messages emphasize the order in which &SCons; does its work: all of the configuration files (generically referred to as &SConscript; files) are read and executed first, and only then are the target files built. Among other benefits, these messages help to distinguish between errors that occur while the configuration files are read, and errors that occur while targets are being built. One drawback, of course, is that these messages clutter the output. Fortunately, they're easily disabled by using the &Q; option when invoking &SCons;: scons -Q Because we want this User's Guide to focus on what &SCons; is actually doing, we're going to use the &Q; option to remove these messages from the output of all the remaining examples in this Guide.
scons-doc-2.3.0/doc/user/gettext.in0000644000175000017500000003105112114661557017761 0ustar dktrkranzdktrkranz The &t-link-gettext; toolset supports internationalization and localization of SCons-based projects. Builders provided by &t-link-gettext; automatize generation and updates of translation files. You can manage translations and translation templates similarly to how it's done with autotools.
Prerequisites To follow examples provided in this chapter set up your operating system to support two or more languages. In following examples we use locales en_US, de_DE, and pl_PL. Ensure, that you have GNU gettext utilities installed on your system. To edit translation files you may wish to install poedit editor.
Simple project Let's start with a very simple project, the "Hello world" program for example /* hello.c */ #include <stdio.h> int main(int argc, char* argv[]) { printf("Hello world\n"); return 0; } Prepare a SConstruct to compile the program as usual. # SConstruct env = Environment() hello = Program(["hello.c"]) Now we'll convert the project to a multi-lingual one. If you don't already have GNU gettext utilities installed, install them from your preffered package repository, or download from http://ftp.gnu.org/gnu/gettext/. For the purpose of this example, you should have following three locales installed on your system: en_US, de_DE and pl_PL. On debian, for example, you may enable certain locales through dpkg-reconfigure locales. First prepare the hello.c program for internationalization. Change the previous code so it reads as follows: /* hello.c */ #include <stdio.h> #include <libintl.h> #include <locale.h> int main(int argc, char* argv[]) { bindtextdomain("hello", "locale"); setlocale(LC_ALL, ""); textdomain("hello"); printf(gettext("Hello world\n")); return 0; } Detailed recipes for such conversion can be found at http://www.gnu.org/software/gettext/manual/gettext.html#Sources. The gettext("...") has two purposes. First, it marks messages for the xgettext(1) program, which we will use to extract from the sources the messages for localization. Second, it calls the gettext library internals to translate the message at runtime. Now we shall instruct SCons how to generate and maintain translation files. For that, use the &b-link-Translate; builder and &b-link-MOFiles; builder. The first one takes source files, extracts internationalized messages from them, creates so-called POT file (translation template), and then creates PO translation files, one for each requested language. Later, during the development lifecycle, the builder keeps all these files up-to date. The &b-link-MOFiles; builder compiles the PO files to binary form. Then install the MO files under directory called locale. The completed SConstruct is as follows: # SConstruct env = Environment( tools = ['default', 'gettext'] ) hello = env.Program(["hello.c"]) env['XGETTEXTFLAGS'] = [ '--package-name=%s' % 'hello', '--package-version=%s' % '1.0', ] po = env.Translate(["pl","en", "de"], ["hello.c"], POAUTOINIT = 1) mo = env.MOFiles(po) InstallAs(["locale/en/LC_MESSAGES/hello.mo"], ["en.mo"]) InstallAs(["locale/pl/LC_MESSAGES/hello.mo"], ["pl.mo"]) InstallAs(["locale/de/LC_MESSAGES/hello.mo"], ["de.mo"]) Generate the translation files with scons po-update. You should see the output from SCons simillar to this: user@host:$ scons po-update scons: Reading SConscript files ... scons: done reading SConscript files. scons: Building targets ... Entering '/home/ptomulik/projects/tmp' xgettext --package-name=hello --package-version=1.0 -o - hello.c Leaving '/home/ptomulik/projects/tmp' Writting 'messages.pot' (new file) msginit --no-translator -l pl -i messages.pot -o pl.po Created pl.po. msginit --no-translator -l en -i messages.pot -o en.po Created en.po. msginit --no-translator -l de -i messages.pot -o de.po Created de.po. scons: done building targets. If everything is right, you should see following new files. user@host:$ ls *.po* de.po en.po messages.pot pl.po Open en.po in poedit and provide the English translation to message "Hello world\n". Do the same for de.po (deutsch) and pl.po (polish). Let the translations be, for example: en: "Welcome to beautiful world!\n" de: "Hallo Welt!\n" pl: "Witaj swiecie!\n" Now compile the project by executing scons. The output should be similar to this: user@host:$ scons scons: Reading SConscript files ... scons: done reading SConscript files. scons: Building targets ... msgfmt -c -o de.mo de.po msgfmt -c -o en.mo en.po gcc -o hello.o -c hello.c gcc -o hello hello.o Install file: "de.mo" as "locale/de/LC_MESSAGES/hello.mo" Install file: "en.mo" as "locale/en/LC_MESSAGES/hello.mo" msgfmt -c -o pl.mo pl.po Install file: "pl.mo" as "locale/pl/LC_MESSAGES/hello.mo" scons: done building targets. SCons automatically compiled the PO files to binary format MO, and the InstallAs lines installed these files under locale folder. Your program should be now ready. You may try it as follows (linux): user@host:$ LANG=en_US.UTF-8 ./hello Welcome to beautiful world user@host:$ LANG=de_DE.UTF-8 ./hello Hallo Welt user@host:$ LANG=pl_PL.UTF-8 ./hello Witaj swiecie To demonstrate the further life of translation files, let's change Polish translation (poedit pl.po) to "Witaj drogi swiecie\n". Run scons to see how scons reacts to this user@host:$scons scons: Reading SConscript files ... scons: done reading SConscript files. scons: Building targets ... msgfmt -c -o pl.mo pl.po Install file: "pl.mo" as "locale/pl/LC_MESSAGES/hello.mo" scons: done building targets. Now, open hello.c and add another one printf line with new message. /* hello.c */ #include <stdio.h> #include <libintl.h> #include <locale.h> int main(int argc, char* argv[]) { bindtextdomain("hello", "locale"); setlocale(LC_ALL, ""); textdomain("hello"); printf(gettext("Hello world\n")); printf(gettext("and good bye\n")); return 0; } Compile project with scons. This time, the msgmerge(1) program is used by SCons to update PO file. The output from compilation is like: user@host:$scons scons: Reading SConscript files ... scons: done reading SConscript files. scons: Building targets ... Entering '/home/ptomulik/projects/tmp' xgettext --package-name=hello --package-version=1.0 -o - hello.c Leaving '/home/ptomulik/projects/tmp' Writting 'messages.pot' (messages in file were outdated) msgmerge --update de.po messages.pot ... done. msgfmt -c -o de.mo de.po msgmerge --update en.po messages.pot ... done. msgfmt -c -o en.mo en.po gcc -o hello.o -c hello.c gcc -o hello hello.o Install file: "de.mo" as "locale/de/LC_MESSAGES/hello.mo" Install file: "en.mo" as "locale/en/LC_MESSAGES/hello.mo" msgmerge --update pl.po messages.pot ... done. msgfmt -c -o pl.mo pl.po Install file: "pl.mo" as "locale/pl/LC_MESSAGES/hello.mo" scons: done building targets. The next example demonstrates what happens if we change the source code in such way that the internationalized messages do not change. The answer is that none of translation files (POT, PO) are touched (i.e. no content changes, no creation/modification time changed and so on). Let's append another line to the program (after the last printf), so its code becomes: /* hello.c */ #include <stdio.h> #include <libintl.h> #include <locale.h> int main(int argc, char* argv[]) { bindtextdomain("hello", "locale"); setlocale(LC_ALL, ""); textdomain("hello"); printf(gettext("Hello world\n")); printf(gettext("and good bye\n")); printf("----------------\n"); return a; } Compile the project. You'll see on your screen user@host:$scons scons: Reading SConscript files ... scons: done reading SConscript files. scons: Building targets ... Entering '/home/ptomulik/projects/tmp' xgettext --package-name=hello --package-version=1.0 -o - hello.c Leaving '/home/ptomulik/projects/tmp' Not writting 'messages.pot' (messages in file found to be up-to-date) gcc -o hello.o -c hello.c gcc -o hello hello.o scons: done building targets. As you see, the internationalized messages ditn't change, so the POT and the rest of translation files have not even been touched.
scons-doc-2.3.0/doc/user/add-method.xml0000644000175000017500000001055712114661557020505 0ustar dktrkranzdktrkranz The &AddMethod; function is used to add a method to an environment. It's typically used to add a "pseudo-builder," a function that looks like a &Builder; but wraps up calls to multiple other &Builder;s or otherwise processes its arguments before calling one or more &Builder;s. In the following example, we want to install the program into the standard /usr/bin directory hierarchy, but also copy it into a local install/bin directory from which a package might be built: def install_in_bin_dirs(env, source): """Install source in both bin dirs""" i1 = env.Install("$BIN", source) i2 = env.Install("$LOCALBIN", source) return [i1[0], i2[0]] # Return a list, like a normal builder env = Environment(BIN='/usr/bin', LOCALBIN='#install/bin') env.AddMethod(install_in_bin_dirs, "InstallInBinDirs") env.InstallInBinDirs(Program('hello.c')) # installs hello in both bin dirs This produces the following: % scons -Q / cc -o hello.o -c hello.c cc -o hello hello.o Install file: "hello" as "/usr/bin/hello" Install file: "hello" as "install/bin/hello" As mentioned, a pseudo-builder also provides more flexibility in parsing arguments than you can get with a &Builder;. The next example shows a pseudo-builder with a named argument that modifies the filename, and a separate argument for the resource file (rather than having the builder figure it out by file extension). This example also demonstrates using the global &AddMethod; function to add a method to the global Environment class, so it will be used in all subsequently created environments. def BuildTestProg(env, testfile, resourcefile, testdir="tests"): """Build the test program; prepends "test_" to src and target, and puts target into testdir.""" srcfile = "test_%s.c" % testfile target = "%s/test_%s" % (testdir, testfile) if env['PLATFORM'] == 'win32': resfile = env.RES(resourcefile) p = env.Program(target, [srcfile, resfile]) else: p = env.Program(target, srcfile) return p AddMethod(Environment, BuildTestProg) env = Environment() env.BuildTestProg('stuff', resourcefile='res.rc') This produces the following on Linux: % scons -Q cc -o test_stuff.o -c test_stuff.c cc -o tests/test_stuff test_stuff.o And the following on Windows: C:\>scons -Q rc /fores.res res.rc cl /Fotest_stuff.obj /c test_stuff.c /nologo link /nologo /OUT:tests\test_stuff.exe test_stuff.obj res.res embedManifestExeCheck(target, source, env) Using &AddMethod; is better than just adding an instance method to a &consenv; because it gets called as a proper method, and because &AddMethod; provides for copying the method to any clones of the &consenv; instance. scons-doc-2.3.0/doc/user/repositories.xml0000644000175000017500000004176312114661557021231 0ustar dktrkranzdktrkranz Often, a software project will have one or more central repositories, directory trees that contain source code, or derived files, or both. You can eliminate additional unnecessary rebuilds of files by having &SCons; use files from one or more code repositories to build files in your local build tree.
The &Repository; Method It's often useful to allow multiple programmers working on a project to build software from source files and/or derived files that are stored in a centrally-accessible repository, a directory copy of the source code tree. (Note that this is not the sort of repository maintained by a source code management system like BitKeeper, CVS, or Subversion.) You use the &Repository; method to tell &SCons; to search one or more central code repositories (in order) for any source files and derived files that are not present in the local build tree: env = Environment() env.Program('hello.c') Repository('/usr/repository1', '/usr/repository2') Multiple calls to the &Repository; method will simply add repositories to the global list that &SCons; maintains, with the exception that &SCons; will automatically eliminate the current directory and any non-existent directories from the list.
Finding source files in repositories The above example specifies that &SCons; will first search for files under the /usr/repository1 tree and next under the /usr/repository2 tree. &SCons; expects that any files it searches for will be found in the same position relative to the top-level directory. In the above example, if the &hello_c; file is not found in the local build tree, &SCons; will search first for a /usr/repository1/hello.c file and then for a /usr/repository2/hello.c file to use in its place. So given the &SConstruct; file above, if the &hello_c; file exists in the local build directory, &SCons; will rebuild the &hello; program as normal: % scons -Q cc -o hello.o -c hello.c cc -o hello hello.o If, however, there is no local &hello_c; file, but one exists in /usr/repository1, &SCons; will recompile the &hello; program from the source file it finds in the repository: % scons -Q cc -o hello.o -c /usr/repository1/hello.c cc -o hello hello.o And similarly, if there is no local &hello_c; file and no /usr/repository1/hello.c, but one exists in /usr/repository2: % scons -Q cc -o hello.o -c /usr/repository2/hello.c cc -o hello hello.o
Finding <literal>#include</literal> files in repositories We've already seen that SCons will scan the contents of a source file for #include file names and realize that targets built from that source file also depend on the #include file(s). For each directory in the &cv-link-CPPPATH; list, &SCons; will actually search the corresponding directories in any repository trees and establish the correct dependencies on any #include files that it finds in repository directory. Unless the C compiler also knows about these directories in the repository trees, though, it will be unable to find the #include files. If, for example, the &hello_c; file in our previous example includes the &hello_h; in its current directory, and the &hello_h; only exists in the repository: % scons -Q cc -o hello.o -c hello.c hello.c:1: hello.h: No such file or directory In order to inform the C compiler about the repositories, &SCons; will add appropriate -I flags to the compilation commands for each directory in the &cv-CPPPATH; list. So if we add the current directory to the construction environment &cv-CPPPATH; like so: env = Environment(CPPPATH = ['.']) env.Program('hello.c') Repository('/usr/repository1') Then re-executing &SCons; yields: % scons -Q cc -o hello.o -c -I. -I/usr/repository1 hello.c cc -o hello hello.o The order of the -I options replicates, for the C preprocessor, the same repository-directory search path that &SCons; uses for its own dependency analysis. If there are multiple repositories and multiple &cv-CPPPATH; directories, &SCons; will add the repository directories to the beginning of each &cv-CPPPATH; directory, rapidly multiplying the number of -I flags. If, for example, the &cv-CPPPATH; contains three directories (and shorter repository path names!): env = Environment(CPPPATH = ['dir1', 'dir2', 'dir3']) env.Program('hello.c') Repository('/r1', '/r2') Then we'll end up with nine -I options on the command line, three (for each of the &cv-CPPPATH; directories) times three (for the local directory plus the two repositories): % scons -Q cc -o hello.o -c -Idir1 -I/r1/dir1 -I/r2/dir1 -Idir2 -I/r1/dir2 -I/r2/dir2 -Idir3 -I/r1/dir3 -I/r2/dir3 hello.c cc -o hello hello.o
Limitations on <literal>#include</literal> files in repositories &SCons; relies on the C compiler's -I options to control the order in which the preprocessor will search the repository directories for #include files. This causes a problem, however, with how the C preprocessor handles #include lines with the file name included in double-quotes. As we've seen, &SCons; will compile the &hello_c; file from the repository if it doesn't exist in the local directory. If, however, the &hello_c; file in the repository contains a #include line with the file name in double quotes: #include "hello.h" int main(int argc, char *argv[]) { printf(HELLO_MESSAGE); return (0); } Then the C preprocessor will always use a &hello_h; file from the repository directory first, even if there is a &hello_h; file in the local directory, despite the fact that the command line specifies -I as the first option: % scons -Q cc -o hello.o -c -I. -I/usr/repository1 /usr/repository1/hello.c cc -o hello hello.o This behavior of the C preprocessor--always search for a #include file in double-quotes first in the same directory as the source file, and only then search the -I--can not, in general, be changed. In other words, it's a limitation that must be lived with if you want to use code repositories in this way. There are three ways you can possibly work around this C preprocessor behavior: Some modern versions of C compilers do have an option to disable or control this behavior. If so, add that option to &cv-link-CFLAGS; (or &cv-link-CXXFLAGS; or both) in your construction environment(s). Make sure the option is used for all construction environments that use C preprocessing! Change all occurrences of #include "file.h" to #include <file.h>. Use of #include with angle brackets does not have the same behavior--the -I directories are searched first for #include files--which gives &SCons; direct control over the list of directories the C preprocessor will search. Require that everyone working with compilation from repositories check out and work on entire directories of files, not individual files. (If you use local wrapper scripts around your source code control system's command, you could add logic to enforce this restriction there.
Finding the &SConstruct; file in repositories &SCons; will also search in repositories for the &SConstruct; file and any specified &SConscript; files. This poses a problem, though: how can &SCons; search a repository tree for an &SConstruct; file if the &SConstruct; file itself contains the information about the pathname of the repository? To solve this problem, &SCons; allows you to specify repository directories on the command line using the -Y option: % scons -Q -Y /usr/repository1 -Y /usr/repository2 When looking for source or derived files, &SCons; will first search the repositories specified on the command line, and then search the repositories specified in the &SConstruct; or &SConscript; files.
Finding derived files in repositories If a repository contains not only source files, but also derived files (such as object files, libraries, or executables), &SCons; will perform its normal MD5 signature calculation to decide if a derived file in a repository is up-to-date, or the derived file must be rebuilt in the local build directory. For the &SCons; signature calculation to work correctly, a repository tree must contain the &sconsign; files that &SCons; uses to keep track of signature information. Usually, this would be done by a build integrator who would run &SCons; in the repository to create all of its derived files and &sconsign; files, or who would run &SCons; in a separate build directory and copy the resulting tree to the desired repository: % cd /usr/repository1 % scons -Q cc -o file1.o -c file1.c cc -o file2.o -c file2.c cc -o hello.o -c hello.c cc -o hello hello.o file1.o file2.o (Note that this is safe even if the &SConstruct; file lists /usr/repository1 as a repository, because &SCons; will remove the current build directory from its repository list for that invocation.) Now, with the repository populated, we only need to create the one local source file we're interested in working with at the moment, and use the -Y option to tell &SCons; to fetch any other files it needs from the repository: % cd $HOME/build % edit hello.c % scons -Q -Y /usr/repository1 cc -c -o hello.o hello.c cc -o hello hello.o /usr/repository1/file1.o /usr/repository1/file2.o Notice that &SCons; realizes that it does not need to rebuild local copies file1.o and file2.o files, but instead uses the already-compiled files from the repository.
Guaranteeing local copies of files If the repository tree contains the complete results of a build, and we try to build from the repository without any files in our local tree, something moderately surprising happens: % mkdir $HOME/build2 % cd $HOME/build2 % scons -Q -Y /usr/all/repository hello scons: `hello' is up-to-date. Why does &SCons; say that the &hello; program is up-to-date when there is no &hello; program in the local build directory? Because the repository (not the local directory) contains the up-to-date &hello; program, and &SCons; correctly determines that nothing needs to be done to rebuild that up-to-date copy of the file. There are, however, many times when you want to ensure that a local copy of a file always exists. A packaging or testing script, for example, may assume that certain generated files exist locally. To tell &SCons; to make a copy of any up-to-date repository file in the local build directory, use the &Local; function: env = Environment() hello = env.Program('hello.c') Local(hello) If we then run the same command, &SCons; will make a local copy of the program from the repository copy, and tell you that it is doing so: % scons -Y /usr/all/repository hello Local copy of hello from /usr/all/repository/hello scons: `hello' is up-to-date. (Notice that, because the act of making the local copy is not considered a "build" of the &hello; file, &SCons; still reports that it is up-to-date.)
scons-doc-2.3.0/doc/user/tasks.in0000644000175000017500000001021212114661557017416 0ustar dktrkranzdktrkranz There is a common set of simple tasks that many build configurations rely on as they become more complex. Most build tools have special purpose constructs for performing these tasks, but since &SConscript; files are &Python; scripts, you can use more flexible built-in &Python; services to perform these tasks. This appendix lists a number of these tasks and how to implement them in &Python; and &SCons;. Wildcard globbing to create a list of filenames files = Glob(wildcard) Filename extension substitution import os.path filename = os.path.splitext(filename)[0]+extension Appending a path prefix to a list of filenames import os.path filenames = [os.path.join(prefix, x) for x in filenames] Substituting a path prefix with another one if filename.find(old_prefix) == 0: filename = filename.replace(old_prefix, new_prefix) Filtering a filename list to exclude/retain only a specific set of extensions import os.path filenames = [x for x in filenames if os.path.splitext(x)[1] in extensions] The "backtick function": run a shell command and capture the output import os output = os.popen(command).read() Generating source code: how code can be generated and used by SCons The Copy builders here could be any arbitrary shell or python function that produces one or more files. This example shows how to create those files and use them in &SCons;. #### SConstruct env = Environment() env.Append(CPPPATH = "#") ## Header example env.Append(BUILDERS = {'Copy1' : Builder(action = 'cat < $SOURCE > $TARGET', suffix='.h', src_suffix='.bar')}) env.Copy1('test.bar') # produces test.h from test.bar. env.Program('app','main.cpp') # indirectly depends on test.bar ## Source file example env.Append(BUILDERS = {'Copy2' : Builder(action = 'cat < $SOURCE > $TARGET', suffix='.cpp', src_suffix='.bar2')}) foo = env.Copy2('foo.bar2') # produces foo.cpp from foo.bar2. env.Program('app2',['main2.cpp'] + foo) # compiles main2.cpp and foo.cpp into app2. #include "test.h" // nothing here //// main2.cpp // nothing here Where main.cpp looks like this: produces this: scons -Q scons-doc-2.3.0/doc/user/scanners.in0000644000175000017500000003114212114661557020112 0ustar dktrkranzdktrkranz &SCons; has built-in scanners that know how to look in C, Fortran and IDL source files for information about other files that targets built from those files depend on--for example, in the case of files that use the C preprocessor, the .h files that are specified using #include lines in the source. You can use the same mechanisms that &SCons; uses to create its built-in scanners to write scanners of your own for file types that &SCons; does not know how to scan "out of the box."
A Simple Scanner Example Suppose, for example, that we want to create a simple scanner for .foo files. A .foo file contains some text that will be processed, and can include other files on lines that begin with include followed by a file name: include filename.foo Scanning a file will be handled by a Python function that you must supply. Here is a function that will use the Python re module to scan for the include lines in our example: import re include_re = re.compile(r'^include\s+(\S+)$', re.M) def kfile_scan(node, env, path, arg): contents = node.get_text_contents() return env.File(include_re.findall(contents)) It is important to note that you have to return a list of File nodes from the scanner function, simple strings for the file names won't do. As in the examples we are showing here, you can use the &File; function of your current Environment in order to create nodes on the fly from a sequence of file names with relative paths. The scanner function must accept the four specified arguments and return a list of implicit dependencies. Presumably, these would be dependencies found from examining the contents of the file, although the function can perform any manipulation at all to generate the list of dependencies. node An &SCons; node object representing the file being scanned. The path name to the file can be used by converting the node to a string using the str() function, or an internal &SCons; get_text_contents() object method can be used to fetch the contents. env The construction environment in effect for this scan. The scanner function may choose to use construction variables from this environment to affect its behavior. path A list of directories that form the search path for included files for this scanner. This is how &SCons; handles the &cv-link-CPPPATH; and &cv-link-LIBPATH; variables. arg An optional argument that you can choose to have passed to this scanner function by various scanner instances. A Scanner object is created using the &Scanner; function, which typically takes an skeys argument to associate the type of file suffix with this scanner. The Scanner object must then be associated with the &cv-link-SCANNERS; construction variable of a construction environment, typically by using the &Append; method: kscan = Scanner(function = kfile_scan, skeys = ['.k']) env.Append(SCANNERS = kscan) When we put it all together, it looks like: import re include_re = re.compile(r'^include\s+(\S+)$', re.M) def kfile_scan(node, env, path): contents = node.get_text_contents() includes = include_re.findall(contents) return env.File(includes) kscan = Scanner(function = kfile_scan, skeys = ['.k']) env = Environment(ENV = {'PATH' : '__ROOT__/usr/local/bin'}) env.Append(SCANNERS = kscan) env.Command('foo', 'foo.k', 'kprocess < $SOURCES > $TARGET') include other_file other_file cat
Adding a search path to a scanner: &FindPathDirs; Many scanners need to search for included files or dependencies using a path variable; this is how &cv-link-CPPPATH; and &cv-link-LIBPATH; work. The path to search is passed to your scanner as the path argument. Path variables may be lists of nodes, semicolon-separated strings, or even contain SCons variables which need to be expanded. Fortunately, &SCons; provides the &FindPathDirs; function which itself returns a function to expand a given path (given as a SCons construction variable name) to a list of paths at the time the scanner is called. Deferring evaluation until that point allows, for instance, the path to contain $TARGET references which differ for each file scanned. Using &FindPathDirs; is quite easy. Continuing the above example, using KPATH as the construction variable with the search path (analogous to &cv-link-CPPPATH;), we just modify the &Scanner; constructor call to include a path keyword arg: kscan = Scanner(function = kfile_scan, skeys = ['.k'], path=FindPathDirs('KPATH')) FindPathDirs returns a callable object that, when called, will essentially expand the elements in env['KPATH'] and tell the scanner to search in those dirs. It will also properly add related repository and variant dirs to the search list. As a side note, the returned method stores the path in an efficient way so lookups are fast even when variable substitutions may be needed. This is important since many files get scanned in a typical build.
scons-doc-2.3.0/doc/user/parseflags.in0000644000175000017500000001200312114661557020420 0ustar dktrkranzdktrkranz &SCons; has a bewildering array of construction variables for different types of options when building programs. Sometimes you may not know exactly which variable should be used for a particular option. &SCons; construction environments have a &ParseFlags; method that takes a set of typical command-line options and distrbutes them into the appropriate construction variables. Historically, it was created to support the &ParseConfig; method, so it focuses on options used by the GNU Compiler Collection (GCC) for the C and C++ toolchains. &ParseFlags; returns a dictionary containing the options distributed into their respective construction variables. Normally, this dictionary would be passed to &MergeFlags; to merge the options into a &consenv;, but the dictionary can be edited if desired to provide additional functionality. (Note that if the flags are not going to be edited, calling &MergeFlags; with the options directly will avoid an additional step.) env = Environment() d = env.ParseFlags("-I/opt/include -L/opt/lib -lfoo") for k,v in sorted(d.items()): if v: print k, v env.MergeFlags(d) env.Program('f1.c') int main() { return 0; } scons -Q Note that if the options are limited to generic types like those above, they will be correctly translated for other platform types: scons -Q Since the assumption is that the flags are used for the GCC toolchain, unrecognized flags are placed in &cv-link-CCFLAGS; so they will be used for both C and C++ compiles: env = Environment() d = env.ParseFlags("-whatever") for k,v in sorted(d.items()): if v: print k, v env.MergeFlags(d) env.Program('f1.c') int main() { return 0; } scons -Q &ParseFlags; will also accept a (recursive) list of strings as input; the list is flattened before the strings are processed: env = Environment() d = env.ParseFlags(["-I/opt/include", ["-L/opt/lib", "-lfoo"]]) for k,v in sorted(d.items()): if v: print k, v env.MergeFlags(d) env.Program('f1.c') int main() { return 0; } scons -Q If a string begins with a "!" (an exclamation mark, often called a bang), the string is passed to the shell for execution. The output of the command is then parsed: env = Environment() d = env.ParseFlags(["!echo -I/opt/include", "!echo -L/opt/lib", "-lfoo"]) for k,v in sorted(d.items()): if v: print k, v env.MergeFlags(d) env.Program('f1.c') int main() { return 0; } scons -Q &ParseFlags; is regularly updated for new options; consult the man page for details about those currently recognized. scons-doc-2.3.0/doc/user/command-line.xml0000644000175000017500000020333612114661557021041 0ustar dktrkranzdktrkranz &SCons; provides a number of ways for the writer of the &SConscript; files to give the users who will run &SCons; a great deal of control over the build execution. The arguments that the user can specify on the command line are broken down into three types: Options Command-line options always begin with one or two - (hyphen) characters. &SCons; provides ways for you to examine and set options values from within your &SConscript; files, as well as the ability to define your own custom options. See , below. Variables Any command-line argument containing an = (equal sign) is considered a variable setting with the form variable=value. &SCons; provides direct access to all of the command-line variable settings, the ability to apply command-line variable settings to construction environments, and functions for configuring specific types of variables (Boolean values, path names, etc.) with automatic validation of the user's specified values. See , below. Targets Any command-line argument that is not an option or a variable setting (does not begin with a hyphen and does not contain an equal sign) is considered a target that the user (presumably) wants &SCons; to build. A list of Node objects representing the target or targets to build. &SCons; provides access to the list of specified targets, as well as ways to set the default list of targets from within the &SConscript; files. See , below.
Command-Line Options &SCons; has many command-line options that control its behavior. A &SCons; command-line option always begins with one or two - (hyphen) characters.
Not Having to Specify Command-Line Options Each Time: the &SCONSFLAGS; Environment Variable Users may find themselves supplying the same command-line options every time they run &SCons;. For example, you might find it saves time to specify a value of -j 2 to have &SCons; run up to two build commands in parallel. To avoid having to type -j 2 by hand every time, you can set the external environment variable &SCONSFLAGS; to a string containing command-line options that you want &SCons; to use. If, for example, you're using a POSIX shell that's compatible with the Bourne shell, and you always want &SCons; to use the -Q option, you can set the &SCONSFLAGS; environment as follows: % scons scons: Reading SConscript files ... scons: done reading SConscript files. scons: Building targets ... ... [build output] ... scons: done building targets. % export SCONSFLAGS="-Q" % scons ... [build output] ... Users of &csh;-style shells on POSIX systems can set the &SCONSFLAGS; environment as follows: $ setenv SCONSFLAGS "-Q" Windows users may typically want to set the &SCONSFLAGS; in the appropriate tab of the System Properties window.
Getting Values Set by Command-Line Options: the &GetOption; Function &SCons; provides the &GetOption; function to get the values set by the various command-line options. One common use of this is to check whether or not the -h or --help option has been specified. Normally, &SCons; does not print its help text until after it has read all of the &SConscript; files, because it's possible that help text has been added by some subsidiary &SConscript; file deep in the source tree hierarchy. Of course, reading all of the &SConscript; files takes extra time. If you know that your configuration does not define any additional help text in subsidiary &SConscript; files, you can speed up the command-line help available to users by using the &GetOption; function to load the subsidiary &SConscript; files only if the the user has not specified the -h or --help option, like so: if not GetOption('help'): SConscript('src/SConscript', export='env') In general, the string that you pass to the &GetOption; function to fetch the value of a command-line option setting is the same as the "most common" long option name (beginning with two hyphen characters), although there are some exceptions. The list of &SCons; command-line options and the &GetOption; strings for fetching them, are available in the section, below.
Setting Values of Command-Line Options: the &SetOption; Function You can also set the values of &SCons; command-line options from within the &SConscript; files by using the &SetOption; function. The strings that you use to set the values of &SCons; command-line options are available in the section, below. One use of the &SetOption; function is to specify a value for the -j or --jobs option, so that users get the improved performance of a parallel build without having to specify the option by hand. A complicating factor is that a good value for the -j option is somewhat system-dependent. One rough guideline is that the more processors your system has, the higher you want to set the -j value, in order to take advantage of the number of CPUs. For example, suppose the administrators of your development systems have standardized on setting a NUM_CPU environment variable to the number of processors on each system. A little bit of Python code to access the environment variable and the &SetOption; function provide the right level of flexibility: import os num_cpu = int(os.environ.get('NUM_CPU', 2)) SetOption('num_jobs', num_cpu) print "running with -j", GetOption('num_jobs') The above snippet of code sets the value of the --jobs option to the value specified in the $NUM_CPU environment variable. (This is one of the exception cases where the string is spelled differently from the from command-line option. The string for fetching or setting the --jobs value is num_jobs for historical reasons.) The code in this example prints the num_jobs value for illustrative purposes. It uses a default value of 2 to provide some minimal parallelism even on single-processor systems: % scons -Q running with -j 2 scons: `.' is up to date. But if the $NUM_CPU environment variable is set, then we use that for the default number of jobs: % export NUM_CPU="4" % scons -Q running with -j 4 scons: `.' is up to date. But any explicit -j or --jobs value the user specifies an the command line is used first, regardless of whether or not the $NUM_CPU environment variable is set: % scons -Q -j 7 running with -j 7 scons: `.' is up to date. % export NUM_CPU="4" % scons -Q -j 3 running with -j 3 scons: `.' is up to date.
Strings for Getting or Setting Values of &SCons; Command-Line Options The strings that you can pass to the &GetOption; and &SetOption; functions usually correspond to the first long-form option name (beginning with two hyphen characters: --), after replacing any remaining hyphen characters with underscores. The full list of strings and the variables they correspond to is as follows: String for &GetOption; and &SetOption; Command-Line Option(s) cache_debug cache_disable cache_force cache_show clean , , config directory , diskcheck duplicate file , , , help , ignore_errors implicit_cache implicit_deps_changed implicit_deps_unchanged interactive , keep_going , max_drift no_exec , , , , no_site_dir num_jobs , profile_file question , random repository , , silent , , site_dir stack_size taskmastertrace_file warn
Adding Custom Command-Line Options: the &AddOption; Function &SCons; also allows you to define your own command-line options with the &AddOption; function. The &AddOption; function takes the same arguments as the optparse.add_option function from the standard Python library. The &AddOption; function is, in fact, implemented using a subclass of the optparse.OptionParser. Once you have added a custom command-line option with the &AddOption; function, the value of the option (if any) is immediately available using the standard &GetOption; function. (The value can also be set using &SetOption;, although that's not very useful in practice because a default value can be specified in directly in the &AddOption; call.) One useful example of using this functionality is to provide a for users: AddOption('--prefix', dest='prefix', type='string', nargs=1, action='store', metavar='DIR', help='installation prefix') env = Environment(PREFIX = GetOption('prefix')) installed_foo = env.Install('$PREFIX/usr/bin', 'foo.in') Default(installed_foo) The above code uses the &GetOption; function to set the $PREFIX construction variable to any value that the user specifies with a command-line option of --prefix. Because $PREFIX will expand to a null string if it's not initialized, running &SCons; without the option of --prefix will install the file in the /usr/bin/ directory: % scons -Q -n Install file: "foo.in" as "/usr/bin/foo.in" But specifying --prefix=/tmp/install on the command line causes the file to be installed in the /tmp/install/usr/bin/ directory: % scons -Q -n --prefix=/tmp/install Install file: "foo.in" as "/tmp/install/usr/bin/foo.in"
Command-Line <varname>variable</varname>=<varname>value</varname> Build Variables You may want to control various aspects of your build by allowing the user to specify variable=value values on the command line. For example, suppose you want users to be able to build a debug version of a program by running &SCons; as follows: % scons -Q debug=1 &SCons; provides an &ARGUMENTS; dictionary that stores all of the variable=value assignments from the command line. This allows you to modify aspects of your build in response to specifications on the command line. (Note that unless you want to require that users always specify a variable, you probably want to use the Python ARGUMENTS.get() function, which allows you to specify a default value to be used if there is no specification on the command line.) The following code sets the &cv-link-CCFLAGS; construction variable in response to the debug flag being set in the &ARGUMENTS; dictionary: env = Environment() debug = ARGUMENTS.get('debug', 0) if int(debug): env.Append(CCFLAGS = '-g') env.Program('prog.c') This results in the -g compiler option being used when debug=1 is used on the command line: % scons -Q debug=0 cc -o prog.o -c prog.c cc -o prog prog.o % scons -Q debug=0 scons: `.' is up to date. % scons -Q debug=1 cc -o prog.o -c -g prog.c cc -o prog prog.o % scons -Q debug=1 scons: `.' is up to date. Notice that &SCons; keeps track of the last values used to build the object files, and as a result correctly rebuilds the object and executable files only when the value of the debug argument has changed. The &ARGUMENTS; dictionary has two minor drawbacks. First, because it is a dictionary, it can only store one value for each specified keyword, and thus only "remembers" the last setting for each keyword on the command line. This makes the &ARGUMENTS; dictionary inappropriate if users should be able to specify multiple values on the command line for a given keyword. Second, it does not preserve the order in which the variable settings were specified, which is a problem if you want the configuration to behave differently in response to the order in which the build variable settings were specified on the command line. To accomodate these requirements, &SCons; provides an &ARGLIST; variable that gives you direct access to variable=value settings on the command line, in the exact order they were specified, and without removing any duplicate settings. Each element in the &ARGLIST; variable is itself a two-element list containing the keyword and the value of the setting, and you must loop through, or otherwise select from, the elements of &ARGLIST; to process the specific settings you want in whatever way is appropriate for your configuration. For example, the following code to let the user add to the &CPPDEFINES; construction variable by specifying multiple define= settings on the command line: cppdefines = [] for key, value in ARGLIST: if key == 'define': cppdefines.append(value) env = Environment(CPPDEFINES = cppdefines) env.Object('prog.c') Yields the following output: % scons -Q define=FOO cc -o prog.o -c -DFOO prog.c % scons -Q define=FOO define=BAR cc -o prog.o -c -DFOO -DBAR prog.c Note that the &ARGLIST; and &ARGUMENTS; variables do not interfere with each other, but merely provide slightly different views into how the user specified variable=value settings on the command line. You can use both variables in the same &SCons; configuration. In general, the &ARGUMENTS; dictionary is more convenient to use, (since you can just fetch variable settings through a dictionary access), and the &ARGLIST; list is more flexible (since you can examine the specific order in which the user's command-line variabe settings).
Controlling Command-Line Build Variables Being able to use a command-line build variable like debug=1 is handy, but it can be a chore to write specific Python code to recognize each such variable, check for errors and provide appropriate messages, and apply the values to a construction variable. To help with this, &SCons; supports a class to define such build variables easily, and a mechanism to apply the build variables to a construction environment. This allows you to control how the build variables affect construction environments. For example, suppose that you want users to set a &RELEASE; construction variable on the command line whenever the time comes to build a program for release, and that the value of this variable should be added to the command line with the appropriate -D option (or other command line option) to pass the value to the C compiler. Here's how you might do that by setting the appropriate value in a dictionary for the &cv-link-CPPDEFINES; construction variable: vars = Variables(None, ARGUMENTS) vars.Add('RELEASE', 'Set to 1 to build for release', 0) env = Environment(variables = vars, CPPDEFINES={'RELEASE_BUILD' : '${RELEASE}'}) env.Program(['foo.c', 'bar.c']) This &SConstruct; file first creates a &Variables; object which uses the values from the command-line options dictionary &ARGUMENTS; (the vars = Variables(None, ARGUMENTS) call). It then uses the object's &Add; method to indicate that the &RELEASE; variable can be set on the command line, and that its default value will be 0 (the third argument to the &Add; method). The second argument is a line of help text; we'll learn how to use it in the next section. We then pass the created &Variables; object as a &variables; keyword argument to the &Environment; call used to create the construction environment. This then allows a user to set the &RELEASE; build variable on the command line and have the variable show up in the command line used to build each object from a C source file: % scons -Q RELEASE=1 cc -o bar.o -c -DRELEASE_BUILD=1 bar.c cc -o foo.o -c -DRELEASE_BUILD=1 foo.c cc -o foo foo.o bar.o NOTE: Before &SCons; release 0.98.1, these build variables were known as "command-line build options." The class was actually named the &Options; class, and in the sections below, the various functions were named &BoolOption;, &EnumOption;, &ListOption;, &PathOption;, &PackageOption; and &AddOptions;. These older names still work, and you may encounter them in older &SConscript; files, but they have been officially deprecated as of &SCons; version 2.0.
Providing Help for Command-Line Build Variables To make command-line build variables most useful, you ideally want to provide some help text that will describe the available variables when the user runs scons -h. You could write this text by hand, but &SCons; provides an easier way. &Variables; objects support a &GenerateHelpText; method that will, as its name suggests, generate text that describes the various variables that have been added to it. You then pass the output from this method to the &Help; function: vars = Variables(None, ARGUMENTS) vars.Add('RELEASE', 'Set to 1 to build for release', 0) env = Environment(variables = vars) Help(vars.GenerateHelpText(env)) &SCons; will now display some useful text when the -h option is used: % scons -Q -h RELEASE: Set to 1 to build for release default: 0 actual: 0 Use scons -H for help about command-line options. Notice that the help output shows the default value, and the current actual value of the build variable.
Reading Build Variables From a File Giving the user a way to specify the value of a build variable on the command line is useful, but can still be tedious if users must specify the variable every time they run &SCons;. We can let users provide customized build variable settings in a local file by providing a file name when we create the &Variables; object: vars = Variables('custom.py') vars.Add('RELEASE', 'Set to 1 to build for release', 0) env = Environment(variables = vars, CPPDEFINES={'RELEASE_BUILD' : '${RELEASE}'}) env.Program(['foo.c', 'bar.c']) Help(vars.GenerateHelpText(env)) This then allows the user to control the &RELEASE; variable by setting it in the &custom_py; file: RELEASE = 1 Note that this file is actually executed like a Python script. Now when we run &SCons;: % scons -Q cc -o bar.o -c -DRELEASE_BUILD=1 bar.c cc -o foo.o -c -DRELEASE_BUILD=1 foo.c cc -o foo foo.o bar.o And if we change the contents of &custom_py; to: RELEASE = 0 The object files are rebuilt appropriately with the new variable: % scons -Q cc -o bar.o -c -DRELEASE_BUILD=0 bar.c cc -o foo.o -c -DRELEASE_BUILD=0 foo.c cc -o foo foo.o bar.o Finally, you can combine both methods with: vars = Variables('custom.py', ARGUMENTS) where values in the option file &custom_py; get overwritten by the ones specified on the command line.
Pre-Defined Build Variable Functions &SCons; provides a number of functions that provide ready-made behaviors for various types of command-line build variables.
True/False Values: the &BoolVariable; Build Variable Function It's often handy to be able to specify a variable that controls a simple Boolean variable with a &true; or &false; value. It would be even more handy to accomodate users who have different preferences for how to represent &true; or &false; values. The &BoolVariable; function makes it easy to accomodate these common representations of &true; or &false;. The &BoolVariable; function takes three arguments: the name of the build variable, the default value of the build variable, and the help string for the variable. It then returns appropriate information for passing to the &Add; method of a &Variables; object, like so: vars = Variables('custom.py') vars.Add(BoolVariable('RELEASE', 'Set to build for release', 0)) env = Environment(variables = vars, CPPDEFINES={'RELEASE_BUILD' : '${RELEASE}'}) env.Program('foo.c') With this build variable, the &RELEASE; variable can now be enabled by setting it to the value yes or t: % scons -Q RELEASE=yes foo.o cc -o foo.o -c -DRELEASE_BUILD=True foo.c % scons -Q RELEASE=t foo.o cc -o foo.o -c -DRELEASE_BUILD=True foo.c Other values that equate to &true; include y, 1, on and all. Conversely, &RELEASE; may now be given a &false; value by setting it to no or f: % scons -Q RELEASE=no foo.o cc -o foo.o -c -DRELEASE_BUILD=False foo.c % scons -Q RELEASE=f foo.o cc -o foo.o -c -DRELEASE_BUILD=False foo.c Other values that equate to &false; include n, 0, off and none. Lastly, if a user tries to specify any other value, &SCons; supplies an appropriate error message: % scons -Q RELEASE=bad_value foo.o scons: *** Error converting option: RELEASE Invalid value for boolean option: bad_value File "/home/my/project/SConstruct", line 4, in <module>
Single Value From a List: the &EnumVariable; Build Variable Function Suppose that we want a user to be able to set a &COLOR; variable that selects a background color to be displayed by an application, but that we want to restrict the choices to a specific set of allowed colors. This can be set up quite easily using the &EnumVariable;, which takes a list of &allowed_values; in addition to the variable name, default value, and help text arguments: vars = Variables('custom.py') vars.Add(EnumVariable('COLOR', 'Set background color', 'red', allowed_values=('red', 'green', 'blue'))) env = Environment(variables = vars, CPPDEFINES={'COLOR' : '"${COLOR}"'}) env.Program('foo.c') The user can now explicity set the &COLOR; build variable to any of the specified allowed values: % scons -Q COLOR=red foo.o cc -o foo.o -c -DCOLOR="red" foo.c % scons -Q COLOR=blue foo.o cc -o foo.o -c -DCOLOR="blue" foo.c % scons -Q COLOR=green foo.o cc -o foo.o -c -DCOLOR="green" foo.c But, almost more importantly, an attempt to set &COLOR; to a value that's not in the list generates an error message: % scons -Q COLOR=magenta foo.o scons: *** Invalid value for option COLOR: magenta. Valid values are: ('red', 'green', 'blue') File "/home/my/project/SConstruct", line 5, in <module> The &EnumVariable; function also supports a way to map alternate names to allowed values. Suppose, for example, that we want to allow the user to use the word navy as a synonym for blue. We do this by adding a ↦ dictionary that will map its key values to the desired legal value: vars = Variables('custom.py') vars.Add(EnumVariable('COLOR', 'Set background color', 'red', allowed_values=('red', 'green', 'blue'), map={'navy':'blue'})) env = Environment(variables = vars, CPPDEFINES={'COLOR' : '"${COLOR}"'}) env.Program('foo.c') As desired, the user can then use navy on the command line, and &SCons; will translate it into blue when it comes time to use the &COLOR; variable to build a target: % scons -Q COLOR=navy foo.o cc -o foo.o -c -DCOLOR="blue" foo.c By default, when using the &EnumVariable; function, arguments that differ from the legal values only in case are treated as illegal values: % scons -Q COLOR=Red foo.o scons: *** Invalid value for option COLOR: Red. Valid values are: ('red', 'green', 'blue') File "/home/my/project/SConstruct", line 5, in <module> % scons -Q COLOR=BLUE foo.o scons: *** Invalid value for option COLOR: BLUE. Valid values are: ('red', 'green', 'blue') File "/home/my/project/SConstruct", line 5, in <module> % scons -Q COLOR=nAvY foo.o scons: *** Invalid value for option COLOR: nAvY. Valid values are: ('red', 'green', 'blue') File "/home/my/project/SConstruct", line 5, in <module> The &EnumVariable; function can take an additional &ignorecase; keyword argument that, when set to 1, tells &SCons; to allow case differences when the values are specified: vars = Variables('custom.py') vars.Add(EnumVariable('COLOR', 'Set background color', 'red', allowed_values=('red', 'green', 'blue'), map={'navy':'blue'}, ignorecase=1)) env = Environment(variables = vars, CPPDEFINES={'COLOR' : '"${COLOR}"'}) env.Program('foo.c') Which yields the output: % scons -Q COLOR=Red foo.o cc -o foo.o -c -DCOLOR="Red" foo.c % scons -Q COLOR=BLUE foo.o cc -o foo.o -c -DCOLOR="BLUE" foo.c % scons -Q COLOR=nAvY foo.o cc -o foo.o -c -DCOLOR="blue" foo.c % scons -Q COLOR=green foo.o cc -o foo.o -c -DCOLOR="green" foo.c Notice that an &ignorecase; value of 1 preserves the case-spelling that the user supplied. If you want &SCons; to translate the names into lower-case, regardless of the case used by the user, specify an &ignorecase; value of 2: vars = Variables('custom.py') vars.Add(EnumVariable('COLOR', 'Set background color', 'red', allowed_values=('red', 'green', 'blue'), map={'navy':'blue'}, ignorecase=2)) env = Environment(variables = vars, CPPDEFINES={'COLOR' : '"${COLOR}"'}) env.Program('foo.c') Now &SCons; will use values of red, green or blue regardless of how the user spells those values on the command line: % scons -Q COLOR=Red foo.o cc -o foo.o -c -DCOLOR="red" foo.c % scons -Q COLOR=nAvY foo.o cc -o foo.o -c -DCOLOR="blue" foo.c % scons -Q COLOR=GREEN foo.o cc -o foo.o -c -DCOLOR="green" foo.c
Multiple Values From a List: the &ListVariable; Build Variable Function Another way in which you might want to allow users to control a build variable is to specify a list of one or more legal values. &SCons; supports this through the &ListVariable; function. If, for example, we want a user to be able to set a &COLORS; variable to one or more of the legal list of values: vars = Variables('custom.py') vars.Add(ListVariable('COLORS', 'List of colors', 0, ['red', 'green', 'blue'])) env = Environment(variables = vars, CPPDEFINES={'COLORS' : '"${COLORS}"'}) env.Program('foo.c') A user can now specify a comma-separated list of legal values, which will get translated into a space-separated list for passing to the any build commands: % scons -Q COLORS=red,blue foo.o cc -o foo.o -c -DCOLORS="red blue" foo.c % scons -Q COLORS=blue,green,red foo.o cc -o foo.o -c -DCOLORS="blue green red" foo.c In addition, the &ListVariable; function allows the user to specify explicit keywords of &all; or &none; to select all of the legal values, or none of them, respectively: % scons -Q COLORS=all foo.o cc -o foo.o -c -DCOLORS="red green blue" foo.c % scons -Q COLORS=none foo.o cc -o foo.o -c -DCOLORS="" foo.c And, of course, an illegal value still generates an error message: % scons -Q COLORS=magenta foo.o scons: *** Error converting option: COLORS Invalid value(s) for option: magenta File "/home/my/project/SConstruct", line 5, in <module>
Path Names: the &PathVariable; Build Variable Function &SCons; supports a &PathVariable; function to make it easy to create a build variable to control an expected path name. If, for example, you need to define a variable in the preprocessor that controls the location of a configuration file: vars = Variables('custom.py') vars.Add(PathVariable('CONFIG', 'Path to configuration file', '/etc/my_config')) env = Environment(variables = vars, CPPDEFINES={'CONFIG_FILE' : '"$CONFIG"'}) env.Program('foo.c') This then allows the user to override the &CONFIG; build variable on the command line as necessary: % scons -Q foo.o cc -o foo.o -c -DCONFIG_FILE="/etc/my_config" foo.c % scons -Q CONFIG=/usr/local/etc/other_config foo.o scons: `foo.o' is up to date. By default, &PathVariable; checks to make sure that the specified path exists and generates an error if it doesn't: % scons -Q CONFIG=/does/not/exist foo.o scons: *** Path for option CONFIG does not exist: /does/not/exist File "/home/my/project/SConstruct", line 6, in <module> &PathVariable; provides a number of methods that you can use to change this behavior. If you want to ensure that any specified paths are, in fact, files and not directories, use the &PathVariable_PathIsFile; method: vars = Variables('custom.py') vars.Add(PathVariable('CONFIG', 'Path to configuration file', '/etc/my_config', PathVariable.PathIsFile)) env = Environment(variables = vars, CPPDEFINES={'CONFIG_FILE' : '"$CONFIG"'}) env.Program('foo.c') Conversely, to ensure that any specified paths are directories and not files, use the &PathVariable_PathIsDir; method: vars = Variables('custom.py') vars.Add(PathVariable('DBDIR', 'Path to database directory', '/var/my_dbdir', PathVariable.PathIsDir)) env = Environment(variables = vars, CPPDEFINES={'DBDIR' : '"$DBDIR"'}) env.Program('foo.c') If you want to make sure that any specified paths are directories, and you would like the directory created if it doesn't already exist, use the &PathVariable_PathIsDirCreate; method: vars = Variables('custom.py') vars.Add(PathVariable('DBDIR', 'Path to database directory', '/var/my_dbdir', PathVariable.PathIsDirCreate)) env = Environment(variables = vars, CPPDEFINES={'DBDIR' : '"$DBDIR"'}) env.Program('foo.c') Lastly, if you don't care whether the path exists, is a file, or a directory, use the &PathVariable_PathAccept; method to accept any path that the user supplies: vars = Variables('custom.py') vars.Add(PathVariable('OUTPUT', 'Path to output file or directory', None, PathVariable.PathAccept)) env = Environment(variables = vars, CPPDEFINES={'OUTPUT' : '"$OUTPUT"'}) env.Program('foo.c')
Enabled/Disabled Path Names: the &PackageVariable; Build Variable Function Sometimes you want to give users even more control over a path name variable, allowing them to explicitly enable or disable the path name by using yes or no keywords, in addition to allow them to supply an explicit path name. &SCons; supports the &PackageVariable; function to support this: vars = Variables('custom.py') vars.Add(PackageVariable('PACKAGE', 'Location package', '/opt/location')) env = Environment(variables = vars, CPPDEFINES={'PACKAGE' : '"$PACKAGE"'}) env.Program('foo.c') When the &SConscript; file uses the &PackageVariable; funciton, user can now still use the default or supply an overriding path name, but can now explicitly set the specified variable to a value that indicates the package should be enabled (in which case the default should be used) or disabled: % scons -Q foo.o cc -o foo.o -c -DPACKAGE="/opt/location" foo.c % scons -Q PACKAGE=/usr/local/location foo.o cc -o foo.o -c -DPACKAGE="/usr/local/location" foo.c % scons -Q PACKAGE=yes foo.o cc -o foo.o -c -DPACKAGE="True" foo.c % scons -Q PACKAGE=no foo.o cc -o foo.o -c -DPACKAGE="False" foo.c
Adding Multiple Command-Line Build Variables at Once Lastly, &SCons; provides a way to add multiple build variables to a &Variables; object at once. Instead of having to call the &Add; method multiple times, you can call the &AddVariables; method with a list of build variables to be added to the object. Each build variable is specified as either a tuple of arguments, just like you'd pass to the &Add; method itself, or as a call to one of the pre-defined functions for pre-packaged command-line build variables. in any order: vars = Variables() vars.AddVariables( ('RELEASE', 'Set to 1 to build for release', 0), ('CONFIG', 'Configuration file', '/etc/my_config'), BoolVariable('warnings', 'compilation with -Wall and similiar', 1), EnumVariable('debug', 'debug output and symbols', 'no', allowed_values=('yes', 'no', 'full'), map={}, ignorecase=0), # case sensitive ListVariable('shared', 'libraries to build as shared libraries', 'all', names = list_of_libs), PackageVariable('x11', 'use X11 installed here (yes = search some places)', 'yes'), PathVariable('qtdir', 'where the root of Qt is installed', qtdir), )
Handling Unknown Command-Line Build Variables: the &UnknownVariables; Function Users may, of course, occasionally misspell variable names in their command-line settings. &SCons; does not generate an error or warning for any unknown variables the users specifies on the command line. (This is in no small part because you may be processing the arguments directly using the &ARGUMENTS; dictionary, and therefore &SCons; can't know in the general case whether a given "misspelled" variable is really unknown and a potential problem, or something that your &SConscript; file will handle directly with some Python code.) If, however, you're using a &Variables; object to define a specific set of command-line build variables that you expect users to be able to set, you may want to provide an error message or warning of your own if the user supplies a variable setting that is not among the defined list of variable names known to the &Variables; object. You can do this by calling the &UnknownVariables; method of the &Variables; object: vars = Variables(None) vars.Add('RELEASE', 'Set to 1 to build for release', 0) env = Environment(variables = vars, CPPDEFINES={'RELEASE_BUILD' : '${RELEASE}'}) unknown = vars.UnknownVariables() if unknown: print "Unknown variables:", unknown.keys() Exit(1) env.Program('foo.c') The &UnknownVariables; method returns a dictionary containing the keywords and values of any variables the user specified on the command line that are not among the variables known to the &Variables; object (from having been specified using the &Variables; object's&Add; method). In the examble above, we check for whether the dictionary returned by the &UnknownVariables; is non-empty, and if so print the Python list containing the names of the unknwown variables and then call the &Exit; function to terminate &SCons;: % scons -Q NOT_KNOWN=foo Unknown variables: ['NOT_KNOWN'] Of course, you can process the items in the dictionary returned by the &UnknownVariables; function in any way appropriate to your build configuration, including just printing a warning message but not exiting, logging an error somewhere, etc. Note that you must delay the call of &UnknownVariables; until after you have applied the &Variables; object to a construction environment with the variables= keyword argument of an &Environment; call.
Command-Line Targets
Fetching Command-Line Targets: the &COMMAND_LINE_TARGETS; Variable &SCons; supports a &COMMAND_LINE_TARGETS; variable that lets you fetch the list of targets that the user specified on the command line. You can use the targets to manipulate the build in any way you wish. As a simple example, suppose that you want to print a reminder to the user whenever a specific program is built. You can do this by checking for the target in the &COMMAND_LINE_TARGETS; list: if 'bar' in COMMAND_LINE_TARGETS: print "Don't forget to copy `bar' to the archive!" Default(Program('foo.c')) Program('bar.c') Then, running &SCons; with the default target works as it always does, but explicity specifying the &bar; target on the command line generates the warning message: % scons -Q cc -o foo.o -c foo.c cc -o foo foo.o % scons -Q bar Don't forget to copy `bar' to the archive! cc -o bar.o -c bar.c cc -o bar bar.o Another practical use for the &COMMAND_LINE_TARGETS; variable might be to speed up a build by only reading certain subsidiary &SConscript; files if a specific target is requested.
Controlling the Default Targets: the &Default; Function One of the most basic things you can control is which targets &SCons; will build by default--that is, when there are no targets specified on the command line. As mentioned previously, &SCons; will normally build every target in or below the current directory by default--that is, when you don't explicitly specify one or more targets on the command line. Sometimes, however, you may want to specify explicitly that only certain programs, or programs in certain directories, should be built by default. You do this with the &Default; function: env = Environment() hello = env.Program('hello.c') env.Program('goodbye.c') Default(hello) This &SConstruct; file knows how to build two programs, &hello; and &goodbye;, but only builds the &hello; program by default: % scons -Q cc -o hello.o -c hello.c cc -o hello hello.o % scons -Q scons: `hello' is up to date. % scons -Q goodbye cc -o goodbye.o -c goodbye.c cc -o goodbye goodbye.o Note that, even when you use the &Default; function in your &SConstruct; file, you can still explicitly specify the current directory (.) on the command line to tell &SCons; to build everything in (or below) the current directory: % scons -Q . cc -o goodbye.o -c goodbye.c cc -o goodbye goodbye.o cc -o hello.o -c hello.c cc -o hello hello.o You can also call the &Default; function more than once, in which case each call adds to the list of targets to be built by default: env = Environment() prog1 = env.Program('prog1.c') Default(prog1) prog2 = env.Program('prog2.c') prog3 = env.Program('prog3.c') Default(prog3) Or you can specify more than one target in a single call to the &Default; function: env = Environment() prog1 = env.Program('prog1.c') prog2 = env.Program('prog2.c') prog3 = env.Program('prog3.c') Default(prog1, prog3) Either of these last two examples will build only the prog1 and prog3 programs by default: % scons -Q cc -o prog1.o -c prog1.c cc -o prog1 prog1.o cc -o prog3.o -c prog3.c cc -o prog3 prog3.o % scons -Q . cc -o prog2.o -c prog2.c cc -o prog2 prog2.o You can list a directory as an argument to &Default;: env = Environment() env.Program(['prog1/main.c', 'prog1/foo.c']) env.Program(['prog2/main.c', 'prog2/bar.c']) Default('prog1') In which case only the target(s) in that directory will be built by default: % scons -Q cc -o prog1/foo.o -c prog1/foo.c cc -o prog1/main.o -c prog1/main.c cc -o prog1/main prog1/main.o prog1/foo.o % scons -Q scons: `prog1' is up to date. % scons -Q . cc -o prog2/bar.o -c prog2/bar.c cc -o prog2/main.o -c prog2/main.c cc -o prog2/main prog2/main.o prog2/bar.o Lastly, if for some reason you don't want any targets built by default, you can use the Python None variable: env = Environment() prog1 = env.Program('prog1.c') prog2 = env.Program('prog2.c') Default(None) Which would produce build output like: % scons -Q scons: *** No targets specified and no Default() targets found. Stop. % scons -Q . cc -o prog1.o -c prog1.c cc -o prog1 prog1.o cc -o prog2.o -c prog2.c cc -o prog2 prog2.o
Fetching the List of Default Targets: the &DEFAULT_TARGETS; Variable &SCons; supports a &DEFAULT_TARGETS; variable that lets you get at the current list of default targets. The &DEFAULT_TARGETS; variable has two important differences from the &COMMAND_LINE_TARGETS; variable. First, the &DEFAULT_TARGETS; variable is a list of internal &SCons; nodes, so you need to convert the list elements to strings if you want to print them or look for a specific target name. Fortunately, you can do this easily by using the Python map function to run the list through str: prog1 = Program('prog1.c') Default(prog1) print "DEFAULT_TARGETS is", map(str, DEFAULT_TARGETS) (Keep in mind that all of the manipulation of the &DEFAULT_TARGETS; list takes place during the first phase when &SCons; is reading up the &SConscript; files, which is obvious if we leave off the -Q flag when we run &SCons;:) % scons scons: Reading SConscript files ... DEFAULT_TARGETS is ['prog1'] scons: done reading SConscript files. scons: Building targets ... cc -o prog1.o -c prog1.c cc -o prog1 prog1.o scons: done building targets. Second, the contents of the &DEFAULT_TARGETS; list change in response to calls to the &Default; function, as you can see from the following &SConstruct; file: prog1 = Program('prog1.c') Default(prog1) print "DEFAULT_TARGETS is now", map(str, DEFAULT_TARGETS) prog2 = Program('prog2.c') Default(prog2) print "DEFAULT_TARGETS is now", map(str, DEFAULT_TARGETS) Which yields the output: % scons scons: Reading SConscript files ... DEFAULT_TARGETS is now ['prog1'] DEFAULT_TARGETS is now ['prog1', 'prog2'] scons: done reading SConscript files. scons: Building targets ... cc -o prog1.o -c prog1.c cc -o prog1 prog1.o cc -o prog2.o -c prog2.c cc -o prog2 prog2.o scons: done building targets. In practice, this simply means that you need to pay attention to the order in which you call the &Default; function and refer to the &DEFAULT_TARGETS; list, to make sure that you don't examine the list before you've added the default targets you expect to find in it.
Fetching the List of Build Targets, Regardless of Origin: the &BUILD_TARGETS; Variable We've already been introduced to the &COMMAND_LINE_TARGETS; variable, which contains a list of targets specified on the command line, and the &DEFAULT_TARGETS; variable, which contains a list of targets specified via calls to the &Default; method or function. Sometimes, however, you want a list of whatever targets &SCons; will try to build, regardless of whether the targets came from the command line or a &Default; call. You could code this up by hand, as follows: if COMMAND_LINE_TARGETS: targets = COMMAND_LINE_TARGETS else: targets = DEFAULT_TARGETS &SCons;, however, provides a convenient &BUILD_TARGETS; variable that eliminates the need for this by-hand manipulation. Essentially, the &BUILD_TARGETS; variable contains a list of the command-line targets, if any were specified, and if no command-line targets were specified, it contains a list of the targets specified via the &Default; method or function. Because &BUILD_TARGETS; may contain a list of &SCons; nodes, you must convert the list elements to strings if you want to print them or look for a specific target name, just like the &DEFAULT_TARGETS; list: prog1 = Program('prog1.c') Program('prog2.c') Default(prog1) print "BUILD_TARGETS is", map(str, BUILD_TARGETS) Notice how the value of &BUILD_TARGETS; changes depending on whether a target is specified on the command line: % scons -Q BUILD_TARGETS is ['prog1'] cc -o prog1.o -c prog1.c cc -o prog1 prog1.o % scons -Q prog2 BUILD_TARGETS is ['prog2'] cc -o prog2.o -c prog2.c cc -o prog2 prog2.o % scons -Q -c . BUILD_TARGETS is ['.'] Removed prog1.o Removed prog1 Removed prog2.o Removed prog2
scons-doc-2.3.0/doc/user/sourcecode.in0000644000175000017500000000700012114661557020425 0ustar dktrkranzdktrkranz XXX
Fetching Source Code From BitKeeper XXX env = Environment() env.SourceCode('.', env.BitKeeper()) env.Program('hello.c') s.hello.c scons -Q
Fetching Source Code From CVS XXX env = Environment() env.SourceCode('.', env.CVS('/usr/local/CVS')) env.Program('hello.c') scons -Q
Fetching Source Code From RCS XXX env = Environment() env.SourceCode('.', env.RCS()) env.Program('hello.c') hello.c,v scons -Q
Fetching Source Code From SCCS XXX env = Environment() env.SourceCode('.', env.SCCS()) env.Program('hello.c') s.hello.c scons -Q
scons-doc-2.3.0/doc/user/alias.xml0000644000175000017500000000664112114661557017567 0ustar dktrkranzdktrkranz We've already seen how you can use the &Alias; function to create a target named install: env = Environment() hello = env.Program('hello.c') env.Install('/usr/bin', hello) env.Alias('install', '/usr/bin') You can then use this alias on the command line to tell &SCons; more naturally that you want to install files: % scons -Q install cc -o hello.o -c hello.c cc -o hello hello.o Install file: "hello" as "/usr/bin/hello" Like other &Builder; methods, though, the &Alias; method returns an object representing the alias being built. You can then use this object as input to anothother &Builder;. This is especially useful if you use such an object as input to another call to the &Alias; &Builder;, allowing you to create a hierarchy of nested aliases: env = Environment() p = env.Program('foo.c') l = env.Library('bar.c') env.Install('/usr/bin', p) env.Install('/usr/lib', l) ib = env.Alias('install-bin', '/usr/bin') il = env.Alias('install-lib', '/usr/lib') env.Alias('install', [ib, il]) This example defines separate install, install-bin, and install-lib aliases, allowing you finer control over what gets installed: % scons -Q install-bin cc -o foo.o -c foo.c cc -o foo foo.o Install file: "foo" as "/usr/bin/foo" % scons -Q install-lib cc -o bar.o -c bar.c ar rc libbar.a bar.o ranlib libbar.a Install file: "libbar.a" as "/usr/lib/libbar.a" % scons -Q -c / Removed foo.o Removed foo Removed /usr/bin/foo Removed bar.o Removed libbar.a Removed /usr/lib/libbar.a % scons -Q install cc -o foo.o -c foo.c cc -o foo foo.o Install file: "foo" as "/usr/bin/foo" cc -o bar.o -c bar.c ar rc libbar.a bar.o ranlib libbar.a Install file: "libbar.a" as "/usr/lib/libbar.a" scons-doc-2.3.0/doc/user/build-install.in0000644000175000017500000004311212114661557021041 0ustar dktrkranzdktrkranz This chapter will take you through the basic steps of installing &SCons; on your system, and building &SCons; if you don't have a pre-built package available (or simply prefer the flexibility of building it yourself). Before that, however, this chapter will also describe the basic steps involved in installing Python on your system, in case that is necessary. Fortunately, both &SCons; and Python are very easy to install on almost any system, and Python already comes installed on many systems.
Installing Python Because &SCons; is written in Python, you must obviously have Python installed on your system to use &SCons;. Before you try to install Python, you should check to see if Python is already available on your system by typing python -V (capital 'V') or python --version at your system's command-line prompt. $ python -V Python 2.5.1 And on a Windows system with Python installed: C:\>python -V Python 2.5.1 If Python is not installed on your system, you will see an error message stating something like "command not found" (on UNIX or Linux) or "'python' is not recognized as an internal or external command, operable progam or batch file" (on Windows). In that case, you need to install Python before you can install &SCons;. The standard location for information about downloading and installing Python is http://www.python.org/download/. See that page for information about how to download and install Python on your system. &SCons; will work with any 2.x version of Python from 2.4 on; 3.0 and later are not yet supported. If you need to install Python and have a choice, we recommend using the most recent 2.x Python version available. Newer Pythons have significant improvements that help speed up the performance of &SCons;.
Installing &SCons; From Pre-Built Packages &SCons; comes pre-packaged for installation on a number of systems, including Linux and Windows systems. You do not need to read this entire section, you should need to read only the section appropriate to the type of system you're running on.
Installing &SCons; on Red Hat (and Other RPM-based) Linux Systems &SCons; comes in RPM (Red Hat Package Manager) format, pre-built and ready to install on Red Hat Linux, Fedora, or any other Linux distribution that uses RPM. Your distribution may already have an &SCons; RPM built specifically for it; many do, including SUSE, Mandrake and Fedora. You can check for the availability of an &SCons; RPM on your distribution's download servers, or by consulting an RPM search site like http://www.rpmfind.net/ or http://rpm.pbone.net/. If your distribution supports installation via yum, you should be able to install &SCons; by running: # yum install scons If your Linux distribution does not already have a specific &SCons; RPM file, you can download and install from the generic RPM provided by the &SCons; project. This will install the SCons script(s) in /usr/bin, and the SCons library modules in /usr/lib/scons. To install from the command line, simply download the appropriate .rpm file, and then run: # rpm -Uvh scons-2.3.0-1.noarch.rpm Or, you can use a graphical RPM package manager. See your package manager application's documention for specific instructions about how to use it to install a downloaded RPM.
Installing &SCons; on Debian Linux Systems Debian Linux systems use a different package management format that also makes it very easy to install &SCons;. If your system is connected to the Internet, you can install the latest official Debian package by running: # apt-get install scons
Installing &SCons; on Windows Systems &SCons; provides a Windows installer that makes installation extremely easy. Download the scons-2.3.0.win32.exe file from the &SCons; download page at http://www.scons.org/download.php. Then all you need to do is execute the file (usually by clicking on its icon in Windows Explorer). These will take you through a small sequence of windows that will install &SCons; on your system.
Building and Installing &SCons; on Any System If a pre-built &SCons; package is not available for your system, then you can still easily build and install &SCons; using the native Python distutils package. The first step is to download either the scons-2.3.0.tar.gz or scons-2.3.0.zip, which are available from the SCons download page at http://www.scons.org/download.html. Unpack the archive you downloaded, using a utility like tar on Linux or UNIX, or WinZip on Windows. This will create a directory called scons-2.3.0, usually in your local directory. Then change your working directory to that directory and install &SCons; by executing the following commands: # cd scons-2.3.0 # python setup.py install This will build &SCons;, install the &scons; script in the python which is used to run the setup.py's scripts directory (/usr/local/bin or C:\Python25\Scripts), and will install the &SCons; build engine in the corresponding library directory for the python used (/usr/local/lib/scons or C:\Python25\scons). Because these are system directories, you may need root (on Linux or UNIX) or Administrator (on Windows) privileges to install &SCons; like this.
Building and Installing Multiple Versions of &SCons; Side-by-Side The &SCons; setup.py script has some extensions that support easy installation of multiple versions of &SCons; in side-by-side locations. This makes it easier to download and experiment with different versions of &SCons; before moving your official build process to a new version, for example. To install &SCons; in a version-specific location, add the option when you call setup.py: # python setup.py install --version-lib This will install the &SCons; build engine in the /usr/lib/scons-2.3.0 or C:\Python25\scons-2.3.0 directory, for example. If you use the option the first time you install &SCons;, you do not need to specify it each time you install a new version. The &SCons; setup.py script will detect the version-specific directory name(s) and assume you want to install all versions in version-specific directories. You can override that assumption in the future by explicitly specifying the option.
Installing &SCons; in Other Locations You can install &SCons; in locations other than the default by specifying the option: # python setup.py install --prefix=/opt/scons This would install the scons script in /opt/scons/bin and the build engine in /opt/scons/lib/scons, Note that you can specify both the and the options at the same type, in which case setup.py will install the build engine in a version-specific directory relative to the specified prefix. Adding to the above example would install the build engine in /opt/scons/lib/scons-2.3.0.
Building and Installing &SCons; Without Administrative Privileges If you don't have the right privileges to install &SCons; in a system location, simply use the --prefix= option to install it in a location of your choosing. For example, to install &SCons; in appropriate locations relative to the user's $HOME directory, the &scons; script in $HOME/bin and the build engine in $HOME/lib/scons, simply type: $ python setup.py install --prefix=$HOME You may, of course, specify any other location you prefer, and may use the option if you would like to install version-specific directories relative to the specified prefix. This can also be used to experiment with a newer version of &SCons; than the one installed in your system locations. Of course, the location in which you install the newer version of the &scons; script ($HOME/bin in the above example) must be configured in your &PATH; variable before the directory containing the system-installed version of the &scons; script.
scons-doc-2.3.0/doc/user/copyright.in0000644000175000017500000000242412114661557020307 0ustar dktrkranzdktrkranz
SCons User's Guide Copyright (c) 2004, 2005, 2006, 2007 Steven Knight
scons-doc-2.3.0/doc/user/MANIFEST0000644000175000017500000000134212114661557017076 0ustar dktrkranzdktrkranzactions.xml add-method.xml alias.xml ant.xml builders.xml builders-built-in.xml builders-commands.xml builders-writing.xml build-install.xml caching.xml command-line.xml cons.pl copyright.xml depends.xml environments.xml errors.xml example.xml factories.xml file-removal.xml functions.xml gettext.xml hierarchy.xml install.xml java.xml libraries.xml less-simple.xml main.xml make.xml mergeflags.xml misc.xml nodes.xml output.xml parseconfig.xml parseflags.xml preface.xml python.xml repositories.xml run.xml scanners.xml sconf.xml separate.xml simple.xml sourcecode.xml tasks.xml tools.xml troubleshoot.xml variants.xml variables.xml SCons-win32-install-1.jpg SCons-win32-install-2.jpg SCons-win32-install-3.jpg SCons-win32-install-4.jpg scons-doc-2.3.0/doc/user/variables.xml0000644000175000017500000000356112114661557020444 0ustar dktrkranzdktrkranz This appendix contains descriptions of all of the construction variables that are potentially available "out of the box" in this version of SCons. Whether or not setting a construction variable in a construction environment will actually have an effect depends on whether any of the Tools and/or Builders that use the variable have been included in the construction environment. In this appendix, we have appended the initial $ (dollar sign) to the beginning of each variable name when it appears in the text, but left off the dollar sign in the left-hand column where the name appears for each entry. &variables-gen; scons-doc-2.3.0/doc/user/hierarchy.in0000644000175000017500000005244512114661557020265 0ustar dktrkranzdktrkranz The source code for large software projects rarely stays in a single directory, but is nearly always divided into a hierarchy of directories. Organizing a large software build using &SCons; involves creating a hierarchy of build scripts using the &SConscript; function.
&SConscript; Files As we've already seen, the build script at the top of the tree is called &SConstruct;. The top-level &SConstruct; file can use the &SConscript; function to include other subsidiary scripts in the build. These subsidiary scripts can, in turn, use the &SConscript; function to include still other scripts in the build. By convention, these subsidiary scripts are usually named &SConscript;. For example, a top-level &SConstruct; file might arrange for four subsidiary scripts to be included in the build as follows: SConscript(['drivers/display/SConscript', 'drivers/mouse/SConscript', 'parser/SConscript', 'utilities/SConscript']) In this case, the &SConstruct; file lists all of the &SConscript; files in the build explicitly. (Note, however, that not every directory in the tree necessarily has an &SConscript; file.) Alternatively, the drivers subdirectory might contain an intermediate &SConscript; file, in which case the &SConscript; call in the top-level &SConstruct; file would look like: SConscript(['drivers/SConscript', 'parser/SConscript', 'utilities/SConscript']) And the subsidiary &SConscript; file in the drivers subdirectory would look like: SConscript(['display/SConscript', 'mouse/SConscript']) Whether you list all of the &SConscript; files in the top-level &SConstruct; file, or place a subsidiary &SConscript; file in intervening directories, or use some mix of the two schemes, is up to you and the needs of your software.
Path Names Are Relative to the &SConscript; Directory Subsidiary &SConscript; files make it easy to create a build hierarchy because all of the file and directory names in a subsidiary &SConscript; files are interpreted relative to the directory in which the &SConscript; file lives. Typically, this allows the &SConscript; file containing the instructions to build a target file to live in the same directory as the source files from which the target will be built, making it easy to update how the software is built whenever files are added or deleted (or other changes are made). For example, suppose we want to build two programs &prog1; and &prog2; in two separate directories with the same names as the programs. One typical way to do this would be with a top-level &SConstruct; file like this: SConscript(['prog1/SConscript', 'prog2/SConscript']) env = Environment() env.Program('prog1', ['main.c', 'foo1.c', 'foo2.c']) env = Environment() env.Program('prog2', ['main.c', 'bar1.c', 'bar2.c']) x x x x x x And subsidiary &SConscript; files that look like this: And this: Then, when we run &SCons; in the top-level directory, our build looks like: scons -Q Notice the following: First, you can have files with the same names in multiple directories, like main.c in the above example. Second, unlike standard recursive use of &Make;, &SCons; stays in the top-level directory (where the &SConstruct; file lives) and issues commands that use the path names from the top-level directory to the target and source files within the hierarchy.
Top-Level Path Names in Subsidiary &SConscript; Files If you need to use a file from another directory, it's sometimes more convenient to specify the path to a file in another directory from the top-level &SConstruct; directory, even when you're using that file in a subsidiary &SConscript; file in a subdirectory. You can tell &SCons; to interpret a path name as relative to the top-level &SConstruct; directory, not the local directory of the &SConscript; file, by appending a &hash; (hash mark) to the beginning of the path name: SConscript('src/prog/SConscript') env = Environment() env.Program('prog', ['main.c', '#lib/foo1.c', 'foo2.c']) x x x In this example, the lib directory is directly underneath the top-level &SConstruct; directory. If the above &SConscript; file is in a subdirectory named src/prog, the output would look like: scons -Q (Notice that the lib/foo1.o object file is built in the same directory as its source file. See , below, for information about how to build the object file in a different subdirectory.)
Absolute Path Names Of course, you can always specify an absolute path name for a file--for example: SConscript('src/prog/SConscript') env = Environment() env.Program('prog', ['main.c', '__ROOT__/usr/joe/lib/foo1.c', 'foo2.c']) x x x Which, when executed, would yield: scons -Q (As was the case with top-relative path names, notice that the /usr/joe/lib/foo1.o object file is built in the same directory as its source file. See , below, for information about how to build the object file in a different subdirectory.)
Sharing Environments (and Other Variables) Between &SConscript; Files In the previous example, each of the subsidiary &SConscript; files created its own construction environment by calling &Environment; separately. This obviously works fine, but if each program must be built with the same construction variables, it's cumbersome and error-prone to initialize separate construction environments in the same way over and over in each subsidiary &SConscript; file. &SCons; supports the ability to export variables from a parent &SConscript; file to its subsidiary &SConscript; files, which allows you to share common initialized values throughout your build hierarchy.
Exporting Variables There are two ways to export a variable, such as a construction environment, from an &SConscript; file, so that it may be used by other &SConscript; files. First, you can call the &Export; function with a list of variables, or a string of white-space separated variable names. Each call to &Export; adds one or more variables to a global list of variables that are available for import by other &SConscript; files. env = Environment() Export('env') You may export more than one variable name at a time: env = Environment() debug = ARGUMENTS['debug'] Export('env', 'debug') Because white space is not legal in Python variable names, the &Export; function will even automatically split a string into separate names for you: Export('env debug') Second, you can specify a list of variables to export as a second argument to the &SConscript; function call: SConscript('src/SConscript', 'env') Or as the &exports; keyword argument: SConscript('src/SConscript', exports='env') These calls export the specified variables to only the listed &SConscript; files. You may, however, specify more than one &SConscript; file in a list: SConscript(['src1/SConscript', 'src2/SConscript'], exports='env') This is functionally equivalent to calling the &SConscript; function multiple times with the same &exports; argument, one per &SConscript; file.
Importing Variables Once a variable has been exported from a calling &SConscript; file, it may be used in other &SConscript; files by calling the &Import; function: Import('env') env.Program('prog', ['prog.c']) The &Import; call makes the env construction environment available to the &SConscript; file, after which the variable can be used to build programs, libraries, etc. Like the &Export; function, the &Import; function can be used with multiple variable names: Import('env', 'debug') env = env.Clone(DEBUG = debug) env.Program('prog', ['prog.c']) And the &Import; function will similarly split a string along white-space into separate variable names: Import('env debug') env = env.Clone(DEBUG = debug) env.Program('prog', ['prog.c']) Lastly, as a special case, you may import all of the variables that have been exported by supplying an asterisk to the &Import; function: Import('*') env = env.Clone(DEBUG = debug) env.Program('prog', ['prog.c']) If you're dealing with a lot of &SConscript; files, this can be a lot simpler than keeping arbitrary lists of imported variables in each file.
Returning Values From an &SConscript; File Sometimes, you would like to be able to use information from a subsidiary &SConscript; file in some way. For example, suppose that you want to create one library from source files scattered throughout a number of subsidiary &SConscript; files. You can do this by using the &Return; function to return values from the subsidiary &SConscript; files to the calling file. If, for example, we have two subdirectories &foo; and &bar; that should each contribute a source file to a Library, what we'd like to be able to do is collect the object files from the subsidiary &SConscript; calls like this: env = Environment() Export('env') objs = [] for subdir in ['foo', 'bar']: o = SConscript('%s/SConscript' % subdir) objs.append(o) env.Library('prog', objs) Import('env') obj = env.Object('foo.c') Return('obj') Import('env') obj = env.Object('bar.c') Return('obj') void foo(void) { printf("foo/foo.c\n"); } void bar(void) { printf("bar/bar.c\n"); } We can do this by using the &Return; function in the foo/SConscript file like this: (The corresponding bar/SConscript file should be pretty obvious.) Then when we run &SCons;, the object files from the subsidiary subdirectories are all correctly archived in the desired library: scons -Q
scons-doc-2.3.0/doc/user/copyright.xml0000644000175000017500000000242412114661557020501 0ustar dktrkranzdktrkranz
SCons User's Guide Copyright (c) 2004, 2005, 2006, 2007 Steven Knight
scons-doc-2.3.0/doc/user/sideeffect.xml0000644000175000017500000001472212114661557020576 0ustar dktrkranzdktrkranz Sometimes a program the you need to call to build a target file will also update another file, such as a log file describing what the program does while building the target. For example, we the folowing configuration would have &SCons; invoke a hypothetical script named build (in the local directory) with command-line arguments that write log information to a common logfile.txt file: env = Environment() env.Command('file1.out', 'file.in', './build --log logfile.txt $SOURCE $TARGET') env.Command('file2.out', 'file.in', './build --log logfile.txt $SOURCE $TARGET') This can cause problems when running the build in parallel if &SCons; decides to update both targets by running both program invocations at the same time. The multiple program invocations may interfere with each other writing to the common log file, leading at best to intermixed output in the log file, and at worst to an actual failed build (on a system like Windows, for example, where only one process at a time can open the log file for writing). We can make sure that &SCons; does not run these build commands at the same time by using the &SideEffect; function to specify that updating the logfile.txt file is a side effect of building the specified file1 and file2 target files: env = Environment() f1 = env.Command('file1.out', 'file1.in', './build --log logfile.txt $SOURCE $TARGET') f2 = env.Command('file2.out', 'file2.in', './build --log logfile.txt $SOURCE $TARGET') env.SideEffect('logfile.txt', f1 + f2) This makes sure the the two ./build steps are run sequentially, even withthe --jobs=2 in the command line: % scons -Q --jobs=2 ./build --log logfile.txt file1.in file1.out ./build --log logfile.txt file2.in file2.out The &SideEffect; function can be called multiple times for the same side-effect file. Additionally, the name used as a &SideEffect; does not even need to actually exist as a file on disk. &SCons; will still make sure that the relevant targets will be executed sequentially, not in parallel: env = Environment() f1 = env.Command('file1.out', [], 'echo >$TARGET data1') env.SideEffect('not_really_updated', f1) f2 = env.Command('file2.out', [], 'echo >$TARGET data2') env.SideEffect('not_really_updated', f2) % scons -Q --jobs=2 echo > file1.out data1 echo > file2.out data2 Note that it might be tempting to use &SideEffect; for additional target files that a command produces. For example, versions the Microsoft Visual C/C++ compiler produce a foo.ilk alongside compiling foo.obj file. Specifying foo.ilk as a side-effect of foo.obj is not a recommended use of &SideEffect;, because &SCons; handle side-effect files slightly differently in its analysis of the dependency graph. When a command produces multiple output files, they should be specified as multiple targets of the call to the relevant builder function, and the &SideEffect; function itself should really only be used when it's important to ensure that commands are not executed in parallel, such as when a "peripheral" file (such as a log file) may actually updated by more than one command invocation. scons-doc-2.3.0/doc/user/builders.in0000644000175000017500000000335512114661557020114 0ustar dktrkranzdktrkranz This appendix contains descriptions of all of the Builders that are potentially available "out of the box" in this version of SCons. &builders-gen; scons-doc-2.3.0/doc/user/file-removal.xml0000644000175000017500000001353312114661557021056 0ustar dktrkranzdktrkranz There are two occasions when &SCons; will, by default, remove target files. The first is when &SCons; determines that an target file needs to be rebuilt and removes the existing version of the target before executing The second is when &SCons; is invoked with the -c option to "clean" a tree of its built targets. These behaviours can be suppressed with the &Precious; and &NoClean; functions, respectively.
Preventing target removal during build: the &Precious; Function By default, &SCons; removes targets before building them. Sometimes, however, this is not what you want. For example, you may want to update a library incrementally, not by having it deleted and then rebuilt from all of the constituent object files. In such cases, you can use the &Precious; method to prevent &SCons; from removing the target before it is built: env = Environment(RANLIBCOM='') lib = env.Library('foo', ['f1.c', 'f2.c', 'f3.c']) env.Precious(lib) Although the output doesn't look any different, &SCons; does not, in fact, delete the target library before rebuilding it: % scons -Q cc -o f1.o -c f1.c cc -o f2.o -c f2.c cc -o f3.o -c f3.c ar rc libfoo.a f1.o f2.o f3.o &SCons; will, however, still delete files marked as &Precious; when the -c option is used.
Preventing target removal during clean: the &NoClean; Function By default, &SCons; removes all built targets when invoked with the -c option to clean a source tree of built targets. Sometimes, however, this is not what you want. For example, you may want to remove only intermediate generated files (such as object files), but leave the final targets (the libraries) untouched. In such cases, you can use the &NoClean; method to prevent &SCons; from removing a target during a clean: env = Environment(RANLIBCOM='') lib = env.Library('foo', ['f1.c', 'f2.c', 'f3.c']) env.NoClean(lib) Notice that the libfoo.a is not listed as a removed file: % scons -Q cc -o f1.o -c f1.c cc -o f2.o -c f2.c cc -o f3.o -c f3.c ar rc libfoo.a f1.o f2.o f3.o % scons -c scons: Reading SConscript files ... scons: done reading SConscript files. scons: Cleaning targets ... Removed f1.o Removed f2.o Removed f3.o scons: done cleaning targets.
Removing additional files during clean: the &Clean; Function There may be additional files that you want removed when the -c option is used, but which &SCons; doesn't know about because they're not normal target files. For example, perhaps a command you invoke creates a log file as part of building the target file you want. You would like the log file cleaned, but you don't want to have to teach SCons that the command "builds" two files. You can use the &Clean; function to arrange for additional files to be removed when the -c option is used. Notice, however, that the &Clean; function takes two arguments, and the second argument is the name of the additional file you want cleaned (foo.log in this example): t = Command('foo.out', 'foo.in', 'build -o $TARGET $SOURCE') Clean(t, 'foo.log') The first argument is the target with which you want the cleaning of this additional file associated. In the above example, we've used the return value from the &Command; function, which represents the foo.out target. Now whenever the foo.out target is cleaned by the -c option, the foo.log file will be removed as well: % scons -Q build -o foo.out foo.in % scons -Q -c Removed foo.out Removed foo.log
scons-doc-2.3.0/doc/user/troubleshoot.in0000644000175000017500000006027212114661557021035 0ustar dktrkranzdktrkranz The experience of configuring any software build tool to build a large code base usually, at some point, involves trying to figure out why the tool is behaving a certain way, and how to get it to behave the way you want. &SCons; is no different. This appendix contains a number of different ways in which you can get some additional insight into &SCons;' behavior. Note that we're always interested in trying to improve how you can troubleshoot configuration problems. If you run into a problem that has you scratching your head, and which there just doesn't seem to be a good way to debug, odds are pretty good that someone else will run into the same problem, too. If so, please let the SCons development team know (preferably by filing a bug report or feature request at our project pages at tigris.org) so that we can use your feedback to try to come up with a better way to help you, and others, get the necessary insight into &SCons; behavior to help identify and fix configuration issues.
Why is That Target Being Rebuilt? the &debug-explain; Option Let's look at a simple example of a misconfigured build that causes a target to be rebuilt every time &SCons; is run: # Intentionally misspell the output file name in the # command used to create the file: Command('file.out', 'file.in', 'cp $SOURCE file.oout') file.in (Note to Windows users: The POSIX &cp; command copies the first file named on the command line to the second file. In our example, it copies the &file_in; file to the &file_out; file.) Now if we run &SCons; multiple times on this example, we see that it re-runs the &cp; command every time: scons -Q scons -Q scons -Q In this example, the underlying cause is obvious: we've intentionally misspelled the output file name in the &cp; command, so the command doesn't actually build the &file_out; file that we've told &SCons; to expect. But if the problem weren't obvious, it would be helpful to specify the &debug-explain; option on the command line to have &SCons; tell us very specifically why it's decided to rebuild the target: scons -Q --debug=explain If this had been a more complicated example involving a lot of build output, having &SCons; tell us that it's trying to rebuild the target file because it doesn't exist would be an important clue that something was wrong with the command that we invoked to build it. The &debug-explain; option also comes in handy to help figure out what input file changed. Given a simple configuration that builds a program from three source files, changing one of the source files and rebuilding with the &debug-explain; option shows very specifically why &SCons; rebuilds the files that it does: Program('prog', ['file1.c', 'file2.c', 'file3.c']) file1.c file2.c file3.c scons -Q edit file2.c scons -Q --debug=explain This becomes even more helpful in identifying when a file is rebuilt due to a change in an implicit dependency, such as an incuded .h file. If the file1.c and file3.c files in our example both included a &hello_h; file, then changing that included file and re-running &SCons; with the &debug-explain; option will pinpoint that it's the change to the included file that starts the chain of rebuilds: Program('prog', ['file1.c', 'file2.c', 'file3.c'], CPPPATH='.') #include <hello.h> file1.c file2.c #include <hello.h> file3.c #define string "world" scons -Q edit hello.h scons -Q --debug=explain (Note that the &debug-explain; option will only tell you why &SCons; decided to rebuild necessary targets. It does not tell you what files it examined when deciding not to rebuild a target file, which is often a more valuable question to answer.)
What's in That Construction Environment? the &Dump; Method When you create a construction environment, &SCons; populates it with construction variables that are set up for various compilers, linkers and utilities that it finds on your system. Although this is usually helpful and what you want, it might be frustrating if &SCons; doesn't set certain variables that you expect to be set. In situations like this, it's sometimes helpful to use the construction environment &Dump; method to print all or some of the construction variables. Note that the &Dump; method returns the representation of the variables in the environment for you to print (or otherwise manipulate): env = Environment() print env.Dump() On a POSIX system with gcc installed, this might generate: scons On a Windows system with Visual C++ the output might look like: scons The construction environments in these examples have actually been restricted to just gcc and Visual C++, respectively. In a real-life situation, the construction environments will likely contain a great many more variables. Also note that we've massaged the example output above to make the memory address of all objects a constant 0x700000. In reality, you would see a different hexadecimal number for each object. To make it easier to see just what you're interested in, the &Dump; method allows you to specify a specific constrcution variable that you want to disply. For example, it's not unusual to want to verify the external environment used to execute build commands, to make sure that the PATH and other environment variables are set up the way they should be. You can do this as follows: env = Environment() print env.Dump('ENV') Which might display the following when executed on a POSIX system: scons And the following when executed on a Windows system: scons
What Dependencies Does &SCons; Know About? the &tree; Option Sometimes the best way to try to figure out what &SCons; is doing is simply to take a look at the dependency graph that it constructs based on your &SConscript; files. The --tree option will display all or part of the &SCons; dependency graph in an "ASCII art" graphical format that shows the dependency hierarchy. For example, given the following input &SConstruct; file: env = Environment(CPPPATH = ['.']) env.Program('prog', ['f1.c', 'f2.c', 'f3.c']) #include "inc.h" #include "inc.h" #include "inc.h" inc.h Running &SCons; with the --tree=all option yields: scons -Q --tree=all The tree will also be printed when the -n (no execute) option is used, which allows you to examine the dependency graph for a configuration without actually rebuilding anything in the tree. The --tree option only prints the dependency graph for the specified targets (or the default target(s) if none are specified on the command line). So if you specify a target like f2.o on the command line, the --tree option will only print the dependency graph for that file: scons -Q --tree=all f2.o This is, of course, useful for restricting the output from a very large build configuration to just a portion in which you're interested. Multiple targets are fine, in which case a tree will be printed for each specified target: scons -Q --tree=all f1.o f3.o The status argument may be used to tell &SCons; to print status information about each file in the dependency graph: scons -Q --tree=status Note that --tree=all,status is equivalent; the all is assumed if only status is present. As an alternative to all, you can specify --tree=derived to have &SCons; only print derived targets in the tree output, skipping source files (like .c and .h files): scons -Q --tree=derived You can use the status modifier with derived as well: scons -Q --tree=derived,status Note that the order of the --tree= arguments doesn't matter; --tree=status,derived is completely equivalent. The default behavior of the --tree option is to repeat all of the dependencies each time the library dependency (or any other dependency file) is encountered in the tree. If certain target files share other target files, such as two programs that use the same library: env = Environment(CPPPATH = ['.'], LIBS = ['foo'], LIBPATH = ['.']) env.Library('foo', ['f1.c', 'f2.c', 'f3.c']) env.Program('prog1.c') env.Program('prog2.c') #include "inc.h" #include "inc.h" #include "inc.h" #include "inc.h" #include "inc.h" inc.h Then there can be a lot of repetition in the --tree= output: scons -Q --tree=all In a large configuration with many internal libraries and include files, this can very quickly lead to huge output trees. To help make this more manageable, a prune modifier may be added to the option list, in which case &SCons; will print the name of a target that has already been visited during the tree-printing in [square brackets] as an indication that the dependencies of the target file may be found by looking farther up the tree: scons -Q --tree=prune Like the status keyword, the prune argument by itself is equivalent to --tree=all,prune.
How is &SCons; Constructing the Command Lines It Executes? the &debug-presub; Option Sometimes it's useful to look at the pre-substitution string that &SCons; uses to generate the command lines it executes. This can be done with the &debug-presub; option: env = Environment(CPPPATH = ['.']) env.Program('prog', 'prog.c') prog.c % scons -Q --debug=presub Building prog.o with action: $CC -o $TARGET -c $CFLAGS $CCFLAGS $_CCOMCOM $SOURCES cc -o prog.o -c -I. prog.c Building prog with action: $SMART_LINKCOM cc -o prog prog.o
Where is &SCons; Searching for Libraries? the &debug-findlibs; Option To get some insight into what library names &SCons; is searching for, and in which directories it is searching, Use the --debug=findlibs option. Given the following input &SConstruct; file: env = Environment(LIBPATH = ['libs1', 'libs2']) env.Program('prog.c', LIBS=['foo', 'bar']) prog.c libs1/libfoo.a libs2/libbar.a And the libraries libfoo.a and libbar.a in libs1 and libs2, respectively, use of the --debug=findlibs option yields: scons -Q --debug=findlibs
Where is &SCons; Blowing Up? the &debug-stacktrace; Option In general, &SCons; tries to keep its error messages short and informative. That means we usually try to avoid showing the stack traces that are familiar to experienced Python programmers, since they usually contain much more information than is useful to most people. For example, the following &SConstruct; file: Program('prog.c') Generates the following error if the prog.c file does not exist: scons -Q In this case, the error is pretty obvious. But if it weren't, and you wanted to try to get more information about the error, the &debug-stacktrace; option would show you exactly where in the &SCons; source code the problem occurs: scons -Q --debug=stacktrace Of course, if you do need to dive into the &SCons; source code, we'd like to know if, or how, the error messages or troubleshooting options could have been improved to avoid that. Not everyone has the necessary time or Python skill to dive into the source code, and we'd like to improve &SCons; for those people as well...
How is &SCons; Making Its Decisions? the &taskmastertrace; Option The internal &SCons; subsystem that handles walking the dependency graph and controls the decision-making about what to rebuild is the Taskmaster. &SCons; supports a --taskmastertrace option that tells the Taskmaster to print information about the children (dependencies) of the various Nodes on its walk down the graph, which specific dependent Nodes are being evaluated, and in what order. The --taskmastertrace option takes as an argument the name of a file in which to put the trace output, with - (a single hyphen) indicating that the trace messages should be printed to the standard output: env = Environment(CPPPATH = ['.']) env.Program('prog.c') #include "inc.h" prog.c #define STRING "one" scons -Q --taskmastertrace=- prog The --taskmastertrace option doesn't provide information about the actual calculations involved in deciding if a file is up-to-date, but it does show all of the dependencies it knows about for each Node, and the order in which those dependencies are evaluated. This can be useful as an alternate way to determine whether or not your &SCons; configuration, or the implicit dependency scan, has actually identified all the correct dependencies you want it to.
Watch &SCons; prepare targets for building: the &debug-prepare; Option Sometimes SCons doesn't build the target you want and it's difficult to figure out why. You can use the --debug=prepare option to see all the targets &SCons; is considering, whether they are already up-to-date or not. The message is printed before &SCons; decides whether to build the target.
Why is a file disappearing? the --debug=duplicate Option When using the &Duplicate; option to create variant dirs, sometimes you may find files not getting copied to where you expect (or not at all), or files mysteriously disappearing. These are usually because of a misconfiguration of some kind in the SConstruct/SConscript, but they can be tricky to debug. The --debug=duplicate option shows each time a variant file is unlinked and relinked from its source (or copied, depending on settings), and also shows a message for removing "stale" variant-dir files that no longer have a corresponding source file. It also prints a line for each target that's removed just before building, since that can also be mistaken for the same thing.
scons-doc-2.3.0/doc/user/install.xml0000644000175000017500000001456012114661557020143 0ustar dktrkranzdktrkranz Once a program is built, it is often appropriate to install it in another directory for public use. You use the &Install; method to arrange for a program, or any other file, to be copied into a destination directory: env = Environment() hello = env.Program('hello.c') env.Install('/usr/bin', hello) Note, however, that installing a file is still considered a type of file "build." This is important when you remember that the default behavior of &SCons; is to build files in or below the current directory. If, as in the example above, you are installing files in a directory outside of the top-level &SConstruct; file's directory tree, you must specify that directory (or a higher directory, such as /) for it to install anything there: % scons -Q cc -o hello.o -c hello.c cc -o hello hello.o % scons -Q /usr/bin Install file: "hello" as "/usr/bin/hello" It can, however, be cumbersome to remember (and type) the specific destination directory in which the program (or any other file) should be installed. This is an area where the &Alias; function comes in handy, allowing you, for example, to create a pseudo-target named install that can expand to the specified destination directory: env = Environment() hello = env.Program('hello.c') env.Install('/usr/bin', hello) env.Alias('install', '/usr/bin') This then yields the more natural ability to install the program in its destination as follows: % scons -Q cc -o hello.o -c hello.c cc -o hello hello.o % scons -Q install Install file: "hello" as "/usr/bin/hello"
Installing Multiple Files in a Directory You can install multiple files into a directory simply by calling the &Install; function multiple times: env = Environment() hello = env.Program('hello.c') goodbye = env.Program('goodbye.c') env.Install('/usr/bin', hello) env.Install('/usr/bin', goodbye) env.Alias('install', '/usr/bin') Or, more succinctly, listing the multiple input files in a list (just like you can do with any other builder): env = Environment() hello = env.Program('hello.c') goodbye = env.Program('goodbye.c') env.Install('/usr/bin', [hello, goodbye]) env.Alias('install', '/usr/bin') Either of these two examples yields: % scons -Q install cc -o goodbye.o -c goodbye.c cc -o goodbye goodbye.o Install file: "goodbye" as "/usr/bin/goodbye" cc -o hello.o -c hello.c cc -o hello hello.o Install file: "hello" as "/usr/bin/hello"
Installing a File Under a Different Name The &Install; method preserves the name of the file when it is copied into the destination directory. If you need to change the name of the file when you copy it, use the &InstallAs; function: env = Environment() hello = env.Program('hello.c') env.InstallAs('/usr/bin/hello-new', hello) env.Alias('install', '/usr/bin') This installs the hello program with the name hello-new as follows: % scons -Q install cc -o hello.o -c hello.c cc -o hello hello.o Install file: "hello" as "/usr/bin/hello-new"
Installing Multiple Files Under Different Names Lastly, if you have multiple files that all need to be installed with different file names, you can either call the &InstallAs; function multiple times, or as a shorthand, you can supply same-length lists for both the target and source arguments: env = Environment() hello = env.Program('hello.c') goodbye = env.Program('goodbye.c') env.InstallAs(['/usr/bin/hello-new', '/usr/bin/goodbye-new'], [hello, goodbye]) env.Alias('install', '/usr/bin') In this case, the &InstallAs; function loops through both lists simultaneously, and copies each source file into its corresponding target file name: % scons -Q install cc -o goodbye.o -c goodbye.c cc -o goodbye goodbye.o Install file: "goodbye" as "/usr/bin/goodbye-new" cc -o hello.o -c hello.c cc -o hello hello.o Install file: "hello" as "/usr/bin/hello-new"
scons-doc-2.3.0/doc/user/sconf.in0000644000175000017500000002720212114661557017410 0ustar dktrkranzdktrkranz &SCons; has integrated support for multi-platform build configuration similar to that offered by GNU &Autoconf;, such as figuring out what libraries or header files are available on the local system. This section describes how to use this &SCons; feature. This chapter is still under development, so not everything is explained as well as it should be. See the &SCons; man page for additional information.
&Configure_Contexts; The basic framework for multi-platform build configuration in &SCons; is to attach a &configure_context; to a construction environment by calling the &Configure; function, perform a number of checks for libraries, functions, header files, etc., and to then call the configure context's &Finish; method to finish off the configuration: env = Environment() conf = Configure(env) # Checks for libraries, header files, etc. go here! env = conf.Finish() &SCons; provides a number of basic checks, as well as a mechanism for adding your own custom checks. Note that &SCons; uses its own dependency mechanism to determine when a check needs to be run--that is, &SCons; does not run the checks every time it is invoked, but caches the values returned by previous checks and uses the cached values unless something has changed. This saves a tremendous amount of developer time while working on cross-platform build issues. The next sections describe the basic checks that &SCons; supports, as well as how to add your own custom checks.
Checking for the Existence of Header Files Testing the existence of a header file requires knowing what language the header file is. A configure context has a &CheckCHeader; method that checks for the existence of a C header file: env = Environment() conf = Configure(env) if not conf.CheckCHeader('math.h'): print 'Math.h must be installed!' Exit(1) if conf.CheckCHeader('foo.h'): conf.env.Append('-DHAS_FOO_H') env = conf.Finish() Note that you can choose to terminate the build if a given header file doesn't exist, or you can modify the construction environment based on the existence of a header file. If you need to check for the existence a C++ header file, use the &CheckCXXHeader; method: env = Environment() conf = Configure(env) if not conf.CheckCXXHeader('vector.h'): print 'vector.h must be installed!' Exit(1) env = conf.Finish()
Checking for the Availability of a Function Check for the availability of a specific function using the &CheckFunc; method: env = Environment() conf = Configure(env) if not conf.CheckFunc('strcpy'): print 'Did not find strcpy(), using local version' conf.env.Append(CPPDEFINES = '-Dstrcpy=my_local_strcpy') env = conf.Finish()
Checking for the Availability of a Library Check for the availability of a library using the &CheckLib; method. You only specify the basename of the library, you don't need to add a lib prefix or a .a or .lib suffix: env = Environment() conf = Configure(env) if not conf.CheckLib('m'): print 'Did not find libm.a or m.lib, exiting!' Exit(1) env = conf.Finish() Because the ability to use a library successfully often depends on having access to a header file that describes the library's interface, you can check for a library and a header file at the same time by using the &CheckLibWithHeader; method: env = Environment() conf = Configure(env) if not conf.CheckLibWithHeader('m', 'math.h', 'c'): print 'Did not find libm.a or m.lib, exiting!' Exit(1) env = conf.Finish() This is essentially shorthand for separate calls to the &CheckHeader; and &CheckLib; functions.
Checking for the Availability of a &typedef; Check for the availability of a &typedef; by using the &CheckType; method: env = Environment() conf = Configure(env) if not conf.CheckType('off_t'): print 'Did not find off_t typedef, assuming int' conf.env.Append(CCFLAGS = '-Doff_t=int') env = conf.Finish() You can also add a string that will be placed at the beginning of the test file that will be used to check for the &typedef;. This provide a way to specify files that must be included to find the &typedef;: env = Environment() conf = Configure(env) if not conf.CheckType('off_t', '#include &lt;sys/types.h&gt;\n'): print 'Did not find off_t typedef, assuming int' conf.env.Append(CCFLAGS = '-Doff_t=int') env = conf.Finish()
Adding Your Own Custom Checks A custom check is a Python function that checks for a certain condition to exist on the running system, usually using methods that &SCons; supplies to take care of the details of checking whether a compilation succeeds, a link succeeds, a program is runnable, etc. A simple custom check for the existence of a specific library might look as follows: mylib_test_source_file = """ #include &lt;mylib.h&gt; int main(int argc, char **argv) { MyLibrary mylib(argc, argv); return 0; } """ def CheckMyLibrary(context): context.Message('Checking for MyLibrary...') result = context.TryLink(mylib_test_source_file, '.c') context.Result(result) return result The &Message; and &Result; methods should typically begin and end a custom check to let the user know what's going on: the &Message; call prints the specified message (with no trailing newline) and the &Result; call prints yes if the check succeeds and no if it doesn't. The &TryLink; method actually tests for whether the specified program text will successfully link. (Note that a custom check can modify its check based on any arguments you choose to pass it, or by using or modifying the configure context environment in the context.env attribute.) This custom check function is then attached to the &configure_context; by passing a dictionary to the &Configure; call that maps a name of the check to the underlying function: env = Environment() conf = Configure(env, custom_tests = {'CheckMyLibrary' : CheckMyLibrary}) You'll typically want to make the check and the function name the same, as we've done here, to avoid potential confusion. We can then put these pieces together and actually call the CheckMyLibrary check as follows: mylib_test_source_file = """ #include &lt;mylib.h&gt; int main(int argc, char **argv) { MyLibrary mylib(argc, argv); return 0; } """ def CheckMyLibrary(context): context.Message('Checking for MyLibrary... ') result = context.TryLink(mylib_test_source_file, '.c') context.Result(result) return result env = Environment() conf = Configure(env, custom_tests = {'CheckMyLibrary' : CheckMyLibrary}) if not conf.CheckMyLibrary(): print 'MyLibrary is not installed!' Exit(1) env = conf.Finish() # We would then add actual calls like Program() to build # something using the "env" construction environment. If MyLibrary is not installed on the system, the output will look like: % scons scons: Reading SConscript file ... Checking for MyLibrary... failed MyLibrary is not installed! If MyLibrary is installed, the output will look like: % scons scons: Reading SConscript file ... Checking for MyLibrary... failed scons: done reading SConscript scons: Building targets ... . . .
Not Configuring When Cleaning Targets Using multi-platform configuration as described in the previous sections will run the configuration commands even when invoking scons -c to clean targets: % scons -Q -c Checking for MyLibrary... yes Removed foo.o Removed foo Although running the platform checks when removing targets doesn't hurt anything, it's usually unnecessary. You can avoid this by using the &GetOption; method to check whether the (clean) option has been invoked on the command line: env = Environment() if not env.GetOption('clean'): conf = Configure(env, custom_tests = {'CheckMyLibrary' : CheckMyLibrary}) if not conf.CheckMyLibrary(): print 'MyLibrary is not installed!' Exit(1) env = conf.Finish() % scons -Q -c Removed foo.o Removed foo
scons-doc-2.3.0/doc/user/caching.xml0000644000175000017500000003315312114661557020070 0ustar dktrkranzdktrkranz On multi-developer software projects, you can sometimes speed up every developer's builds a lot by allowing them to share the derived files that they build. &SCons; makes this easy, as well as reliable.
Specifying the Shared Cache Directory To enable sharing of derived files, use the &CacheDir; function in any &SConscript; file: CacheDir('/usr/local/build_cache') Note that the directory you specify must already exist and be readable and writable by all developers who will be sharing derived files. It should also be in some central location that all builds will be able to access. In environments where developers are using separate systems (like individual workstations) for builds, this directory would typically be on a shared or NFS-mounted file system. Here's what happens: When a build has a &CacheDir; specified, every time a file is built, it is stored in the shared cache directory along with its MD5 build signature. Actually, the MD5 signature is used as the name of the file in the shared cache directory in which the contents are stored. On subsequent builds, before an action is invoked to build a file, &SCons; will check the shared cache directory to see if a file with the exact same build signature already exists. If so, the derived file will not be built locally, but will be copied into the local build directory from the shared cache directory, like so: % scons -Q cc -o hello.o -c hello.c cc -o hello hello.o % scons -Q -c Removed hello.o Removed hello % scons -Q Retrieved `hello.o' from cache Retrieved `hello' from cache Note that the &CacheDir; feature still calculates MD5 build sigantures for the shared cache file names even if you configure &SCons; to use timestamps to decide if files are up to date. (See the chapter for information about the &Decider; function.) Consequently, using &CacheDir; may reduce or eliminate any potential performance improvements from using timestamps for up-to-date decisions.
Keeping Build Output Consistent One potential drawback to using a shared cache is that the output printed by &SCons; can be inconsistent from invocation to invocation, because any given file may be rebuilt one time and retrieved from the shared cache the next time. This can make analyzing build output more difficult, especially for automated scripts that expect consistent output each time. If, however, you use the --cache-show option, &SCons; will print the command line that it would have executed to build the file, even when it is retrieving the file from the shared cache. This makes the build output consistent every time the build is run: % scons -Q cc -o hello.o -c hello.c cc -o hello hello.o % scons -Q -c Removed hello.o Removed hello % scons -Q --cache-show cc -o hello.o -c hello.c cc -o hello hello.o The trade-off, of course, is that you no longer know whether or not &SCons; has retrieved a derived file from cache or has rebuilt it locally.
Not Using the Shared Cache for Specific Files You may want to disable caching for certain specific files in your configuration. For example, if you only want to put executable files in a central cache, but not the intermediate object files, you can use the &NoCache; function to specify that the object files should not be cached: env = Environment() obj = env.Object('hello.c') env.Program('hello.c') CacheDir('cache') NoCache('hello.o') Then when you run &scons; after cleaning the built targets, it will recompile the object file locally (since it doesn't exist in the shared cache directory), but still realize that the shared cache directory contains an up-to-date executable program that can be retrieved instead of re-linking: % scons -Q cc -o hello.o -c hello.c cc -o hello hello.o % scons -Q -c Removed hello.o Removed hello % scons -Q cc -o hello.o -c hello.c Retrieved `hello' from cache
Disabling the Shared Cache Retrieving an already-built file from the shared cache is usually a significant time-savings over rebuilding the file, but how much of a savings (or even whether it saves time at all) can depend a great deal on your system or network configuration. For example, retrieving cached files from a busy server over a busy network might end up being slower than rebuilding the files locally. In these cases, you can specify the --cache-disable command-line option to tell &SCons; to not retrieve already-built files from the shared cache directory: % scons -Q cc -o hello.o -c hello.c cc -o hello hello.o % scons -Q -c Removed hello.o Removed hello % scons -Q Retrieved `hello.o' from cache Retrieved `hello' from cache % scons -Q -c Removed hello.o Removed hello % scons -Q --cache-disable cc -o hello.o -c hello.c cc -o hello hello.o
Populating a Shared Cache With Already-Built Files Sometimes, you may have one or more derived files already built in your local build tree that you wish to make available to other people doing builds. For example, you may find it more effective to perform integration builds with the cache disabled (per the previous section) and only populate the shared cache directory with the built files after the integration build has completed successfully. This way, the cache will only get filled up with derived files that are part of a complete, successful build not with files that might be later overwritten while you debug integration problems. In this case, you can use the the --cache-force option to tell &SCons; to put all derived files in the cache, even if the files already exist in your local tree from having been built by a previous invocation: % scons -Q --cache-disable cc -o hello.o -c hello.c cc -o hello hello.o % scons -Q -c Removed hello.o Removed hello % scons -Q --cache-disable cc -o hello.o -c hello.c cc -o hello hello.o % scons -Q --cache-force scons: `.' is up to date. % scons -Q scons: `.' is up to date. Notice how the above sample run demonstrates that the --cache-disable option avoids putting the built hello.o and hello files in the cache, but after using the --cache-force option, the files have been put in the cache for the next invocation to retrieve.
Minimizing Cache Contention: the <literal>--random</literal> Option If you allow multiple builds to update the shared cache directory simultaneously, two builds that occur at the same time can sometimes start "racing" with one another to build the same files in the same order. If, for example, you are linking multiple files into an executable program: Program('prog', ['f1.c', 'f2.c', 'f3.c', 'f4.c', 'f5.c']) &SCons; will normally build the input object files on which the program depends in their normal, sorted order: % scons -Q cc -o f1.o -c f1.c cc -o f2.o -c f2.c cc -o f3.o -c f3.c cc -o f4.o -c f4.c cc -o f5.o -c f5.c cc -o prog f1.o f2.o f3.o f4.o f5.o But if two such builds take place simultaneously, they may each look in the cache at nearly the same time and both decide that f1.o must be rebuilt and pushed into the shared cache directory, then both decide that f2.o must be rebuilt (and pushed into the shared cache directory), then both decide that f3.o must be rebuilt... This won't cause any actual build problems--both builds will succeed, generate correct output files, and populate the cache--but it does represent wasted effort. To alleviate such contention for the cache, you can use the --random command-line option to tell &SCons; to build dependencies in a random order: % scons -Q --random cc -o f3.o -c f3.c cc -o f1.o -c f1.c cc -o f5.o -c f5.c cc -o f2.o -c f2.c cc -o f4.o -c f4.c cc -o prog f1.o f2.o f3.o f4.o f5.o Multiple builds using the --random option will usually build their dependencies in different, random orders, which minimizes the chances for a lot of contention for same-named files in the shared cache directory. Multiple simultaneous builds might still race to try to build the same target file on occasion, but long sequences of inefficient contention should be rare. Note, of course, the --random option will cause the output that &SCons; prints to be inconsistent from invocation to invocation, which may be an issue when trying to compare output from different build runs. If you want to make sure dependencies will be built in a random order without having to specify the --random on very command line, you can use the &SetOption; function to set the random option within any &SConscript; file: Program('prog', ['f1.c', 'f2.c', 'f3.c', 'f4.c', 'f5.c']) SetOption('random', 1) Program('prog', ['f1.c', 'f2.c', 'f3.c', 'f4.c', 'f5.c'])
scons-doc-2.3.0/doc/user/SCons-win32-install-1.jpg0000644000175000017500000007027112114661557022245 0ustar dktrkranzdktrkranzÿØÿàJFIF,,ÿÛC    $.' ",#(7),01444'9=82<.342ÿÛC  2!!22222222222222222222222222222222222222222222222222ÿÀU"ÿÄ ÿĵ}!1AQa"q2‘¡#B±ÁRÑð$3br‚ %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyzƒ„…†‡ˆ‰Š’“”•–—˜™š¢£¤¥¦§¨©ª²³´µ¶·¸¹ºÂÃÄÅÆÇÈÉÊÒÓÔÕÖרÙÚáâãäåæçèéêñòóôõö÷øùúÿÄ ÿĵw!1AQaq"2B‘¡±Á #3RðbrÑ $4á%ñ&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz‚ƒ„…†‡ˆ‰Š’“”•–—˜™š¢£¤¥¦§¨©ª²³´µ¶·¸¹ºÂÃÄÅÆÇÈÉÊÒÓÔÕÖרÙÚâãäåæçèéêòóôõö÷øùúÿÚ ?/¯¼9á?xNyü¤êSjrÉ$²ÆªÛ‚®I;NIÝYð°ð¿ý}ÿÿâ*Õ®•á x²ê̾­i {‡»šÞF••Ñ%x‡î21žj¸ð&—ÿ=ï?äSþÛûëþ¿û¿wî{u÷ªµ>·ûßù“Í>âÂyáú''þ;ÿÄRÿÂwáú''þ;ÿÄT¾(ð.¥Áâq§É~³h-hK\J’-ÂÎ:`"ì ‘Î[8è3Çž ¸S§5u½ÿ™2œÖìïÿá9ð¿ýÿÿâ)á8ð¿ýÿÿâ+áUì!ç÷¿óµŸs½ÿ„ßÂÿôNtOÉøŠ_øM¼/ÿDçDü—ÿˆ® S…Â{ÿ0ö³îwŸðšx_þ‰Ö‰ù/ÿKÿ Ÿ…ÿèhŸ’ÿñŠp£ØÃÏïæÖ}Îçþ/ ÿÑ;Ñ?%ÿâ)á0ð¿ý½ò_þ"¸qO{yýïüÃÚϹÛÂ_áú'š'ýò¿üE/ü%¾ÿ¢y¢ß+ÿÄW)—±‡ŸÞÿÌ=¬û§ü%žÿ¢y¢ß+ÿÄS¿á+ð¿ýÝþù_þ"¸±O{ùýïüÃÚϹÙÂSáú'º'ýò¿üE/ü%ÿ¢}¢ß+ÿÄW)Âc?½ÿ˜{Y÷;â ÿÑ>Ñ?ï•ÿâ)á&ð¿ýíþø_þ&¹N½Œ|þ÷þaígÜë¿á$ð¿ýýþø_þ&—þ? ÿÑ?Ñ?ï…ÿâk“áG±ŸÞÿÌ=¬û_ü$>ÿ¡Dÿ¾ÿ‰§ÂAáú4OûáøšåçR5ï ÿЃ¢ß ÿÄÒÿnø_þ„þý¯ÿ\¸§ =”|þ÷þaígÜéÿ·çD5 ÿЇ¢ßµÿâiµ|/ÿB‰ÿ~—ÿ‰®xS…Ê>{ÿ1ûY÷:íO ÿЉ¢ߥÿâiF¥áú4Oûô¿üM` p£ÙGÏïæÖ}Íá¨ø_þ„]þý/ÿKý¡áútOûô¿üMa p¥ì£ç÷¿óµŸssíÞÿ¡Dÿ¿KÿÄÒý·Âÿô#hŸ÷åøšÅáG²ŸÞÿÌ=¬û›?lð¿ýÚ'ýù_þ&”]x_þ„}þü¯ÿXâœ(öQóûßù‡µŸs_í>ÿ¡Dÿ¿+ÿÄÓ¾ÑáúôOûò¿áYž(öQóûßù‡µŸsTMáú4Oûò¿áJ%ð¿ý 'ýù_ð¬±N½œ|þ÷þaígÜÓßáútOûð¿áJÂÿô$èŸ÷á³…8Qìãç÷¿ók>æ€>ÿ¡'Dÿ¿ þ¸ð¿ý Z'ýø_ðªœ(öqóûßù‡µŸrþß ÿЕ¢ß…ÿ 6x_þ„½þü/øU1J(ökÏïæÖ}Ë¢? Ÿù’ôOü_ð¥ø_þ„½ÿ×ü* § ^Íyýì=¬û–„ÿ¡3DÿÀuÿ Qmásÿ2f‰ÿ€ëþXSÅÍyýì=´û“‹_ ÿЛ¢à:ÿ…(´ð¿ý º'þ¯øT"œ(ökÏïaí§Ü—ì^ÿ¡;DÿÀeÿ wØ|/ÿBv‰ÿ€Ëþ§Š=šóûØ{i÷ö ÿТà2ÿ…/öw…ÿèOÑ?ðš)—"óûØ{i÷i¾ÿ¡?DÿÀeÿ _ìÏ ÿП¢à2ÿ…œ(ä^{m>âex_þ… ÿ—ü)F“áú4OüZx§ 9ŸÞÃÛO¹ÒfãÜú×®øgþD Ø.ý´«W‹98¤·˜%-nx¨ðïį·}»ÍÔ¾×åù?hþÓ_3ËÎvîó3·<ã¦i°ø_â,qÓ¡7ñØ•d6ɨ¨Œ«gpÚ99òkÛ(¥õéÿ*þ¾böîÏ"ñ.“ñÅW²O¨Ú8…Ù\ZGx¾DlnU ‡üIõ¬1ðëÅô ÿɈ¿øª÷š(Ž>¤U’_×̽[g„‡~*ÿ _þLEÿÅS¿á^x§þù1ÿ^ëE?í ½—õóÕâx_ü+ßÿÐ/ÿ&"ÿâ©GÃïÐ/ÿ&"ÿâ«Üè£ûB¯eý|Ãêñ<8|?ñGý?òb/þ*œ<âúäx¿øªöú(þЫÙ_0ú¼Oñ7ý?ò<üU(ð‰¿èÿ‘ãÿâ«Û(£ûB¯eý|Ãêñ¿S²þ¾aõxž><â/úÿähÿøªpðOˆèÿ‘£ÿâ«×¨£ëõ;/ëæW‰äCÁ^!ÿ þFÿЧøƒþÿù?þ*½nŠ>¿S²þ¾aõxžL<¯ÿχþFÿЧëÿóáÿ‘£ÿâ«Õè£ëõ;/ëæW‰å#ÁÚ÷üøädÿâ©ÃÁú÷üøÿädÿâ«Õ(£ëõ;/ëæV‰å£ÂïüøÿäTÿâ©ÃÂ:çüøÿäTÿâ«Ô(£ëõ;/ëæV‰æÂZçüøÿäTÿpðž·ÿ>_ù?ƽ6Š_^©Ù_0ú´3ÖÿçËÿ"§øÓ‡…u¯ùòÿÈ©þ5éTQõê—õó«Dóqámgþ|ÿò*(ð¾³ÿ>ù?ƽŠ>½S²þ¾aõxžt<1¬ÏŸþEOñ§ ëóçÿ‘ükÐè£ëÕ; ú´=ÕÿçÓÿ"'øÓ‡†õùôÿȉþ5èQõê}Z<9«ϧþDOñ¥Õç×ÿ"/ø×{E]©ÙÕ pƒÃº¯üúÿäEÿpðö©ÿ>¿ùÆ»š)}v§dVÃêŸóëÿ‘üiÃ@ÔÿçÛÿ"/ø×mE]©ÙÕ qcAÔ¿çÛÿ_ñ§ RÿŸoü}Æ»*(úíNÈ>­Ž£ÿ>ÿøúÿ8hšüûÿãëþ5×ÑG×jvAõh—ö.¡ÿ>ÿøúÿ(ѵù÷ÿÇ×ük¬¢®ÔìƒêÐ9Q£ßÿÏü}Æ”ißóÃÿ_ñ®¦Š>»S²«@æ“}ÿ¹>È>­0ZOýÏÔS…¬ßÜýEhÑG×'ÙÕ PÒÿsõáo/÷QWh£ësìƒêÐ)ˆ$þïëN¿÷ZµE[ŸdV‡5¯‰fžI¶­“{Ú¶#' Ëæª_hzî£c5ε C*í`,€8úﮞŠÇ*üÌב÷‡ùWñ<Ô´ðzù ÿ¡QNø§ÿ![úàô*(o™Ý‚VV=Ã?ò#økþÁpè5¥Y¾ÿ‘Ã_ö ƒÿA­*+^¥-‚Š*¯ö‡üþÛßÕÿÌeª*¯ö‡üþÛßÕÿ?´ì?çöÛþþ¯øÐª*¯ö‡üþÛßÕÿ?´ì?çöÛþþ¯øÐª*¯ö‡üþÛßÕÿ?´ì?çöÛþþ¯øÐª*¯ö‡üþÛßÕÿ?´ì?çöÛþþ¯øÐª*¯ö‡üþÛßÕÿ?´ì?çöÛþþ¯øÐª*¯ö‡üþÛßÕÿ?´ì?çöÛþþ¯øÐª*¯ö‡üþÛßÕÿ?´ì?çöÛþþ¯øÐª*¯ö‡üþÛßÕÿ?´ì?çöÛþþ¯øÐª*¯ö‡üþÛßÕÿ?´ì?çöÛþþ¯øÐª)‘MñùÈ’&JîFgÓ#ê?:}QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEyoÅ?ù Ø×ÿ¡QGÅ?ù Ø×ÿ¡QV¶%žáŸùü5ÿ`¸?ôÒ¬ß ÿÈá¯ûÁÿ Ö•:ßÄ—¨ÖÁ^tªon Pzý¿Ú5ïõà7?ñùqÿ]_ÿB5Û–$ê»öýQææ’jгëú2 ‹ýÑùQ±º?*uîû8vGƒí'ÝØ¿Ý•û£ò§QG³‡dÒ}ØÝ‹ýÑùQ±º?*u{8vAí'ÝØ¿Ý•û£ò§QG³‡dÒ}ØÝ‹ýÑùQ±º?*u{8vAí'ÝØ¿Ý•û£ò§QG³‡dÒ}ØÝ‹ýÑùQ±º?*u{8vAí'ÝØ¿Ý•û£ò§QG³‡dÒ}ØÝ‹ýÑùQ±º?*u{8vAí'ÝžÑà€´°?y?þ‡[u‰àŸù´Ïúé?þ‡[uòø+OÕŸU†w£Eùdkúþ•oövV×Jólj®Z­$‰c¶F_ž˜ŒÖ½gë6ê61Á "²ÝÛNK’ØçI§|)Ǿ+r×b‡T:~ m­&[0cp6É,­*ùi»Žb8îsÐb­C©Ú´‘C-ÕšÜO$© IpÉå± ·¡,ù€)ÈÉÆk#Yð五úìñ‹a5öŽ4ûy;‘³1lœp¤¼}3½8 ¿‡/íïa|X´fîi¦c¸’†æI‘J‘‡#z•9S‚Ap̤®úÜÓjËz>ΚtkpÏ’û (O™€8ù’UÛÉù3üB‹­_ÈÕ–Æ4„íŽ9&’i¼±™(Ž5ÌØ—w@7Pköõ¬Ë&È›÷7¨ð¼)ù¾u †ãd³­T¢ðÍÒiF.¾Ñx×¶²ç» ™ )lgqD,GO2G#¨R/hSZÏuµ§Ioo·Î•.¤{ŽqÎO=joí3þ‚6ñíö¿õëþ£þzõûŸít÷®}tOc+]¥šßI"<ío¨JŸjPybEŒ<¥•”.ìà†$»³UÓ¼!¨ÚÙÝ $¶;A4iö‰%⽚ä#H˸‚$EÞA9ÜpqÈA¦xŠÃS‹S¹†âØÙXÏ囤Z7QH͸pÞAçøOÐZ¼ÔímmîíVk,[—l÷5#ó6³s·äù…ç¬Ë]êóO×íõu†íyrZNd؆á8fEù¾B~î9k"ÿÁw÷Ö /Ý\ÚN÷ÊÞ_Û9#R¿.|°³È¼üÛb„ Èî¯ÿkêÖ¿fò¾ËŸ›ÌÝ»ÏL ¨ÝÿÇl›VšÍ…ÀµŒßØ›«…b†ådÜÛÈCÁ`•³òp3Yú…u¥jšÔòBÉs $fææ^rð΃ê±9zGƒn´ý­d6kk:F•3ó%²Û‚¤íÉæ) ŽŸ0é“€†éMmÚá“Ͻ6£‘HŽ}ŒÛç†ùq޹e棻ñ„QÈm.-¯&†î YâŠu- K2ÅóœI8=v‘ÅfYxrþ ‹IÜX¯ØîÑâŽ-Ø1䌀ÄeyÌéÎåCía³?þ-fmRÚææh\Áåù“½üҙȹ·•œD˲ˆ[äBFXƒ4ÓÙk–³xjÏ[¼’{‹h§s4 ${À 8X ñš²ú•„Z„Z|—¶É{*ïŽÙ¥Q#¯<…ÎHùOä}+hwðx?IÓ¡6Ía(Ìdä{ G&Ó´çÕ2åYpÇž‰aq$Á¥Éeq<9FA GI¶8pF?r ãiaÃlù€5âÖ4ɤž8µ9ÞE†eIԘݛj« ðŸòO&±¦Ikotš›[ÜÈ!‚Q:”•É *œá› Œx5Ê¿ƒïîìtÛ Å±km6mgQ,öîìêP%mñ³. |É»}áËù¯¯®!-ö«½à͸•‰ †6 0C©ò˜4GRA ‚ÿR°ÒàYõ ÛkHY¶ .%XÔ¶ ÆIàʃ©X-âY›ÛatìȰW{0PÄÎI ÊÇØƒÞ©jvwÿÚ–zžŸ´óAÖæ‰š%+#FÛ·~Aˆ cÙÈÆ.àÉtÝ.[D’ÚIÞžâãiVx­–ÜÜÅ!$ Ýy4ÕÉyk—0£®Ì«8ob©ÇûL¤`TkdÒOZœo"Ã2¤êLn͵U†xbÜy'ŠÅ¾ÓŸRñnØ„Él±Àײ< h%À±1À9.ûÈ߀~RsT“º›ZiÐHökým¬²³}«d°H ü£ÊÏÙ”`y˜ÞO;~`¨êV x–föØ];2,UÞÌ1s’B²±ö ÷¡õ+µ´ù/m’öUß³J¢G^y œ‘òŸÈúV“¡jpxš}bþK?ßyäÇc·zZª¯#œ}ëR°²žÞ »Ûky®[dË*£JÙ ùŽHéê+ MSžêeu³K{ËÛmBw³ôØnŽE"9ö3lsžåÆ:å”cšçµO êwúmΘf¶âKÛˆ'2¶÷{„B2mª›ƒóbv”nãBJÎi¬Å¾›d¶°yRÈ’LR[y™À=â ¹(NA|àv–—–·ö©ugs Í»çd°¸tlÁäøUXuíâçƒV±–'é"\£+JFD`ƒËÛ­eÛønFðί¦ÌRÚmMeWe¸–ì¡h„`™%!œáAè  q¸±tMNïÄVšÕâÙÛ¼RFgiFÄŠåC(¿1kÆ0ç'8=Q@Š( Š( -ø§ÿ![úàô*(ø§ÿ![úàô**ÖijÐ<3ÿ"?†¿ìþƒZU›áŸùü5ÿ`¸?ôÒ§[ø’õØ+Ànãòãþº¿þ„kß«Ànãòãþº¿þ„k»*þ+ôýQæfßÁ^¿£"¢Š+Þ>|(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢ŠöÿÈ…¦×Iÿô:Û¬OÿÈ…¦×Iÿô:Û¯•Äÿ~¯ó>· üz/È+ VŒßx‹NÓešå-d´¹ÖÞáábèðªÈC` Œàç‘ÀÆíRÔ4«MOË7exó²H'’8ÈÞŒiÀ$g¨=…bnbë:øð¬Q›PŠÚk‚Í,“E$©ù#`x ‘“;9bw0dž3kX&½¼Ó;ç»·Y`òHöâV$& Vä°;¸Ûœè\øOF»µÒ[̱ÌN"º–#2’I`dÉf?>yv=XæÓèzd$Z$¤òÎ#rYKÊ$ ä,œ>nœ eê>"Ôt‹HÍjžu†Ým§’xÜ•w9+˜¶q¯?x© j·¦¹y$°ÜÛ/Ø-›ìÓ]æÜ«0Vá¶ U%Bä‚èðΖ xÌw,ÌÊÞ{ÞLÓ©¶RÛÔÎ0¿¼sjÇI±Ó¤i-`òÝãX‹³ ÎÀ’IËn‘ÉcÉ,I&€9;ÅºŠøjÒòêΉmô˜uBO?i1¸r5 †r#rTìÈ×ñˆdѧ´¶·±{»›•’@¡e*¨…CåG#g.¸ùq×$3øgH‘"Clëkåùi<ŠIº†ã˜`T@*Ö¡¥Zj~Y¸+Ç’A<ÈÆFô`ÛN#8%Aì(›Ô¼f©mwnúeÊN,üÛ¼Æ E¯[y |æþëh;ùº µ}Lx#Ä:‹H‹shÚˆµ•pÇlO BWh¸Ç9 9$ 9¼1£Om%³Ø ‚F,cFdPLgà0<¯—ׯ5utë4‚æn† –fš&ù‘‹›å<Ü’Á$“É$€s–šæ£iªê¿l´Ýlº´8¹Ü!ia·UHe>Xœ6@c,øsÅRx‚qÿ»˜-¥ƒíNÑʦF—T1 ÎæÀµDÓÒ‡ÉvY'ŠáËÌìÍ$B0ŒX’IT}ùÇ9ÉÉa¢iúlí5¬.­·b™ÝbLƒ²5bDkÂü¨ùW”`"ÌOiâ‘ÔRââæd†qpÖá~wŽ!ã±–5¶ÄËF~c»çÌo_ê:rÍa¦M%«ÚÍ*J±º=ÄHRFx‚«2Éÿ,̃ˆ<z¨´M>@ß$.&ÜΪfs;gs¬dìV9l°Í“óÀžÒ#IP[9W]Š­<Œ!\†,·î@*¤y{pQq÷Wú‹ÿ³ôC©}‡ÌÅÍì_ø÷[†ÎvÿÙúcÝñÍØn®µOíþ?°]‹ec%É}©.õVW(¤81·ðà|§'$uoiZ¥½æ"ò®§ŽeŽmÌ–7GeMÛFíä°ÜpNHiéúU¦™æq3<˜ß$óÉ4„ àov-´d3€Xžæ€9ûÇØ´žvÙJúŽÈÜù7 »(¸ÎW÷}3þ¨sÊîŽÛTÔt»yâÝ$ÔRÒÞæì]_H^y'&(ÕRL•q±B‚X\«ž‘ô›<ýðgϹŽêO¾icÙ±ºñ)8sÔæCÃún§t.®b˜\ ˜–™!q°HÄÒ}Üö  ¹üY5¥õ…½Î·Îò#º<’›Yå Ù–?+‚éœÈ§!NT6f§âOPðmäöV0ÛÜ6‹ý¡+ýµ”À’¬žY„yg6$€OQÑÉá.Yá™ã¹-DøûdÁdxÊ”yv$aµ~g£$àS.|'£]XÅföó%¼VÂÐ$7Rž0ʰ. €ÙÆæõ94jŠdQ,(UKY›çrÇ$’y$ñ“ÀèÀSèQEQEQEQEQEQEQEQEQEQEQEQEQEQEå¿ÿä+aÿ\þ…Eÿä+aÿ\þ…EZØ–z†äGð×ý‚àÿÐkJ³|3ÿ"?†¿ìþƒZTë^£[x Ïü~\×WÿÐ{õx Ïü~\×WÿÐwe_Å~Ÿª<ÌÛø+×ôdTQE{ÇÏ…Q@Q@Q@Q@Q@Q@Q@Q@ÑàŸù´Ïúé?þ‡[u‰àŸù´Ïúé?þ‡[uò¸ŸãOÕþgÖῃEùdj:ÍŦ©…¦›%ä¯ ›ä•S ïc¹kÖÿÈ÷ýƒ$ÿÑ©X£`þ×Ö¿èYŸÿáÿâ¨þ×Ö¿èYŸÿáÿâ«rŠÃþ×Ö¿èYŸÿáÿâ¨þ×Ö¿èYŸÿáÿâ«rŠÃþ×Ö¿èYŸÿáÿâ¨þ×Ö¿èYŸÿáÿâ«rŠÃþ×Ö¿èYŸÿáÿâ¨þ×Ö¿èYŸÿáÿâ«rŠÃþ×Ö¿èYŸÿáÿâ¨þ×Ö¿èYŸÿáÿâ«rŠÃþ×Ö¿èYŸÿáÿâ¨þ×Ö¿èYŸÿáÿâ«rŠÃþ×Ö¿èYŸÿáÿâ¨þ×Ö¿èYŸÿáÿâ«rŠÃþ×Ö¿èYŸÿáÿâ¨þ×Ö¿èYŸÿáÿâ«rŠÃþ×Ö¿èYŸÿáÿâ¨þ×Ö¿èYŸÿáÿâ«rŠÃþ×Ö¿èYŸÿáÿâ¨þ×Ö¿èYŸÿáÿâ«rŠÃþ×Ö¿èYŸÿáÿâ¨þ×Ö¿èYŸÿáÿâ«rŠÃþ×Ö¿èYŸÿáÿâ¨þ×Ö¿èYŸÿáÿâ«rŠÃþ×Ö¿èYŸÿáÿâ¨þ×Ö¿èYŸÿáÿâ«rŠÃþ×Ö¿èYŸÿáÿâ¨þ×Ö¿èYŸÿáÿâ«rŠÃþ×Ö¿èYŸÿáÿâ¨þ×Ö¿èYŸÿáÿâ«rŠÃþ×Ö¿èYŸÿáÿâ¨þ×Ö¿èYŸÿáÿâ«rŠÃþ×Ö¿èYŸÿáÿâ¨þ×Ö¿èYŸÿáÿâ«rŠÃþ×Ö¿èYŸÿáÿâ¨þ×Ö¿èYŸÿáÿâ«rŠÃþ×Ö¿èYŸÿáÿâ¨þ×Ö¿èYŸÿáÿâ«rŠÃþ×Ö¿èYŸÿáÿâ¨þ×Ö¿èYŸÿáÿâ«rŠÃþ×Ö¿èYŸÿáÿâ¨þ×Ö¿èYŸÿáÿâ«rŠÃþ×Ö¿èYŸÿáÿâ©É©k²¶Øü/rç®ê#ýkj¦‡ýUÇýsúÐÛQEQEQEQEQEQEQEQE{G‚äBÓ?ë¤ÿúmÖ'‚äBÓ?ë¤ÿúm×Êâ?WùŸ[†þ =ä†ÿò=Ãÿ`É?ôjVåa¿üpÿØ2Oý•Š67(¢ŠQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEÕÍõ¼L,t¹oÞAµ‚L‘ì?1Î1Çøfz¹cϘ>œ~cúúwï¬!CVñB¨Uðíæ-ü?§ÍôüǨ¦hvº’êšÕýþžöks ´©&J1Ï*OªŸÆºÎ¾ùüsüý}ú÷ÏÏ Ç1HéŸ_øŸëëß¾w3›ER(òߊò°ÿ®ÿB¢Šò°ÿ®ÿB¢­lK=Ã?ò#økþÁpè5¥Y¾ÿ‘Ã_ö ƒÿA­*u¿‰/Q­‚¼çþ?.?ë«ÿèF½ú¼çþ?.?ë«ÿèF»²¯â¿OÕfmüëú2*(¢½ãçŠ( Š( Š( Š( Š( Š( Š( Š( hðOüˆZgýtŸÿC­ºÄðOüˆZgýtŸÿC­ºù\Oñ§êÿ3ëpßÁ‡¢ü‚°ßþG¸ì'þJܬ7ÿ‘îûIÿ£R±FÆåa>½,>5$° µ–Ò9"œ»Ïc)òÈô) °,Ñ–IQ®&_*GÙ­e61WmÅq±Ã>v®FHÈÎ.£à·h–šoÚìäò4ä°ó/,|ÿ/j•óaÇ”ç<œ¶v§÷y»qá´Z\Áöí¾u¶£ï'8û\¢LýïàÆ1߯h#PøƒMRÛ‰fgxÊÛHÈ쀖D`»dqµ²ŠK|­ÇÊqfÿP¶Ó`YnYÀf؉m#»`œ*(,ÇžÀô°­¼ –:¼ú…©Ó’S$ÓÅ3iÊ×d›‰ß.ì²ç ¡Øv¾µ§KªiæÚ„…·Ì‘—SŽ™• YX2©ÏéMu uæy›6Ílð‚àP +0eÀ, ܸûÃ07‹4D†I¥½ò`M§Îš'Ž7Vu@èÌt˦]IPI h·Ñ¯­/ä– WýâHæºó-Õ¦‘Õ8pB*°r6Ë`®FÜøü/—a ºŠ4:jà ˜Kr¬"I¡‰󽈷A¸,vœ€4ω´…%{—@Y•’H$W‹hšD+º5”–pRNg^¹û¿ Ï5ÝÍÄ—g½[¯õŒxŠ8òCâ3‚r¤HÊÈà×A@Q@Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( ®Xóæ§˜þ¾ûçkS«–<ùƒéÇæ?¯§~ùÚÂs¯¾ÿ?_~½óóÃqÌRúg×þ§úú÷ïÍ7_|þ9þ~¾ý{çç†ã˜¤?ôϯü Oõõïß;™ˆÍ¢Š)yoÅ?ù Ø×ÿ¡QGÅ?ù Ø×ÿ¡QV¶%žáŸùü5ÿ`¸?ôÒ¬ß ÿÈá¯ûÁÿ Ö•:ßÄ—¨ÖÁ^sÿ—õÕÿô#^ý^sÿ—õÕÿô#]ÙWñ_§ê36þ õýQ^ñóáEPEPEPEPEPEPEPEP´x'þD-3þºOÿ¡ÖÝbx'þD-3þºOÿ¡ÖÝ|®'øÓõ™õ¸oàÃÑ~AXoÿ#Ü?ö “ÿF¥nVÿÈ÷ýƒ$ÿÑ©X£cr¹]b[Í'PÔRÀ:ͬ@¢ÍÕ7„¼».ù†&ÀÏÉ­ŽðÅ+ÄòFŽÑ6øÙ”‚¹‡ GПZr1êÚ†« +NÓQt]:{}<³²$x>cÊ%PËrÅ@Ý–ùdÓ|E©ÜÚèSÜI§{m ×U—cò/Þ&>2ˆa#)\ÆJç£}6Â]B-BK+g½‰vGrÑ)‘žc |Çó>´ 6Á^ÑÖÊØ5š”¶aæ#i ÇÊ0ãµ9-Ä—óèöW±Ác”Mci%¤P°%§HämØE_èÛƒêµJML—ì~ffÿbÇÙw@§ÈÆ1³—W¦:J»@Q@Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( ®Xóæ§˜þ¾ûçkS«–<ùƒéÇæ?¯§~ùÚÂs¯¾ÿ?_~½óóÃqÌRúg×þ§úú÷ïÍ7_|þ9þ~¾ý{çç†ã˜¤?ôϯü Oõõïß;™ˆÍ¢Š)yoÅ?ù Ø×ÿ¡QGÅ?ù Ø×ÿ¡QV¶%žáŸùü5ÿ`¸?ôÒ¬ß ÿÈá¯ûÁÿ Ö•:ßÄ—¨ÖÁ^sÿ—õÕÿô#^ý^sÿ—õÕÿô#]ÙWñ_§ê36þ õýQ^ñóáEPEPEPEPEPEPEPEP´x'þD-3þºOÿ¡ÖÝbx'þD-3þºOÿ¡ÖÝ|®'øÓõ™õ¸oàÃÑ~AXoÿ#Ü?ö “ÿF¥nVÿÈ÷ýƒ$ÿÑ©X£crŠ( aEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPW,yóÓÌ_Nýóµ©ÕË|Áôãó×Ó¿|ía¹×ß?ŽŸ¯¿^ùùá¸æ)ý3ëÿSý}{÷Îæ›¯¾ÿ?_~½óóÃqÌRúg×þ§úú÷ïÌÄfÑEŠ<·âŸü…l?ëÿШ£âŸü…l?ëÿШ«[Ï@ðÏüˆþÿ°\ú iVo†äGð×ý‚àÿÐkJoâKÔk`¯¹ÿËúêÿú¯~¯¹ÿËúêÿú®ì«ø¯ÓõG™›zþŒŠŠ(¯xùð¨/dxl.%ŒáÒ&e>„SÕ]KþAwõÅÿôS="˦¯4v?ð†x/Hð¶©x‹Å½´Ú°•Drîí$à úÈzÓíþÁ?Чˆo$Ñ.ìd»²‘.˜q޹=9?ž+cľ1Ò¼1à.§áûmgí  ”ˆñ ä±çpýkϧøÃâ¿EªiÐù}¤L‰§«|‚¬úð9íÆ+ææôMŸTáMjÒ!øy¢ø†ëK¿Ôî †ÎÒIàuVrcžzËêk¬ðwƒ|â›u¶‹Å:Ãê‘[ù·‰vmÆàHÎG íëWükâkøfÛVðÖ• –£âhüöH&0O÷»qœžµÍüY£ñ–¢ónV›L’@Y°XnýáèúÝiº•·`©ÓZY:oƒ|â/>¡x§Wž(m'–lÉŒ2¶ HÈç°ç‘šåþh‡Æ¾!ºÒïõ;¨a³´–DxUœƒ€X瞣òú“³ð@ž?ÕØœ±JHÝ׿í×½w_üy¢øÄZ­–Ÿá›m.e·’F¸‰—t€3uÆ?¼÷ç¡¥ífº°t©½yQÅÙx+Ãú'‡­/ükâ›ûiuÒZ­¬Å†À3ŒäôöÎhÚü'†ëÅ ‘x‚ö]òÆK«Žày‡9<˜ãëŠâ¦‘©ëÞð{iVwë³y†Ü6£®¿º}zuí^• ù¶GÃV³1Šê A$Lÿ2°Ø9g¨ô?QG´Ÿv?gÈñx^ëľ,¾Ðu»»ë&Óíel!ÈÌlžz^ŸRq´+ùn–âÞfÞmØ(žXsŒûñÖ½·áïŒ4oßÝ^Ë [x’ÞÚHfØHó£Îwgw wãÖ¼Aº‚ ÍDÏx(¢ŠöÿÈ…¦×Iÿô:Û¬OÿÈ…¦×Iÿô:Û¯•Äÿ~¯ó>· üz/È+ ÿä{‡þÁ’èÔ­ÊÃùáÿ°dŸú5+lnQE (¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š*å>`úqùëéß¾vµ:¹cϘ>œ~cúúwï¬!:ûçñÏóõ÷ëß?<7Å!ÿ¦}àJ¯¯~ùÜÓu÷ÏãŸçëï×¾~xn9ŠCÿLúÿÀ”ÿ_^ý󹘌Ú(¢‘G–üSÿ­‡ýp?ú|Sÿ­‡ýp?úkbYèÿ‘Ã_ö ƒÿA­*ÍðÏüˆþÿ°\ú iS­üIzlà7?ñùqÿ]_ÿB5ïÕà7?ñùqÿ]_ÿB5Ý•ú~¨ó3oà¯_Ñ‘QEï>º~‡«x³Y“AÒ¢9´y¤’åÈùy\ õúÒWSð®â+oˆ×,Š«ýÇ ás‡Ïr?¯á\˜éÊ[‹;pŒë¥$eøÇáÇÖæûTµÕ-´˜¶Coo&çTÉÉÆ9ÀQï¥C¢ø#Å/Ѧ½¶[ ƒ$?mb¯"í?0ÇÓÿ׊ŸXøÛÚßÁ xz 2örSí‰ fÆæ$àOÍž§’}«wSÑgø¥àO ¶ƒ¬Û$št+µ¸•ïÙß“è×W… ³‚j.×=ú”aQ§%{ž«àëºÔÔï!’=.Ñä´–GÄoÿtã“ÐsÓ½PðTž&Ö¼QqÿÕä67Ñiò-Ã;¬ œíëžHÁí^ÅŠômÄÚ†¯ue{ûm5ìå)æ»rÛø'iëϸ«6ÖÞðl ª ›föwÏß’Fà €ù' ðryê*TÚ‹ŠÙ–àœ”žèòo‡þñÏ“>»¤jú[N¯ë™0fRXpxÈàþ=©ºüyáï^éº]ÜV×­i+›’ÿ»ž>ûIœãèk×¼?«iÞ+ðF˜Ú}¶Ÿ©›h¼©RþSÂ0èwþ±<ÔoŒ¤½ø›u£ÜÛÛY*Â`+­êù*Gu;dsÒ¤£‚±ð÷Å?øJk;T‹Ëÿ_%’0–TÎàÄN¼J­á]âOˆî.<_³ •Åäm{·ÙæFTýÕÆäqùö®:/xÆoêzÕ–¡3]H­ÅHÆÃœ¹íÎ1š÷¿ ê¶>)ð’líôýRKhS¥ô¥ rØ»?Èšn-$ÚÜJI¶“Øñk/ xïÁÞ;m+Kv]Bò7AsÍ‘Ÿ¼ù=‡\ö5Üø#á·âK©¼E †§k-œØu>b¤™Ç9#Œÿˆ­åø‰nŸ?°uH­ôÿ³[Km‘\#wr ‚Ax¨Î}*ÏÃÿ øÁòj©ªë¶w\«$‰ÌY•ÉnrHÇ'ŸåšC<Ãr¹{è7~æ)–½—%³Ž¾žµ½\÷†Îo53œæEç9îÞçù×C_EmÐÿ­OšÇ¤±·—äQEug´x'þD-3þºOÿ¡ÖÝbx'þD-3þºOÿ¡ÖÝ|®'øÓõ™õ¸oàÃÑ~AXoÿ#Ü?ö “ÿF¥nVÿÈ÷ýƒ$ÿÑ©X£crŠ( aEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPW,yóÓÌ_Nýóµ©ÕË|Áôãó×Ó¿|ía¹×ß?ŽŸ¯¿^ùùá¸æ)ý3ëÿSý}{÷Îæ›¯¾ÿ?_~½óóÃqÌRúg×þ§úú÷ïÌÄfÑEŠ<·âŸü…l?ëÿШ£âŸü…l?ëÿШ«[Ï@ðÏüˆþÿ°\ú iVo†äGð×ý‚àÿÐkJoâKÔk`¯¹ÿËúêÿú¯~¯¹ÿËúêÿú®ì«ø¯ÓõG™›zþŒŠŠ(¯xù𪖕 ¡ƒ˜gQ…•zã¸>£üúÕú*gÎ<²WE¤©Ëš.ÌÍÒ4…Ò’P&2´„díÀtãñ5þ„·S™­î^Ø¿2 }q‘Ï'üæµè¬Þ“‚¦ÖˆÑbjªŽ¢z³Ÿ‹ÂÑùÍ%ÍÜ“d‹´ç×95xZF Rs¸îlÇÔúŸšºJ+?©Pµ¹3_¯â/~oÈÀo ªÅÛÞËd9õÆF:‘ôüsü"Òy†Oí'ÞÙËy|œõçut”PðTÙüÁcñ í~Fn‘¤.”’1•¤#'n§‰¨¯ô%ºœÍoröÅù(Èc댎y?ç5¯Ehðôœ6´FKUTuÕœü^Îi.nä› ô]§>¹É¤>¸9Ϋ)Ï_ûÿµî:èh¬þ¥BÖåüÍ~¿ˆ½ù¿#7HÒJI@˜ÊÒ“·ÓÄÖ•WD!qåŽÇ5J’©')=BŠ(ª öÿÈ…¦×Iÿô:Û¬OÿÈ…¦×Iÿô:Û¯•Äÿ~¯ó>· üz/È+ ÿä{‡þÁ’èÔ­ÊÃùáÿ°dŸú5+lnQE (¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š*å>`úqùëéß¾vµ:¹cϘ>œ~cúúwï¬!:ûçñÏóõ÷ëß?<7Å!ÿ¦}àJ¯¯~ùÜÓu÷ÏãŸçëï×¾~xn9ŠCÿLúÿÀ”ÿ_^ý󹘌Ú(¢‘G–üSÿ­‡ýp?ú|Sÿ­‡ýp?úkbYèÿ‘Ã_ö ƒÿA­*ÍðÏüˆþÿ°\ú iS­üIzlà7?ñùqÿ]_ÿB5ïÕà7?ñùqÿ]_ÿB5Ý•ú~¨ó3oà¯_Ñ‘QEï>QEQEQEQEQEQEQEQE{G‚äBÓ?ë¤ÿúmÖ'‚äBÓ?ë¤ÿúm×Êâ?WùŸ[†þ =ä†ÿò=Ãÿ`É?ôjVåa¿üpÿØ2Oý•Š67(¢ŠQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQErÇŸ0}8üÇõôïß;Z\±çÌN?1ý};÷ÎÖ‹}óøçùúûõŽbÿÓ>¿ð%?×׿|îiºûçñÏóõ÷ëß?<7Å!ÿ¦}àJ¯¯~ùÜÌFmQH£Ë~)ÿÈVÃþ¸ý Š>)ÿÈVÃþ¸ý е±,ô ÿÈá¯ûÁÿ Ö•føgþD Ø.ý´©Öþ$½F¶ ðŸøü¸ÿ®¯ÿ¡÷êðŸøü¸ÿ®¯ÿ¡îÊ¿Šý?Ty™·ðW¯èȨ¢Š÷Ÿ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€=£Á?ò!iŸõÒý¶ëÁ?ò!iŸõÒý¶ëåq?ÆŸ«üϭËò Ãùáÿ°dŸú5+r°ßþG¸ì'þJÅ”QE (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ ¹cϘ>œ~cúúwï­N®Xóæ§˜þ¾ûçkEξùüsüý}ú÷ÏÏ Ç1HéŸ_øŸëëß¾w4Ý}óøçùúûõŽbÿÓ>¿ð%?×׿|îf#6Š(¤Qå¿ÿä+aÿ\þ…Eÿä+aÿ\þ…EZØ–z†äGð×ý‚àÿÐkJ³|3ÿ"?†¿ìþƒZTë^£[x Ïü~\×WÿÐ{õx Ïü~\×WÿÐwe_Å~Ÿª<ÌÛø+×ôdTQE{ÇÏ…Q@Q@Q@Q@Q@Q@Q@Q@ÑàŸù´Ïúé?þ‡[u‰àŸù´Ïúé?þ‡[uò¸ŸãOÕþgÖῃEùa¿üpÿØ2Oý•¹Xoÿ#Ü?ö “ÿF¥bÊ(¢…Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@\±çÌN?1ý};÷ÎÖ§W,yóÓÌ_Nýóµ„"ç_|þ9þ~¾ý{çç†ã˜¤?ôϯü Oõõïß;šn¾ùüsüý}ú÷ÏÏ Ç1HéŸ_øŸëëß¾w3›ER(òߊò°ÿ®ÿB¢Šò°ÿ®ÿB¢­lK=Ã?ò#økþÁpè5¥Y¾ÿ‘Ã_ö ƒÿA­*u¿‰/Q­‚¼çþ?.?ë«ÿèF½ú¼çþ?.?ë«ÿèF»²¯â¿OÕfmüëú2*(¢½ãçŠ( Š( Š( Š( Š( Š( Š( Š( hðOüˆZgýtŸÿC­ºÄðOüˆZgýtŸÿC­ºù\Oñ§êÿ3ëpßÁ‡¢ü‚°ßþG¸ì'þJܬ7ÿ‘îûIÿ£R±FÆåQ@Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( ­Yº¨“s:Ÿ¯ùéùô5j{gdK†F*Â>8?yhxË9‘=þaïõ÷õï×?<O †x’TiV Ì¡ 'ØÝû½N=¯ˆ./5awþzä¸b@:±;°¿¸ÇZ©¥;?ŠüJÎŘÛZä““Õ©«ERå¿ÿä+aÿ\þ…Eÿä+aÿ\þ…EZØ–z†äGð×ý‚àÿÐkJ³|3ÿ"?†¿ìþƒZTë^£[x Ïü~\×WÿÐ{õx Ïü~\×WÿÐwe_Å~Ÿª<ÌÛø+×ôdTQE{ÇÏ…Q@Q@Q@Q@Q@Q@Q@Q@ÑàŸù´Ïúé?þ‡[u‰àŸù´Ïúé?þ‡[uò¸ŸãOÕþgÖῃEùa¿üpÿØ2Oý•¹YVä¯ÄYIi3Go™kljÒe„ee,u  ǦYGâ=j­\n 5%˜3£ÈîÌØÀF`¯N¿äUÞ_ˆQœÿeH2Ç?òÕ>¾¿¯~…Ø A‡Îz|§ÛüGæ(HqˆÜç§Ê}¿Ä~bµzûçñÏóõ÷ëß?9×ß?ŽŸ¯¿^ùùËÌ¡‡Îz|§ÛüGæ(HqˆÜç§Ê}¿Ä~bµzûçñÏóõ÷ëß?9×ß?ŽŸ¯¿^ùùËÌ¡‡Îz|§ÛüGæ(HqˆÜç§Ê}¿Ä~bµzûçñÏóõ÷ëß?9×ß?ŽŸ¯¿^ùùËÌ¡‡Îz|§ÛüGæ(HqˆÜç§Ê}¿Ä~bµzûçñÏóõ÷ëß?9×ß?ŽŸ¯¿^ùùËÌ¡‡Îz|§ÛüGæ(HqˆÜç§Ê}¿Ä~bµzûçñÏóõ÷ëß?9×ß?ŽŸ¯¿^ùùËÌ¡‡Îz|§ÛüGæ(HqˆÜç§Ê}¿Ä~bµzûçñÏóõ÷ëß?9×ß?ŽŸ¯¿^ùùËÌ¡‡Îz|§ÛüGæ(HqˆÜç§Ê}¿Ä~bµzûçñÏóõ÷ëß?9×ß?ŽŸ¯¿^ùùËÌ¡‡Îz|§ÛüGæ(HqˆÜç§Ê}¿Ä~bµzûçñÏóõ÷ëß?9×ß?ŽŸ¯¿^ùùËÌ¡‡Îz|§ÛüGæ(HqˆÜç§Ê}¿Ä~bµzûçñÏóõ÷ëß?9×ß?ŽŸ¯¿^ùùËÌ¡‡Îz|§ÛüGæ(HqˆÜç§Ê}¿Ä~bµzûçñÏóõ÷ëß?9×ß?ŽŸ¯¿^ùùËÌ¡‡Îz|§ÛüGæ(HqˆÜç§Ê}¿Ä~bµzûçñÏóõ÷ëß?9×ß?ŽŸ¯¿^ùùËÌ¡‡Îz|§ÛüGæ(HqˆÜç§Ê}¿Ä~bµzûçñÏóõ÷ëß?9×ß?ŽŸ¯¿^ùùËÌ¡‡Îz|§ÛüGæ(HqˆÜç§Ê}¿Ä~bµzûçñÏóõ÷ëß?9×ß?ŽŸ¯¿^ùùËÌ¡‡Îz|§ÛüGæ(HqˆÜç§Ê}¿Ä~bµzûçñÏóõ÷ëß?9×ß?ŽŸ¯¿^ùùËÌ¡‡Îz|§ÛüGæ(HqˆÜç§Ê}¿Ä~bµzûçñÏóõ÷ëß?9×ß?ŽŸ¯¿^ùùËÌ¡‡Îz|§ÛüGæ(HqˆÜç§Ê}¿Ä~bµzûçñÏóõ÷ëß?9×ß?ŽŸ¯¿^ùùËÌ¡‡Îz|§ÛüGæ(HqˆÜç§Ê}¿Ä~bµzûçñÏóõ÷ëß?9×ß?ŽŸ¯¿^ùùËÌ¡‡Îz|§ÛüGæ(HqˆÜç§Ê}¿Ä~bµzûçñÏóõ÷ëß?9×ß?ŽŸ¯¿^ùùËÌ¡‡Îz|§ÛüGæ(HqˆÜç§Ê}¿Ä~bµzûçñÏóõ÷ëß?9×ß?ŽŸ¯¿^ùùËÌ¡‡Îz|§ÛüGæ)¥@,¤Ó#ñþ¢µúûçñÏóõ÷ëß?s"{üÃßëïëß®~~foCo£ÉªOsq¬wfÑÙÜ.Öù‰-€¡¹Î~ï>ÕiµðºÜZGÚ¥7r[=ÎÑ'ÝEe\‘œŒ—㌭éEÀÜ2ÇÎdO˜{ý}ý{õÏÎcç2'¿Ì=þ¾þ½úçç¡ö»Ÿùø—þû5KUׯ`oo.¥Ku’8Ý̘ ½ÕHA`Iì¢àncç2'¿Ì=þ¾þ½úçç ±ó™ßæÿ_^ýsóá¶¾[‹HûT¦îKg¹Ú$û¨¬«’3‘’üqƒµ½*ïÚîçâ_ûìÑp/™cç2'¿Ì=þ¾þ½úçç ±ó™ßæÿ_^ýsóãjÌšm›\Ë5ÀÉ¢7Ìîìd’ÌIg’5 Ÿˆ¤º´º™þÝ–ŒRâÝþy#`¡ñˆË%YHÚOÞ®@.ù–>s"{üÃßëïëß®~pË9‘=þaïõ÷õï×??3Ф»ÒRþÎRãÌžH]’Gel‡*|Œ~r½‡Þ!jx¼E$úI¿‡íÒaš3nœÈ%W(ÉÁÛà©lí'vÞh¸æXù̉ï󯿯~¹ùÃ,|æD÷ù‡¿×ß׿\üü˜ñ”®âÞ+MRMD3¬–*ñ‰# ‰,d‘‰b<9?8ã†Å×ñ¥Û_Ù}ºþ;¥W-øgV]ÀüåB¼üÄzu ào™cç2'¿Ì=þ¾þ½úçç ±ó™ßæÿ_^ýsóòrxÐÇgöÁ¨öÑ+µÜ£m3+ï%†â¥"=çåÏ!—v†¡­^Ùyk ¶£}+äì¶e@ÆIgeQÔqœžÀ€H.á–>s"{üÃßëïëß®~pË9‘=þaïõ÷õï×??'¡¸xžÝ¯¦²v†3z¤ÑåcR¥ƒå„‘ò¼dŒ6&Ós"{üÃßëïëß®~pË9‘=þaïõ÷õï×?>Ÿ®Í©,¯»H£’H¼Çq†d‘ã`bx)ž@á‡|4³\ÍsWr³ÛH"˜naµŠ+ãßåuöÖ=ªP9ÀcûÅû¹Æpq˜5?.ycq}2ÛÀ.n䉆ÛhIl;n`X|Â?)ã‘’àtÆXù̉ï󯿯~¹ùÃ,|æD÷ù‡¿×ß׿\üøz†µ{eå¬6Úô¯“²Ù”m%•GQÆr{"k=Y¯ìmï-nå’Þâ5–'ÜÃr°È8<Žz.±–>s"{üÃßëïëß®~pË9‘=þaïõ÷õï×?=µÜÿÏÄ¿÷Ù£íw?óñ/ýöh¸X¾eœÈžÿ0÷úûú÷럜2ÇÎdO˜{ý}ý{õÏÏCíw?óñ/ýöhû]ÏüüKÿ}š./™cç2'¿Ì=þ¾þ½úçç ±ó™ßæÿ_^ýsóÐû]ÏüüKÿ}š>×sÿ?ÿßf‹…‹æXù̉ï󯿯~¹ùÃ,|æD÷ù‡¿×ß׿\üô>×sÿ?ÿßfµÜÿÏÄ¿÷Ù¢ábù–>s"{üÃßëïëß®~pË9‘=þaïõ÷õï×?=µÜÿÏÄ¿÷Ù£íw?óñ/ýöh¸X¾eœÈžÿ0÷úûú÷럟/YÖ Ó¢ »¹iT¨°4¤aËc8èzž}ùc/Úîçâ_ûìÑö»Ÿùø—þû4\,s±øš–Q«©™ƒJÂÁòøO ÇOsQèr½Þ¿«Þý–ꥎÝSí´d•ßž£ž£ó®›íw?óñ/ýöhû]ÏüüKÿ}š.54?ê®?ë˜ÿЖµÜÿÏÄ¿÷Ù¦½Äî¥^iOPX‘H袊yoÅ?ù Ø×ÿ¡QGÅ?ù Ø×ÿ¡QV¶%žáŸùü5ÿ`¸?ôÒ¬ß ÿÈá¯ûÁÿ Ö•:ßÄ—¨ÖÁ^K/€áÝs=åµÅäe?wtÐIC»8?»™pÂÿ£dŒÈHô(¸]§„‘õ¤þÐÓ!–ÑdÔe˜’¦9Úiâ’2ËŸŸù‡ ôF6¦Ó5 ¼+ C¨Y¾¡%²ÄÚ›º;\‘ ){r$e™±òd@ÏUEÎ-|1•èm lmh¬‹BÁ@º»‘PÅ»Ëu ,YBÊ$«EÆšhWÖŸöŠé¾t©{ ÓgY$•™·òÄ’.Èä¶\€U² bV»J(¸}káUg’êçN„]Ms îŽU%SS{‡Ž VI†>YÈS´Z‡õ I4ÅšÉí¦KŠÞ[Å™¢e„&òr9ˆ«Dp²nÞŠ.A°¹_i—%žhmì. –w*ž¹‘žvâ²uË J÷ûSÈÓ¦oí%,†dŒ}•Ç“/Í÷|>æóò·3ÖÑ@´·2ÙùcJ¾ž’E´»Xg]­ò°"EùXxpܨ+Ëmµ£ÛOe¢XZ]MÄÑÇ)BǹT´\ŽŽÂ®Ñ@Š( Š( Š( Š( Š( Š( Š( Š( Š( -ø§ÿ![úàô*(ø§ÿ![úàô**ÖijÐ<3ÿ"?†¿ìþƒZU›áŸùü5ÿ`¸?ôÑfTBîÁUFI' u¿‰/Q­…¢ªÿiØÏí·ýý_ñ£ûNÃþm¿ïêÿf2ÕWûNÃþm¿ïêÿÚvóûmÿWühÕWûNÃþm¿ïêÿÚvóûmÿWühÕWûNÃþm¿ïêÿÚvóûmÿWühÕWûNÃþm¿ïêÿÚvóûmÿWühÕWûNÃþm¿ïêÿÚvóûmÿWühÕWûNÃþm¿ïêÿÚvóûmÿWühÕWûNÃþm¿ïêÿÚvóûmÿWühÕWûNÃþm¿ïêÿÚvóûmÿWühÕWûNÃþm¿ïêÿÚvóûmÿWühÕWûNÃþm¿ïêÿÚvóûmÿWühÕWûNÃþm¿ïêÿÚvóûmÿWühÕWûNÃþm¿ïêÿÚvóûmÿWühÕWûNÃþm¿ïêÿÚvóûmÿWühÕWûNÃþm¿ïêÿÚvóûmÿWühÕWûNÃþm¿ïêÿÚvóûmÿWühÕWûNÃþm¿ïêÿÚvóûmÿWühÕWûNÃþm¿ïêÿÚvóûmÿWühÕWûNÃþm¿ïêÿÚvóûmÿWühÕWûNÃþm¿ïêÿÚvóûmÿWühÕWûNÃþm¿ïêÿÚvóûmÿWühÕWûNÃþm¿ïêÿÚvóûmÿWühÕWûNÃþm¿ïêÿÚvóûmÿWühÕWûNÃþm¿ïêÿÚvóûmÿWühÕWûNÃþm¿ïêÿÚvóûmÿWühÕWûNÃþm¿ïêÿÚvóûmÿWühÕWûNÃþm¿ïêÿÚvóûmÿWühÕWûNÃþm¿ïêÿÚvóûmÿWühÕWûNÃþm¿ïêÿÚvóûmÿWühÕWûNÃþm¿ïêÿÚvóûmÿWühÕWûNÃþm¿ïêÿÚvóûmÿWühÕWûNÃþm¿ïêÿÚvóûmÿWühÕWûNÃþm¿ïêÿÚvóûmÿWühÕWûNÃþm¿ïêÿÚvóûmÿWühÕWûNÃþm¿ïêÿÚvóûmÿWühÕ^;û9dXã»ÝÈUUIè«å¿ÿä+aÿ\þ…Eÿä+aÿ\þ…EZØ–z†äGð×ý‚àÿÐkL¬IríYžÿ‘Ã_ö ƒÿA­*u¿‰/Q­‰¾×sÿ?ÿßfµÜÿÏÄ¿÷Ù¨h¬ÆMö»Ÿùø—þû4}®çþ~%ÿ¾ÍCEMö»Ÿùø—þû4}®çþ~%ÿ¾ÍCEMö»Ÿùø—þû4}®çþ~%ÿ¾ÍCEMö»Ÿùø—þû4}®çþ~%ÿ¾ÍCEMö»Ÿùø—þû4}®çþ~%ÿ¾ÍCEMö»Ÿùø—þû4}®çþ~%ÿ¾ÍCEMö»Ÿùø—þû4}®çþ~%ÿ¾ÍCEMö»Ÿùø—þû4}®çþ~%ÿ¾ÍCEMö»Ÿùø—þû4}®çþ~%ÿ¾ÍCEMö»Ÿùø—þû4}®çþ~%ÿ¾ÍCEMö»Ÿùø—þû4}®çþ~%ÿ¾ÍCEMö»Ÿùø—þû4}®çþ~%ÿ¾ÍCEMö»Ÿùø—þû4}®çþ~%ÿ¾ÍCEMö»Ÿùø—þû4}®çþ~%ÿ¾ÍCEMö»Ÿùø—þû4}®çþ~%ÿ¾ÍCEMö»Ÿùø—þû4}®çþ~%ÿ¾ÍCEMö»Ÿùø—þû4}®çþ~%ÿ¾ÍCEMö»Ÿùø—þû4}®çþ~%ÿ¾ÍCEMö»Ÿùø—þû4}®çþ~%ÿ¾ÍCEMö»Ÿùø—þû4}®çþ~%ÿ¾ÍCRE›»çT 7Ùõ·Öû]ÏüüKÿ}š>×sÿ?ÿßf&?ùù‹òoþ&&?ùù‹òoþ&€µÜÿÏÄ¿÷Ù£íw?óñ/ýöhòcÿŸ˜¿&ÿâhòcÿŸ˜¿&ÿâhû]ÏüüKÿ}š>×sÿ?ÿßf&?ùù‹òoþ&&?ùù‹òoþ&€µÜÿÏÄ¿÷Ù£íw?óñ/ýöhòcÿŸ˜¿&ÿâhòcÿŸ˜¿&ÿâhû]ÏüüKÿ}š>×sÿ?ÿßfªêW6Ú^›q}= B쨬Xã°ã©éÍdYë·×ö6×¶ÞÔÚÞæ$š&7jYC)ÁœAäQ¨Úîçâ_ûìÑö»Ÿùø—þû5‹ý¥ªб©àU—ÿ$Qý¥ªб©àU—ÿ$Q¨_k¹ÿŸ‰ï³GÚîçâ_ûìÖ/ö–©ÿBÆ¥ÿV_ü‘Gö–©ÿBÆ¥ÿV_ü‘F m}®çþ~%ÿ¾Ík¹ÿŸ‰ï³X¿ÚZ§ý —þYòEÚZ§ý —þYòEµö»Ÿùø—þû4}®çþ~%ÿ¾ÍbÿijŸô,j_øeÿÉijŸô,j_øeÿÉj×Úîçâ_ûìÑö»Ÿùø—þû5‰¡kÐkðÝÉ ¼ð5¥ËZÊ“'z…'”˜r lÅ›»çT 7Ùõ·Ö€ö»Ÿùø—þû4}®çþ~%ÿ¾ÍLóóäßüMLóóäßüMk¹ÿŸ‰ï³GÚîçâ_ûìÑäÇÿ?1~MÿÄÑäÇÿ?1~MÿÄÐö»Ÿùø—þû4}®çþ~%ÿ¾ÍLóóäßüMLóóäßüMk¹ÿŸ‰ï³GÚîçâ_ûìÑäÇÿ?1~MÿÄÑäÇÿ?1~MÿÄÐö»Ÿùø—þû5 G©\ÛizmÅô÷b ²¢±cŽÃާ§5%yoÅ?ù Ø×ÿ¡QGÅ?ù Ø×ÿ¡QV¶=Ã?ò#økþÁpè5¥Y¾ÿ‘Ã_ö ƒÿA­*u¿‰/Q­‚Š(¬ÆQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE4?ê®?ë˜ÿЖ¡©¡ÿUqÿ\Çþ„´†Š( cCî˜D ³mÞØè‹ê}2x§ñªw7žDÓ/Ú!CJñÂË—Žÿ•~aºC÷ºU¡ÙË«”VæE!ÈcÛ>ž¼a¯nò›”ûQ†˜–)UbÜÅFüàîáÏPhB%™„ ‡Ê¥Â1@s–ÆOLŸJƒíK!„DHóež6 ÊΪJ“€Ànê29´öµŠ]KíMå ¤Ã6áÝB°;UË «ÇaüF‘-¡Šçó!ÊO$B0ÈŒ§ I;‰@9'=RHÝddL”nU±€Ã8Èõu§ScFHö´­&w$.9ç¿á޽iÔ†aøÇþEKþ¹QV<1ÿ"w‡ÿìiÿ¢«øÇþEKþ¹QV<1ÿ"w‡ÿìiÿ¢ŸAu5+?^¿—Kð@¨ÓZÚK:+‚T²¡aœcŒŠÐªZÆŸý­¢_é¾o•ö»i ó6îÙ½JçÆzfÌË_y÷u†Ö ,®¦»óþ=å‚H‘ת–ÆÐAÇ[¶Þ Ón¦Š–hç–O)!žÚHd'c¸;]A Do†#©äb©j>µÔu{»ç¸š4¼Ó¦°žðó6(ô}¨’B§÷y¤¾µ–Æ[+µÓ–ÒæD7VÚ~ž-c1U%X¸mì­ØÂËÖÑš×=ïÚ¥¹ò¤šCBÓ,k0à+ÐíÇw$Ú~ŸöO2i¥ûEìø3ÎWnìg £'j.N'$’Ř€]¢Š(Äü5ÿwþÂò訫¼‡ýUÇýsú×ð×þ»ë&ø¦ÚÒÞÚ/íŠÞ‚5~0ˆ¡Tq/ ØiØÏí·ýý_ñ£ûNÃþm¿ïêÿ;°9?øDüYÿC»à ÿã´Â'âÏúÛÿOÿ®³ûNÃþm¿ïêÿÚvóûmÿWüh»“ÿ„OÅŸô;·þŸþ;Gü"~,ÿ¡Ý¿ðÿñÚë?´ì?çöÛþþ¯øÑý§aÿ?¶ß÷õÆ‹°9?øDüYÿC»à ÿã´Â'âÏúÛÿOÿ®³ûNÃþm¿ïêÿÚvóûmÿWüh»“ÿ„OÅŸô;·þŸþ;Gü"~,ÿ¡Ý¿ðÿñÚë?´ì?çöÛþþ¯øÑý§aÿ?¶ß÷õÆ‹°2¼'áÉ|7cw÷Ëy-ÍÓ\´‹”(«Œnoîg¯zؼ[Ù-^+¨í¥|$ù˜\ç¸s9ç¿Ã?´ì?çöÛþþ¯øÑý§aÿ?¶ß÷õÆ¿ÙÞ!ÿ¡‚üþ.ìïÿÐÁþ ÇÿZ_ÚvóûmÿWühþÓ°ÿŸÛoûú¿ãF fÿgx‡þ†?ð^?øº?³¼CÿCø/ü]iiØÏí·ýý_ñ£ûNÃþm¿ïêÿ›ýâú ÿÁxÿâèþÎñý à¼ñu¥ý§aÿ?¶ß÷õÆí;ùý¶ÿ¿«þ4joöwˆè`ƒÿãÿ‹£û;Ä?ô0Aÿ‚ñÿÅÖ—ö‡üþÛßÕÿ?´ì?çöÛþþ¯øÑ¨·Úµ¨ØÍgs¯BÐÊ»X >»ë¤ª¿ÚvóûmÿWühþÓ°ÿŸÛoûú¿ã@oñOþB¶õÀÿèTT§†ãR±xeIBÀ”`FsíEZØLóûOøòƒþ¹¯ò©¨¢µ«ñËÕ’¶ (¢³QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEÿÙscons-doc-2.3.0/doc/user/tools.xml0000644000175000017500000000253312114661557017632 0ustar dktrkranzdktrkranz This appendix contains descriptions of all of the Tools modules that are available "out of the box" in this version of SCons. &tools-gen; scons-doc-2.3.0/doc/user/builders.xml0000644000175000017500000000335512114661557020306 0ustar dktrkranzdktrkranz This appendix contains descriptions of all of the Builders that are potentially available "out of the box" in this version of SCons. &builders-gen; scons-doc-2.3.0/doc/user/misc.xml0000644000175000017500000003546612114661557017440 0ustar dktrkranzdktrkranz &SCons; supports a lot of additional functionality that doesn't readily fit into the other chapters.
Verifying the Python Version: the &EnsurePythonVersion; Function Although the &SCons; code itself will run on any 2.x Python version 2.4 or later, you are perfectly free to make use of Python syntax and modules from more modern versions (for example, Python 2.5 or 2.6) when writing your &SConscript; files or your own local modules. If you do this, it's usually helpful to configure &SCons; to exit gracefully with an error message if it's being run with a version of Python that simply won't work with your code. This is especially true if you're going to use &SCons; to build source code that you plan to distribute publicly, where you can't be sure of the Python version that an anonymous remote user might use to try to build your software. &SCons; provides an &EnsurePythonVersion; function for this. You simply pass it the major and minor versions numbers of the version of Python you require: EnsurePythonVersion(2, 5) And then &SCons; will exit with the following error message when a user runs it with an unsupported earlier version of Python: % scons -Q Python 2.5 or greater required, but you have Python 2.3.6
Verifying the SCons Version: the &EnsureSConsVersion; Function You may, of course, write your &SConscript; files to use features that were only added in recent versions of &SCons;. When you publicly distribute software that is built using &SCons;, it's helpful to have &SCons; verify the version being used and exit gracefully with an error message if the user's version of &SCons; won't work with your &SConscript; files. &SCons; provides an &EnsureSConsVersion; function that verifies the version of &SCons; in the same the &EnsurePythonVersion; function verifies the version of Python, by passing in the major and minor versions numbers of the version of SCons you require: EnsureSConsVersion(1, 0) And then &SCons; will exit with the following error message when a user runs it with an unsupported earlier version of &SCons;: % scons -Q SCons 1.0 or greater required, but you have SCons 0.98.5
Explicitly Terminating &SCons; While Reading &SConscript; Files: the &Exit; Function &SCons; supports an &Exit; function which can be used to terminate &SCons; while reading the &SConscript; files, usually because you've detected a condition under which it doesn't make sense to proceed: if ARGUMENTS.get('FUTURE'): print "The FUTURE option is not supported yet!" Exit(2) env = Environment() env.Program('hello.c') % scons -Q FUTURE=1 The FUTURE option is not supported yet! % scons -Q cc -o hello.o -c hello.c cc -o hello hello.o The &Exit; function takes as an argument the (numeric) exit status that you want &SCons; to exit with. If you don't specify a value, the default is to exit with 0, which indicates successful execution. Note that the &Exit; function is equivalent to calling the Python sys.exit function (which the it actually calls), but because &Exit; is a &SCons; function, you don't have to import the Python sys module to use it.
Searching for Files: the &FindFile; Function The &FindFile; function searches for a file in a list of directories. If there is only one directory, it can be given as a simple string. The function returns a File node if a matching file exists, or None if no file is found. (See the documentation for the &Glob; function for an alternative way of searching for entries in a directory.) # one directory print FindFile('missing', '.') t = FindFile('exists', '.') print t.__class__, t % scons -Q None <class 'SCons.Node.FS.File'> exists scons: `.' is up to date. # several directories includes = [ '.', 'include', 'src/include'] headers = [ 'nonesuch.h', 'config.h', 'private.h', 'dist.h'] for hdr in headers: print '%-12s' % ('%s:' % hdr), FindFile(hdr, includes) % scons -Q nonesuch.h: None config.h: config.h private.h: src/include/private.h dist.h: include/dist.h scons: `.' is up to date. If the file exists in more than one directory, only the first occurrence is returned. print FindFile('multiple', ['sub1', 'sub2', 'sub3']) print FindFile('multiple', ['sub2', 'sub3', 'sub1']) print FindFile('multiple', ['sub3', 'sub1', 'sub2']) % scons -Q sub1/multiple sub2/multiple sub3/multiple scons: `.' is up to date. In addition to existing files, &FindFile; will also find derived files (that is, non-leaf files) that haven't been built yet. (Leaf files should already exist, or the build will fail!) # Neither file exists, so build will fail Command('derived', 'leaf', 'cat >$TARGET $SOURCE') print FindFile('leaf', '.') print FindFile('derived', '.') % scons -Q None derived scons: *** [derived] Source `leaf' not found, needed by target `derived'. # Neither file exists, so build will fail Command('derived', 'leaf', 'cat >$TARGET $SOURCE') print FindFile('leaf', '.') print FindFile('derived', '.') # Only 'leaf' exists Command('derived', 'leaf', 'cat >$TARGET $SOURCE') print FindFile('leaf', '.') print FindFile('derived', '.') % scons -Q leaf derived cat > derived leaf If a source file exists, &FindFile; will correctly return the name in the build directory. # Only 'src/leaf' exists VariantDir('build', 'src') print FindFile('leaf', 'build') % scons -Q build/leaf scons: `.' is up to date.
Handling Nested Lists: the &Flatten; Function &SCons; supports a &Flatten; function which takes an input Python sequence (list or tuple) and returns a flattened list containing just the individual elements of the sequence. This can be handy when trying to examine a list composed of the lists returned by calls to various Builders. For example, you might collect object files built in different ways into one call to the &Program; Builder by just enclosing them in a list, as follows: objects = [ Object('prog1.c'), Object('prog2.c', CCFLAGS='-DFOO'), ] Program(objects) Because the Builder calls in &SCons; flatten their input lists, this works just fine to build the program: % scons -Q cc -o prog1.o -c prog1.c cc -o prog2.o -c -DFOO prog2.c cc -o prog1 prog1.o prog2.o But if you were debugging your build and wanted to print the absolute path of each object file in the objects list, you might try the following simple approach, trying to print each Node's abspath attribute: objects = [ Object('prog1.c'), Object('prog2.c', CCFLAGS='-DFOO'), ] Program(objects) for object_file in objects: print object_file.abspath This does not work as expected because each call to str is operating an embedded list returned by each &Object; call, not on the underlying Nodes within those lists: % scons -Q AttributeError: 'NodeList' object has no attribute 'abspath': File "/home/my/project/SConstruct", line 8: print object_file.abspath The solution is to use the &Flatten; function so that you can pass each Node to the str separately: objects = [ Object('prog1.c'), Object('prog2.c', CCFLAGS='-DFOO'), ] Program(objects) for object_file in Flatten(objects): print object_file.abspath % scons -Q /home/me/project/prog1.o /home/me/project/prog2.o cc -o prog1.o -c prog1.c cc -o prog2.o -c -DFOO prog2.c cc -o prog1 prog1.o prog2.o
Finding the Invocation Directory: the &GetLaunchDir; Function If you need to find the directory from which the user invoked the &scons; command, you can use the &GetLaunchDir; function: env = Environment( LAUNCHDIR = GetLaunchDir(), ) env.Command('directory_build_info', '$LAUNCHDIR/build_info' Copy('$TARGET', '$SOURCE')) Because &SCons; is usually invoked from the top-level directory in which the &SConstruct; file lives, the Python os.getcwd() is often equivalent. However, the &SCons; -u, -U and -D command-line options, when invoked from a subdirectory, will cause &SCons; to change to the directory in which the &SConstruct; file is found. When those options are used, &GetLaunchDir; will still return the path to the user's invoking subdirectory, allowing the &SConscript; configuration to still get at configuration (or other) files from the originating directory.
scons-doc-2.3.0/doc/user/python.in0000644000175000017500000000632512114661557017624 0ustar dktrkranzdktrkranz scons-doc-2.3.0/doc/user/nodes.in0000644000175000017500000002547012114661557017415 0ustar dktrkranzdktrkranz Internally, &SCons; represents all of the files and directories it knows about as &Nodes;. These internal objects (not object files) can be used in a variety of ways to make your &SConscript; files portable and easy to read.
Builder Methods Return Lists of Target Nodes All builder methods return a list of &Node; objects that identify the target file or files that will be built. These returned &Nodes; can be passed as arguments to other builder methods. For example, suppose that we want to build the two object files that make up a program with different options. This would mean calling the &b-link-Object; builder once for each object file, specifying the desired options: Object('hello.c', CCFLAGS='-DHELLO') Object('goodbye.c', CCFLAGS='-DGOODBYE') One way to combine these object files into the resulting program would be to call the &b-link-Program; builder with the names of the object files listed as sources: Object('hello.c', CCFLAGS='-DHELLO') Object('goodbye.c', CCFLAGS='-DGOODBYE') Program(['hello.o', 'goodbye.o']) The problem with specifying the names as strings is that our &SConstruct; file is no longer portable across operating systems. It won't, for example, work on Windows because the object files there would be named &hello_obj; and &goodbye_obj;, not &hello_o; and &goodbye_o;. A better solution is to assign the lists of targets returned by the calls to the &b-Object; builder to variables, which we can then concatenate in our call to the &b-Program; builder: hello_list = Object('hello.c', CCFLAGS='-DHELLO') goodbye_list = Object('goodbye.c', CCFLAGS='-DGOODBYE') Program(hello_list + goodbye_list) int main() { printf("Hello, world!\n"); } int main() { printf("Goodbye, world!\n"); } This makes our &SConstruct; file portable again, the build output on Linux looking like: scons -Q And on Windows: scons -Q We'll see examples of using the list of nodes returned by builder methods throughout the rest of this guide.
Explicitly Creating File and Directory Nodes It's worth mentioning here that &SCons; maintains a clear distinction between Nodes that represent files and Nodes that represent directories. &SCons; supports &File; and &Dir; functions that, respectively, return a file or directory Node: hello_c = File('hello.c') Program(hello_c) classes = Dir('classes') Java(classes, 'src') Normally, you don't need to call &File; or &Dir; directly, because calling a builder method automatically treats strings as the names of files or directories, and translates them into the Node objects for you. The &File; and &Dir; functions can come in handy in situations where you need to explicitly instruct &SCons; about the type of Node being passed to a builder or other function, or unambiguously refer to a specific file in a directory tree. There are also times when you may need to refer to an entry in a file system without knowing in advance whether it's a file or a directory. For those situations, &SCons; also supports an &Entry; function, which returns a Node that can represent either a file or a directory. xyzzy = Entry('xyzzy') The returned xyzzy Node will be turned into a file or directory Node the first time it is used by a builder method or other function that requires one vs. the other.
Printing &Node; File Names One of the most common things you can do with a Node is use it to print the file name that the node represents. Keep in mind, though, that because the object returned by a builder call is a list of Nodes, you must use Python subscripts to fetch individual Nodes from the list. For example, the following &SConstruct; file: object_list = Object('hello.c') program_list = Program(object_list) print "The object file is:", object_list[0] print "The program file is:", program_list[0] int main() { printf("Hello, world!\n"); } Would print the following file names on a POSIX system: scons -Q And the following file names on a Windows system: scons -Q Note that in the above example, the object_list[0] extracts an actual Node object from the list, and the Python print statement converts the object to a string for printing.
Using a &Node;'s File Name as a String Printing a &Node;'s name as described in the previous section works because the string representation of a &Node; object is the name of the file. If you want to do something other than print the name of the file, you can fetch it by using the builtin Python &str; function. For example, if you want to use the Python os.path.exists to figure out whether a file exists while the &SConstruct; file is being read and executed, you can fetch the string as follows: import os.path program_list = Program('hello.c') program_name = str(program_list[0]) if not os.path.exists(program_name): print program_name, "does not exist!" int main() { printf("Hello, world!\n"); } Which executes as follows on a POSIX system: scons -Q
&GetBuildPath;: Getting the Path From a &Node; or String env.GetBuildPath(file_or_list) returns the path of a &Node; or a string representing a path. It can also take a list of &Node;s and/or strings, and returns the list of paths. If passed a single &Node;, the result is the same as calling str(node) (see above). The string(s) can have embedded construction variables, which are expanded as usual, using the calling environment's set of variables. The paths can be files or directories, and do not have to exist. env=Environment(VAR="value") n=File("foo.c") print env.GetBuildPath([n, "sub/dir/$VAR"]) Would print the following file names: scons -Q There is also a function version of &GetBuildPath; which can be called without an &Environment;; that uses the default SCons &Environment; to do substitution on any string arguments.
scons-doc-2.3.0/doc/user/libraries.xml0000644000175000017500000002524112114661557020447 0ustar dktrkranzdktrkranz It's often useful to organize large software projects by collecting parts of the software into one or more libraries. &SCons; makes it easy to create libraries and to use them in the programs.
Building Libraries You build your own libraries by specifying &b-link-Library; instead of &b-link-Program;: Library('foo', ['f1.c', 'f2.c', 'f3.c']) &SCons; uses the appropriate library prefix and suffix for your system. So on POSIX or Linux systems, the above example would build as follows (although &ranlib; may not be called on all systems): % scons -Q cc -o f1.o -c f1.c cc -o f2.o -c f2.c cc -o f3.o -c f3.c ar rc libfoo.a f1.o f2.o f3.o ranlib libfoo.a On a Windows system, a build of the above example would look like: C:\>scons -Q cl /Fof1.obj /c f1.c /nologo cl /Fof2.obj /c f2.c /nologo cl /Fof3.obj /c f3.c /nologo lib /nologo /OUT:foo.lib f1.obj f2.obj f3.obj The rules for the target name of the library are similar to those for programs: if you don't explicitly specify a target library name, &SCons; will deduce one from the name of the first source file specified, and &SCons; will add an appropriate file prefix and suffix if you leave them off.
Building Libraries From Source Code or Object Files 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: Library('foo', ['f1.c', 'f2.o', 'f3.c', 'f4.o']) And SCons realizes that only the source code files must be compiled into object files before creating the final library: % scons -Q 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 Of course, in this example, the object files must already exist for the build to succeed. See , below, for information about how you can build object files explicitly and include the built files in a library.
Building Static Libraries Explicitly: the &b-StaticLibrary; Builder The &b-link-Library; function builds a traditional static library. If you want to be explicit about the type of library being built, you can use the synonym &b-link-StaticLibrary; function instead of &b-Library;: StaticLibrary('foo', ['f1.c', 'f2.c', 'f3.c']) There is no functional difference between the &b-link-StaticLibrary; and &b-Library; functions.
Building Shared (DLL) Libraries: the &b-SharedLibrary; Builder If you want to build a shared library (on POSIX systems) or a DLL file (on Windows systems), you use the &b-link-SharedLibrary; function: SharedLibrary('foo', ['f1.c', 'f2.c', 'f3.c']) The output on POSIX: % scons -Q cc -o f1.os -c f1.c cc -o f2.os -c f2.c cc -o f3.os -c f3.c cc -o libfoo.so -shared f1.os f2.os f3.os And the output on Windows: C:\>scons -Q cl /Fof1.obj /c f1.c /nologo cl /Fof2.obj /c f2.c /nologo cl /Fof3.obj /c f3.c /nologo link /nologo /dll /out:foo.dll /implib:foo.lib f1.obj f2.obj f3.obj RegServerFunc(target, source, env) embedManifestDllCheck(target, source, env) Notice again that &SCons; takes care of building the output file correctly, adding the -shared option for a POSIX compilation, and the /dll option on Windows.
Linking with Libraries Usually, you build a library because you want to link it with one or more programs. You link libraries with a program by specifying the libraries in the &cv-link-LIBS; construction variable, and by specifying the directory in which the library will be found in the &cv-link-LIBPATH; construction variable: Library('foo', ['f1.c', 'f2.c', 'f3.c']) Program('prog.c', LIBS=['foo', 'bar'], LIBPATH='.') Notice, of course, that you don't need to specify a library prefix (like lib) or suffix (like .a or .lib). &SCons; uses the correct prefix or suffix for the current system. On a POSIX or Linux system, a build of the above example would look like: % scons -Q cc -o f1.o -c f1.c cc -o f2.o -c f2.c cc -o f3.o -c f3.c ar rc libfoo.a f1.o f2.o f3.o ranlib libfoo.a cc -o prog.o -c prog.c cc -o prog prog.o -L. -lfoo -lbar On a Windows system, a build of the above example would look like: C:\>scons -Q cl /Fof1.obj /c f1.c /nologo cl /Fof2.obj /c f2.c /nologo cl /Fof3.obj /c f3.c /nologo lib /nologo /OUT:foo.lib f1.obj f2.obj f3.obj cl /Foprog.obj /c prog.c /nologo link /nologo /OUT:prog.exe /LIBPATH:. foo.lib bar.lib prog.obj embedManifestExeCheck(target, source, env) As usual, notice that &SCons; has taken care of constructing the correct command lines to link with the specified library on each system. Note also that, if you only have a single library to link with, you can specify the library name in single string, instead of a Python list, so that: Program('prog.c', LIBS='foo', LIBPATH='.') is equivalent to: Program('prog.c', LIBS=['foo'], LIBPATH='.') This is similar to the way that &SCons; handles either a string or a list to specify a single source file.
Finding Libraries: the &cv-LIBPATH; Construction Variable By default, the linker will only look in certain system-defined directories for libraries. &SCons; knows how to look for libraries in directories that you specify with the &cv-link-LIBPATH; construction variable. &cv-LIBPATH; consists of a list of directory names, like so: Program('prog.c', LIBS = 'm', LIBPATH = ['/usr/lib', '/usr/local/lib']) Using a Python list is preferred because it's portable across systems. Alternatively, you could put all of the directory names in a single string, separated by the system-specific path separator character: a colon on POSIX systems: LIBPATH = '/usr/lib:/usr/local/lib' or a semi-colon on Windows systems: LIBPATH = 'C:\\lib;D:\\lib' (Note that Python requires that the backslash separators in a Windows path name be escaped within strings.) When the linker is executed, &SCons; will create appropriate flags so that the linker will look for libraries in the same directories as &SCons;. So on a POSIX or Linux system, a build of the above example would look like: % scons -Q cc -o prog.o -c prog.c cc -o prog prog.o -L/usr/lib -L/usr/local/lib -lm On a Windows system, a build of the above example would look like: C:\>scons -Q cl /Foprog.obj /c prog.c /nologo link /nologo /OUT:prog.exe /LIBPATH:\usr\lib /LIBPATH:\usr\local\lib m.lib prog.obj embedManifestExeCheck(target, source, env) Note again that &SCons; has taken care of the system-specific details of creating the right command-line options.
scons-doc-2.3.0/doc/user/troubleshoot.xml0000644000175000017500000011710212114661557021222 0ustar dktrkranzdktrkranz The experience of configuring any software build tool to build a large code base usually, at some point, involves trying to figure out why the tool is behaving a certain way, and how to get it to behave the way you want. &SCons; is no different. This appendix contains a number of different ways in which you can get some additional insight into &SCons;' behavior. Note that we're always interested in trying to improve how you can troubleshoot configuration problems. If you run into a problem that has you scratching your head, and which there just doesn't seem to be a good way to debug, odds are pretty good that someone else will run into the same problem, too. If so, please let the SCons development team know (preferably by filing a bug report or feature request at our project pages at tigris.org) so that we can use your feedback to try to come up with a better way to help you, and others, get the necessary insight into &SCons; behavior to help identify and fix configuration issues.
Why is That Target Being Rebuilt? the &debug-explain; Option Let's look at a simple example of a misconfigured build that causes a target to be rebuilt every time &SCons; is run: # Intentionally misspell the output file name in the # command used to create the file: Command('file.out', 'file.in', 'cp $SOURCE file.oout') (Note to Windows users: The POSIX &cp; command copies the first file named on the command line to the second file. In our example, it copies the &file_in; file to the &file_out; file.) Now if we run &SCons; multiple times on this example, we see that it re-runs the &cp; command every time: % scons -Q cp file.in file.oout % scons -Q cp file.in file.oout % scons -Q cp file.in file.oout In this example, the underlying cause is obvious: we've intentionally misspelled the output file name in the &cp; command, so the command doesn't actually build the &file_out; file that we've told &SCons; to expect. But if the problem weren't obvious, it would be helpful to specify the &debug-explain; option on the command line to have &SCons; tell us very specifically why it's decided to rebuild the target: % scons -Q --debug=explain scons: building `file.out' because it doesn't exist cp file.in file.oout If this had been a more complicated example involving a lot of build output, having &SCons; tell us that it's trying to rebuild the target file because it doesn't exist would be an important clue that something was wrong with the command that we invoked to build it. The &debug-explain; option also comes in handy to help figure out what input file changed. Given a simple configuration that builds a program from three source files, changing one of the source files and rebuilding with the &debug-explain; option shows very specifically why &SCons; rebuilds the files that it does: % scons -Q cc -o file1.o -c file1.c cc -o file2.o -c file2.c cc -o file3.o -c file3.c cc -o prog file1.o file2.o file3.o % edit file2.c [CHANGE THE CONTENTS OF file2.c] % scons -Q --debug=explain scons: rebuilding `file2.o' because `file2.c' changed cc -o file2.o -c file2.c scons: rebuilding `prog' because `file2.o' changed cc -o prog file1.o file2.o file3.o This becomes even more helpful in identifying when a file is rebuilt due to a change in an implicit dependency, such as an incuded .h file. If the file1.c and file3.c files in our example both included a &hello_h; file, then changing that included file and re-running &SCons; with the &debug-explain; option will pinpoint that it's the change to the included file that starts the chain of rebuilds: % scons -Q cc -o file1.o -c -I. file1.c cc -o file2.o -c -I. file2.c cc -o file3.o -c -I. file3.c cc -o prog file1.o file2.o file3.o % edit hello.h [CHANGE THE CONTENTS OF hello.h] % scons -Q --debug=explain scons: rebuilding `file1.o' because `hello.h' changed cc -o file1.o -c -I. file1.c scons: rebuilding `file3.o' because `hello.h' changed cc -o file3.o -c -I. file3.c scons: rebuilding `prog' because: `file1.o' changed `file3.o' changed cc -o prog file1.o file2.o file3.o (Note that the &debug-explain; option will only tell you why &SCons; decided to rebuild necessary targets. It does not tell you what files it examined when deciding not to rebuild a target file, which is often a more valuable question to answer.)
What's in That Construction Environment? the &Dump; Method When you create a construction environment, &SCons; populates it with construction variables that are set up for various compilers, linkers and utilities that it finds on your system. Although this is usually helpful and what you want, it might be frustrating if &SCons; doesn't set certain variables that you expect to be set. In situations like this, it's sometimes helpful to use the construction environment &Dump; method to print all or some of the construction variables. Note that the &Dump; method returns the representation of the variables in the environment for you to print (or otherwise manipulate): env = Environment() print env.Dump() On a POSIX system with gcc installed, this might generate: % scons scons: Reading SConscript files ... { 'BUILDERS': {'_InternalInstall': <function InstallBuilderWrapper at 0x700000>, '_InternalInstallAs': <function InstallAsBuilderWrapper at 0x700000>}, 'CONFIGUREDIR': '#/.sconf_temp', 'CONFIGURELOG': '#/config.log', 'CPPSUFFIXES': [ '.c', '.C', '.cxx', '.cpp', '.c++', '.cc', '.h', '.H', '.hxx', '.hpp', '.hh', '.F', '.fpp', '.FPP', '.m', '.mm', '.S', '.spp', '.SPP', '.sx'], 'DSUFFIXES': ['.d'], 'Dir': <SCons.Defaults.Variable_Method_Caller object at 0x700000>, 'Dirs': <SCons.Defaults.Variable_Method_Caller object at 0x700000>, 'ENV': { 'PATH': '/usr/local/bin:/opt/bin:/bin:/usr/bin'}, 'ESCAPE': <function escape at 0x700000>, 'File': <SCons.Defaults.Variable_Method_Caller object at 0x700000>, 'HOST_ARCH': None, 'HOST_OS': None, 'IDLSUFFIXES': ['.idl', '.IDL'], 'INSTALL': <function copyFunc at 0x700000>, 'LIBPREFIX': 'lib', 'LIBPREFIXES': ['$LIBPREFIX'], 'LIBSUFFIX': '.a', 'LIBSUFFIXES': ['$LIBSUFFIX', '$SHLIBSUFFIX'], 'MAXLINELENGTH': 128072, 'OBJPREFIX': '', 'OBJSUFFIX': '.o', 'PLATFORM': 'posix', 'PROGPREFIX': '', 'PROGSUFFIX': '', 'PSPAWN': <function piped_env_spawn at 0x700000>, 'RDirs': <SCons.Defaults.Variable_Method_Caller object at 0x700000>, 'SCANNERS': [], 'SHELL': 'sh', 'SHLIBPREFIX': '$LIBPREFIX', 'SHLIBSUFFIX': '.so', 'SHOBJPREFIX': '$OBJPREFIX', 'SHOBJSUFFIX': '$OBJSUFFIX', 'SPAWN': <function spawnvpe_spawn at 0x700000>, 'TARGET_ARCH': None, 'TARGET_OS': None, 'TEMPFILE': <class 'SCons.Platform.TempFileMunge'>, 'TEMPFILEPREFIX': '@', 'TOOLS': ['install', 'install'], '_CPPDEFFLAGS': '${_defines(CPPDEFPREFIX, CPPDEFINES, CPPDEFSUFFIX, __env__)}', '_CPPINCFLAGS': '$( ${_concat(INCPREFIX, CPPPATH, INCSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)', '_LIBDIRFLAGS': '$( ${_concat(LIBDIRPREFIX, LIBPATH, LIBDIRSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)', '_LIBFLAGS': '${_concat(LIBLINKPREFIX, LIBS, LIBLINKSUFFIX, __env__)}', '__RPATH': '$_RPATH', '_concat': <function _concat at 0x700000>, '_defines': <function _defines at 0x700000>, '_stripixes': <function _stripixes at 0x700000>} scons: done reading SConscript files. scons: Building targets ... scons: `.' is up to date. scons: done building targets. On a Windows system with Visual C++ the output might look like: C:\>scons scons: Reading SConscript files ... { 'BUILDERS': {'_InternalInstall': <function InstallBuilderWrapper at 0x700000>, 'Object': <SCons.Builder.CompositeBuilder object at 0x700000>, 'PCH': <SCons.Builder.BuilderBase object at 0x700000>, 'RES': <SCons.Builder.BuilderBase object at 0x700000>, 'SharedObject': <SCons.Builder.CompositeBuilder object at 0x700000>, 'StaticObject': <SCons.Builder.CompositeBuilder object at 0x700000>, '_InternalInstallAs': <function InstallAsBuilderWrapper at 0x700000>}, 'CC': 'cl', 'CCCOM': <SCons.Action.FunctionAction object at 0x700000>, 'CCFLAGS': ['/nologo'], 'CCPCHFLAGS': ['${(PCH and "/Yu%s \\"/Fp%s\\""%(PCHSTOP or "",File(PCH))) or ""}'], 'CCPDBFLAGS': ['${(PDB and "/Z7") or ""}'], 'CFILESUFFIX': '.c', 'CFLAGS': [], 'CONFIGUREDIR': '#/.sconf_temp', 'CONFIGURELOG': '#/config.log', 'CPPDEFPREFIX': '/D', 'CPPDEFSUFFIX': '', 'CPPSUFFIXES': [ '.c', '.C', '.cxx', '.cpp', '.c++', '.cc', '.h', '.H', '.hxx', '.hpp', '.hh', '.F', '.fpp', '.FPP', '.m', '.mm', '.S', '.spp', '.SPP', '.sx'], 'CXX': '$CC', 'CXXCOM': '${TEMPFILE("$CXX $_MSVC_OUTPUT_FLAG /c $CHANGED_SOURCES $CXXFLAGS $CCFLAGS $_CCCOMCOM")}', 'CXXFILESUFFIX': '.cc', 'CXXFLAGS': ['$(', '/TP', '$)'], 'DSUFFIXES': ['.d'], 'Dir': <SCons.Defaults.Variable_Method_Caller object at 0x700000>, 'Dirs': <SCons.Defaults.Variable_Method_Caller object at 0x700000>, 'ENV': { 'PATH': 'C:\\WINDOWS\\System32', 'PATHEXT': '.COM;.EXE;.BAT;.CMD', 'SystemRoot': 'C:\\WINDOWS'}, 'ESCAPE': <function escape at 0x700000>, 'File': <SCons.Defaults.Variable_Method_Caller object at 0x700000>, 'HOST_ARCH': '', 'HOST_OS': 'win32', 'IDLSUFFIXES': ['.idl', '.IDL'], 'INCPREFIX': '/I', 'INCSUFFIX': '', 'INSTALL': <function copyFunc at 0x700000>, 'LIBPREFIX': '', 'LIBPREFIXES': ['$LIBPREFIX'], 'LIBSUFFIX': '.lib', 'LIBSUFFIXES': ['$LIBSUFFIX'], 'MAXLINELENGTH': 2048, 'MSVC_SETUP_RUN': True, 'OBJPREFIX': '', 'OBJSUFFIX': '.obj', 'PCHCOM': '$CXX /Fo${TARGETS[1]} $CXXFLAGS $CCFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS /c $SOURCES /Yc$PCHSTOP /Fp${TARGETS[0]} $CCPDBFLAGS $PCHPDBFLAGS', 'PCHPDBFLAGS': ['${(PDB and "/Yd") or ""}'], 'PLATFORM': 'win32', 'PROGPREFIX': '', 'PROGSUFFIX': '.exe', 'PSPAWN': <function piped_spawn at 0x700000>, 'RC': 'rc', 'RCCOM': <SCons.Action.FunctionAction object at 0x700000>, 'RCFLAGS': [], 'RCSUFFIXES': ['.rc', '.rc2'], 'RDirs': <SCons.Defaults.Variable_Method_Caller object at 0x700000>, 'SCANNERS': [], 'SHCC': '$CC', 'SHCCCOM': <SCons.Action.FunctionAction object at 0x700000>, 'SHCCFLAGS': ['$CCFLAGS'], 'SHCFLAGS': ['$CFLAGS'], 'SHCXX': '$CXX', 'SHCXXCOM': '${TEMPFILE("$SHCXX $_MSVC_OUTPUT_FLAG /c $CHANGED_SOURCES $SHCXXFLAGS $SHCCFLAGS $_CCCOMCOM")}', 'SHCXXFLAGS': ['$CXXFLAGS'], 'SHELL': None, 'SHLIBPREFIX': '', 'SHLIBSUFFIX': '.dll', 'SHOBJPREFIX': '$OBJPREFIX', 'SHOBJSUFFIX': '$OBJSUFFIX', 'SPAWN': <function spawn at 0x700000>, 'STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME': 1, 'TARGET_ARCH': '', 'TARGET_OS': 'win32', 'TEMPFILE': <class 'SCons.Platform.TempFileMunge'>, 'TEMPFILEPREFIX': '@', 'TOOLS': ['msvc', 'install', 'install'], '_CCCOMCOM': '$CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS $CCPCHFLAGS $CCPDBFLAGS', '_CPPDEFFLAGS': '${_defines(CPPDEFPREFIX, CPPDEFINES, CPPDEFSUFFIX, __env__)}', '_CPPINCFLAGS': '$( ${_concat(INCPREFIX, CPPPATH, INCSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)', '_LIBDIRFLAGS': '$( ${_concat(LIBDIRPREFIX, LIBPATH, LIBDIRSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)', '_LIBFLAGS': '${_concat(LIBLINKPREFIX, LIBS, LIBLINKSUFFIX, __env__)}', '_MSVC_OUTPUT_FLAG': <function msvc_output_flag at 0x700000>, '_concat': <function _concat at 0x700000>, '_defines': <function _defines at 0x700000>, '_stripixes': <function _stripixes at 0x700000>} scons: done reading SConscript files. scons: Building targets ... scons: `.' is up to date. scons: done building targets. The construction environments in these examples have actually been restricted to just gcc and Visual C++, respectively. In a real-life situation, the construction environments will likely contain a great many more variables. Also note that we've massaged the example output above to make the memory address of all objects a constant 0x700000. In reality, you would see a different hexadecimal number for each object. To make it easier to see just what you're interested in, the &Dump; method allows you to specify a specific constrcution variable that you want to disply. For example, it's not unusual to want to verify the external environment used to execute build commands, to make sure that the PATH and other environment variables are set up the way they should be. You can do this as follows: env = Environment() print env.Dump('ENV') Which might display the following when executed on a POSIX system: % scons scons: Reading SConscript files ... { 'PATH': '/usr/local/bin:/opt/bin:/bin:/usr/bin'} scons: done reading SConscript files. scons: Building targets ... scons: `.' is up to date. scons: done building targets. And the following when executed on a Windows system: C:\>scons scons: Reading SConscript files ... { 'PATH': 'C:\\WINDOWS\\System32', 'PATHEXT': '.COM;.EXE;.BAT;.CMD', 'SystemRoot': 'C:\\WINDOWS'} scons: done reading SConscript files. scons: Building targets ... scons: `.' is up to date. scons: done building targets.
What Dependencies Does &SCons; Know About? the &tree; Option Sometimes the best way to try to figure out what &SCons; is doing is simply to take a look at the dependency graph that it constructs based on your &SConscript; files. The --tree option will display all or part of the &SCons; dependency graph in an "ASCII art" graphical format that shows the dependency hierarchy. For example, given the following input &SConstruct; file: env = Environment(CPPPATH = ['.']) env.Program('prog', ['f1.c', 'f2.c', 'f3.c']) Running &SCons; with the --tree=all option yields: % scons -Q --tree=all cc -o f1.o -c -I. f1.c cc -o f2.o -c -I. f2.c cc -o f3.o -c -I. f3.c cc -o prog f1.o f2.o f3.o +-. +-SConstruct +-f1.c +-f1.o | +-f1.c | +-inc.h +-f2.c +-f2.o | +-f2.c | +-inc.h +-f3.c +-f3.o | +-f3.c | +-inc.h +-inc.h +-prog +-f1.o | +-f1.c | +-inc.h +-f2.o | +-f2.c | +-inc.h +-f3.o +-f3.c +-inc.h The tree will also be printed when the -n (no execute) option is used, which allows you to examine the dependency graph for a configuration without actually rebuilding anything in the tree. The --tree option only prints the dependency graph for the specified targets (or the default target(s) if none are specified on the command line). So if you specify a target like f2.o on the command line, the --tree option will only print the dependency graph for that file: % scons -Q --tree=all f2.o cc -o f2.o -c -I. f2.c +-f2.o +-f2.c +-inc.h This is, of course, useful for restricting the output from a very large build configuration to just a portion in which you're interested. Multiple targets are fine, in which case a tree will be printed for each specified target: % scons -Q --tree=all f1.o f3.o cc -o f1.o -c -I. f1.c +-f1.o +-f1.c +-inc.h cc -o f3.o -c -I. f3.c +-f3.o +-f3.c +-inc.h The status argument may be used to tell &SCons; to print status information about each file in the dependency graph: % scons -Q --tree=status cc -o f1.o -c -I. f1.c cc -o f2.o -c -I. f2.c cc -o f3.o -c -I. f3.c cc -o prog f1.o f2.o f3.o E = exists R = exists in repository only b = implicit builder B = explicit builder S = side effect P = precious A = always build C = current N = no clean H = no cache [E b ]+-. [E C ] +-SConstruct [E C ] +-f1.c [E B C ] +-f1.o [E C ] | +-f1.c [E C ] | +-inc.h [E C ] +-f2.c [E B C ] +-f2.o [E C ] | +-f2.c [E C ] | +-inc.h [E C ] +-f3.c [E B C ] +-f3.o [E C ] | +-f3.c [E C ] | +-inc.h [E C ] +-inc.h [E B C ] +-prog [E B C ] +-f1.o [E C ] | +-f1.c [E C ] | +-inc.h [E B C ] +-f2.o [E C ] | +-f2.c [E C ] | +-inc.h [E B C ] +-f3.o [E C ] +-f3.c [E C ] +-inc.h Note that --tree=all,status is equivalent; the all is assumed if only status is present. As an alternative to all, you can specify --tree=derived to have &SCons; only print derived targets in the tree output, skipping source files (like .c and .h files): % scons -Q --tree=derived cc -o f1.o -c -I. f1.c cc -o f2.o -c -I. f2.c cc -o f3.o -c -I. f3.c cc -o prog f1.o f2.o f3.o +-. +-f1.o +-f2.o +-f3.o +-prog +-f1.o +-f2.o +-f3.o You can use the status modifier with derived as well: % scons -Q --tree=derived,status cc -o f1.o -c -I. f1.c cc -o f2.o -c -I. f2.c cc -o f3.o -c -I. f3.c cc -o prog f1.o f2.o f3.o E = exists R = exists in repository only b = implicit builder B = explicit builder S = side effect P = precious A = always build C = current N = no clean H = no cache [E b ]+-. [E B C ] +-f1.o [E B C ] +-f2.o [E B C ] +-f3.o [E B C ] +-prog [E B C ] +-f1.o [E B C ] +-f2.o [E B C ] +-f3.o Note that the order of the --tree= arguments doesn't matter; --tree=status,derived is completely equivalent. The default behavior of the --tree option is to repeat all of the dependencies each time the library dependency (or any other dependency file) is encountered in the tree. If certain target files share other target files, such as two programs that use the same library: env = Environment(CPPPATH = ['.'], LIBS = ['foo'], LIBPATH = ['.']) env.Library('foo', ['f1.c', 'f2.c', 'f3.c']) env.Program('prog1.c') env.Program('prog2.c') Then there can be a lot of repetition in the --tree= output: % scons -Q --tree=all cc -o f1.o -c -I. f1.c cc -o f2.o -c -I. f2.c cc -o f3.o -c -I. f3.c ar rc libfoo.a f1.o f2.o f3.o ranlib libfoo.a cc -o prog1.o -c -I. prog1.c cc -o prog1 prog1.o -L. -lfoo cc -o prog2.o -c -I. prog2.c cc -o prog2 prog2.o -L. -lfoo +-. +-SConstruct +-f1.c +-f1.o | +-f1.c | +-inc.h +-f2.c +-f2.o | +-f2.c | +-inc.h +-f3.c +-f3.o | +-f3.c | +-inc.h +-inc.h +-libfoo.a | +-f1.o | | +-f1.c | | +-inc.h | +-f2.o | | +-f2.c | | +-inc.h | +-f3.o | +-f3.c | +-inc.h +-prog1 | +-prog1.o | | +-prog1.c | | +-inc.h | +-libfoo.a | +-f1.o | | +-f1.c | | +-inc.h | +-f2.o | | +-f2.c | | +-inc.h | +-f3.o | +-f3.c | +-inc.h +-prog1.c +-prog1.o | +-prog1.c | +-inc.h +-prog2 | +-prog2.o | | +-prog2.c | | +-inc.h | +-libfoo.a | +-f1.o | | +-f1.c | | +-inc.h | +-f2.o | | +-f2.c | | +-inc.h | +-f3.o | +-f3.c | +-inc.h +-prog2.c +-prog2.o +-prog2.c +-inc.h In a large configuration with many internal libraries and include files, this can very quickly lead to huge output trees. To help make this more manageable, a prune modifier may be added to the option list, in which case &SCons; will print the name of a target that has already been visited during the tree-printing in [square brackets] as an indication that the dependencies of the target file may be found by looking farther up the tree: % scons -Q --tree=prune cc -o f1.o -c -I. f1.c cc -o f2.o -c -I. f2.c cc -o f3.o -c -I. f3.c ar rc libfoo.a f1.o f2.o f3.o ranlib libfoo.a cc -o prog1.o -c -I. prog1.c cc -o prog1 prog1.o -L. -lfoo cc -o prog2.o -c -I. prog2.c cc -o prog2 prog2.o -L. -lfoo +-. +-SConstruct +-f1.c +-f1.o | +-f1.c | +-inc.h +-f2.c +-f2.o | +-f2.c | +-inc.h +-f3.c +-f3.o | +-f3.c | +-inc.h +-inc.h +-libfoo.a | +-[f1.o] | +-[f2.o] | +-[f3.o] +-prog1 | +-prog1.o | | +-prog1.c | | +-inc.h | +-[libfoo.a] +-prog1.c +-[prog1.o] +-prog2 | +-prog2.o | | +-prog2.c | | +-inc.h | +-[libfoo.a] +-prog2.c +-[prog2.o] Like the status keyword, the prune argument by itself is equivalent to --tree=all,prune.
How is &SCons; Constructing the Command Lines It Executes? the &debug-presub; Option Sometimes it's useful to look at the pre-substitution string that &SCons; uses to generate the command lines it executes. This can be done with the &debug-presub; option: % scons -Q --debug=presub Building prog.o with action: $CC -o $TARGET -c $CFLAGS $CCFLAGS $_CCOMCOM $SOURCES cc -o prog.o -c -I. prog.c Building prog with action: $SMART_LINKCOM cc -o prog prog.o
Where is &SCons; Searching for Libraries? the &debug-findlibs; Option To get some insight into what library names &SCons; is searching for, and in which directories it is searching, Use the --debug=findlibs option. Given the following input &SConstruct; file: env = Environment(LIBPATH = ['libs1', 'libs2']) env.Program('prog.c', LIBS=['foo', 'bar']) And the libraries libfoo.a and libbar.a in libs1 and libs2, respectively, use of the --debug=findlibs option yields: % scons -Q --debug=findlibs findlibs: looking for 'libfoo.a' in 'libs1' ... findlibs: ... FOUND 'libfoo.a' in 'libs1' findlibs: looking for 'libfoo.so' in 'libs1' ... findlibs: looking for 'libfoo.so' in 'libs2' ... findlibs: looking for 'libbar.a' in 'libs1' ... findlibs: looking for 'libbar.a' in 'libs2' ... findlibs: ... FOUND 'libbar.a' in 'libs2' findlibs: looking for 'libbar.so' in 'libs1' ... findlibs: looking for 'libbar.so' in 'libs2' ... cc -o prog.o -c prog.c cc -o prog prog.o -Llibs1 -Llibs2 -lfoo -lbar
Where is &SCons; Blowing Up? the &debug-stacktrace; Option In general, &SCons; tries to keep its error messages short and informative. That means we usually try to avoid showing the stack traces that are familiar to experienced Python programmers, since they usually contain much more information than is useful to most people. For example, the following &SConstruct; file: Program('prog.c') Generates the following error if the prog.c file does not exist: % scons -Q scons: *** [prog.o] Source `prog.c' not found, needed by target `prog.o'. In this case, the error is pretty obvious. But if it weren't, and you wanted to try to get more information about the error, the &debug-stacktrace; option would show you exactly where in the &SCons; source code the problem occurs: % scons -Q --debug=stacktrace scons: *** [prog.o] Source `prog.c' not found, needed by target `prog.o'. scons: internal stack trace: File "bootstrap/src/engine/SCons/Job.py", line 199, in start task.prepare() File "bootstrap/src/engine/SCons/Script/Main.py", line 168, in prepare return SCons.Taskmaster.OutOfDateTask.prepare(self) File "bootstrap/src/engine/SCons/Taskmaster.py", line 189, in prepare executor.prepare() File "bootstrap/src/engine/SCons/Executor.py", line 392, in prepare raise SCons.Errors.StopError(msg % (s, self.batches[0].targets[0])) Of course, if you do need to dive into the &SCons; source code, we'd like to know if, or how, the error messages or troubleshooting options could have been improved to avoid that. Not everyone has the necessary time or Python skill to dive into the source code, and we'd like to improve &SCons; for those people as well...
How is &SCons; Making Its Decisions? the &taskmastertrace; Option The internal &SCons; subsystem that handles walking the dependency graph and controls the decision-making about what to rebuild is the Taskmaster. &SCons; supports a --taskmastertrace option that tells the Taskmaster to print information about the children (dependencies) of the various Nodes on its walk down the graph, which specific dependent Nodes are being evaluated, and in what order. The --taskmastertrace option takes as an argument the name of a file in which to put the trace output, with - (a single hyphen) indicating that the trace messages should be printed to the standard output: env = Environment(CPPPATH = ['.']) env.Program('prog.c') % scons -Q --taskmastertrace=- prog Taskmaster: Looking for a node to evaluate Taskmaster: Considering node <no_state 0 'prog'> and its children: Taskmaster: <no_state 0 'prog.o'> Taskmaster: adjusted ref count: <pending 1 'prog'>, child 'prog.o' Taskmaster: Considering node <no_state 0 'prog.o'> and its children: Taskmaster: <no_state 0 'prog.c'> Taskmaster: <no_state 0 'inc.h'> Taskmaster: adjusted ref count: <pending 1 'prog.o'>, child 'prog.c' Taskmaster: adjusted ref count: <pending 2 'prog.o'>, child 'inc.h' Taskmaster: Considering node <no_state 0 'prog.c'> and its children: Taskmaster: Evaluating <pending 0 'prog.c'> Task.make_ready_current(): node <pending 0 'prog.c'> Task.prepare(): node <up_to_date 0 'prog.c'> Task.executed_with_callbacks(): node <up_to_date 0 'prog.c'> Task.postprocess(): node <up_to_date 0 'prog.c'> Task.postprocess(): removing <up_to_date 0 'prog.c'> Task.postprocess(): adjusted parent ref count <pending 1 'prog.o'> Taskmaster: Looking for a node to evaluate Taskmaster: Considering node <no_state 0 'inc.h'> and its children: Taskmaster: Evaluating <pending 0 'inc.h'> Task.make_ready_current(): node <pending 0 'inc.h'> Task.prepare(): node <up_to_date 0 'inc.h'> Task.executed_with_callbacks(): node <up_to_date 0 'inc.h'> Task.postprocess(): node <up_to_date 0 'inc.h'> Task.postprocess(): removing <up_to_date 0 'inc.h'> Task.postprocess(): adjusted parent ref count <pending 0 'prog.o'> Taskmaster: Looking for a node to evaluate Taskmaster: Considering node <pending 0 'prog.o'> and its children: Taskmaster: <up_to_date 0 'prog.c'> Taskmaster: <up_to_date 0 'inc.h'> Taskmaster: Evaluating <pending 0 'prog.o'> Task.make_ready_current(): node <pending 0 'prog.o'> Task.prepare(): node <executing 0 'prog.o'> Task.execute(): node <executing 0 'prog.o'> cc -o prog.o -c -I. prog.c Task.executed_with_callbacks(): node <executing 0 'prog.o'> Task.postprocess(): node <executed 0 'prog.o'> Task.postprocess(): removing <executed 0 'prog.o'> Task.postprocess(): adjusted parent ref count <pending 0 'prog'> Taskmaster: Looking for a node to evaluate Taskmaster: Considering node <pending 0 'prog'> and its children: Taskmaster: <executed 0 'prog.o'> Taskmaster: Evaluating <pending 0 'prog'> Task.make_ready_current(): node <pending 0 'prog'> Task.prepare(): node <executing 0 'prog'> Task.execute(): node <executing 0 'prog'> cc -o prog prog.o Task.executed_with_callbacks(): node <executing 0 'prog'> Task.postprocess(): node <executed 0 'prog'> Taskmaster: Looking for a node to evaluate Taskmaster: No candidate anymore. The --taskmastertrace option doesn't provide information about the actual calculations involved in deciding if a file is up-to-date, but it does show all of the dependencies it knows about for each Node, and the order in which those dependencies are evaluated. This can be useful as an alternate way to determine whether or not your &SCons; configuration, or the implicit dependency scan, has actually identified all the correct dependencies you want it to.
Watch &SCons; prepare targets for building: the &debug-prepare; Option Sometimes SCons doesn't build the target you want and it's difficult to figure out why. You can use the --debug=prepare option to see all the targets &SCons; is considering, whether they are already up-to-date or not. The message is printed before &SCons; decides whether to build the target.
Why is a file disappearing? the --debug=duplicate Option When using the &Duplicate; option to create variant dirs, sometimes you may find files not getting copied to where you expect (or not at all), or files mysteriously disappearing. These are usually because of a misconfiguration of some kind in the SConstruct/SConscript, but they can be tricky to debug. The --debug=duplicate option shows each time a variant file is unlinked and relinked from its source (or copied, depending on settings), and also shows a message for removing "stale" variant-dir files that no longer have a corresponding source file. It also prints a line for each target that's removed just before building, since that can also be mistaken for the same thing.
scons-doc-2.3.0/doc/user/mergeflags.xml0000644000175000017500000001032612114661557020605 0ustar dktrkranzdktrkranz &SCons; construction environments have a &MergeFlags; method that merges a dictionary of values into the construction environment. &MergeFlags; treats each value in the dictionary as a list of options such as one might pass to a command (such as a compiler or linker). &MergeFlags; will not duplicate an option if it already exists in the construction environment variable. &MergeFlags; tries to be intelligent about merging options. When merging options to any variable whose name ends in PATH, &MergeFlags; keeps the leftmost occurrence of the option, because in typical lists of directory paths, the first occurrence "wins." When merging options to any other variable name, &MergeFlags; keeps the rightmost occurrence of the option, because in a list of typical command-line options, the last occurrence "wins." env = Environment() env.Append(CCFLAGS = '-option -O3 -O1') flags = { 'CCFLAGS' : '-whatever -O3' } env.MergeFlags(flags) print env['CCFLAGS'] % scons -Q ['-option', '-O1', '-whatever', '-O3'] scons: `.' is up to date. Note that the default value for &cv-link-CCFLAGS; is an internal &SCons; object which automatically converts the options we specified as a string into a list. env = Environment() env.Append(CPPPATH = ['/include', '/usr/local/include', '/usr/include']) flags = { 'CPPPATH' : ['/usr/opt/include', '/usr/local/include'] } env.MergeFlags(flags) print env['CPPPATH'] % scons -Q ['/include', '/usr/local/include', '/usr/include', '/usr/opt/include'] scons: `.' is up to date. Note that the default value for &cv-link-CPPPATH; is a normal Python list, so we must specify its values as a list in the dictionary we pass to the &MergeFlags; function. If &MergeFlags; is passed anything other than a dictionary, it calls the &ParseFlags; method to convert it into a dictionary. env = Environment() env.Append(CCFLAGS = '-option -O3 -O1') env.Append(CPPPATH = ['/include', '/usr/local/include', '/usr/include']) env.MergeFlags('-whatever -I/usr/opt/include -O3 -I/usr/local/include') print env['CCFLAGS'] print env['CPPPATH'] % scons -Q ['-option', '-O1', '-whatever', '-O3'] ['/include', '/usr/local/include', '/usr/include', '/usr/opt/include'] scons: `.' is up to date. In the combined example above, &ParseFlags; has sorted the options into their corresponding variables and returned a dictionary for &MergeFlags; to apply to the construction variables in the specified construction environment. scons-doc-2.3.0/doc/user/mergeflags.in0000644000175000017500000001037612114661557020420 0ustar dktrkranzdktrkranz &SCons; construction environments have a &MergeFlags; method that merges a dictionary of values into the construction environment. &MergeFlags; treats each value in the dictionary as a list of options such as one might pass to a command (such as a compiler or linker). &MergeFlags; will not duplicate an option if it already exists in the construction environment variable. &MergeFlags; tries to be intelligent about merging options. When merging options to any variable whose name ends in PATH, &MergeFlags; keeps the leftmost occurrence of the option, because in typical lists of directory paths, the first occurrence "wins." When merging options to any other variable name, &MergeFlags; keeps the rightmost occurrence of the option, because in a list of typical command-line options, the last occurrence "wins." env = Environment() env.Append(CCFLAGS = '-option -O3 -O1') flags = { 'CCFLAGS' : '-whatever -O3' } env.MergeFlags(flags) print env['CCFLAGS'] scons -Q Note that the default value for &cv-link-CCFLAGS; is an internal &SCons; object which automatically converts the options we specified as a string into a list. env = Environment() env.Append(CPPPATH = ['/include', '/usr/local/include', '/usr/include']) flags = { 'CPPPATH' : ['/usr/opt/include', '/usr/local/include'] } env.MergeFlags(flags) print env['CPPPATH'] scons -Q Note that the default value for &cv-link-CPPPATH; is a normal Python list, so we must specify its values as a list in the dictionary we pass to the &MergeFlags; function. If &MergeFlags; is passed anything other than a dictionary, it calls the &ParseFlags; method to convert it into a dictionary. env = Environment() env.Append(CCFLAGS = '-option -O3 -O1') env.Append(CPPPATH = ['/include', '/usr/local/include', '/usr/include']) env.MergeFlags('-whatever -I/usr/opt/include -O3 -I/usr/local/include') print env['CCFLAGS'] print env['CPPPATH'] scons -Q In the combined example above, &ParseFlags; has sorted the options into their corresponding variables and returned a dictionary for &MergeFlags; to apply to the construction variables in the specified construction environment. scons-doc-2.3.0/doc/user/caching.in0000644000175000017500000003413212114661557017674 0ustar dktrkranzdktrkranz On multi-developer software projects, you can sometimes speed up every developer's builds a lot by allowing them to share the derived files that they build. &SCons; makes this easy, as well as reliable.
Specifying the Shared Cache Directory To enable sharing of derived files, use the &CacheDir; function in any &SConscript; file: env = Environment() env.Program('hello.c') CacheDir('cache') hello.c CacheDir('/usr/local/build_cache') Note that the directory you specify must already exist and be readable and writable by all developers who will be sharing derived files. It should also be in some central location that all builds will be able to access. In environments where developers are using separate systems (like individual workstations) for builds, this directory would typically be on a shared or NFS-mounted file system. Here's what happens: When a build has a &CacheDir; specified, every time a file is built, it is stored in the shared cache directory along with its MD5 build signature. Actually, the MD5 signature is used as the name of the file in the shared cache directory in which the contents are stored. On subsequent builds, before an action is invoked to build a file, &SCons; will check the shared cache directory to see if a file with the exact same build signature already exists. If so, the derived file will not be built locally, but will be copied into the local build directory from the shared cache directory, like so: scons -Q scons -Q -c scons -Q Note that the &CacheDir; feature still calculates MD5 build sigantures for the shared cache file names even if you configure &SCons; to use timestamps to decide if files are up to date. (See the chapter for information about the &Decider; function.) Consequently, using &CacheDir; may reduce or eliminate any potential performance improvements from using timestamps for up-to-date decisions.
Keeping Build Output Consistent One potential drawback to using a shared cache is that the output printed by &SCons; can be inconsistent from invocation to invocation, because any given file may be rebuilt one time and retrieved from the shared cache the next time. This can make analyzing build output more difficult, especially for automated scripts that expect consistent output each time. If, however, you use the --cache-show option, &SCons; will print the command line that it would have executed to build the file, even when it is retrieving the file from the shared cache. This makes the build output consistent every time the build is run: scons -Q scons -Q -c scons -Q --cache-show The trade-off, of course, is that you no longer know whether or not &SCons; has retrieved a derived file from cache or has rebuilt it locally.
Not Using the Shared Cache for Specific Files You may want to disable caching for certain specific files in your configuration. For example, if you only want to put executable files in a central cache, but not the intermediate object files, you can use the &NoCache; function to specify that the object files should not be cached: env = Environment() obj = env.Object('hello.c') env.Program('hello.c') CacheDir('cache') NoCache('hello.o') hello.c Then when you run &scons; after cleaning the built targets, it will recompile the object file locally (since it doesn't exist in the shared cache directory), but still realize that the shared cache directory contains an up-to-date executable program that can be retrieved instead of re-linking: % scons -Q cc -o hello.o -c hello.c cc -o hello hello.o % scons -Q -c Removed hello.o Removed hello % scons -Q cc -o hello.o -c hello.c Retrieved `hello' from cache
Disabling the Shared Cache Retrieving an already-built file from the shared cache is usually a significant time-savings over rebuilding the file, but how much of a savings (or even whether it saves time at all) can depend a great deal on your system or network configuration. For example, retrieving cached files from a busy server over a busy network might end up being slower than rebuilding the files locally. In these cases, you can specify the --cache-disable command-line option to tell &SCons; to not retrieve already-built files from the shared cache directory: scons -Q scons -Q -c scons -Q scons -Q -c scons -Q --cache-disable
Populating a Shared Cache With Already-Built Files Sometimes, you may have one or more derived files already built in your local build tree that you wish to make available to other people doing builds. For example, you may find it more effective to perform integration builds with the cache disabled (per the previous section) and only populate the shared cache directory with the built files after the integration build has completed successfully. This way, the cache will only get filled up with derived files that are part of a complete, successful build not with files that might be later overwritten while you debug integration problems. In this case, you can use the the --cache-force option to tell &SCons; to put all derived files in the cache, even if the files already exist in your local tree from having been built by a previous invocation: scons -Q --cache-disable scons -Q -c scons -Q --cache-disable scons -Q --cache-force scons -Q Notice how the above sample run demonstrates that the --cache-disable option avoids putting the built hello.o and hello files in the cache, but after using the --cache-force option, the files have been put in the cache for the next invocation to retrieve.
Minimizing Cache Contention: the <literal>--random</literal> Option If you allow multiple builds to update the shared cache directory simultaneously, two builds that occur at the same time can sometimes start "racing" with one another to build the same files in the same order. If, for example, you are linking multiple files into an executable program: Program('prog', ['f1.c', 'f2.c', 'f3.c', 'f4.c', 'f5.c']) f1.c f2.c f3.c f4.c f5.c f6.c &SCons; will normally build the input object files on which the program depends in their normal, sorted order: scons -Q But if two such builds take place simultaneously, they may each look in the cache at nearly the same time and both decide that f1.o must be rebuilt and pushed into the shared cache directory, then both decide that f2.o must be rebuilt (and pushed into the shared cache directory), then both decide that f3.o must be rebuilt... This won't cause any actual build problems--both builds will succeed, generate correct output files, and populate the cache--but it does represent wasted effort. To alleviate such contention for the cache, you can use the --random command-line option to tell &SCons; to build dependencies in a random order: % scons -Q --random cc -o f3.o -c f3.c cc -o f1.o -c f1.c cc -o f5.o -c f5.c cc -o f2.o -c f2.c cc -o f4.o -c f4.c cc -o prog f1.o f2.o f3.o f4.o f5.o Multiple builds using the --random option will usually build their dependencies in different, random orders, which minimizes the chances for a lot of contention for same-named files in the shared cache directory. Multiple simultaneous builds might still race to try to build the same target file on occasion, but long sequences of inefficient contention should be rare. Note, of course, the --random option will cause the output that &SCons; prints to be inconsistent from invocation to invocation, which may be an issue when trying to compare output from different build runs. If you want to make sure dependencies will be built in a random order without having to specify the --random on very command line, you can use the &SetOption; function to set the random option within any &SConscript; file: SetOption('random', 1) Program('prog', ['f1.c', 'f2.c', 'f3.c', 'f4.c', 'f5.c']) f1.c f2.c f3.c f4.c f5.c f6.c
scons-doc-2.3.0/doc/user/example.xml0000644000175000017500000000240412114661557020122 0ustar dktrkranzdktrkranz XXX
XXX XXX
scons-doc-2.3.0/doc/user/tools.in0000644000175000017500000000253312114661557017440 0ustar dktrkranzdktrkranz This appendix contains descriptions of all of the Tools modules that are available "out of the box" in this version of SCons. &tools-gen; scons-doc-2.3.0/doc/user/SCons-win32-install-4.jpg0000644000175000017500000004613512114661557022252 0ustar dktrkranzdktrkranzÿØÿàJFIF,,ÿÛC    $.' ",#(7),01444'9=82<.342ÿÛC  2!!22222222222222222222222222222222222222222222222222ÿÀ‚U"ÿÄ ÿĵ}!1AQa"q2‘¡#B±ÁRÑð$3br‚ %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyzƒ„…†‡ˆ‰Š’“”•–—˜™š¢£¤¥¦§¨©ª²³´µ¶·¸¹ºÂÃÄÅÆÇÈÉÊÒÓÔÕÖרÙÚáâãäåæçèéêñòóôõö÷øùúÿÄ ÿĵw!1AQaq"2B‘¡±Á #3RðbrÑ $4á%ñ&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz‚ƒ„…†‡ˆ‰Š’“”•–—˜™š¢£¤¥¦§¨©ª²³´µ¶·¸¹ºÂÃÄÅÆÇÈÉÊÒÓÔÕÖרÙÚâãäåæçèéêòóôõö÷øùúÿÚ ?©6‹ðóÃÞðÝîµ ^Ý\êv+3=½Ë¸*î$ÉnߥPûÂ?úuü ?ür·u+mòÓáu¿ˆ'òt·°"g/´}ÄÚ vRÛA<`r:ŒË‡s_ß[ªÏOûÅïÛ´›™'Žeˆª²ƒ4¸Ü¯$¢€Ç,qëÆ£å׫îÎy9'¡[í¿ ?èUÖ?ð(ÿñÊ>Ùð“þ…]cÿÿ«x3Ãö~ñ%ÅÖ¦—ËkŒÖ÷–[e{s,Œ®Œ‹/–[+‚7<â³ÇÃÿú‰ÿÌ·ý½ÿÿù ïãߥRšn_{'š}‘gí èVÕÿð(ÿñÊ_µ|%ÿ¡[WÿÀ£ÿÇ*?|>‹Eƒ]6Ú³ÝM¢5°ºY-DJË0ùJí’ üñÏ*áï«ÆrûØI-ÑÝý£á7ý Ú¿þþ9Kçü&ÿ¡[WÿÀ¦ÿ㕊pªä—óËïbö¬î|ï„ßô+jßøßür7á?ý Ú·þ7ÿ® S…’þy}ì=«;o3á?ý Ú·þ7ÿ¥Ýð£þ…}[ÿ›ÿŽW)—$¿ž_{jÎÏ? ?èWÕð)¿øå/üZŸúõ_ü oþ9\h§Š9%üòûØ{Vv~ÿЯªÿàSñÊ]Ÿ èWÕð-¿øºä8QÉ/ç—ÞÃÚ³­òþЯªà[ñt¾W¯ú5Oü oþ.¹1OrKùå÷±{Vu^O¿ú5Oü oþ.—ìÿ ÿèXÕ?ð-¿øºå…8Rå—óËïaíYÔ}›á_ý Ÿþ·ÿGÙ~б©ÿà[ñuÌŠp£–_Ï/½Ú³¥û'ÂÏú5?ü oþ.—ì_ èXÔÿð-¿øºç8QË/ç—ÞÃÚ³¢ûÂßúu/ü oþ.—ì ¿èYÔ¿ð-¿øºç…¬»Ÿ>kŸôÔðÿ”xw\ÿ 6¡ÿ€¯þôhKùCê˹óøðî·ÿ@}Cÿ_ü)ãÃÚßýõüð¯}¢í (}Yw<x{Zÿ >¡ÿ€Ïþááýkþÿø ÿá^óEÚþPú²îx@ðþ³ÿ@‹ÿüð¥³ÿ@‹ÿüð¯v¢í (}Yw<,hÏýoÿðÿœ4cþ7ßø ÿá^åE/í v«.ç‡Xÿ U÷þ¿øS†…«ÿÐ*ûÿßü+Û¨£ûB]ƒê˹âcBÕÿè}ÿ€ïþá¡êÿô ½ÿÀwÿ öª(þЗ`ú²îx¸Ñ5oúÞÿà;ÿ…8hš·ýïðÿ½šŠ>¿.ÁõeÜñ±¢ê¿ô ½ÿ¿ þá¢ê¿ô ¼ÿ¿ þìTQõùv«.çTÿ mçýøoð§ Tÿ mçýøoð¯^¢¯Ë°}Yw<Œhú§ýo?ïÃ…8húŸý®ÿïÃ…zÕ¾¿.ÁõeÜòq¤jô»ÿ¿ þá¤j_ô»ÿ¿-þêÔQõùv«.ç•'Rÿ }ßýùoð§ 'Rÿ }×ýùoð¯R¢¯Ë°}Yw<¼iZüø]ß–ÿ pÒµùðºÿ¿-þéÔQõév«.ç™/PÿŸ ¯ûòßáJ4½Cþ|nïË…z]}z]ƒê˹æÃLÔ?çÆçþý7øS†™ÿ>7?÷é¿Â½Š>½.ÁõeÜó¡¦ßÿÏ•Ïýúoð§ 6ûþ|®?ïÓ…z¾½.ÁõeÜóá§_ÏÇýúoð§ :÷þ|î?ïÓ…wôQõév«.ç4ûßùó¸ÿ¿Mþá§ÞÿϤÿ÷ìÿ…wtQõév«.ç ,/?çÒûöiÂÆóþ}'ÿ¿f»z(úô»Õ—sŠ7óë?ýû4ácwÿ>³ÿß³]}v]ƒêË¹Ç +¿ùõ›þýšp²ºÿŸi¿ïÙ®¾Š>».ÁõeÜä…×üûMÿ|p´¹ÿŸy¿ïƒ]]}v]…õeÜå…¥ÏüûËÿ|pµ¸ÿŸyïƒ]=¾».ÁõeÜæ…­Çüð—þø4ámqÿ<%ÿ¾ ttQõÙv«.ç<-§ÿž2ßœ-çÿž2ß&·è£ë’ì?«.æ·›þxÉÿ|šp‚oùå'ýòkrŠ>¹.Âú²îbˆ&ÿžOÿ|šp†_ùäÿ÷É­Š(úä»Õ—s$C/üóûäÓ„RÏ7ü«RŠ>¹.ÁõeÜÍIýÆü©Â7þã~U¡E\—`ú²îQ¿÷ò§„oîŸÊ­ÑKë’ìV]Îzðkך’ß>•¢¢whG‘žÝB¶Î288Æk?PÒµËû[ø¿³ôX%¾EY牜HûFqÙóc Ïjìh¬9ãØß–]Ï*øžÔtðF?pßúÿŠò°ÿ®ÿB¢›|ÎâJÊÇ xgþD Ø.ý´«7Ã?ò#økþÁpè5¥J·ñ%êZØ(¢ŠÌaEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEP–üSÿ­‡ýp?ú|Sÿ­‡ýp?úkbYèÿ‘Ã_ö ƒÿA­*ÍðÏüˆþÿ°\ú iS­üIzlà·77îp.&JÀ#O½{Õx Ïü~\×WÿÐve°Œêµ%}?Èó³9Ê“‹¶¿£´ÜÿÏÌÿ÷õ¿Æ´ÜÿÏÌÿ÷õ¿Æ¢¢½¿«ÒþU÷#ÃúÅoç{%ûMÏüüÏÿ[ühûMÏüüÏÿ[üj*(ú½/å_r¬Vþw÷²_´ÜÿÏÌÿ÷õ¿Æ´ÜÿÏÌÿ÷õ¿Æ¢¢«ÒþU÷ úÅoç{%ûMÏüüÏÿ[ühûMÏüüÏÿ[üj*(ú½/å_r¬Vþw÷²_´ÜÿÏÌÿ÷õ¿Æ´ÜÿÏÌÿ÷õ¿Æ¢¢«ÒþU÷ úÅoç{%ûMÏüüÏÿ[ühûMÏüüÏÿ[üj*(ú½/å_r¬Vþw÷²_´ÜÿÏÌÿ÷õ¿Æ´ÜÿÏÌÿ÷õ¿Æ¢¢«ÒþU÷ úÅoç{%ûMÏüüÏÿ[ühûMÏüüÏÿ[üj*(ú½/å_r¬Vþw÷²_´ÜÿÏÌÿ÷õ¿Æ´ÜÿÏÌÿ÷õ¿Æ¢¢«ÒþU÷ úÅoç{=£Á\øNrIw–bÌNKØä÷àøVÝbx'þD-3þºOÿ¡ÖÝ|Î!%VIwgÔaÛtbßeùgê:¨±ž h¬îon¦W‘ ·Øb• ÙvUÀ.ƒÏÍÀ88Ь]V;¸5»RÞÆkÔ†Ú{w†ŒH ùÙWhòˆ<ç$pyÆFÄöšÒ^_},¯#™cI&¢¯”ʼüÜá¡a•È;”‚TäYº¿ŠÒâÆ Ë^N`Œ¨ #y9öÄgñÅqúÖ‹¯k©¼·¶û ²[*G!¸¼›äRJòáÎÜà±Á`¹§Íáé¯>ƺ^Œþ….÷¾ÇHÿG ùq>Ð tRÊÂB åv+P#±º»‚Ê–áö#I@àŸ™Ü"Ž=Y€üj; øµwžuUžXpÝtí•8öÅdêšdš—„ã²:\1ùR@Í` 4lÌŒcNŠU•]ÛFÜ1gð®¢,^M>?²ê·º†û¿; Ê.L_0$„ÞоÕèß67dÐuXºWˆ%Õí`º‹CÔb¶ž4–)eh0Êåpp%$pÛŽG@{à^Ñ›Iûa·–K°,7?fO˜nËíÔ È*7XíÁ(Í[ ÜXø3K³HîMê¶œ×1KxÒª¥‰¤ÚЍ7 €pÏ×JíXžRYWj žHà“ßàP]_ÅiqcŠå¯'0FT ¼œûb3øâ¸‹o ßYiÞŠÚËd©mmý¤|Õ%¥ŽâÕÆâOϱ}½BŒªà öþžyíb¹Ò¼«DÔL×Ü毑2±m§÷ª]Õw°H¤ íËv÷ñiÖé<Êì­ØÙqåJ&?„ù›ØÎñ»nN)Oá]DX¼š|eÕn/u ÷~v!”\˜¾`I ½¡}«Ñ¾lnÉ ê³4­i5m¯•äVòÆ&¶¸•Ëž3Œ2•bW ©±§ Œ¿hͤý°‹[ËH%ØŸ³'Ì7e„vêd‰,và€f¬zNªÐjÚu½Î¯a4e½2D.&ƒ Æ8Óçè±ðÉòñ…ê绂Úkh¥}¯s!Š‚w0F|{|¨ÇŸJš¸/²[ÜYκmÊZÁæÖèZ)ÈŽTó8F2ÆÅ‹o">Ÿ"ÏÕ4½J#E·¾ÑüËM6ÚßOcµ?Ú¬ÆIÆÆœ žwã%€ôêdÎÑA$‰ÌÊ¥„h@g t dû=뛳Ф¸ðæ·¦ýŸû.ÛQóÚÛj²#B±Ÿ• Ë‡|+`îÉ “Œ]WÃ:Þ£§Ü;®g¼¶¸¼’ §îîš Hwg÷s"n_ôl‘™ ƒEq£O%þµu†ÒÖêu’•c¶Ù< "ùŸÄL’È °óT )þ"ùú…5m×";i­[_G§ItÐ#Ú¼±Û*2ˆ>XòË9Ê òIå¹,{üRj—xWó ‚)ÙˆJÈΣù³õj¹_iÿaׯ®-¼4úMÌñQnƒzÙœˆÜñ†EÏ\‘Æ#ª aEPEPEPEPEPEPEPEPEPEP–üSÿ­‡ýp?ú|Sÿ­‡ýp?úkbYèÿ‘Ã_ö ƒÿA­*ÍðÏüˆþÿ°\ú iS­üIzlà7?ñùqÿ]_ÿB5ïÕà7?ñùqÿ]_ÿB5Ý•ú~¨ó3oà¯_Ñ‘QEï>QEQEQEQEQEQEQEQE{G‚äBÓ?ë¤ÿúmÖ'‚äBÓ?ë¤ÿúm×Êâ?WùŸ[†þ =äQEbnQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEyoÅ?ù Ø×ÿ¡QGÅ?ù Ø×ÿ¡QV¶%žáŸùü5ÿ`¸?ôÒ¬ß ÿÈá¯ûÁÿ Ö•:ßÄ—¨ÖÁ^sÿ—õÕÿô#^ý^sÿ—õÕÿô#]ÙWñ_§ê36þ õýQ^ñóáEPEPEPEPEPEPEPEP´x'þD-3þºOÿ¡ÖÝbx'þD-3þºOÿ¡ÖÝ|®'øÓõ™õ¸oàÃÑ~AEV&áEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEP–üSÿ­‡ýp?ú|Sÿ­‡ýp?úkbYèÿ‘Ã_ö ƒÿA­*ÍðÏüˆþÿ°\ú iS­üIzlà7?ñùqÿ]_ÿB5ïÕà7?ñùqÿ]_ÿB5Ý•ú~¨ó3oà¯_Ñ‘QEï>QEQEQEQEQEQEQEQE{G‚äBÓ?ë¤ÿúmÖ'‚äBÓ?ë¤ÿúm×Êâ?WùŸ[†þ =äQ\¯Š/åÒõA¨@¨ÓZèzŒè® RÊÖì3Œq‘X›UÊÝkšÅ¬÷62 x«ÊÑÆòlI ¹EpiÙ,~R…”’+µªÛ뚎5y`¸†yîo`ŠÂ—í ¸ÚE+„f’5d+½‡)ОK¢Â;J+ÌS]ó¼Mg«jqý—ìÛEÈÆvù)ª#¶·÷ À-Ž€·S©·¯\kÚf›{Øîö)ŒA°¼YB‰4ƒ¬$‚_©oË󺢸ėóèöW±Ác”Mci%¤P°%§HämØE_]¼¡ó˜A‘¨kzšMô­f×:n¨Ü¼/%!Ûº=çËqæÎ•8`°ƒEyÇÄ-Wí2ÞiŠð²YÛ<°åÕ䲿ʷ<|¨„ gæÏq]>—®Ý_k_`’8A‹íiT|;'U·Ü3òù‘–qŸ½Œ¯X‚Š( aEPEPEPEPEPEPEPEPEPEPEPEPEP–üSÿ­‡ýp?ú|Sÿ­‡ýp?úkbYèÿ‘Ã_ö ƒÿA­*ÍðÏüˆþÿ°\ú iS­üIzlà7?ñùqÿ]_ÿB5ïÕà7?ñùqÿ]_ÿB5Ý•ú~¨ó3oà¯_Ñ‘QEï>QEQEQEQEQEQEQEQE{G‚äBÓ?ë¤ÿúmÖ'‚äBÓ?ë¤ÿúm×Êâ?WùŸ[†þ =ä öv·YûE´3f7„ùˆ(ØÜ¼ÿ ÀÈèp*j+r­Ö›a|’¥Ý•µÂ̪’¬±+‡U%”6G HÏBMZm…òJ—vV× 2ªJ²Ä®T–PÙ€I#= 5jŠ¥m£é–~WÙtë8<¯õ~T »>ÿL?ÖIÿ}·©¦Zh:=‚:Yé66ë##ºÃlˆîBp9*y‡¥hQ@“GÓ#º·ºM:Ín-£Á(CÄ€N2«‚Fšž+;X<Ÿ*ÚüˆÌ0ì@<´ãå_EùWÇÊ=*j(ªi¶jêÙ[%쫲K•‰D޼p[#åô¦Zhúe„ižgl‰!™¹]¥€†ÚHÏ\UÚ("ûÃ>¢,ÒãO¶hmYJÃä¡FUGDFR9U1±éV¤ÑôÉmZÖM:ÍíÚ4„ÄÐ)BˆIEÆ1µI$ƒµ¡EU—M°ŸO|ÖVÒYTϘ®6¤c˜K¦ØOv.æ²¶’ä* ™âRáUƒ¨ÜFp„Ö­Q@£ÑôȾÙåéÖiöÜý«l <üç;øù³¹ºç©õ¦>ƒ£Ë§Å§É¤Ø½”M¾;f¶C7<…Æùæ}kBЇìv¿nûoÙ¡û_—äùû™³9Û»®Ü󎙨ítÛ ‰-,­­Öd‰b‰P"± Áp8€N:*ÕžšÒlV¥klBàŒc¦$“þûoSE–ok©+½ä“N·MrªZI(V!T(*1€>è=rN…QEQEQEQEQEQEQEQEQEQEQEQEQEQEå¿ÿä+aÿ\þ…Eÿä+aÿ\þ…EZØ–z†äGð×ý‚àÿÐkJ³|3ÿ"?†¿ìþƒZTë^£[x Ïü~\×WÿÐ{õx Ïü~\×WÿÐwe_Å~Ÿª<ÌÛø+×ôdTQE{ÇÏ…Q@Q@Q@Q@Q@Q@Q@Q@ÑàŸù´Ïúé?þ‡[u‰àŸù´Ïúé?þ‡[uò¸ŸãOÕþgÖῃEùQX›…Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@[ñOþB¶õÀÿèTQñOþB¶õÀÿèTU­‰g xgþD Ø.ý´«7Ã?ò#økþÁpè5¥N·ñ%ê5°W€ÜÿÇåÇýuý׿W€ÜÿÇåÇýuý×vUüWéú£ÌÍ¿‚½FEEW¼|øQEQEQEQEQEQEQEQEí ÿ‘ Lÿ®“ÿèu·Xž ÿ‘ Lÿ®“ÿèu·_+‰þ4ý_æ}nø0ô_QE‰¸QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEå¿ÿä+aÿ\þ…Eÿä+aÿ\þ…EZØ–z†äGð×ý‚àÿÐkJ³|3ÿ"?†¿ìþƒZTë^£[x Ïü~\×WÿÐ{õx Ïü~\×WÿÐwe_Å~Ÿª<ÌÛø+×ôdTQE{ÇÏ…Cw+Ae<ÊhãfôÈ©ª®¥ÿ »¿úâÿú ©›´[EÓW’Lëí~0ðîªj¾9¶Ó¾Û•{eQ’¥° Æp1úûT1ü/ñ#ø¢];ûi³ÚÕîmo–Ø0˜ q€89#ðéšéÏä¥wÎOËê8ük›ã8‡Å¶O¤éHº ”j4kæ˜N öÀ^žÜ×ÍûzßÌþö}GÕèÿ"û‘‹à­/Tñ¾µq¥E~–Mgm$’J /æ2œ1Ço×è:Ï|7oXE%¿Ž­ZìÃæKo²±‹ïAÁ€9¾Õ¡®]øoá†ŽÞ Ð4é ÿˆ#-gç)Ù ddŒÀäA\ÏÀ5wñ–§#Ç÷ôéXœ°éòžùãÒ‡ˆ¬õæx–ŠÓ‘}Æ”ä½×ÿ³4¿ÚÞm¶ši VÁ™ 6П|ðq\¿‚´½SÆúÕÆ•úY5´’<¢þc)Àãvý~ƒkà2¯ü'ú¾ôÊ‹)r6öÜ8û¿áôí]×ýKÀ7¾"ÕbðÆsi¨-¼†I%RT®æÈqÔqAÚ¬Öþg÷ƒÃQþE÷Nà JM=GÄ~*¶ÐÅÁ&Ù.!LÈ¡IÝÎ=3ޏ©ãø_âGñDºwöÒÿgµ«ÜÚß-°a0ãprGáÓ4ÏŒ¶ò7ƒüR!mX±ã)þàÇCéôï^©á¨Ú<(L$^߯ÐQÑõ½I¡˜~þݶ»€ÝyÇcÁãÿÔ=¿À÷Þñ^¯{â]>%µÖ…´–÷Ö¸cýìmöë߸¯ðØÅæ¦1Þ/Çvöʺðxв®”¥tÿÈâÇa©Fƒ”cf¿Ìèh¢ŠöÏ(¢ŠöÿÈ…¦×Iÿô:Û¬OÿÈ…¦×Iÿô:Û¯•Äÿ~¯ó>· üz/È(¢ŠÄÜ(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Šòߊò°ÿ®ÿB¢Šò°ÿ®ÿB¢­lK=Ã?ò#økþÁpè5¥Y¾ÿ‘Ã_ö ƒÿA­*u¿‰/Q­‚¼çþ?.?ë«ÿèF½ú¼çþ?.?ë«ÿèF»²¯â¿OÕfmüëú2*(¢½ãç¢M?Uׯ%Ñtm9ï.^Ùä|8P‹ÈÏ>ÿNÕ-uŸ °¿nÙ!t–''ïýórãjJ[‰Ù€§•’‘ÌxëÃß%ð¾›ý·¢Å†‹’²@œg8=0£Û¡ïUt k~&Ó®î<;á±-«€\†X·|§8r8þUÚk_¼=ee¨¦‰g¨Í¨Kº0/œ¼C,ù;Xœ}î˜@íVõ»OøÀ>“À· V+b·‚ÊeˆÛÐà/ûC§§zð©Õ•4ÔzŸARŒ*4åÐà5í?Ç'º±ð•ÖŒ‹u¡Z6Ô‰“þ-ÝpÇSIàÝWÄ?ð“Ë7…4Èçº]5âž Pb%äñ^•íöº…­Ÿˆt3T½¶þÜþÈ–)ÑÙL!ÛÆvs’¸Ï¡ªºƒ4ÊšºÇ§—§K£3Ë•êžÇ¦;pjÚ‹rœ’—Tx÷Ãm+Çv×7ºç‡4hî‰%´†uP¹ “´:ÛŽÔÏéþ8ð¯µ+M;FI5Qi š)Pyܧ§\czW¹èòØjžÑßO¶ºÔ#H6·ödâ-­±9ÏÇR8¨´ÿ[j?dÓcÓ.mnôÍ>q)¸U%²ÊW Žz÷èjK<ÓIÖ>'x+Á†êãCŠãL_Þ#]ǹáVÜ1´€8íŸz‹ÃºÏÄÏë7~+Òô¸®bx^Õ#pQ©ü€‘ÜuîzÖü[ñiñ©~Š“[HZ4E¡‰yã±òzóšöÝ[-Wáö‡%•¥Õð[`².™8‡cùl8Ûß#¤S³Ñàz ¾-ðŽ'±·Ò•µ‹˜ÚßÈ’ Áƒ÷\vïé]W‚~^ÿÂMsŒtwXÎYb(ÿ.ðqÕçœúôx¼Y¤Þ|MGšÊKûK)â_µ„Ý#1B 9={ô4߇–ž=ÓåÕ-JòÚóÍû^™{-¥ç–b.ŽT:ªqõ?ç¥Ú**SHòËcJUeJJQz˜šN‚-Sz°NÎFÐWpP=ÈïŸÒ¬ù:ÖšÒ.«OaÄ´Ç3"îÁö'üôÒ¢±xJN ÐÙc+*Ž¥õqÏbê·z£_ßêL×$ïóÕÙ¤ßØäãüŠuÍŸ‰o"¹ŠçWic¹K:¼îDŒ:1ÍoÑYÿgжƿÚ5ï{˜Övž ÑíD:F·=²ÉóL‘ÊÈ¥°FF=Ž?Ï-—‰Q›Q]\‹ÙÕ–YÄï½Á œWAE/ úÌk®¦F‡¥M¦,æyšR8LÏsõ©|kMi@Õ§°‚bZHc™‘w`ŒŒ{þziQZ¼-'MSkDb±uUGQ=YÎÿbê—z£ßßêL×,wùêìÒìrqþEh=çäFWñ-Ë+7R`ç9íþÑüëJŠËû>…¶6þѯ{ÜÈÐô©´ÅœÏ"3JG ’î~µ¯EÕNœiÅB;#’­IU›œ·aEU™žÑàŸù´Ïúé?þ‡[u‰àŸù´Ïúé?þ‡[uò¸ŸãOÕþgÖῃEùQX›…Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@[ñOþB¶õÀÿèTQñOþB¶õÀÿèTU­‰g xgþD Ø.ý´«7Ã?ò#økþÁpè5¥N·ñ%ê5°W€ÜÿÇåÇýuý׿W€ÜÿÇåÇýuý×vUüWéú£ÌÍ¿‚½FEEW¼|øQEQEQEQEQEQEQEQEí ÿ‘ Lÿ®“ÿèu·Xž ÿ‘ Lÿ®“ÿèu·_+‰þ4ý_æ}nø0ô_QE‰¸QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEå¿ÿä+aÿ\þ…Eÿä+aÿ\þ…EZØ–z†äGð×ý‚àÿÐkJ³|3ÿ"?†¿ìþƒZTë^£[x Ïü~\×WÿÐ{õx Ïü~\×WÿÐwe_Å~Ÿª<ÌÛø+×ôdTQE{ÇÏ…Q@Q@Q@Q@Q@Q@Q@Q@ÑàŸù´Ïúé?þ‡[u‰àŸù´Ïúé?þ‡[uò¸ŸãOÕþgÖῃEùQX›…Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@[ñOþB¶õÀÿèTQñOþB¶õÀÿèTU­‰g xgþD Ø.ý´«7Ã?ò#økþÁpè5¥N·ñ%ê5°W€ÜÿÇåÇýuý׿W€ÜÿÇåÇýuý×vUüWéú£ÌÍ¿‚½FEEW¼|øQEQEQEQEQEQEQEQEí ÿ‘ Lÿ®“ÿèu·Xž ÿ‘ Lÿ®“ÿèu·_+‰þ4ý_æ}nø0ô_QE‰¸QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEå¿ÿä+aÿ\þ…Eÿä+aÿ\þ…EZØ–z†äGð×ý‚àÿÐkJ³|3ÿ"?†¿ìþƒZTë^£[x Ïü~\×WÿÐ{õx Ïü~\×WÿÐwe_Å~Ÿª<ÌÛø+×ôdTQE{ÇÏ…Q@Q@Q@Q@Q@Q@Q@Q@ÑàŸù´Ïúé?þ‡[u‰àŸù´Ïúé?þ‡[uò¸ŸãOÕþgÖῃEùQX›…Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@[ñOþB¶õÀÿèTQñOþB¶õÀÿèTU­‰g xgþD Ø.ý´«7Ã?ò#økþÁpè5¥N·ñ%ê5°W€ÜÿÇåÇýuý׿W€ÜÿÇåÇýuý×vUüWéú£ÌÍ¿‚½FEEW¼|øQEQEQEQEQEQEQEQEí ÿ‘ Lÿ®“ÿèu·Xž ÿ‘ Lÿ®“ÿèu·_+‰þ4ý_æ}nø0ô_QE‰¸QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEå¿ÿä+aÿ\þ…Eÿä+aÿ\þ…EZØ–z†äGð×ý‚àÿÐkJ³|3ÿ"?†¿ìþƒZTë^£[x Ïü~\×WÿÐ{õx Ïü~\×WÿÐwe_Å~Ÿª<ÌÛø+×ôdTQE{ÇÏ…Q@Q@Q@Q@Q@Q@Q@Q@ÑàŸù´Ïúé?þ‡[u‰àŸù´Ïúé?þ‡[uò¸ŸãOÕþgÖῃEùQX›…Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@[ñOþB¶õÀÿèTQñOþB¶õÀÿèTU­‰g xgþD Ø.ý´«7Ã?ò#økþÁpè5¥N·ñ%ê5°W‡Í¢jÒÜK$z]ë£È̬¶îCIt¯p©¾×sÿ?ÿßf´Ãb 9%sŸ†Xˆ(·cÁ¿°uúßÿà3ÿ…Ø:Çýoÿðÿ½çíw?óñ/ýöhû]ÏüüKÿ}šíþÕŸò£‡û&ÌÏþÁÖ?èÿ€Ïþ`ëô ¿ÿÀgÿ ÷ŸµÜÿÏÄ¿÷Ù£íw?óñ/ýöhþÕŸò þɇó3Á¿°uúßÿà3ÿ…Ø:Çýoÿðÿ½çíw?óñ/ýöhû]ÏüüKÿ}š?µgü¨?²aüÌðoìcþ7ÿø ÿáGö±ÿ@›ÿüð¯yû]ÏüüKÿ}š>×sÿ?ÿßfíYÿ*ì˜3<ûXÿ Mÿþ?øQýƒ¬Ð&ÿÿŸü+Þ~×sÿ?ÿßfµÜÿÏÄ¿÷Ù£ûVʃû&ÌÏþÁÖ?èÿ€Ïþ`ëô ¿ÿÀgÿ ÷ŸµÜÿÏÄ¿÷Ù£íw?óñ/ýöhþÕŸò þɇó3Á¿°uúßÿà3ÿ…Ø:Çýoÿðÿ½çíw?óñ/ýöhû]ÏüüKÿ}š?µgü¨?²aüÌðoìcþ7ÿø ÿáGö±ÿ@›ÿüð¯yû]ÏüüKÿ}š>×sÿ?ÿßfíYÿ*ì˜3<ûXÿ Mÿþ?øQýƒ¬Ð&ÿÿŸü+Þ~×sÿ?ÿßfµÜÿÏÄ¿÷Ù£ûVʃû&ÌÌ/[Íkà6ˆdŠU’mÉ"•aógk^Ÿ$²K2G|tÜÄâ™^eIóÍ˹êS‡$; ¢Š*K (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€<·âŸü…l?ëÿШ£âŸü…l?ëÿШ«[Ï@ðÏüˆþÿ°\ú iVo†äGð×ý‚àÿÐkJoâKÔk`¢Š+1…Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@[ñOþB¶õÀÿèTQñOþB¶õÀÿèTU­‰g xgþD Ø.ý´H eVSÁVƒìAëYÞÿ‘Ã_ö ƒÿA­*u¿‰/Q­ˆþÍiÿ>?ø ÿGÙ­?çÂÇÿ#ÿâjJ+;°#û5§üøXÿà$üMf´ÿŸ üÿ‰©(¢ìþÍiÿ>?ø ÿGÙ­?çÂÇÿ#ÿâjJ(»?³ZÏ…þGÿÄÑökOùð±ÿÀHÿøš’Š.ÀìÖŸóácÿ€‘ÿñ4}šÓþ|,ð?þ&¤¢‹°#û5§üøXÿà$üMf´ÿŸ üÿ‰©(¢ìþÍiÿ>?ø ÿGÙ­?çÂÇÿ#ÿâjJ(»?³ZÏ…þGÿÄÑökOùð±ÿÀHÿøš’Š.ÀìÖŸóácÿ€‘ÿñ4}šÓþ|,ð?þ&¤¢‹°#û5§üøXÿà$üMf´ÿŸ üÿ‰©(¢ìþÍiÿ>?ø ÿGÙ­?çÂÇÿ#ÿâjJ(»?³ZÏ…þGÿÄÑökOùð±ÿÀHÿøš’Š.ÀìÖŸóácÿ€‘ÿñ4}šÓþ|,ð?þ&¤¢‹°#û5§üøXÿà$üMf´ÿŸ üÿ‰©(¢ìþÍiÿ>?ø ÿGÙ­?çÂÇÿ#ÿâjJ(»?³ZÏ…þGÿÄÑökOùð±ÿÀHÿøš’Š.ÀìÖŸóácÿ€‘ÿñ4}šÓþ|,ð?þ&¤¢‹°#û5§üøXÿà$üMf´ÿŸ üÿ‰©(¢ìþÍiÿ>?ø ÿGÙ­?çÂÇÿ#ÿâjJ(»?³ZÏ…þGÿÄÑökOùð±ÿÀHÿøš’Š.ÀìÖŸóácÿ€‘ÿñ4}šÓþ|,ð?þ&¤§ÇJH\p2I`Ö‹° û5§üøXÿà$üMf´ÿŸ üÿ‰«_f“ûÑßÕÿ>Í'÷¢ÿ¿«þ4j_³ZÏ…þGÿÄÑökOùð±ÿÀHÿøšµöi?½ýý_ñ£ìÒz/ûú¿ãF Uû5§üøXÿà$üMf´ÿŸ üÿ‰«_f“ûÑßÕÿ>Í'÷¢ÿ¿«þ4j_³ZÏ…þGÿÄÑökOùð±ÿÀHÿøšµöi?½ýý_ñ£ìÒz/ûú¿ãF Uû5§üøXÿà$üMf´ÿŸ üÿ‰§Þ2XYÍws41Á vóTà`r~ƒšÈƒÄ¶WVñ\[Úk3A*ŽHôk¶WR2",G9§¨hj}šÓþ|,ð?þ&³ZÏ…þGÿÄÖöôô×?ðIyÿƨþÞƒþúçþ /?øÕ††‡Ù­?çÂÇÿ#ÿâhû5§üøXÿà$üMgÿoAÿ@ýsÿ—Ÿüjíè?è®à’óÿQ¨hh}šÓþ|,ð?þ&³ZÏ…þGÿÄÖöôô×?ðIyÿƨþÞƒþúçþ /?øÕ††‡Ù­?çÂÇÿ#ÿâhû5§üøXÿà$üMgÿoAÿ@ýsÿ—Ÿüjíè?è®à’óÿQ¨hh}šÓþ|,ð?þ&³ZÏ…þGÿÄÕM/X²Öc¸{'‘¾Ï3[̲Àñ4r R®dv­(âiI ŽI,ñúÒ»³ZÏ…þGÿÄÑökOùð±ÿÀHÿøšµöi?½ýý_ñ£ìÒz/ûú¿ãF Uû5§üøXÿà$üMf´ÿŸ üÿ‰«_f“ûÑßÕÿ>Í'÷¢ÿ¿«þ4j_³ZÏ…þGÿÄÑökOùð±ÿÀHÿøšµöi?½ýý_ñ£ìÒz/ûú¿ãF Uû5§üøXÿà$üMf´ÿŸ üÿ‰«_f“ûÑßÕÿ>Í'÷¢ÿ¿«þ4jxá·ŠE’;+4t!•–Ö0A;iÔ—Œ–s]ÜÍ pB…ݼÕ8ØŸ æ–€<·âŸü…l?ëÿШ£âŸü…l?ëÿШ«[ žáŸùü5ÿ`¸?ôÒ¬ß ÿÈá¯ûÁÿ Ö•:ßÄ—¨ÖÁEVc (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ šõWõÌèKPÔÐÿª¸ÿ®cÿBZCEP1¥ÔH‘ò]òB“€2OÐzý=j ®$ˆÜ8Hü›x–Iœ† îá@S“„=H©R7K‡tu (PÝx=zöéÎxç0Md×NÁm’$Dš`KÂFü”ž~`z»BfI#ŠçìÒH‹??»,7{~¢ûTNá!’9ÎŽ&Pãå,áyÇN¿¥>HZ}AœÈ¾C\Ç/ïgaÃmØ «-ýïlÔik$–öM$)¿“ìfßµ] 082?)'$sÆiÙ:²¸Ê°a’2y–™ È$nÈD"mù@ã ÃÓÞK˜üéõSû—tÜÃÊ©)ÉÜpyÆ+B}fÁå!¿±k˜`’c—*€*¬ÌFJ¨`T¶=Æ+øB%ðþ¹VÖ)ªêK|Ÿk€Ì³HìßnâdÏ_»Æp*®¥àÛ«­k+cgÄ׺…ÌŽrùñÜ"Bä°Æ SŒàdÐêå­šºÅ$7\ÛA4 (ß"¢–‘ÃnõÇãWm/-oíRêÎæ›wÎÉapèØ88#ƒÈ#ð®bÿÂ×Wó]G,Zq³’æ)Dl¥·¯Ú#–Q´ƒ³r¦Ae‘€|FKnÝÓ¬%´¾Õç‘­åÚÏRrA|ûæ3øb€4(¢Šq? ãÇ]ÿ°¼Ÿú**ï!ÿUqÿ\Çþ„µÁü5ÿwþÂò訫¼‡ýUÇýsúÓ{ˆ†Š(¤3"óS¿þÔ“OÓ,­®&†žf¹¹hT+³ªíÛäæ7ÎqŽ:äâÒj¶_j·³žâ}Bxĉe,ÉçcŸ”œa²FGš¥wi©ÛksjZlw?h¶Š #¹¸h6ym# £îÏšxÀÆÞùã3Sðþ³©[Ë$ðÜ"\ÚÌd“@‘Þ6uê%ƒ;do? 4è"Ö4É£žHµ9Þ5šfIԈїr³ð¥yðG4Ç×´x´øµ 5k²•¶Gr×(#vç€ÙÁ?)ü¥siàÛ¨t­ÚgévJŽ«•I'Y­¦ê…fòØÈÝœEO¨xR¿ºƒR{{4»_5^Þ×PžÔâ!¹§wHÃÉÃü°Ac¨}²ïRƒÊÙö+‘íÙߘ£“=8ÿYŒsÓ=êíbøsD}¸ „ÄòDb*[Ã0Ä‘ÌgsqŒ’s[T ÃñüŠ:—ýrþ¢·+Æ?ò(ê_õËúŠÜ£ -ø§ÿ![úàô*(ø§ÿ![úàô**ÖÂg xgþD Ø.ý´«7Ã?ò#økþÁpè5¥N·ñ%ê5°QE˜ÂŠ( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( ¡»–ú;IŸ¼“¾ý"FUQIàOÇzñƒ5‡¿Å_óÃFÿ¿Òÿñ4oñWüðÑ¿ïô¿üMnQEÄaïñWüðÑ¿ïô¿üMüUÿ<4oûý/ÿ[”Qp0÷ø«þxhß÷ú_þ&þ*ÿž7ýþ—ÿ‰­Ê(¸{üUÿ<4oûý/ÿFÿÏ þÿKÿÄÖå\cT²ñ6«¦\Xˉ2m,³I‘ÿŽVUžñËN³²Š}EkmºbY9XÐ <Æy!rk¼¢‹…Ž'ìÿ?ççAÿ¿ÿƨû?ÄùùÐïãÿñªí¨¢ác‰û?ÄùùÐïãÿñª>Ïñ#þ~tûøÿüj»j(¸Xâ~Ïñ#þ~tûøÿüj³üHÿŸþþ?ÿ®ÚŠ.8Ÿ³üHÿŸþþ?ÿ£ìÿ?ççAÿ¿ÿÆ«¶¢‹…ŽgÁZ¡ é÷ñêMjgº½kœ[;2€Qe9SÛÒº ¹o£´iñ[É;áÒ$eUž$ñŒqׯ3Q@{üUÿ<4oûý/ÿFÿÏ þÿKÿÄÖå\ =þ*ÿž7ýþ—ÿ‰£Š¿ç†ÿ¥ÿâkrŠ.ÿÏ þÿKÿÄÑ¿Å_óÃFÿ¿Òÿñ5¹EŠ¿ç†ÿ¥ÿâhßâ¯ùá£ßéøšÜ¢‹Ìj–^&ÕtË‹cÒ&M¥–i2?ñÊé袀<·âŸü…l?ëÿШ£âŸü…l?ëÿШ«[ œ¥ýçØm‡Ú犘x@¯@*o·ÞÏÜÿ÷ðÑEiWã—¨}¾óþ~çÿ¿†·ÞÏÜÿ÷ðÑE@Ûï?çîûøhû}çüýÏÿ PöûÏùûŸþþ>ßyÿ?sÿßÃE}¾óþ~çÿ¿†·ÞÏÜÿ÷ðÑEo¼ÿŸ¹ÿïá£í÷Ÿó÷?ýü4Q@Ûï?çîûøhû}çüýÏÿ PöûÏùûŸþþ>ßyÿ?sÿßÃE}¾óþ~çÿ¿†·ÞÏÜÿ÷ðÑEo¼ÿŸ¹ÿïá£í÷Ÿó÷?ýü4Q@Ûï?çîûøhû}çüýÏÿ PöûÏùûŸþþ>ßyÿ?sÿßÃE}¾óþ~çÿ¿†·ÞÏÜÿ÷ðÑEo¼ÿŸ¹ÿïá£í÷Ÿó÷?ýü4Q@Ûï?çîûøhû}çüýÏÿ PöûÏùûŸþþ>ßyÿ?sÿßÃE}¾óþ~çÿ¿†·ÞÏÜÿ÷ðÑEo¼ÿŸ¹ÿïá£í÷Ÿó÷?ýü4Q@Ûï?çîûøhû}çüýÏÿ PöûÏùûŸþþ>ßyÿ?sÿßÃE}¾óþ~çÿ¿†·ÞÏÜÿ÷ðÑEo¼ÿŸ¹ÿïá£í÷Ÿó÷?ýü4Q@Ûï?çîûøhû}çüýÏÿ PöûÏùûŸþþ>ßyÿ?sÿßÃE}¾óþ~çÿ¿†·ÞÏÜÿ÷ðÑEo¼ÿŸ¹ÿïá£í÷Ÿó÷?ýü4Q@Ûï?çîûøhû}çüýÏÿ PöûÏùûŸþþ>ßyÿ?sÿßÃE}¾óþ~çÿ¿†·ÞÏÜÿ÷ðÑEo¼ÿŸ¹ÿïá£í÷Ÿó÷?ýü4Q@Ûï?çîûøhû}çüýÏÿ PöûÏùûŸþþ>ßyÿ?sÿßÃE}¾óþ~çÿ¿†·ÞÏÜÿ÷ðÑEo¼ÿŸ¹ÿïá£í÷Ÿó÷?ýü4Q@Ûï?çîûøhû}çüýÏÿ PöûÏùûŸþþ>ßyÿ?sÿßÃE}¾óþ~çÿ¿†·ÞÏÜÿ÷ðÑEG,óO6Y$ÇMìN(¢ŠÿÙscons-doc-2.3.0/doc/user/run.in0000644000175000017500000002645312114661557017113 0ustar dktrkranzdktrkranz XXX
Command-Line Options XXX
Getting at Command-Line Arguments XXX
Selective Builds XXX
Overriding Construction Variables XXX
scons-doc-2.3.0/doc/user/README0000644000175000017500000000117312114661557016627 0ustar dktrkranzdktrkranz# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation When adding a new file, add it to main.xml and MANIFEST. To build the .xml files from the .in files: scons -D BUILDDOC=1 foo.xml To build the whole PDF doc from this dir, for testing: scons -D ../../build/doc/PDF/scons-user.pdf Writing examples: here's a simple template. env = Environment() print env.Dump("CC") scons -Q scons-doc-2.3.0/doc/user/tasks.xml0000644000175000017500000000772712114661557017631 0ustar dktrkranzdktrkranz There is a common set of simple tasks that many build configurations rely on as they become more complex. Most build tools have special purpose constructs for performing these tasks, but since &SConscript; files are &Python; scripts, you can use more flexible built-in &Python; services to perform these tasks. This appendix lists a number of these tasks and how to implement them in &Python; and &SCons;. Wildcard globbing to create a list of filenames files = Glob(wildcard) Filename extension substitution import os.path filename = os.path.splitext(filename)[0]+extension Appending a path prefix to a list of filenames import os.path filenames = [os.path.join(prefix, x) for x in filenames] Substituting a path prefix with another one if filename.find(old_prefix) == 0: filename = filename.replace(old_prefix, new_prefix) Filtering a filename list to exclude/retain only a specific set of extensions import os.path filenames = [x for x in filenames if os.path.splitext(x)[1] in extensions] The "backtick function": run a shell command and capture the output import os output = os.popen(command).read() Generating source code: how code can be generated and used by SCons The Copy builders here could be any arbitrary shell or python function that produces one or more files. This example shows how to create those files and use them in &SCons;. #### SConstruct env = Environment() env.Append(CPPPATH = "#") ## Header example env.Append(BUILDERS = {'Copy1' : Builder(action = 'cat < $SOURCE > $TARGET', suffix='.h', src_suffix='.bar')}) env.Copy1('test.bar') # produces test.h from test.bar. env.Program('app','main.cpp') # indirectly depends on test.bar ## Source file example env.Append(BUILDERS = {'Copy2' : Builder(action = 'cat < $SOURCE > $TARGET', suffix='.cpp', src_suffix='.bar2')}) foo = env.Copy2('foo.bar2') # produces foo.cpp from foo.bar2. env.Program('app2',['main2.cpp'] + foo) # compiles main2.cpp and foo.cpp into app2. Where main.cpp looks like this: #include "test.h" produces this: % scons -Q cc -o app main.cpp cat < foo.bar2 > foo.cpp cc -o app2 main2.cpp foo.cpp cat < test.bar > test.h scons-doc-2.3.0/doc/user/environments.in0000644000175000017500000014327412114661557021037 0ustar dktrkranzdktrkranz An environment is a collection of values that can affect how a program executes. &SCons; distinguishes between three different types of environments that can affect the behavior of &SCons; itself (subject to the configuration in the &SConscript; files), as well as the compilers and other tools it executes: External Environment The external environment is the set of variables in the user's environment at the time the user runs &SCons;. These variables are available within the &SConscript; files through the Python os.environ dictionary. See , below. &ConsEnv; A &consenv; is a distinct object creating within a &SConscript; file and and which contains values that affect how &SCons; decides what action to use to build a target, and even to define which targets should be built from which sources. One of the most powerful features of &SCons; is the ability to create multiple &consenvs;, including the ability to clone a new, customized &consenv; from an existing &consenv;. See , below. Execution Environment An execution environment is the values that &SCons; sets when executing an external command (such as a compiler or linker) to build one or more targets. Note that this is not the same as the external environment (see above). See , below. Unlike &Make;, &SCons; does not automatically copy or import values between different environments (with the exception of explicit clones of &consenvs;, which inherit values from their parent). This is a deliberate design choice to make sure that builds are, by default, repeatable regardless of the values in the user's external environment. This avoids a whole class of problems with builds where a developer's local build works because a custom variable setting causes a different compiler or build option to be used, but the checked-in change breaks the official build because it uses different environment variable settings. Note that the &SConscript; writer can easily arrange for variables to be copied or imported between environments, and this is often very useful (or even downright necessary) to make it easy for developers to customize the build in appropriate ways. The point is not that copying variables between different environments is evil and must always be avoided. Instead, it should be up to the implementer of the build system to make conscious choices about how and when to import a variable from one environment to another, making informed decisions about striking the right balance between making the build repeatable on the one hand and convenient to use on the other.
Using Values From the External Environment The external environment variable settings that the user has in force when executing &SCons; are available through the normal Python os.environ dictionary. This means that you must add an import os statement to any &SConscript; file in which you want to use values from the user's external environment. import os int main() { } More usefully, you can use the os.environ dictionary in your &SConscript; files to initialize &consenvs; with values from the user's external environment. See the next section, , for information on how to do this.
Construction Environments It is rare that all of the software in a large, complicated system needs to be built the same way. For example, different source files may need different options enabled on the command line, or different executable programs need to be linked with different libraries. &SCons; accommodates these different build requirements by allowing you to create and configure multiple &consenvs; that control how the software is built. A &consenv; is an object that has a number of associated &consvars;, each with a name and a value. (A construction environment also has an attached set of &Builder; methods, about which we'll learn more later.)
Creating a &ConsEnv;: the &Environment; Function A &consenv; is created by the &Environment; method: env = Environment() By default, &SCons; initializes every new construction environment with a set of &consvars; based on the tools that it finds on your system, plus the default set of builder methods necessary for using those tools. The construction variables are initialized with values describing the C compiler, the Fortran compiler, the linker, etc., as well as the command lines to invoke them. When you initialize a construction environment you can set the values of the environment's &consvars; to control how a program is built. For example: env = Environment(CC = 'gcc', CCFLAGS = '-O2') env.Program('foo.c') int main() { } The construction environment in this example is still initialized with the same default construction variable values, except that the user has explicitly specified use of the GNU C compiler &gcc;, and further specifies that the -O2 (optimization level two) flag should be used when compiling the object file. In other words, the explicit initializations of &cv-link-CC; and &cv-link-CCFLAGS; override the default values in the newly-created construction environment. So a run from this example would look like: scons -Q
Fetching Values From a &ConsEnv; You can fetch individual construction variables using the normal syntax for accessing individual named items in a Python dictionary: env = Environment() print "CC is:", env['CC'] This example &SConstruct; file doesn't build anything, but because it's actually a Python script, it will print the value of &cv-link-CC; for us: scons -Q A construction environment, however, is actually an object with associated methods, etc. If you want to have direct access to only the dictionary of construction variables, you can fetch this using the &Dictionary; method: env = Environment(FOO = 'foo', BAR = 'bar') dict = env.Dictionary() for key in ['OBJSUFFIX', 'LIBSUFFIX', 'PROGSUFFIX']: print "key = %s, value = %s" % (key, dict[key]) This &SConstruct; file will print the specified dictionary items for us on POSIX systems as follows: scons -Q And on Windows: scons -Q If you want to loop and print the values of all of the construction variables in a construction environment, the Python code to do that in sorted order might look something like: env = Environment() for item in sorted(env.Dictionary().items()): print "construction variable = '%s', value = '%s'" % item
Expanding Values From a &ConsEnv;: the &subst; Method Another way to get information from a construction environment is to use the &subst; method on a string containing $ expansions of construction variable names. As a simple example, the example from the previous section that used env['CC'] to fetch the value of &cv-link-CC; could also be written as: env = Environment() print "CC is:", env.subst('$CC') One advantage of using &subst; to expand strings is that construction variables in the result get re-expanded until there are no expansions left in the string. So a simple fetch of a value like &cv-link-CCCOM;: env = Environment(CCFLAGS = '-DFOO') print "CCCOM is:", env['CCCOM'] Will print the unexpanded value of &cv-CCCOM;, showing us the construction variables that still need to be expanded: % scons -Q CCCOM is: $CC $CCFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS -c -o $TARGET $SOURCES scons: `.' is up to date. Calling the &subst; method on $CCOM, however: env = Environment(CCFLAGS = '-DFOO') print "CCCOM is:", env.subst('$CCCOM') Will recursively expand all of the construction variables prefixed with $ (dollar signs), showing us the final output: % scons -Q CCCOM is: gcc -DFOO -c -o scons: `.' is up to date. Note that because we're not expanding this in the context of building something there are no target or source files for &cv-link-TARGET; and &cv-link-SOURCES; to expand.
Handling Problems With Value Expansion If a problem occurs when expanding a construction variable, by default it is expanded to '' (a null string), and will not cause scons to fail. env = Environment() print "value is:", env.subst( '->$MISSING<-' ) scons -Q This default behaviour can be changed using the &AllowSubstExceptions; function. When a problem occurs with a variable expansion it generates an exception, and the &AllowSubstExceptions; function controls which of these exceptions are actually fatal and which are allowed to occur safely. By default, &NameError; and &IndexError; are the two exceptions that are allowed to occur: so instead of causing scons to fail, these are caught, the variable expanded to '' and scons execution continues. To require that all construction variable names exist, and that indexes out of range are not allowed, call &AllowSubstExceptions; with no extra arguments. AllowSubstExceptions() env = Environment() print "value is:", env.subst( '->$MISSING<-' ) scons -Q This can also be used to allow other exceptions that might occur, most usefully with the ${...} construction variable syntax. For example, this would allow zero-division to occur in a variable expansion in addition to the default exceptions allowed AllowSubstExceptions(IndexError, NameError, ZeroDivisionError) env = Environment() print "value is:", env.subst( '->${1 / 0}<-' ) scons -Q If &AllowSubstExceptions; is called multiple times, each call completely overwrites the previous list of allowed exceptions.
Controlling the Default &ConsEnv;: the &DefaultEnvironment; Function All of the &Builder; functions that we've introduced so far, like &Program; and &Library;, actually use a default &consenv; that contains settings for the various compilers and other tools that &SCons; configures by default, or otherwise knows about and has discovered on your system. The goal of the default construction environment is to make many configurations to "just work" to build software using readily available tools with a minimum of configuration changes. You can, however, control the settings in the default construction environment by using the &DefaultEnvironment; function to initialize various settings: DefaultEnvironment(CC = '/usr/local/bin/gcc') When configured as above, all calls to the &Program; or &Object; Builder will build object files with the /usr/local/bin/gcc compiler. Note that the &DefaultEnvironment; function returns the initialized default construction environment object, which can then be manipulated like any other construction environment. So the following would be equivalent to the previous example, setting the &cv-CC; variable to /usr/local/bin/gcc but as a separate step after the default construction environment has been initialized: env = DefaultEnvironment() env['CC'] = '/usr/local/bin/gcc' One very common use of the &DefaultEnvironment; function is to speed up &SCons; initialization. As part of trying to make most default configurations "just work," &SCons; will actually search the local system for installed compilers and other utilities. This search can take time, especially on systems with slow or networked file systems. If you know which compiler(s) and/or other utilities you want to configure, you can control the search that &SCons; performs by specifying some specific tool modules with which to initialize the default construction environment: env = DefaultEnvironment(tools = ['gcc', 'gnulink'], CC = '/usr/local/bin/gcc') So the above example would tell &SCons; to explicitly configure the default environment to use its normal GNU Compiler and GNU Linker settings (without having to search for them, or any other utilities for that matter), and specifically to use the compiler found at /usr/local/bin/gcc.
Multiple &ConsEnvs; The real advantage of construction environments is that you can create as many different construction environments as you need, each tailored to a different way to build some piece of software or other file. If, for example, we need to build one program with the -O2 flag and another with the -g (debug) flag, we would do this like so: opt = Environment(CCFLAGS = '-O2') dbg = Environment(CCFLAGS = '-g') opt.Program('foo', 'foo.c') dbg.Program('bar', 'bar.c') int main() { } int main() { } scons -Q We can even use multiple construction environments to build multiple versions of a single program. If you do this by simply trying to use the &b-link-Program; builder with both environments, though, like this: opt = Environment(CCFLAGS = '-O2') dbg = Environment(CCFLAGS = '-g') opt.Program('foo', 'foo.c') dbg.Program('foo', 'foo.c') int main() { } Then &SCons; generates the following error: scons -Q This is because the two &b-Program; calls have each implicitly told &SCons; to generate an object file named foo.o, one with a &cv-link-CCFLAGS; value of -O2 and one with a &cv-link-CCFLAGS; value of -g. &SCons; can't just decide that one of them should take precedence over the other, so it generates the error. To avoid this problem, we must explicitly specify that each environment compile foo.c to a separately-named object file using the &b-link-Object; builder, like so: opt = Environment(CCFLAGS = '-O2') dbg = Environment(CCFLAGS = '-g') o = opt.Object('foo-opt', 'foo.c') opt.Program(o) d = dbg.Object('foo-dbg', 'foo.c') dbg.Program(d) int main() { } Notice that each call to the &b-Object; builder returns a value, an internal &SCons; object that represents the object file that will be built. We then use that object as input to the &b-Program; builder. This avoids having to specify explicitly the object file name in multiple places, and makes for a compact, readable &SConstruct; file. Our &SCons; output then looks like: scons -Q
Making Copies of &ConsEnvs;: the &Clone; Method Sometimes you want more than one construction environment to share the same values for one or more variables. Rather than always having to repeat all of the common variables when you create each construction environment, you can use the &Clone; method to create a copy of a construction environment. Like the &Environment; call that creates a construction environment, the &Clone; method takes &consvar; assignments, which will override the values in the copied construction environment. For example, suppose we want to use &gcc; to create three versions of a program, one optimized, one debug, and one with neither. We could do this by creating a "base" construction environment that sets &cv-link-CC; to &gcc;, and then creating two copies, one which sets &cv-link-CCFLAGS; for optimization and the other which sets &cv-CCFLAGS; for debugging: env = Environment(CC = 'gcc') opt = env.Clone(CCFLAGS = '-O2') dbg = env.Clone(CCFLAGS = '-g') env.Program('foo', 'foo.c') o = opt.Object('foo-opt', 'foo.c') opt.Program(o) d = dbg.Object('foo-dbg', 'foo.c') dbg.Program(d) int main() { } Then our output would look like: scons -Q
Replacing Values: the &Replace; Method You can replace existing construction variable values using the &Replace; method: env = Environment(CCFLAGS = '-DDEFINE1') env.Replace(CCFLAGS = '-DDEFINE2') env.Program('foo.c') int main() { } The replacing value (-DDEFINE2 in the above example) completely replaces the value in the construction environment: scons -Q You can safely call &Replace; for construction variables that don't exist in the construction environment: env = Environment() env.Replace(NEW_VARIABLE = 'xyzzy') print "NEW_VARIABLE =", env['NEW_VARIABLE'] In this case, the construction variable simply gets added to the construction environment: scons -Q Because the variables aren't expanded until the construction environment is actually used to build the targets, and because &SCons; function and method calls are order-independent, the last replacement "wins" and is used to build all targets, regardless of the order in which the calls to Replace() are interspersed with calls to builder methods: env = Environment(CCFLAGS = '-DDEFINE1') print "CCFLAGS =", env['CCFLAGS'] env.Program('foo.c') env.Replace(CCFLAGS = '-DDEFINE2') print "CCFLAGS =", env['CCFLAGS'] env.Program('bar.c') int main() { } int main() { } The timing of when the replacement actually occurs relative to when the targets get built becomes apparent if we run &scons; without the -Q option: scons Because the replacement occurs while the &SConscript; files are being read, the &cv-link-CCFLAGS; variable has already been set to -DDEFINE2 by the time the &foo_o; target is built, even though the call to the &Replace; method does not occur until later in the &SConscript; file.
Setting Values Only If They're Not Already Defined: the &SetDefault; Method Sometimes it's useful to be able to specify that a construction variable should be set to a value only if the construction environment does not already have that variable defined You can do this with the &SetDefault; method, which behaves similarly to the set_default method of Python dictionary objects: env.SetDefault(SPECIAL_FLAG = '-extra-option') This is especially useful when writing your own Tool modules to apply variables to construction environments.
Appending to the End of Values: the &Append; Method You can append a value to an existing construction variable using the &Append; method: env = Environment(CCFLAGS = ['-DMY_VALUE']) env.Append(CCFLAGS = ['-DLAST']) env.Program('foo.c') int main() { } &SCons; then supplies both the -DMY_VALUE and -DLAST flags when compiling the object file: scons -Q If the construction variable doesn't already exist, the &Append; method will create it: env = Environment() env.Append(NEW_VARIABLE = 'added') print "NEW_VARIABLE =", env['NEW_VARIABLE'] Which yields: scons -Q Note that the &Append; function tries to be "smart" about how the new value is appended to the old value. If both are strings, the previous and new strings are simply concatenated. Similarly, if both are lists, the lists are concatenated. If, however, one is a string and the other is a list, the string is added as a new element to the list.
Appending Unique Values: the &AppendUnique; Method Some times it's useful to add a new value only if the existing construction variable doesn't already contain the value. This can be done using the &AppendUnique; method: env.AppendUnique(CCFLAGS=['-g']) In the above example, the -g would be added only if the &cv-CCFLAGS; variable does not already contain a -g value.
Appending to the Beginning of Values: the &Prepend; Method You can append a value to the beginning of an existing construction variable using the &Prepend; method: env = Environment(CCFLAGS = ['-DMY_VALUE']) env.Prepend(CCFLAGS = ['-DFIRST']) env.Program('foo.c') int main() { } &SCons; then supplies both the -DFIRST and -DMY_VALUE flags when compiling the object file: scons -Q If the construction variable doesn't already exist, the &Prepend; method will create it: env = Environment() env.Prepend(NEW_VARIABLE = 'added') print "NEW_VARIABLE =", env['NEW_VARIABLE'] Which yields: scons -Q Like the &Append; function, the &Prepend; function tries to be "smart" about how the new value is appended to the old value. If both are strings, the previous and new strings are simply concatenated. Similarly, if both are lists, the lists are concatenated. If, however, one is a string and the other is a list, the string is added as a new element to the list.
Prepending Unique Values: the &PrependUnique; Method Some times it's useful to add a new value to the beginning of a construction variable only if the existing value doesn't already contain the to-be-added value. This can be done using the &PrependUnique; method: env.PrependUnique(CCFLAGS=['-g']) In the above example, the -g would be added only if the &cv-CCFLAGS; variable does not already contain a -g value.
Controlling the Execution Environment for Issued Commands When &SCons; builds a target file, it does not execute the commands with the same external environment that you used to execute &SCons;. Instead, it uses the dictionary stored in the &cv-link-ENV; construction variable as the external environment for executing commands. The most important ramification of this behavior is that the &PATH; environment variable, which controls where the operating system will look for commands and utilities, is not the same as in the external environment from which you called &SCons;. This means that &SCons; will not, by default, necessarily find all of the tools that you can execute from the command line. The default value of the &PATH; environment variable on a POSIX system is /usr/local/bin:/bin:/usr/bin. The default value of the &PATH; environment variable on a Windows system comes from the Windows registry value for the command interpreter. If you want to execute any commands--compilers, linkers, etc.--that are not in these default locations, you need to set the &PATH; value in the &cv-ENV; dictionary in your construction environment. The simplest way to do this is to initialize explicitly the value when you create the construction environment; this is one way to do that: path = ['/usr/local/bin', '/bin', '/usr/bin'] env = Environment(ENV = {'PATH' : path}) Assign a dictionary to the &cv-ENV; construction variable in this way completely resets the external environment so that the only variable that will be set when external commands are executed will be the &PATH; value. If you want to use the rest of the values in &cv-ENV; and only set the value of &PATH;, the most straightforward way is probably: env['ENV']['PATH'] = ['/usr/local/bin', '/bin', '/usr/bin'] Note that &SCons; does allow you to define the directories in the &PATH; in a string, separated by the pathname-separator character for your system (':' on POSIX systems, ';' on Windows): env['ENV']['PATH'] = '/usr/local/bin:/bin:/usr/bin' But doing so makes your &SConscript; file less portable, (although in this case that may not be a huge concern since the directories you list are likley system-specific, anyway).
Propagating &PATH; From the External Environment You may want to propagate the external &PATH; to the execution environment for commands. You do this by initializing the &PATH; variable with the &PATH; value from the os.environ dictionary, which is Python's way of letting you get at the external environment: import os env = Environment(ENV = {'PATH' : os.environ['PATH']}) Alternatively, you may find it easier to just propagate the entire external environment to the execution environment for commands. This is simpler to code than explicity selecting the &PATH; value: import os env = Environment(ENV = os.environ) Either of these will guarantee that &SCons; will be able to execute any command that you can execute from the command line. The drawback is that the build can behave differently if it's run by people with different &PATH; values in their environment--for example, if both the /bin and /usr/local/bin directories have different &cc; commands, then which one will be used to compile programs will depend on which directory is listed first in the user's &PATH; variable.
Adding to <varname>PATH</varname> Values in the Execution Environment One of the most common requirements for manipulating a variable in the execution environment is to add one or more custom directories to a search like the $PATH variable on Linux or POSIX systems, or the %PATH% variable on Windows, so that a locally-installed compiler or other utility can be found when &SCons; tries to execute it to update a target. &SCons; provides &PrependENVPath; and &AppendENVPath; functions to make adding things to execution variables convenient. You call these functions by specifying the variable to which you want the value added, and then value itself. So to add some /usr/local directories to the $PATH and $LIB variables, you might: env = Environment(ENV = os.environ) env.PrependENVPath('PATH', '/usr/local/bin') env.AppendENVPath('LIB', '/usr/local/lib') Note that the added values are strings, and if you want to add multiple directories to a variable like $PATH, you must include the path separate character (: on Linux or POSIX, ; on Windows) in the string.
scons-doc-2.3.0/doc/user/factories.in0000644000175000017500000003215312114661557020260 0ustar dktrkranzdktrkranz &SCons; provides a number of platform-independent functions, called factories, that perform common file system manipulations like copying, moving or deleting files and directories, or making directories. These functions are factories because they don't perform the action at the time they're called, they each return an &Action; object that can be executed at the appropriate time.
Copying Files or Directories: The &Copy; Factory Suppose you want to arrange to make a copy of a file, and don't have a suitable pre-existing builder. Unfortunately, in the early days of SCons design, we used the name &Copy; for the function that returns a copy of the environment, otherwise that would be the logical choice for a Builder that copies a file or directory tree to a target location. One way would be to use the &Copy; action factory in conjunction with the &Command; builder: Command("file.out", "file.in", Copy("$TARGET", "$SOURCE")) file.in Notice that the action returned by the &Copy; factory will expand the &cv-link-TARGET; and &cv-link-SOURCE; strings at the time &file_out; is built, and that the order of the arguments is the same as that of a builder itself--that is, target first, followed by source: scons -Q You can, of course, name a file explicitly instead of using &cv-TARGET; or &cv-SOURCE;: Command("file.out", [], Copy("$TARGET", "file.in")) file.in Which executes as: scons -Q The usefulness of the &Copy; factory becomes more apparent when you use it in a list of actions passed to the &Command; builder. For example, suppose you needed to run a file through a utility that only modifies files in-place, and can't "pipe" input to output. One solution is to copy the source file to a temporary file name, run the utility, and then copy the modified temporary file to the target, which the &Copy; factory makes extremely easy: Command("file.out", "file.in", [ Copy("tempfile", "$SOURCE"), "modify tempfile", Copy("$TARGET", "tempfile"), ]) env = DefaultEnvironment() import os env['ENV']['PATH'] = env['ENV']['PATH'] + os.pathsep + os.getcwd() SConscript('S') file.in touch $* The output then looks like: scons -Q
Deleting Files or Directories: The &Delete; Factory If you need to delete a file, then the &Delete; factory can be used in much the same way as the &Copy; factory. For example, if we want to make sure that the temporary file in our last example doesn't exist before we copy to it, we could add &Delete; to the beginning of the command list: Command("file.out", "file.in", [ Delete("tempfile"), Copy("tempfile", "$SOURCE"), "modify tempfile", Copy("$TARGET", "tempfile"), ]) env = DefaultEnvironment() import os env['ENV']['PATH'] = env['ENV']['PATH'] + os.pathsep + os.getcwd() SConscript('S') file.in touch $* Which then executes as follows: scons -Q Of course, like all of these &Action; factories, the &Delete; factory also expands &cv-link-TARGET; and &cv-link-SOURCE; variables appropriately. For example: Command("file.out", "file.in", [ Delete("$TARGET"), Copy("$TARGET", "$SOURCE") ]) file.in Executes as: scons -Q Note, however, that you typically don't need to call the &Delete; factory explicitly in this way; by default, &SCons; deletes its target(s) for you before executing any action. One word of caution about using the &Delete; factory: it has the same variable expansions available as any other factory, including the &cv-SOURCE; variable. Specifying Delete("$SOURCE") is not something you usually want to do!
Moving (Renaming) Files or Directories: The &Move; Factory The &Move; factory allows you to rename a file or directory. For example, if we don't want to copy the temporary file, we could use: Command("file.out", "file.in", [ Copy("tempfile", "$SOURCE"), "modify tempfile", Move("$TARGET", "tempfile"), ]) env = DefaultEnvironment() import os env['ENV']['PATH'] = env['ENV']['PATH'] + os.pathsep + os.getcwd() SConscript('S') file.in touch $* Which would execute as: scons -Q
Updating the Modification Time of a File: The &Touch; Factory If you just need to update the recorded modification time for a file, use the &Touch; factory: Command("file.out", "file.in", [ Copy("$TARGET", "$SOURCE"), Touch("$TARGET"), ]) env = DefaultEnvironment() import os env['ENV']['PATH'] = env['ENV']['PATH'] + os.pathsep + os.getcwd() SConscript('S') file.in Which executes as: scons -Q
Creating a Directory: The &Mkdir; Factory If you need to create a directory, use the &Mkdir; factory. For example, if we need to process a file in a temporary directory in which the processing tool will create other files that we don't care about, you could use: Command("file.out", "file.in", [ Delete("tempdir"), Mkdir("tempdir"), Copy("tempdir/${SOURCE.file}", "$SOURCE"), "process tempdir", Move("$TARGET", "tempdir/output_file"), Delete("tempdir"), ]) env = DefaultEnvironment() import os env['ENV']['PATH'] = env['ENV']['PATH'] + os.pathsep + os.getcwd() SConscript('S') file.in touch $* Which executes as: scons -Q
Changing File or Directory Permissions: The &Chmod; Factory To change permissions on a file or directory, use the &Chmod; factory. The permission argument uses POSIX-style permission bits and should typically be expressed as an octal, not decimal, number: Command("file.out", "file.in", [ Copy("$TARGET", "$SOURCE"), Chmod("$TARGET", 0755), ]) file.in Which executes: scons -Q
Executing an action immediately: the &Execute; Function We've been showing you how to use &Action; factories in the &Command; function. You can also execute an &Action; returned by a factory (or actually, any &Action;) at the time the &SConscript; file is read by using the &Execute; function. For example, if we need to make sure that a directory exists before we build any targets, Execute(Mkdir('__ROOT__/tmp/my_temp_directory')) Notice that this will create the directory while the &SConscript; file is being read: scons If you're familiar with Python, you may wonder why you would want to use this instead of just calling the native Python os.mkdir() function. The advantage here is that the &Mkdir; action will behave appropriately if the user specifies the &SCons; or options--that is, it will print the action but not actually make the directory when is specified, or make the directory but not print the action when is specified. The &Execute; function returns the exit status or return value of the underlying action being executed. It will also print an error message if the action fails and returns a non-zero value. &SCons; will not, however, actually stop the build if the action fails. If you want the build to stop in response to a failure in an action called by &Execute;, you must do so by explicitly checking the return value and calling the &Exit; function (or a Python equivalent): if Execute(Mkdir('__ROOT__/tmp/my_temp_directory')): # A problem occurred while making the temp directory. Exit(1)
scons-doc-2.3.0/doc/user/variants.in0000644000175000017500000001157412114661557020134 0ustar dktrkranzdktrkranz The &variant_dir; keyword argument of the &SConscript; function provides everything we need to show how easy it is to create variant builds using &SCons;. Suppose, for example, that we want to build a program for both Windows and Linux platforms, but that we want to build it in a shared directory with separate side-by-side build directories for the Windows and Linux versions of the program. platform = ARGUMENTS.get('OS', Platform()) include = "#export/$PLATFORM/include" lib = "#export/$PLATFORM/lib" bin = "#export/$PLATFORM/bin" env = Environment(PLATFORM = platform, BINDIR = bin, INCDIR = include, LIBDIR = lib, CPPPATH = [include], LIBPATH = [lib], LIBS = 'world') Export('env') env.SConscript('src/SConscript', variant_dir='build/$PLATFORM') Import('env') SConscript('hello/SConscript') SConscript('world/SConscript') Import('env') hello = env.Program('hello.c') env.Install('$BINDIR', hello) #include "world.h" int main(int argc, char *argv[]) { printf "hello.c\n"; world(); } Import('env') world = env.Library('world.c') env.Install('$LIBDIR', world) env.Install('$INCDIR', 'world.h') #define STRING "world.h" extern int world(); int world() { printf "world.c\n"; } This SConstruct file, when run on a Linux system, yields: scons -Q OS=linux The same SConstruct file on Windows would build: scons -Q OS=windows scons-doc-2.3.0/doc/user/output.in0000644000175000017500000004700712114661557017645 0ustar dktrkranzdktrkranz A key aspect of creating a usable build configuration is providing good output from the build so its users can readily understand what the build is doing and get information about how to control the build. &SCons; provides several ways of controlling output from the build configuration to help make the build more useful and understandable.
Providing Build Help: the &Help; Function It's often very useful to be able to give users some help that describes the specific targets, build options, etc., that can be used for your build. &SCons; provides the &Help; function to allow you to specify this help text: Help(""" Type: 'scons program' to build the production program, 'scons debug' to build the debug version. """) (Note the above use of the Python triple-quote syntax, which comes in very handy for specifying multi-line strings like help text.) When the &SConstruct; or &SConscript; files contain such a call to the &Help; function, the specified help text will be displayed in response to the &SCons; -h option: scons -h The &SConscript; files may contain multiple calls to the &Help; function, in which case the specified text(s) will be concatenated when displayed. This allows you to split up the help text across multiple &SConscript; files. In this situation, the order in which the &SConscript; files are called will determine the order in which the &Help; functions are called, which will determine the order in which the various bits of text will get concatenated. Another use would be to make the help text conditional on some variable. For example, suppose you only want to display a line about building a Windows-only version of a program when actually run on Windows. The following &SConstruct; file: env = Environment() Help("\nType: 'scons program' to build the production program.\n") if env['PLATFORM'] == 'win32': Help("\nType: 'scons windebug' to build the Windows debug version.\n") Will display the complete help text on Windows: scons -h But only show the relevant option on a Linux or UNIX system: scons -h If there is no &Help; text in the &SConstruct; or &SConscript; files, &SCons; will revert to displaying its standard list that describes the &SCons; command-line options. This list is also always displayed whenever the -H option is used.
Controlling How &SCons; Prints Build Commands: the <envar>$*COMSTR</envar> Variables Sometimes the commands executed to compile object files or link programs (or build other targets) can get very long, long enough to make it difficult for users to distinguish error messages or other important build output from the commands themselves. All of the default $*COM variables that specify the command lines used to build various types of target files have a corresponding $*COMSTR variable that can be set to an alternative string that will be displayed when the target is built. For example, suppose you want to have &SCons; display a "Compiling" message whenever it's compiling an object file, and a "Linking" when it's linking an executable. You could write a &SConstruct; file that looks like: env = Environment(CCCOMSTR = "Compiling $TARGET", LINKCOMSTR = "Linking $TARGET") env.Program('foo.c') foo.c Which would then yield the output: % scons -Q Compiling foo.o Linking foo &SCons; performs complete variable substitution on $*COMSTR variables, so they have access to all of the standard variables like &cv-TARGET; &cv-SOURCES;, etc., as well as any construction variables that happen to be configured in the construction environment used to build a specific target. Of course, sometimes it's still important to be able to see the exact command that &SCons; will execute to build a target. For example, you may simply need to verify that &SCons; is configured to supply the right options to the compiler, or a developer may want to cut-and-paste a compile command to add a few options for a custom test. One common way to give users control over whether or not &SCons; should print the actual command line or a short, configured summary is to add support for a VERBOSE command-line variable to your &SConstruct; file. A simple configuration for this might look like: env = Environment() if ARGUMENTS.get('VERBOSE') != "1': env['CCCOMSTR'] = "Compiling $TARGET" env['LINKCOMSTR'] = "Linking $TARGET" env.Program('foo.c') foo.c By only setting the appropriate $*COMSTR variables if the user specifies VERBOSE=1 on the command line, the user has control over how &SCons; displays these particular command lines: % scons -Q Compiling foo.o Linking foo % scons -Q -c Removed foo.o Removed foo % scons -Q VERBOSE=1 cc -o foo.o -c foo.c cc -o foo foo.o
Providing Build Progress Output: the &Progress; Function Another aspect of providing good build output is to give the user feedback about what &SCons; is doing even when nothing is being built at the moment. This can be especially true for large builds when most of the targets are already up-to-date. Because &SCons; can take a long time making absolutely sure that every target is, in fact, up-to-date with respect to a lot of dependency files, it can be easy for users to mistakenly conclude that &SCons; is hung or that there is some other problem with the build. One way to deal with this perception is to configure &SCons; to print something to let the user know what it's "thinking about." The &Progress; function allows you to specify a string that will be printed for every file that &SCons; is "considering" while it is traversing the dependency graph to decide what targets are or are not up-to-date. Progress('Evaluating $TARGET\n') Program('f1.c') Program('f2.c') f1.c f2.c Note that the &Progress; function does not arrange for a newline to be printed automatically at the end of the string (as does the Python print statement), and we must specify the \n that we want printed at the end of the configured string. This configuration, then, will have &SCons; print that it is Evaluating each file that it encounters in turn as it traverses the dependency graph: scons -Q Of course, normally you don't want to add all of these additional lines to your build output, as that can make it difficult for the user to find errors or other important messages. A more useful way to display this progress might be to have the file names printed directly to the user's screen, not to the same standard output stream where build output is printed, and to use a carriage return character (\r) so that each file name gets re-printed on the same line. Such a configuration would look like: Progress('$TARGET\r', file=open('/dev/tty', 'w'), overwrite=True) Program('f1.c') Program('f2.c') Note that we also specified the overwrite=True argument to the &Progress; function, which causes &SCons; to "wipe out" the previous string with space characters before printing the next &Progress; string. Without the overwrite=True argument, a shorter file name would not overwrite all of the charactes in a longer file name that precedes it, making it difficult to tell what the actual file name is on the output. Also note that we opened up the /dev/tty file for direct access (on POSIX) to the user's screen. On Windows, the equivalent would be to open the con: file name. Also, it's important to know that although you can use $TARGET to substitute the name of the node in the string, the &Progress; function does not perform general variable substitution (because there's not necessarily a construction environment involved in evaluating a node like a source file, for example). You can also specify a list of strings to the &Progress; function, in which case &SCons; will display each string in turn. This can be used to implement a "spinner" by having &SCons; cycle through a sequence of strings: Progress(['-\r', '\\\r', '|\r', '/\r'], interval=5) Program('f1.c') Program('f2.c') Note that here we have also used the interval= keyword argument to have &SCons; only print a new "spinner" string once every five evaluated nodes. Using an interval= count, even with strings that use $TARGET like our examples above, can be a good way to lessen the work that &SCons; expends printing &Progress; strings, while still giving the user feedback that indicates &SCons; is still working on evaluating the build. Lastly, you can have direct control over how to print each evaluated node by passing a Python function (or other Python callable) to the &Progress; function. Your function will be called for each evaluated node, allowing you to implement more sophisticated logic like adding a counter: screen = open('/dev/tty', 'w') count = 0 def progress_function(node) count += 1 screen.write('Node %4d: %s\r' % (count, node)) Progress(progress_function) Of course, if you choose, you could completely ignore the node argument to the function, and just print a count, or anything else you wish. (Note that there's an obvious follow-on question here: how would you find the total number of nodes that will be evaluated so you can tell the user how close the build is to finishing? Unfortunately, in the general case, there isn't a good way to do that, short of having &SCons; evaluate its dependency graph twice, first to count the total and the second time to actually build the targets. This would be necessary because you can't know in advance which target(s) the user actually requested to be built. The entire build may consist of thousands of Nodes, for example, but maybe the user specifically requested that only a single object file be built.)
Printing Detailed Build Status: the &GetBuildFailures; Function SCons, like most build tools, returns zero status to the shell on success and nonzero status on failure. Sometimes it's useful to give more information about the build status at the end of the run, for instance to print an informative message, send an email, or page the poor slob who broke the build. SCons provides a &GetBuildFailures; method that you can use in a python atexit function to get a list of objects describing the actions that failed while attempting to build targets. There can be more than one if you're using -j. Here's a simple example: import atexit def print_build_failures(): from SCons.Script import GetBuildFailures for bf in GetBuildFailures(): print "%s failed: %s" % (bf.node, bf.errstr) atexit.register(print_build_failures) The atexit.register call registers print_build_failures as an atexit callback, to be called before &SCons; exits. When that function is called, it calls &GetBuildFailures; to fetch the list of failed objects. See the man page for the detailed contents of the returned objects; some of the more useful attributes are .node, .errstr, .filename, and .command. The filename is not necessarily the same file as the node; the node is the target that was being built when the error occurred, while the filenameis the file or dir that actually caused the error. Note: only call &GetBuildFailures; at the end of the build; calling it at any other time is undefined. Here is a more complete example showing how to turn each element of &GetBuildFailures; into a string: # Make the build fail if we pass fail=1 on the command line if ARGUMENTS.get('fail', 0): Command('target', 'source', ['/bin/false']) def bf_to_str(bf): """Convert an element of GetBuildFailures() to a string in a useful way.""" import SCons.Errors if bf is None: # unknown targets product None in list return '(unknown tgt)' elif isinstance(bf, SCons.Errors.StopError): return str(bf) elif bf.node: return str(bf.node) + ': ' + bf.errstr elif bf.filename: return bf.filename + ': ' + bf.errstr return 'unknown failure: ' + bf.errstr import atexit def build_status(): """Convert the build status to a 2-tuple, (status, msg).""" from SCons.Script import GetBuildFailures bf = GetBuildFailures() if bf: # bf is normally a list of build failures; if an element is None, # it's because of a target that scons doesn't know anything about. status = 'failed' failures_message = "\n".join(["Failed building %s" % bf_to_str(x) for x in bf if x is not None]) else: # if bf is None, the build completed successfully. status = 'ok' failures_message = '' return (status, failures_message) def display_build_status(): """Display the build status. Called by atexit. Here you could do all kinds of complicated things.""" status, failures_message = build_status() if status == 'failed': print "FAILED!!!!" # could display alert, ring bell, etc. elif status == 'ok': print "Build succeeded." print failures_message atexit.register(display_build_status) When this runs, you'll see the appropriate output: scons -Q scons -Q fail=1
scons-doc-2.3.0/doc/user/parseconfig.in0000644000175000017500000001061312114661557020576 0ustar dktrkranzdktrkranz Configuring the right options to build programs to work with libraries--especially shared libraries--that are available on POSIX systems can be very complicated. To help this situation, various utilies with names that end in config return the command-line options for the GNU Compiler Collection (GCC) that are needed to use these libraries; for example, the command-line options to use a library named lib would be found by calling a utility named lib-config. A more recent convention is that these options are available from the generic pkg-config program, which has common framework, error handling, and the like, so that all the package creator has to do is provide the set of strings for his particular package. &SCons; construction environments have a &ParseConfig; method that executes a *config utility (either pkg-config or a more specific utility) and configures the appropriate construction variables in the environment based on the command-line options returned by the specified command. env = Environment() env['CPPPATH'] = ['/lib/compat'] env.ParseConfig("pkg-config x11 --cflags --libs") print env['CPPPATH'] &SCons; will execute the specified command string, parse the resultant flags, and add the flags to the appropriate environment variables. % scons -Q ['/lib/compat', '/usr/X11/include'] scons: `.' is up to date. In the example above, &SCons; has added the include directory to CPPPATH. (Depending upon what other flags are emitted by the pkg-config command, other variables may have been extended as well.) Note that the options are merged with existing options using the &MergeFlags; method, so that each option only occurs once in the construction variable: env = Environment() env.ParseConfig("pkg-config x11 --cflags --libs") env.ParseConfig("pkg-config x11 --cflags --libs") print env['CPPPATH'] % scons -Q ['/usr/X11/include'] scons: `.' is up to date. scons-doc-2.3.0/doc/user/main.xml0000644000175000017500000002461012114661557017416 0ustar dktrkranzdktrkranz %version; %scons; %builders-mod; %functions-mod; %tools-mod; %variables-mod; ]> SCons User Guide &buildversion; Steven Knight Revision &buildrevision; (&builddate;) 2004, 2005, 2006, 2007, 2008, 2009, 2010 2004, 2005, 2006, 2007, 2008, 2009, 2010 Steven Knight ©right; version &buildversion; Preface &preface; Building and Installing &SCons; &build-install; Simple Builds &simple; Less Simple Things to Do With Builds &less-simple; Building and Linking with Libraries &libraries; Node Objects &nodes; Dependencies &depends; Environments &environments; Automatically Putting Command-line Options into their Construction Variables This chapter describes the &MergeFlags;, &ParseFlags;, and &ParseConfig; methods of a &consenv;.
Merging Options into the Environment: the &MergeFlags; Function &mergeflags;
Separating Compile Arguments into their Variables: the &ParseFlags; Function &parseflags;
Finding Installed Library Information: the &ParseConfig; Function &parseconfig;
Controlling Build Output &output; Controlling a Build From the Command Line &command-line; Installing Files in Other Directories: the &Install; Builder &install; Platform-Independent File System Manipulation &factories; Controlling Removal of Targets &file-removal; Hierarchical Builds &hierarchy; Separating Source and Build Directories &separate; Variant Builds &variants; Internationalization and localization with gettext &gettext; Writing Your Own Builders &builders-writing; Not Writing a Builder: the &Command; Builder &builders-commands; Pseudo-Builders: the AddMethod function &add-method; Writing Scanners &scanners; Building From Code Repositories &repositories; Multi-Platform Configuration (&Autoconf; Functionality) &sconf; Caching Built Files &caching; Alias Targets &alias; Java Builds &java; Miscellaneous Functionality &misc; Troubleshooting &troubleshoot; Construction Variables &variables-xml; Builders &builders; Tools &tools; Functions and Environment Methods &functions; Handling Common Tasks &tasks;
scons-doc-2.3.0/doc/user/java.in0000644000175000017500000004275512114661557017233 0ustar dktrkranzdktrkranz So far, we've been using examples of building C and C++ programs to demonstrate the features of &SCons;. &SCons; also supports building Java programs, but Java builds are handled slightly differently, which reflects the ways in which the Java compiler and tools build programs differently than other languages' tool chains.
Building Java Class Files: the &b-Java; Builder The basic activity when programming in Java, of course, is to take one or more .java files containing Java source code and to call the Java compiler to turn them into one or more .class files. In &SCons;, you do this by giving the &b-link-Java; Builder a target directory in which to put the .class files, and a source directory that contains the .java files: Java('classes', 'src') public class Example1 { public static void main(String[] args) { System.out.println("Hello Java world!\n"); } } public class Example2 { public static void main(String[] args) { System.out.println("Hello Java world!\n"); } } public class Example3 { public static void main(String[] args) { System.out.println("Hello Java world!\n"); } } If the src directory contains three .java source files, then running &SCons; might look like this: scons -Q &SCons; will actually search the src directory tree for all of the .java files. The Java compiler will then create the necessary class files in the classes subdirectory, based on the class names found in the .java files.
How &SCons; Handles Java Dependencies In addition to searching the source directory for .java files, &SCons; actually runs the .java files through a stripped-down Java parser that figures out what classes are defined. In other words, &SCons; knows, without you having to tell it, what .class files will be produced by the &javac; call. So our one-liner example from the preceding section: Java('classes', 'src') public class Example1 { public static void main(String[] args) { System.out.println("Hello Java world!\n"); } } public class AdditionalClass1 { public static void main(String[] args) { System.out.println("Hello Java world!\n"); } } public class Example2 { class Inner2 { public static void main(String[] args) { System.out.println("Hello Java world!\n"); } } } public class Example3 { public static void main(String[] args) { System.out.println("Hello Java world!\n"); } } public class AdditionalClass3 { public static void main(String[] args) { System.out.println("Hello Java world!\n"); } } Will not only tell you reliably that the .class files in the classes subdirectory are up-to-date: scons -Q scons -Q classes But it will also remove all of the generated .class files, even for inner classes, without you having to specify them manually. For example, if our Example1.java and Example3.java files both define additional classes, and the class defined in Example2.java has an inner class, running scons -c will clean up all of those .class files as well: scons -Q scons -Q -c classes To ensure correct handling of .class dependencies in all cases, you need to tell &SCons; which Java version is being used. This is needed because Java 1.5 changed the .class file names for nested anonymous inner classes. Use the JAVAVERSION construction variable to specify the version in use. With Java 1.6, the one-liner example can then be defined like this: Java('classes', 'src', JAVAVERSION='1.6') See JAVAVERSION in the man page for more information.
Building Java Archive (<filename>.jar</filename>) Files: the &b-Jar; Builder After building the class files, it's common to collect them into a Java archive (.jar) file, which you do by calling the &b-link-Jar; Builder method. If you want to just collect all of the class files within a subdirectory, you can just specify that subdirectory as the &b-Jar; source: Java(target = 'classes', source = 'src') Jar(target = 'test.jar', source = 'classes') public class Example1 { public static void main(String[] args) { System.out.println("Hello Java world!\n"); } } public class Example2 { public static void main(String[] args) { System.out.println("Hello Java world!\n"); } } public class Example3 { public static void main(String[] args) { System.out.println("Hello Java world!\n"); } } &SCons; will then pass that directory to the &jar; command, which will collect all of the underlying .class files: scons -Q If you want to keep all of the .class files for multiple programs in one location, and only archive some of them in each .jar file, you can pass the &b-Jar; builder a list of files as its source. It's extremely simple to create multiple .jar files this way, using the lists of target class files created by calls to the &b-link-Java; builder as sources to the various &b-Jar; calls: prog1_class_files = Java(target = 'classes', source = 'prog1') prog2_class_files = Java(target = 'classes', source = 'prog2') Jar(target = 'prog1.jar', source = prog1_class_files) Jar(target = 'prog2.jar', source = prog2_class_files) public class Example1 { public static void main(String[] args) { System.out.println("Hello Java world!\n"); } } public class Example2 { public static void main(String[] args) { System.out.println("Hello Java world!\n"); } } public class Example3 { public static void main(String[] args) { System.out.println("Hello Java world!\n"); } } public class Example4 { public static void main(String[] args) { System.out.println("Hello Java world!\n"); } } This will then create prog1.jar and prog2.jar next to the subdirectories that contain their .java files: scons -Q
Building C Header and Stub Files: the &b-JavaH; Builder You can generate C header and source files for implementing native methods, by using the &b-link-JavaH; Builder. There are several ways of using the &JavaH; Builder. One typical invocation might look like: classes = Java(target = 'classes', source = 'src/pkg/sub') JavaH(target = 'native', source = classes) package pkg.sub; public class Example1 { public static void main(String[] args) { } } package pkg.sub; public class Example2 { public static void main(String[] args) { } } package pkg.sub; public class Example3 { public static void main(String[] args) { } } The source is a list of class files generated by the call to the &b-link-Java; Builder, and the target is the output directory in which we want the C header files placed. The target gets converted into the when &SCons; runs &javah;: scons -Q In this case, the call to &javah; will generate the header files native/pkg_sub_Example1.h, native/pkg_sub_Example2.h and native/pkg_sub_Example3.h. Notice that &SCons; remembered that the class files were generated with a target directory of classes, and that it then specified that target directory as the option to the call to &javah;. Although it's more convenient to use the list of class files returned by the &b-Java; Builder as the source of a call to the &b-JavaH; Builder, you can specify the list of class files by hand, if you prefer. If you do, you need to set the &cv-link-JAVACLASSDIR; construction variable when calling &b-JavaH;: Java(target = 'classes', source = 'src/pkg/sub') class_file_list = ['classes/pkg/sub/Example1.class', 'classes/pkg/sub/Example2.class', 'classes/pkg/sub/Example3.class'] JavaH(target = 'native', source = class_file_list, JAVACLASSDIR = 'classes') package pkg.sub; public class Example1 { public static void main(String[] args) { } } package pkg.sub; public class Example2 { public static void main(String[] args) { } } package pkg.sub; public class Example3 { public static void main(String[] args) { } } The &cv-JAVACLASSDIR; value then gets converted into the when &SCons; runs &javah;: scons -Q Lastly, if you don't want a separate header file generated for each source file, you can specify an explicit File Node as the target of the &b-JavaH; Builder: classes = Java(target = 'classes', source = 'src/pkg/sub') JavaH(target = File('native.h'), source = classes) package pkg.sub; public class Example1 { public static void main(String[] args) { } } package pkg.sub; public class Example2 { public static void main(String[] args) { } } package pkg.sub; public class Example3 { public static void main(String[] args) { } } Because &SCons; assumes by default that the target of the &b-JavaH; builder is a directory, you need to use the &File; function to make sure that &SCons; doesn't create a directory named native.h. When a file is used, though, &SCons; correctly converts the file name into the &javah; option: scons -Q
Building RMI Stub and Skeleton Class Files: the &b-RMIC; Builder You can generate Remote Method Invocation stubs by using the &b-link-RMIC; Builder. The source is a list of directories, typically returned by a call to the &b-link-Java; Builder, and the target is an output directory where the _Stub.class and _Skel.class files will be placed: classes = Java(target = 'classes', source = 'src/pkg/sub') RMIC(target = 'outdir', source = classes) package pkg.sub; public class Example1 { public static void main(String[] args) { } } package pkg.sub; public class Example2 { public static void main(String[] args) { } } As it did with the &b-link-JavaH; Builder, &SCons; remembers the class directory and passes it as the option to &rmic;: scons -Q This example would generate the files outdir/pkg/sub/Example1_Skel.class, outdir/pkg/sub/Example1_Stub.class, outdir/pkg/sub/Example2_Skel.class and outdir/pkg/sub/Example2_Stub.class.
scons-doc-2.3.0/doc/developer/0000755000175000017500000000000012114661557016754 5ustar dktrkranzdktrkranzscons-doc-2.3.0/doc/developer/packaging.xml0000644000175000017500000000242012114661557021420 0ustar dktrkranzdktrkranz XXX
Packaging XXX
scons-doc-2.3.0/doc/developer/preface.xml0000644000175000017500000001165012114661557021106 0ustar dktrkranzdktrkranz This document assumes that you already know how &SCons; and that you want to learn how to work on the code.
&SCons; Principles There are a few overriding principles we try to live up to in designing and implementing &SCons;: Correctness First and foremost, by default, &SCons; guarantees a correct build even if it means sacrificing performance a little. We strive to guarantee the build is correct regardless of how the software being built is structured, how it may have been written, or how unusual the tools are that build it. Performance Given that the build is correct, we try to make &SCons; build software as quickly as possible. In particular, wherever we may have needed to slow down the default &SCons; behavior to guarantee a correct build, we also try to make it easy to speed up &SCons; through optimization options that let you trade off guaranteed correctness in all end cases for a speedier build in the usual cases. Convenience &SCons; tries to do as much for you out of the box as reasonable, including detecting the right tools on your system and using them correctly to build the software. In a nutshell, we try hard to make &SCons; just "do the right thing" and build software correctly, with a minimum of hassles.
Acknowledgements &SCons; would not exist without a lot of help from a lot of people, many of whom may not even be aware that they helped or served as inspiration. So in no particular order, and at the risk of leaving out someone: First and foremost, &SCons; owes a tremendous debt to Bob Sidebotham, the original author of the classic Perl-based &Cons; tool which Bob first released to the world back around 1996. Bob's work on Cons classic provided the underlying architecture and model of specifying a build configuration using a real scripting language. My real-world experience working on Cons informed many of the design decisions in SCons, including the improved parallel build support, making Builder objects easily definable by users, and separating the build engine from the wrapping interface. Greg Wilson was instrumental in getting &SCons; started as a real project when he initiated the Software Carpentry design competition in February 2000. Without that nudge, marrying the advantages of the Cons classic architecture with the readability of Python might have just stayed no more than a nice idea. Thanks to Peter Miller for his splendid change management system, &Aegis;, which has provided the &SCons; project with a robust development methodology from day one, and which showed me how you could integrate incremental regression tests into a practical development cycle (years before eXtreme Programming arrived on the scene). And last, thanks to Guido van Rossum for his elegant scripting language, which is the basis not only for the &SCons; implementation, but for the interface itself.
scons-doc-2.3.0/doc/developer/architecture.xml0000644000175000017500000000242312114661557022161 0ustar dktrkranzdktrkranz XXX
Architecture XXX
scons-doc-2.3.0/doc/developer/testing.xml0000644000175000017500000000241612114661557021156 0ustar dktrkranzdktrkranz XXX
Testing XXX
scons-doc-2.3.0/doc/developer/branches.xml0000644000175000017500000000242712114661557021270 0ustar dktrkranzdktrkranz XXX
&SCons; Branches XXX
scons-doc-2.3.0/doc/developer/MANIFEST0000644000175000017500000000016412114661557020106 0ustar dktrkranzdktrkranzarchitecture.xml branches.xml copyright.xml cycle.xml main.xml packaging.xml preface.xml sourcetree.xml testing.xml scons-doc-2.3.0/doc/developer/copyright.xml0000644000175000017500000000240712114661557021511 0ustar dktrkranzdktrkranz
SCons Developer's Guide Copyright (c) 2007 Steven Knight
scons-doc-2.3.0/doc/developer/sourcetree.xml0000644000175000017500000000242212114661557021656 0ustar dktrkranzdktrkranz XXX
Source Tree XXX
scons-doc-2.3.0/doc/developer/cycle.xml0000644000175000017500000000243012114661557020574 0ustar dktrkranzdktrkranz XXX
Development Cycle XXX
scons-doc-2.3.0/doc/developer/main.xml0000644000175000017500000000561712114661557020433 0ustar dktrkranzdktrkranz %version; %scons; ]> SCons Developer's Guide &buildversion; Steven Knight Revision &buildrevision; (&builddate;) 2007 2007 Steven Knight ©right; version &buildversion; Preface &preface; Development Cycle &cycle; Source Tree &sourcetree; Testing &testing; Branches &branches; Packaging &packaging; Architecture &architecture; scons-doc-2.3.0/doc/scons.mod0000644000175000017500000005030312114661557016616 0ustar dktrkranzdktrkranz Aegis"> Ant"> ar"> as"> Autoconf"> Automake"> bison"> cc"> Cons"> cp"> csh"> f77"> f90"> f95"> gas"> gcc"> g77"> gXX"> Jam"> jar"> javac"> javah"> latex"> lex"> m4"> Make"> Make++"> pdflatex"> pdftex"> Python"> ranlib"> rmic"> SCons"> ScCons"> sleep"> swig"> tar"> tex"> touch"> yacc"> zip"> Action"> ActionBase"> CommandAction"> FunctionAction"> ListAction"> Builder"> BuilderBase"> CompositeBuilder"> MultiStepBuilder"> Job"> Jobs"> Serial"> Parallel"> Node"> Node.FS"> Scanner"> Sig"> Signature"> Taskmaster"> TimeStamp"> Walker"> Wrapper"> --config"> --debug=explain"> --debug=findlibs"> --debug=includes"> --debug=prepare"> --debug=presub"> --debug=stacktrace"> --implicit-cache"> --implicit-deps-changed"> --implicit-deps-unchanged"> --profile"> --taskmastertrace"> --tree"> -Q"> implicit_cache"> implicit_deps_changed"> implicit_deps_unchanged"> build"> Makefile"> Makefiles"> scons"> SConscript"> SConstruct"> Sconstruct"> sconstruct"> .sconsign"> src"> Add"> AddMethod"> AddPostAction"> AddPreAction"> AddOption"> AddOptions"> AddVariables"> Alias"> Aliases"> AllowSubstExceptions"> AlwaysBuild"> Append"> AppendENVPath"> AppendUnique"> BoolOption"> BoolVariable"> Build"> CacheDir"> Chmod"> Clean"> Clone"> Command"> Configure"> Copy"> Decider"> Default"> DefaultEnvironment"> DefaultRules"> Delete"> Depends"> Dir"> Dump"> Duplicate"> Entry"> EnumOption"> EnumVariable"> EnsurePythonVersion"> EnsureSConsVersion"> Environment"> Execute"> Exit"> Export"> File"> FindFile"> FindInstalledFiles"> FindPathDirs"> Finish"> Flatten"> GenerateHelpText"> GetBuildFailures"> GetBuildPath"> GetLaunchDir"> GetOption"> Glob"> Help"> Ignore"> Import"> Install"> InstallAs"> Link"> ListOption"> ListVariable"> Local"> MergeFlags"> Mkdir"> Module"> Move"> NoClean"> NoCache"> Objects"> Options"> Variables"> PackageOption"> PackageVariable"> ParseConfig"> ParseDepends"> ParseFlags"> PathOption"> PathOption.PathAccept"> PathOption.PathExists"> PathOption.PathIsDir"> PathOption.PathIsDirCreate"> PathOption.PathIsFile"> PathVariable"> PathVariable.PathAccept"> PathVariable.PathExists"> PathVariable.PathIsDir"> PathVariable.PathIsDirCreate"> PathVariable.PathIsFile"> Precious"> Prepend"> PrependENVPath"> PrependUnique"> Progress"> Replace"> Repository"> Requires"> Return"> RuleSet"> Salt"> SetBuildSignatureType"> SetContentSignatureType"> SetDefault"> SetOption"> SideEffect"> SourceSignature"> SourceSignatures"> Split"> Tag"> TargetSignatures"> Task"> Touch"> UnknownOptions"> UnknownVariables"> subst"> Message"> Result"> CheckCHeader"> CheckCXXHeader"> CheckFunc"> CheckHeader"> CheckLib"> CheckLibWithHeader"> CheckType"> TryAction"> TryBuild"> TryCompile"> TryLink"> TryRun"> IndexError"> NameError"> str"> zipfile"> Cache"> ARGLIST"> ARGUMENTS"> BUILD_TARGETS"> COMMAND_LINE_TARGETS"> DEFAULT_TARGETS"> BUILDERMAP"> COLOR"> COLORS"> CONFIG"> CPPDEFINES"> RELEASE"> RELEASE_BUILD"> SCANNERMAP"> TARFLAGS"> TARSUFFIX"> PATH"> PYTHONPATH"> SCONSFLAGS"> allowed_values"> build_dir"> map"> ignorecase"> options"> exports"> source"> target"> variables"> variant_dir"> all"> none"> BuildDir"> CFile"> CXXFile"> DVI"> Jar"> Java"> JavaH"> Library"> Object"> PCH"> PDF"> PostScript"> Program"> RES"> RMIC"> SharedLibrary"> SharedObject"> StaticLibrary"> StaticObject"> Substfile"> Tar"> Textfile"> VariantDir"> Zip"> Make"> builder function"> build action"> build actions"> builder method"> Configure Contexts"> configure context"> Construction Environment"> Construction Environments"> Construction environment"> Construction environments"> construction environment"> construction environments"> Construction Variable"> Construction Variables"> Construction variable"> Construction variables"> construction variable"> construction variables"> CPPPATH"> Dictionary"> Emitter"> emitter"> factory"> Generator"> generator"> Nodes"> signature"> build signature"> true"> false"> typedef"> action="> batch_key="> cmdstr="> exitstatfunc="> strfunction="> varlist="> bar"> common1.c"> common2.c"> custom.py"> goodbye"> goodbye.o"> goodbye.obj"> file.dll"> file.in"> file.lib"> file.o"> file.obj"> file.out"> foo"> foo.o"> foo.obj"> hello"> hello.c"> hello.exe"> hello.h"> hello.o"> hello.obj"> libfile_a"> libfile_so"> new_hello"> new_hello.exe"> prog"> prog1"> prog2"> prog.c"> prog.exe"> stdio.h"> +"> #"> announce@scons.tigris.org"> dev@scons.tigris.org"> users@scons.tigris.org"> scons-doc-2.3.0/doc/python10/0000755000175000017500000000000012114661557016451 5ustar dktrkranzdktrkranzscons-doc-2.3.0/doc/python10/process.xml0000644000175000017500000002161012114661557020651 0ustar dktrkranzdktrkranz The &SCons; project has paid particular attention from day one to the development process. One of the first internal documents produced was a set of Developer's Guidelines to provide a loose framework for what we were trying to accomplish and how we would go about accomplishing it. These Guidelines cover things like: &SCons; will be written to Python version 2.4 (to ensure usability by a wide install base). How &SCons; is be tested: which infrastructure modules to use, what platforms to test on, etc. Expectations for developers (subscribe to the mailing list, encouraged to register at SourceForge). Brief outline of how to use the change management systems (Aegis and CVS) for &SCons; development;. Establishing these guidelines up front had two purposes: 1) Demonstrate the seriousness of the project to anyone wondering about joining the effort; 2) Give potential developers an idea up front as to whether their development style would mesh with the rest of the project.
Aegis One of the most important aspects of the &SCons; development process is the use of Peter Miller's Aegis change management system. I had been using Aegis for personal projects for several years, and found its development methodology vastly improved the quality of my programming. I was consequently committed to using it for &SCons; development. Aegis provides a number of things, including: A flexible source code control and branching model. A defined process with separate development, review and integration steps. A distributed development model based on distribution of atomic change sets. The single most important reason for using Aegis, however, is its management of automated tests as part of the development process.
Testing, Testing, Testing The &SCons; project has made extensive use of automated tests from day one, taking inspiration mostly from Aegis, partly from the eXtreme Programming model, and with a little home-brew scripting for glue.
Testing Criteria The underlying criteria for testing changes to the &SCons; code are taken from Aegis: Every change must have one or more new or modified tests checked in along with the code. The new code being checked in must pass all of the new and/or modified tests. The old, already checked-in code in must fail all of the new and/or modified tests. The new code being checked in must pass all unmodified, already checked-in tests. In practice, these restrictions can be overridden as necessary--for example, when changing comments or documentation. The criterion that surprises many people is having the old code fail the tests in the change. This makes sure that the new tests or modified tests really do exercise the bug fix or feature being added by the change. Together, these criteria ensure that every newly checked-in version &SCons; conforms to defined behavior, as defined by the tests. Whenever a bug is found, its fix is checked in with a new or modified test that guarantees the bug will not recur in the future. We have already built up a regression test base of almost 90 tests that cover the vast majority of &SCons;' functionality.
Testing Infrastructure Testing standards are no good if they're too much of a burden for developers, who will at best work around or ignore the testing requirements, or at worst stop contributing code and go join a project that's more fun. To this end, good testing infrastructure that makes it easy to write tests is crucial. &SCons; development uses two development methodologies, one for the individual modules in the build engine, and the other for end-to-end tests of the &SCons; script. For the build engine modules, we use PyUnit. Every change to a build engine module must have a change to its corresponding unit tests, which live side-by-side in a separate file that imports module. As we build up a large body of unit tests, this ensures that the build engine will perform correctly whenever someone uses it in some application other than the &SCons; script itself. For end-to-end script tests, we have developed two modules to make writing tests easy. The first, TestCmd.py, is a generic module for testing commands or scripts (in any language, not just Python). The second module, TestScons.py, is a subclass of the generic TestCmd.py module. TestScons.py takes care of initialization and displaying error conditions specific to testing &SCons;. In practice, simple tests only need to initialize a test object, use the object to write some input files, run &SCons;, and then check whatever criteria determine whether the test passed or failed. A complete test of the &Program; method, for example, looks like this: test = TestSCons.TestSCons() test.write('SConstruct', """env = Environment() env.Program(target = 'foo', source = 'foo.c') """) test.write('foo.c', """ int main(int argc, char *argv[]) { argv[argc++] = "-"; /* dummy use of args */ printf("foo.c successfully compiled\\n"); exit (0); } """) test.run(arguments = 'foo') # runs SCons test.run(program = test.workpath('foo')) test.fail_test(test.stdout() != "foo.c successfully compiled\n") test.pass_test()
SourceForge Registration of the &SCons; project was approved at SourceForge on 29 June 2001. Within a week, the initial code base was checked in, mailing lists were created, and the web site was set up. We started making use of the task-list manager to track what we had to finish for initial release. The obvious complication was how to use structured testing methodology of Aegis when SourceForge uses CVS for source control. Not using the SourceForge CVS tree would have had two significant disadvantages: one, missing out on the archiving and central location in the event of disaster; two, people coming to the SourceForge project page wouldn't be able to browse the source. The latter was particularly important in the early stages of development, in order to avoid any impression that this was Yet Another Project that starts with a bang and then dwindles as the initial enthusiasm starts to wear off. The solution was to use the SourceForge CVS repository for read-only access to the source. &SCons; developers are welcome to use CVS for their development, but the changes are not committed to the SourceForge repository. Instead, patches are sent to the integrator for processing through Aegis. When the change has been integrated into the Aegis repository, a home-brew script translates the Aegis change into a virtual shell script of commands that copy the necessary files from Aegis and check them in to CVS at SourceForge. (In practice, write access is not actually disabled for registered developers, but if they do make any changes directly at SourceForge, they can be overwritten at the next Aegis update.)
scons-doc-2.3.0/doc/python10/job-task.eps0000644000175000017500000001504412114661557020700 0ustar dktrkranzdktrkranz%!PS-Adobe-2.0 EPSF-2.0 %%Title: build/doc/python10/job-task.fig %%Creator: /usr/bin/fig2dev Version 3.2 Patchlevel 3d %%CreationDate: Sun Jan 2 01:21:05 2005 %%For: knight@casablanca.home.baldmt.com (Steven Knight) %%BoundingBox: 0 0 416 236 %%Magnification: 1.0000 %%EndComments /$F2psDict 200 dict def $F2psDict begin $F2psDict /mtrx matrix put /col-1 {0 setgray} bind def /col0 {0.000 0.000 0.000 srgb} bind def /col1 {0.000 0.000 1.000 srgb} bind def /col2 {0.000 1.000 0.000 srgb} bind def /col3 {0.000 1.000 1.000 srgb} bind def /col4 {1.000 0.000 0.000 srgb} bind def /col5 {1.000 0.000 1.000 srgb} bind def /col6 {1.000 1.000 0.000 srgb} bind def /col7 {1.000 1.000 1.000 srgb} bind def /col8 {0.000 0.000 0.560 srgb} bind def /col9 {0.000 0.000 0.690 srgb} bind def /col10 {0.000 0.000 0.820 srgb} bind def /col11 {0.530 0.810 1.000 srgb} bind def /col12 {0.000 0.560 0.000 srgb} bind def /col13 {0.000 0.690 0.000 srgb} bind def /col14 {0.000 0.820 0.000 srgb} bind def /col15 {0.000 0.560 0.560 srgb} bind def /col16 {0.000 0.690 0.690 srgb} bind def /col17 {0.000 0.820 0.820 srgb} bind def /col18 {0.560 0.000 0.000 srgb} bind def /col19 {0.690 0.000 0.000 srgb} bind def /col20 {0.820 0.000 0.000 srgb} bind def /col21 {0.560 0.000 0.560 srgb} bind def /col22 {0.690 0.000 0.690 srgb} bind def /col23 {0.820 0.000 0.820 srgb} bind def /col24 {0.500 0.190 0.000 srgb} bind def /col25 {0.630 0.250 0.000 srgb} bind def /col26 {0.750 0.380 0.000 srgb} bind def /col27 {1.000 0.500 0.500 srgb} bind def /col28 {1.000 0.630 0.630 srgb} bind def /col29 {1.000 0.750 0.750 srgb} bind def /col30 {1.000 0.880 0.880 srgb} bind def /col31 {1.000 0.840 0.000 srgb} bind def end save newpath 0 236 moveto 0 0 lineto 416 0 lineto 416 236 lineto closepath clip newpath -35.3 342.7 translate 1 -1 scale /cp {closepath} bind def /ef {eofill} bind def /gr {grestore} bind def /gs {gsave} bind def /sa {save} bind def /rs {restore} bind def /l {lineto} bind def /m {moveto} bind def /rm {rmoveto} bind def /n {newpath} bind def /s {stroke} bind def /sh {show} bind def /slc {setlinecap} bind def /slj {setlinejoin} bind def /slw {setlinewidth} bind def /srgb {setrgbcolor} bind def /rot {rotate} bind def /sc {scale} bind def /sd {setdash} bind def /ff {findfont} bind def /sf {setfont} bind def /scf {scalefont} bind def /sw {stringwidth} bind def /tr {translate} bind def /tnt {dup dup currentrgbcolor 4 -2 roll dup 1 exch sub 3 -1 roll mul add 4 -2 roll dup 1 exch sub 3 -1 roll mul add 4 -2 roll dup 1 exch sub 3 -1 roll mul add srgb} bind def /shd {dup dup currentrgbcolor 4 -2 roll mul 4 -2 roll mul 4 -2 roll mul srgb} bind def /$F2psBegin {$F2psDict begin /$F2psEnteredState save def} def /$F2psEnd {$F2psEnteredState restore end} def $F2psBegin 10 setmiterlimit 0.06000 0.06000 sc % % Fig objects follow % % Polyline 7.500 slw n 4200 3900 m 5100 3900 l 5100 4500 l 4200 4500 l cp gs col0 s gr /Times-Roman ff 240.00 scf sf 4650 4275 m gs 1 -1 sc (Task) dup sw pop 2 div neg 0 rm col0 sh gr % Polyline n 4200 5100 m 5100 5100 l 5100 5700 l 4200 5700 l cp gs col0 s gr /Times-Roman ff 240.00 scf sf 4650 5475 m gs 1 -1 sc (Node) dup sw pop 2 div neg 0 rm col0 sh gr % Polyline n 6300 2100 m 7200 2100 l 7200 2700 l 6300 2700 l cp gs col0 s gr /Times-Roman ff 240.00 scf sf 6750 2475 m gs 1 -1 sc (Sig) dup sw pop 2 div neg 0 rm col0 sh gr % Polyline n 6300 3300 m 7500 3300 l 7500 3900 l 6300 3900 l cp gs col0 s gr /Times-Roman ff 240.00 scf sf 6900 3675 m gs 1 -1 sc (Walker) dup sw pop 2 div neg 0 rm col0 sh gr % Polyline n 4200 2100 m 5700 2100 l 5700 2700 l 4200 2700 l cp gs col0 s gr /Times-Roman ff 240.00 scf sf 4950 2475 m gs 1 -1 sc (TaskMaster) dup sw pop 2 div neg 0 rm col0 sh gr % Polyline n 2400 3300 m 3600 3300 l 3600 3900 l 2400 3900 l cp gs col0 s gr /Times-Roman ff 240.00 scf sf 3000 3675 m gs 1 -1 sc (Parallel) dup sw pop 2 div neg 0 rm col0 sh gr % Polyline n 600 3300 m 1800 3300 l 1800 3900 l 600 3900 l cp gs col0 s gr /Times-Roman ff 240.00 scf sf 1200 3675 m gs 1 -1 sc (Serial) dup sw pop 2 div neg 0 rm col0 sh gr % Polyline n 1200 2100 m 3000 2100 l 3000 2700 l 1200 2700 l cp gs col0 s gr /Times-Roman ff 255.00 scf sf 2099 2475 m gs 1 -1 sc (Jobs) dup sw pop 2 div neg 0 rm col0 sh gr % Polyline n 2700 2700 m 2660 2775 l 2700 2850 l 2740 2775 l cp gs col0 s gr % Polyline n 5700 2400 m 5775 2440 l 5850 2400 l 5775 2360 l cp gs col0 s gr % Polyline n 5400 2700 m 5360 2775 l 5400 2850 l 5440 2775 l cp gs col0 s gr % Polyline n 4650 2700 m 4610 2775 l 4650 2850 l 4690 2775 l cp gs col0 s gr % Polyline n 1500 2700 m 1460 2775 l 1500 2850 l 1540 2775 l cp gs col0 s gr % Polyline n 4650 4500 m 4610 4575 l 4650 4650 l 4690 4575 l cp gs col0 s gr % Polyline gs clippath 1470 3315 m 1530 3315 l 1530 3164 l 1500 3284 l 1470 3164 l cp eoclip n 1500 2850 m 1500 3300 l gs col0 s gr gr % arrowhead n 1470 3164 m 1500 3284 l 1530 3164 l 1470 3164 l cp gs 0.00 setgray ef gr col0 s % Polyline gs clippath 2670 3315 m 2730 3315 l 2730 3164 l 2700 3284 l 2670 3164 l cp eoclip n 2700 2850 m 2700 3300 l gs col0 s gr gr % arrowhead n 2670 3164 m 2700 3284 l 2730 3164 l 2670 3164 l cp gs 0.00 setgray ef gr col0 s % Polyline gs clippath 4620 3915 m 4680 3915 l 4680 3764 l 4650 3884 l 4620 3764 l cp eoclip n 4650 2850 m 4650 3900 l gs col0 s gr gr % arrowhead n 4620 3764 m 4650 3884 l 4680 3764 l 4620 3764 l cp gs 0.00 setgray ef gr col0 s % Polyline gs clippath 4620 5115 m 4680 5115 l 4680 4964 l 4650 5084 l 4620 4964 l cp eoclip n 4650 4650 m 4650 5100 l gs col0 s gr gr % arrowhead n 4620 4964 m 4650 5084 l 4680 4964 l 4620 4964 l cp gs 0.00 setgray ef gr col0 s % Polyline gs clippath 6315 3630 m 6315 3570 l 6164 3570 l 6284 3600 l 6164 3630 l cp eoclip n 5400 2850 m 5400 3600 l 6300 3600 l gs col0 s gr gr % arrowhead n 6164 3630 m 6284 3600 l 6164 3570 l 6164 3630 l cp gs 0.00 setgray ef gr col0 s % Polyline gs clippath 6315 2430 m 6315 2370 l 6164 2370 l 6284 2400 l 6164 2430 l cp eoclip n 5850 2400 m 6300 2400 l gs col0 s gr gr % arrowhead n 6164 2430 m 6284 2400 l 6164 2370 l 6164 2430 l cp gs 0.00 setgray ef gr col0 s % Polyline n 3000 2400 m 3075 2440 l 3150 2400 l 3075 2360 l cp gs col0 s gr % Polyline gs clippath 4215 2430 m 4215 2370 l 4064 2370 l 4184 2400 l 4064 2430 l cp eoclip n 3150 2400 m 4200 2400 l gs col0 s gr gr % arrowhead n 4064 2430 m 4184 2400 l 4064 2370 l 4064 2430 l cp gs 0.00 setgray ef gr col0 s % Polyline [60] 0 sd n 2100 2100 m 2100 1800 l gs col0 s gr [] 0 sd % Polyline [60] 0 sd n 4950 2100 m 4950 1800 l gs col0 s gr [] 0 sd % Polyline [60] 0 sd n 6750 2100 m 6750 1800 l gs col0 s gr [] 0 sd $F2psEnd rs scons-doc-2.3.0/doc/python10/builder.fig0000644000175000017500000001004512114661557020566 0ustar dktrkranzdktrkranz#FIG 3.2 Landscape Center Inches Letter 100.00 Single -2 1200 2 6 2700 1200 4500 1800 2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5 2700 1200 4500 1200 4500 1800 2700 1800 2700 1200 4 0 0 50 0 0 16 0.0000 4 165 1290 2925 1575 Environment\001 -6 6 2700 2400 4500 3000 2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5 2700 2400 4500 2400 4500 3000 2700 3000 2700 2400 4 1 0 50 0 0 16 0.0000 4 225 1620 3600 2775 BuilderWrapper\001 -6 6 2700 3600 4500 4200 2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5 2700 3600 4500 3600 4500 4200 2700 4200 2700 3600 4 1 0 50 0 0 16 0.0000 4 165 1215 3600 3975 BuilderBase\001 -6 6 8400 3600 9900 4200 2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5 8400 3600 9900 3600 9900 4200 8400 4200 8400 3600 4 1 0 50 0 0 16 0.0000 4 165 1140 9150 3975 ActionBase\001 -6 6 3900 4800 5400 5700 6 4050 4950 5250 5475 4 1 0 50 0 0 16 0.0000 4 225 1140 4650 5175 MultiStep-\001 4 1 0 50 0 0 16 0.0000 4 165 750 4650 5460 Builder\001 -6 2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5 3900 4800 5400 4800 5400 5700 3900 5700 3900 4800 -6 6 1800 4800 3300 5700 6 1950 4950 3150 5475 4 1 0 50 0 0 16 0.0000 4 225 1200 2550 5175 Composite-\001 4 1 0 50 0 0 16 0.0000 4 165 750 2550 5460 Builder\001 -6 2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5 1800 4800 3300 4800 3300 5700 1800 5700 1800 4800 -6 6 6300 4800 7800 5700 6 6525 4950 7575 5475 4 1 0 50 0 0 16 0.0000 4 165 1020 7050 5175 Command\001 4 1 0 50 0 0 16 0.0000 4 165 675 7050 5460 Action\001 -6 2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5 6300 4800 7800 4800 7800 5700 6300 5700 6300 4800 -6 6 8400 4800 9900 5700 6 8700 4950 9600 5475 4 1 0 50 0 0 16 0.0000 4 165 675 9150 5460 Action\001 4 1 0 50 0 0 16 0.0000 4 165 870 9150 5175 Function\001 -6 2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5 8400 4800 9900 4800 9900 5700 8400 5700 8400 4800 -6 6 10500 4800 12000 5700 6 10875 4950 11625 5475 4 1 0 50 0 0 16 0.0000 4 165 390 11250 5175 List\001 4 1 0 50 0 0 16 0.0000 4 165 675 11250 5460 Action\001 -6 2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5 10500 4800 12000 4800 12000 5700 10500 5700 10500 4800 -6 6 900 2400 2100 3000 2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5 900 2400 2100 2400 2100 3000 900 3000 900 2400 4 1 0 50 0 0 16 0.0000 4 165 525 1500 2775 Node\001 -6 2 3 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 4 3600 4200 3525 4350 3675 4350 3600 4200 2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 4 3150 4800 3150 4500 4050 4500 4050 4800 2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 2 3600 4350 3600 4500 2 3 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 4 9150 4200 9075 4350 9225 4350 9150 4200 2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 4 7050 4800 7050 4500 10950 4500 10950 4800 2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 2 9150 4350 9150 4800 2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 0 3 1 1 1.00 60.00 120.00 11550 4650 11550 3900 9900 3900 2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 0 2 1 1 1.00 60.00 120.00 4650 3900 8400 3900 2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 0 2 1 1 1.00 60.00 120.00 3900 2250 3900 1800 2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 0 2 1 1 1.00 60.00 120.00 3300 1950 3300 2400 2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 0 2 1 1 1.00 60.00 120.00 3600 3150 3600 3600 2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 0 2 1 1 1.00 60.00 120.00 4350 4650 4350 4200 2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 0 2 1 1 1.00 60.00 120.00 2850 4650 2850 4200 2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 0 3 1 1 1.00 60.00 120.00 1500 3150 1500 3900 2700 3900 2 3 0 1 0 7 50 0 -1 4.000 0 0 7 0 0 5 4650 3900 4575 3860 4500 3900 4575 3940 4650 3900 2 3 0 1 0 7 50 0 -1 4.000 0 0 7 0 0 5 1500 3000 1460 3075 1500 3150 1540 3075 1500 3000 2 3 0 1 0 7 50 0 -1 4.000 0 0 7 0 0 5 3600 3000 3560 3075 3600 3150 3640 3075 3600 3000 2 3 0 1 0 7 50 0 -1 4.000 0 0 7 0 0 5 3300 1800 3260 1875 3300 1950 3340 1875 3300 1800 2 3 0 1 0 7 50 0 -1 4.000 0 0 7 0 0 5 3900 2250 3860 2325 3900 2400 3940 2325 3900 2250 2 3 0 1 0 7 50 0 -1 4.000 0 0 7 0 0 5 4350 4650 4310 4725 4350 4800 4390 4725 4350 4650 2 3 0 1 0 7 50 0 -1 4.000 0 0 7 0 0 5 2850 4650 2810 4725 2850 4800 2890 4725 2850 4650 2 3 0 1 0 7 50 0 -1 4.000 0 0 7 0 0 5 11550 4650 11510 4725 11550 4800 11590 4725 11550 4650 2 1 1 1 0 7 50 0 -1 4.000 0 0 -1 0 0 2 3600 1200 3600 900 scons-doc-2.3.0/doc/python10/scons.mod0000644000175000017500000003521212114661557020302 0ustar dktrkranzdktrkranz Aegis"> Ant"> Autoconf"> Automake"> cc"> Cons"> cp"> csh"> gcc"> Jam"> jar"> javac"> javah"> Make"> Make++"> Python"> ranlib"> rmic"> SCons"> scons"> ScCons"> tar"> touch"> zip"> Action"> ActionBase"> CommandAction"> FunctionAction"> ListAction"> Builder"> BuilderBase"> CompositeBuilder"> MultiStepBuilder"> Job"> Jobs"> Serial"> Parallel"> Node"> Node.FS"> Scanner"> Sig"> Signature"> Taskmaster"> TimeStamp"> Walker"> Wrapper"> --debug=explain"> --implicit-cache"> --implicit-deps-changed"> --implicit-deps-unchanged"> -Q"> implicit_cache"> implicit_deps_changed"> implicit_deps_unchanged"> build"> Makefile"> Makefiles"> SConscript"> SConstruct"> Sconstruct"> sconstruct"> .sconsign"> src"> Add"> AddOptions"> Alias"> Aliases"> Append"> BoolOption"> Build"> CacheDir"> Clean"> Clone"> Command"> Configure"> Copy"> Default"> DefaultRules"> Depends"> Dir"> Entry"> EnumOption"> Environment"> Export"> File"> Finish"> GenerateHelpText"> Help"> Ignore"> Import"> Install"> InstallAs"> Link"> ListOption"> Local"> Module"> Objects"> Options"> PackageOption"> PathOption"> Precious"> Prepend"> Replace"> Repository"> Return"> RuleSet"> Salt"> SetBuildSignatureType"> SetContentSignatureType"> SourceSignature"> SourceSignatures"> Split"> TargetSignatures"> Task"> subst"> Message"> Result"> CheckCHeader"> CheckCXXHeader"> CheckFunc"> CheckHeader"> CheckLib"> CheckLibWithHeader"> CheckType"> TryAction"> TryBuild"> TryCompile"> TryLink"> TryRun"> str"> zipfile"> Cache"> ARGUMENTS"> BUILD_TARGETS"> COMMAND_LINE_TARGETS"> DEFAULT_TARGETS"> BUILDERMAP"> BUILDERS"> CC"> CCFLAGS"> CCCOM"> COLOR"> COLORS"> CONFIG"> CPPDEFINES"> ENV"> JAVACLASSDIR"> LIBDIRPREFIX"> LIBDIRSUFFIX"> LIBLINKPREFIX"> LIBLINKSUFFIX"> LIBPATH"> LIBS"> LINK"> LINKCOM"> LINKFLAGS"> RELEASE"> RELEASE_BUILD"> SCANNERMAP"> SCANNERS"> TARFLAGS"> TARSUFFIX"> PATH"> PYTHONPATH"> SCONSFLAGS"> allowed_values"> build_dir"> map"> ignorecase"> options"> exports"> source"> target"> all"> none"> BuildDir"> CFile"> CXXFile"> DVI"> Jar"> Java"> JavaH"> Library"> Object"> PCH"> PDF"> PostScript"> Program"> RES"> RMIC"> SharedLibrary"> SharedObject"> StaticLibrary"> StaticObject"> Tar"> Zip"> Make"> builder function"> builder method"> Configure Contexts"> configure context"> Construction Environment"> Construction Environments"> Construction environment"> Construction environments"> construction environment"> construction environments"> Construction Variable"> Construction Variables"> Construction variable"> Construction variables"> construction variable"> construction variables"> CPPPATH"> Dictionary"> Emitter"> emitter"> Generator"> generator"> Nodes"> signature"> build signature"> true"> false"> typedef"> bar"> common1.c"> common2.c"> custom.py"> goodbye"> goodbye.o"> goodbye.obj"> file.dll"> file.in"> file.lib"> file.o"> file.obj"> file.out"> foo"> foo.o"> foo.obj"> hello"> hello.c"> hello.exe"> hello.h"> hello.o"> hello.obj"> libfile_a"> libfile_so"> new_hello"> new_hello.exe"> prog"> prog1"> prog2"> prog.c"> prog.exe"> stdio.h"> +"> #"> announce@scons.tigris.org"> dev@scons.tigris.org"> users@scons.tigris.org"> scons-doc-2.3.0/doc/python10/sig.jpg0000644000175000017500000003313112114661557017736 0ustar dktrkranzdktrkranzÿØÿàJFIFPPÿÛCÿÛCÿÀŽV"ÿÄ ÿĵ}!1AQa"q2‘¡#B±ÁRÑð$3br‚ %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyzƒ„…†‡ˆ‰Š’“”•–—˜™š¢£¤¥¦§¨©ª²³´µ¶·¸¹ºÂÃÄÅÆÇÈÉÊÒÓÔÕÖרÙÚáâãäåæçèéêñòóôõö÷øùúÿÄ ÿĵw!1AQaq"2B‘¡±Á #3RðbrÑ $4á%ñ&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz‚ƒ„…†‡ˆ‰Š’“”•–—˜™š¢£¤¥¦§¨©ª²³´µ¶·¸¹ºÂÃÄÅÆÇÈÉÊÒÓÔÕÖרÙÚâãäåæçèéêòóôõö÷øùúÿÚ ?þþ(¢Š(¢Š(¢Šùƒà†~2è_¿mÝSâ}ψ'ðO?iÿ ø›ös‹Yñd#Ó¬þ ZþÅÿ²'ƒµËo èðë:¤žðû~Ð~øíysá;«Oy⫯xétií¼ioâwéúùƒà†~2è_¿mÝSâ}ψ'ðO?iÿ ø›ös‹Yñd#Ó¬þ ZþÅÿ²'ƒµËo èðë:¤žðû~Ð~øíysá;«Oy⫯xétií¼ioâwéú(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Šùƒö"ðÏÆ_þÅÿ²'ƒ¿hËŸ^~Ð~ý˜>xgãµç‹xgãµç‹|`øàߊ_¼Mÿ Ÿü+Dÿ„‹ÀŸ<9¦ø³Â:ïö7ˆ¿à£šGˆ4íêú~¡ý™®éZf±aö²êz}•ìSÛEèðÙ´Wý"wöÿÿÃÿ²ÿé–Qÿÿ”YÁ4ÿìÀ?cýg_‡5÷ý|ÿ ‘ûEÒ'oÿü8ßðK/þ™eðÙ´Wý"wöÿÿÃÿ²ÿé–WßôPÀðÙ´Wý"wöÿÿÃÿ²ÿé–Qÿ ‘ûEÒ'oÿü8ßðK/þ™e}ÿE~@üý¢¿mß|Eý¬|Eñþ ¥ûø«Â_ÿhxÿà^…ÿ «þ ©®µøY§~˳OÂÝ_Á_Ùšßü~×LðwÛþ6ü5øÅñþÏϨørëþïøK®¯cñoмS§Ø{ÿü6GíÿHý¿ÿðãÁ,¿úe•÷ýðü6GíÿHý¿ÿðãÁ,¿úe”Ãd~Ñ_ô‰ßÛÿÿ7üËÿ¦Y_Ñ@Ãd~Ñ_ô‰ßÛÿÿ7üËÿ¦YGü6GíÿHý¿ÿðãÁ,¿úe•÷ýñÂoÛ?[ñïÇß~οcÿÚöcñ·>üUø×àSã^³û#øÂ¾,ð¯Á|ð/Ä ?O¾ý›j¿Ú WÓ xsMñg„ußìoÁG4iÚþÕôýCû3]Ò´ÍbÃíeÔôû+ا¶‹ôþ¾ÿ‚NÿÊ,¿àšö`±¿þ³¯Ãš?á²?h¯úDïíÿÿ‡þ eÿÓ,£þ#öŠÿ¤Nþßÿøq¿à–_ý2ÊûþŠøþ#öŠÿ¤Nþßÿøq¿à–_ý2Ê?á²?h¯úDïíÿÿ‡þ eÿÓ,¯¿è ÈÙ;öŠý·~~˳OÂߎŸðM/ÛÿâWÆß†¿³ÿÁ¿|bø‹ÿ «þ ©ãøO¾)ø;áׇ<;ñÆ¿ð—xÃþ ?§x·Å_ð•x·NÕõßøHüS§Øx\ûöž·ek©Ý]@žÿÿ ‘ûEÒ'oÿü8ßðK/þ™e}ÿE|ÿ ‘ûEÒ'oÿü8ßðK/þ™eðÙ´Wý"wöÿÿÃÿ²ÿé–WßôPÀðÙ´Wý"wöÿÿÃÿ²ÿé–WÓÿ³×Æ¿ þÒŸ¾þÑžÓüA¤ø'ã÷Áÿ†Ÿü¥ø²×N±ñV›á_Šž Ñ|uáí?ÄÖ:>«¯i~ ³Ò5Û;}f×K×5:ßQŽæWQ¶H¯&ö øþ ;ÿ(²ÿ‚iÿÙ€~Æÿúοhïú(¢€ (¢€ (¢€ (¢€>ÿ‚NÿÊ,¿àšö`±¿þ³¯ÃšöÛ'Yý®4ÙãÆ7ß°¯„þøÓö£ŸÄ 4o‡:?Çën×àÕžâ?‹~ð÷į|B—Âþ&ðŸŠ[ÃþøSªxãÇS[xSTºñUäþ·³ð׆|i®OaàíwÇÿà“¿ò‹/ø&Ÿý˜ìoÿ¬ëðæ¾ÿ æ†×öËÿ‚Ù|Aý¡ÿàªÿ²×ìù¬ÿÁ8>)üdÿ‚oøöLÖ| ¡x›ö]ý þéßµ£ûLü$Õ>/ÞxNÛ_¾ÿ‚€ø—Hø1â i&£á¿ Üø†ëÅÞñ¿Š®4dñ_‰¾xnïTñ.‡û¿ÿ cû,Âöÿ†\ÿ†–ýŸÿᦿèÝ?ár|:ÿ…íÿ"wü,Où$_ð‘ÿÂÀÿ’ÿÏü‹ßò'ÅMÿ _ôêüÀý…þ~Óÿà¯_ðXÿÚâwìÛñáÏÀ_ÚûþïþGÅ}wdz޹£ø£þ#á>«ðKÅ?ÚðÆÏüMð×ü,‹ß/>ÿÂAàM?Íð&•©¯ÄøVÞ::_µ?ð‚nOþÐÿ±Wо.Á?>0xWøÿý­>0øëSøñ¿ö9‹ö%ð_ÁÚá'íYð*ÏÆ_ <#cûE|$øÉñÃÄ÷‰>(ü5ý§~,|Xý ¿eè?iYüqñöºÕ¾èžÒ>"é³…ÐÛÿੲƯá-þö¬ý?i‰·´ìYàxá×Ç?‡_5#ágí ûq~Í¿²çÄÏំ?eñ‡ü#¾øã/ü#¾#½ÝáÍ#Çw¾þÞ²Öì®[ÃڿПü'öÑ5Š^ÖnÙHñÀïíø]z§ûK|°Ö>ÿdxïBø[ªÿÂÒÓ.¼kïÃÿìω¾(ðÏíCþÈ4±xïÄZ„n|¯jú~ŸqüÐÜÿÁ4lÏüñ—ƒ¼û0~Óÿ¼¦üýмàÏøãâ·üûÆ?ðP¿ÙNø_ÿ$øûQx·àGüGþ IàŒçÅ>0ý˜> ü)Ѿ'KðÇÂ߷Lj|â¯øûáìÁ¬|*Õ5Ëo|CðoÃo·þüÿ‚‡xþ ϯ~Éf߈~0~È?·ÿìÕñáÇÆ DzV‡¬ÁE~ü&ÿ‚¡ü6ýª¼Añ÷OÐãg‡ì¾þÐ%øeàÏø›â–ûIøÇKñÄÏêºgÄ?|jø‘ñƒâWÄááPÓÿø{üËþ’Yûâd~οüñ«èþÖ?²ÇŠ~ øñKö–ýŸþümø•ÿçü+¯ƒ¾?øÉðëÁßü}ÿ ˆï|áøB¾ø‹Äzw‹|Uÿ W‹tíCÂÞþÂÒ/ÿ· ü@øÅû_ø‹áÿ„üwðSÅÿ4~É´‹¿à¤ãâ‡ÂïÙÓâ•ðþëö¿ý•~6|I×><êß²íÿûøË£üø  x'\ý§|ðÛÇ>=OÛkö$ý¿þxkWÔ> ü ø…û7øûÀž5ñ?ìãð"÷ã¥×ÃÿˆÞ6ñÏÃÆØ£þõûm|+ø!ûÚ~×ß²wí?ñáOÃÙƒöÂý‚ÿhØ[àÏíÇà€þ*Òtïß´¯?jÛoÚ‡áω>~Úÿ ¾ üzø?ñŸÃ~ ð‡ìÁûE| ø¡ñkàߊŸQð„6øýáÿx³àOƒµ/¿ l|Uñ«Â¿54†&øIáë¯E«üGðÿÄ}^)t¿k>³Ötïj1ÉcáëFå!ôý~ ÿÁ<> üvýš¾6x3MÖÿaˆþüFý€?dƒZ~›áßÚãÁßµ¿?dþÏ¿à¢_î¾üEø±ñ¯â—„¿hßÿcø3ö€ø=à߿„ß~x/XÔ'øià߃ÿ4¯?íõ~p|sñg…|ÿ$ý—=ð^µá[x›áŽ¿lgðOÅOè×:´Z޳ðãÆ(ÞñÆmsá«i¥à üý¡~~ÒžÔ:Âý·ÿÏü&ð©>xÇÿð‹ÂGý‘âøGÿá ÿ„û'ûoû[þÊû_Ûÿ²5/³ýŽo€?b_Úöúøï£þÆ¿.~$~À¶ì‹ûL|?ñçÅ_þÉ~øÑð+Xýž|c. ðè÷í ¬üeðçÀ/Ž!ýœü'áÿþÐzÁÿ‰zÏÀŸø²æ ? øÓã.—à½jûᇄüMyuâoÛZøÄ~6ƒCÑõ››øN4ëË™fñ6…¶©køCû6þËß´Ä?ðSÿ‚Ÿ¶OÁߨ/âü3Â_>üqñOüÞ2øóû=üFøYûT|SÕ¼à{/€:GÂ| øëñ£LÕ~ |9øÛ}ñâ¥ñÒëà·ìµâ?xsUø—âkwÞ-ø·â¯x¬ôöuý·|+áÏØ—àÏíû{~×ðN Wñïˆ<_á=Gã·ìëñ÷NÒÿb_xªÏÇ­|1ៃ?þ3x² Ÿø‚×Á> žßÅú4ú忣Ž<'ñ>ÙtÖ×ëÿ‚Ÿ´/À/ÚSº‡Ž¿g?Ž?þ?x'IñׄõOüø—࿊žÓ|Uc§iZÅ÷†uøZ×´‹?Yé]hבê6úv³¥_Ml–Úœ³4_´Eïíû8|:ýˆþ"i¿ÿh ö§ø_û_ÿÁ^o¯ ~ΞøkuûmüS±øEû@~ÔÿþÀ|kû#|ý°>üø½ýà_ø(¿ÂmcÄüûXx·ãgì÷â=JÏIøiðÓâÁþÖ¾6øCû=ÿ¥øÉðkâgì_ð»áßÁþÓÿôÙ#Ãþ ýüCð‹öÐø_?Â?ÚƒáÖ£ðsáOãàë/‹þŠÊ×ÃsøƒÆŸüGð»â½–½à™.|+¬xâqZ®ºÇ…tÑú(¢€ øþ ;ÿ(²ÿ‚iÿÙ€~Æÿúοkïúøþ ;ÿ(²ÿ‚iÿÙ€~Æÿúοhïú(¢€ (¢€ (¢€ (¢€>ÿ‚NÿÊ,¿àšö`±¿þ³¯Ãšûþ¿b/‹ßµ§ì×ûþÈŸ³ŸŽ¿à•¿¶þ­ão€?³À/‚ž1Õ<'ñGþ ‡}á]KÅ_ þøOÀ¾!Ô<3}¬ÁEô^óÃ÷š¾…yq£]jš¨ÜiÒ[M}¥i×/-œ?OÿÃd~Ñ_ô‰ßÛÿÿ7üËÿ¦Y@Ñ_Ãd~Ñ_ô‰ßÛÿÿ7üËÿ¦YGü6GíÿHý¿ÿðãÁ,¿úe”÷ýðü6GíÿHý¿ÿðãÁ,¿úe”Ãd~Ñ_ô‰ßÛÿÿ7üËÿ¦Y@Ñ_Ãd~Ñ_ô‰ßÛÿÿ7üËÿ¦YGü6GíÿHý¿ÿðãÁ,¿úe”÷ýðü6GíÿHý¿ÿðãÁ,¿úe”Ãd~Ñ_ô‰ßÛÿÿ7üËÿ¦Y@Ñ_Ãd~Ñ_ô‰ßÛÿÿ7üËÿ¦YGü6GíÿHý¿ÿðãÁ,¿úe”|Fÿ”¦þÆÿö`ðRÏýh¯ø$í}ÿ_˜ÔÿhŽ?·×Á~.ýŒhÙŸá—ÁÿÙöÆø[¬x›ã§¿d oþ/üuøÑûø³Á…´oÙ¿ö¦ý üA'—áÿÙóâ.¡­ê~"Ò¼9£Ø}ŸHµƒP½½Õ#¶‹ôþ€ (¢€ (¢€ (¢€ (¢€ (¢€ øþ ;ÿ(²ÿ‚iÿÙ€~Æÿúοkïúüaýˆ¾/~ÖŸ³_ì_û"~Î~:ÿ‚VþÛú·¾þ̾ xÇTðŸÅø&÷…u/|+øSá?ø‡PðÍö±ÿÐu{ÏÞjúåÆuªhz6£q§Im5ö•§\¼¶p€~ÏQ_Ãd~Ñ_ô‰ßÛÿÿ7üËÿ¦YGü6GíÿHý¿ÿðãÁ,¿úe”÷ýðü6GíÿHý¿ÿðãÁ,¿úe•Ïø³öõøÁà/ ø›Ç^:ÿ‚aþÛþ ðO‚ü?¬ø³Æ>1ñgÅÿø%‡<+á? øsN¹Öxëá§ÅoxÇ>ÔGñÏ„ôïÁá_Ác¡Ëâ}U¹ðæ>™ëÿ þøàwÂφŸ>è_ð‹ü2ø?ðÿÁ¿ ~xgûOXÖÿáð'Ãÿi¾ðŽ…ý³â-CWñ¯ý‘áý#OÓÿ´õÝWSÖ/þÏö­OP½½–{™8>ÁP<}ûJ|3Òþ.ü"ÿ‚XÿÁGõoêÞ ñÿ„ã“Å“ÿÁ=>ø«MñW¿ˆ^)øQñÃ>&øwñ_þ àŸˆžñ„>"x'Å^ÖtoxWFÔmõä‹g¶{{‰½ƒþ#öŠÿ¤Nþßÿøq¿à–_ý2ÊûþŠøþ#öŠÿ¤Nþßÿøq¿à–_ý2Ê?á²?h¯úDïíÿÿ‡þ eÿÓ, ¿ëàø$ïü¢Ëþ §ÿfûÿë:ü9£þ#öŠÿ¤Nþßÿøq¿à–_ý2Êôø'·Âßüýa´/øEþ&üý?fŸ…¿|3ý§£ëðŽøïáÿÁxOÅÚöχu _Ãú¿öGˆ4COþÓе]OG¿û?Ú´ÍBöÊX.eúþŠ( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( ¿ à¤ßµì± |SýžaÚö–ýŸþxKãOöŸÇßÚCþoÆO‡_ ¬|Gû,|ñ‡|¿‚§þækðÖ_uO|.ñwÃxGUøuñßö6ð‡íéà•ÖtŸè?™úýE~0üý·bóÿÔ< û:~×³Ç ÿÁ@¼?uâÍSÁß>>ü)ø³â¯~Ø_³§Âý+G¾ñ6¡áïø³Ç~<ŸÃÿ´?ìyðëCÒîµ›‹?ü ø+¨þšUŒ×:·ÆOÚúÎ-göz¾ÿ‚NÿÊ,¿àšö`±¿þ³¯Ãšûþ€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€?0?j‡v¿o¯Ùà§‹¾ ~Ðøe¨þÈ·çÅ-cÃ?ÿiïÚ;ödÿ„‹Çþ4Á7ü'àwÅ:ÏìßñSáOˆÔ¼Uð¯ãÏüƒGøâm?Ãß´—Æß‹G†¼Aá­#âÇÄm.×Y𽞨ÜiÞ.Õloîníž¡ý_¯€>#ÊScû0ø)gþ´Wüv€>ÿ¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¯?h_ÚÚëàwÅ?„Ÿ<#û5þдÇÄߌþ3|RÑü3ð/PýœtOøG| ð+Ä<'ã}wÅ:Ïí!ûC~ÏžÌñíðëOÑ4Ïê¾#Ö/þÑ«ÝO§ÙYir\Ëõý|ñþR›ûÿÙ€ÁK?õ¢¿à“´Ãd~Ñ_ô‰ßÛÿÿ7üËÿ¦YGü6GíÿHý¿ÿðãÁ,¿úe•÷ýðü6GíÿHý¿ÿðãÁ,¿úe”Ãd~Ñ_ô‰ßÛÿÿ7üËÿ¦Y_Ñ@Œ>,ø½ûZk¿¶‡À/Ú2Ïþ [ûoÇàŸ…³íwðSÄ:]ÏÅø&xªóÅ_¾+~Ä^:ðv¡£XÅÿ›H¸ðþ›¤~Í~:·ñ5Õö¹§j6zŽ­á8t½+Y¶¾Ö/4§ÿá²?h¯úDïíÿÿ‡þ eÿÓ,¯¿è €?á²?h¯úDïíÿÿ‡þ eÿÓ,£þ#öŠÿ¤Nþßÿøq¿à–_ý2ÊûþŠøþ#öŠÿ¤Nþßÿøq¿à–_ý2Êè>~Ùúß~>øsösø•ûþÓÿ³¼iðâ¯Æ¿jŸõŸÙÄ~ñg…~ øÓà~ iú}÷ìÛûUþÐZ¾™â 3Wý ¾ÜZÚø£CÐ4íSNºÕf°Õg¹Ò¦³o·ëàˆßò”ߨßþÌþ Yÿ­ÿ ¿è¢Š(¢Šùö…ý­®¾|SøIðSÂ?³_íûL|MøÁðÿã7Å-Ã?õÙÇDÿ„wÀŸ¼GðCÂ~7×|S¬þÒ´7ìùáøüÏ~дýLðî«â=bÿí½Ôú}•–—%̾ÿ ‘ûEÒ'oÿü8ßðK/þ™e¿å)¿±¿ý˜ü³ÿZ+þ ;_ÐÀðÙ´Wý"wöÿÿÃÿ²ÿé–Qÿ ‘ûEÒ'oÿü8ßðK/þ™e}ÿE|ÿ ‘ûEÒ'oÿü8ßðK/þ™e|Áâϋߵ¦»ûh|ý£,ÿà•¿¶ü~ øWû0~×ÿ†Èý¢¿é¿·ÿþoø%—ÿL²ølÚ+þ‘;ûÿáÆÿ‚YôË+ïú(àølÚ+þ‘;ûÿáÆÿ‚YôË(ÿ†Èý¢¿é¿·ÿþoø%—ÿL²¾ÿ¢€> øMûgë~=øûáÏÙÏâWìûOþÌ~6ñ§ÁÿŠ¿üª|kÖdøWÅžø/ãO‚>ø§é÷ß³oíWûAjúgˆ4Í_ö‚øsqkkâ@ÓµM:ëUšÃUžçJšÍŠçþ#ÊScû0ø)gþ´WüvŠûþŠ( Š( Š( Š( Š( Š( Š( Š( ¾øÿ)MýÿìÀ?य़úÑ_ðIÚûþ¾øÿ)MýÿìÀ?य़úÑ_ðIÚûþŠ( Êø,wí1ûh~ÆŸ±ï‰iÏØ»Mø?ñÆÞñÃO Éðâ?ÀoŠß|UñÃÅ_>9üøðïÃ? u…¾jþñŸ«üAÔîÛFoüWÔ~"j7ðî‡má+˜n/u ÿ‚ŠÁV|wð;þ eð¯öëý¾ÂÀø›ûMü?ð?Å/ÙÿÀÿt|9áßKû:øßöÞø­®ünÓ´_Œ¾šÏþoÙà¯ÆU4χŸ|@ý ¼[§~×ÿ°¿Å-SÁß¼KðOºÆàOÙÏö¿ø/ûLøÿ]ŸQøïñoàׄ§óü%ðkUðŸ‡tÍ?Äךƣã¿xFÚëOÓ¼$Þ)ñ‡…áן¶Á߃_ðZ†ºG‚|?ñ³áO‰¾þ×?³ü·à—ÂKŸ5„Ÿ ÿà¤sÞülý©|!yâÿ‡Ÿ ´¿ƒþøí⯃ÞÓu‹?µˆZo…e_ßüøkà xßÂþ'€t²wüSþ ›ûP~Ôÿ³OìÑÿ ?öð?ü4Güàßüoþ_øbOÚ+Äßð‡ÿÂÛø‹áÏÂ…ÿ„sþ;áÿøH?áÿ„ƒû[þöö‰ý«öO°ºÓ~ÑöÈ~¿øeÿwÖ>xöÌýœ¿à Ÿ¿áVþ×_°Gì/í¯ñÛöwÿ„ïÇÿjÙÇ <;âo‰e/|^ðoÁûÛìO‰·ºß«ÿ…ÿoÚ×ÃÞ1‚ßÃzÆoŠxoâ~³à/ˆ?fÙWöäýŽ¿mØ?öŒÖ?bOŒ<ð¯þ áý—¿àž?´¿‚Ÿ?c”ñW‚?iÿüVðÿ޼iá]BÇãWíCðcHÖü?á­#ÃÛÝxÇÁÚç‰|?©j:¶•‡®õ›aªÞi_oüøûL\~×ÿ¿à°Ÿ¿f߈GÆ gûOØcö_ý~øãö{ñÅÛ/ÙcHý !ø}ñGã×Ä¿|lð¿ìå©þÐ|OøµñOâÿíðÿþ/‹ñ§üßþ ›ð öð—üSã.“ûxÃöeøÁû~Ïß<9ã]á×íðþÏö§ý¨>1~Ë>ðW…¾:xrÏãgí7ñâ¿ìÿðóáÿíâߊš¾·ðGÁüEø™ÿ Å>M#àþµªø÷Çß³ßðRÿ|Lø©ÿôý¶>|øm⋟¾3þ̾ |>ð†uχ¾Ôuo|cð¹ðËIÔ.uïŠ~6øyà/Ãþ¹ñRx«Å—ZŠ­µ𮉬 i^'ñKh¾Ö~`ø2ß·gì¿ÿmý•ü7ðwöEÿ„ãöÚýÿgÿÙ àî¯û'üKø§ðwÃ?ð˜¤×~|$øék¦ü_ð·Å_|%ðÿü$ |?ñ Çÿ üauã]oû+í~ ¼ñ‡ÃÝKĶúßÂ)€7ôoÚËãïìá§x³ö…ý¶þ7~ÄÿàœCö`¶øù ~Üß³ç‡kþ!¾ñÅׂ`øÁໟxÇÃ^;ðâÍoQð犼 ã}Y'“ä°~èðN¯ÚcCÿ‡³ø7ö:ý> ~ÿ±/íeÿÀý ü¡~Å?>/~Ïz§ü-ø)ÿÆøZÚV™ñcàÇ…>þÑŸ´gÂ_Ù÷áúü%¼ðÇÂi¶þøO;áž›oðÓXð×Ã}?_ðÓÿüûr|'øÿÅø[ã/ø'çÆ gÁ??àœ÷ìÉûAÜ~Ï¿ÿcŸþÚø·¤ø/áÁ GámŸÄÏþÑ_ ô…¿³Ä#À¿ðÑzÄÏÙ;ö¤ÚÄ¿¾~ÎÒøÖÏà-·Â¿xCâÀêÿÿà¡?°/ÂøB¿ái~Ü?²Ã_øY_ü9ñcá×ü'ÿ´·ÁÂ}ð³Æ?mÿ„Gâ_‚¿á"ñ®ÿ WÃÿgjðŽxËBû‡5ϰ^ÿfjW_eŸaàø(Oì ñ_þ_øU¿·ìñ+þ¯ÃÿüXø‹ÿí-ð_Æ?ð€ü,ðwØÿá.ø—ã_øG|k¨ÿÂ+ðÿ¿Ú:ü$~2×~ÁáÍíöÚz•¯Ú ßùÿêðíwû+ÿÂÿ…™û þÐ?aÿ‚@Á>ÿeÏÿÂ-ñ+ö(ÕáøíûÃxÂkàm_ퟵ¶•öïø]ð±~ÿ‹ñ7…ÿá!ðtßð±´Oø\þ!ø/ý…ãÿøC~ øñðŸöªøkÿÛøSá²Æi³?üCÿ"ýþ<øëUøƒû(ë~øqñ—Tø û9XèWV~ý¤|WãoøÄ6ß±Š5?^ðƒ¼Yžñ“à¤ZôZ6®ßô¿ƒ€Ò÷À¿ÚÇöXý¨?á)ÿ†hý¥¿gÿÚ#þìOøMáEüdøuñoþÿøI¿µÿáÿ„§þøƒþÿøH?á׿±?µ¾Éý«ý‰«ýƒíÙ·žO¿×äìõð§â/Œo¯†ÿ´ðO¯øa_‚_³ŸìãÏÙÃ7ž?ñì°¿þ%ÂÆøÑð#Æ¿>x+À²_Ä?Ž~ð¯ìÿû0xKö~ñ¾_|Vð÷Ù|GûA^迾Y隌|Gúý@|Fÿ”¦þÆÿö`ðRÏýh¯ø$í|Fÿ”¦þÆÿö`ðRÏýh¯ø$í÷ýQ@Q@Q@Q@Q@Q@Q@Q@|ñþR›ûÿÙ€ÁK?õ¢¿à“µ÷ý0xÓÁ>4ý˜?à¡¿4½/à¦à¿øªßÅ^#ø­ÿÝñÕŽ¡¨XøëâÃ"/Ť|8×-î®­õË­E5­*t©í§¼¼±þ‡¨¯ä þ#Vÿ‚YÑý¿ÿðÖ~οýTÄjßðK/ú ·ÿþÏÙ×ÿ¢ª€?¯Ú+ùÿˆÕ¿à–_ô@ÿoÿü5Ÿ³¯ÿEUñ·üËþˆíÿÿ†³öuÿ誠ëöŠþ@¿â5oø%—ý?Ûÿÿ gìëÿÑUGüF­ÿ²ÿ¢ûÿá¬ýú*¨úý¢¿/ø[þ eÿDöÿÿÃYû:ÿôUQÿ«Á,¿èþßÿøk?g_þŠªþ¿kàˆßò”ߨßþÌþ Yÿ­ÿ¯Àø[þ eÿDöÿÿÃYû:ÿôUWÐðN¿ø-_ì±ÿƒÿ‚¦ü0ÿ†hðíàøgØöÛÿ„×þ§…~xgûSþßíÿâÿ„sþoø@>*üMûoØ¿áYkßÛÚߨŸfû^‘öí/´^ý€ú}¢Š(¢Š(àˆßò”ߨßþÌþ Yÿ­ÿ¯¿ëùáÿ‚ÃÿÁP>Á&mØ#öŒý£ix~-#áÆ¹ouuo®]j)¨ÝiPÃ¥Om=ååÄñ·üËþˆíÿÿ†³öuÿ誠ëöŠþ@¿â5oø%—ý?Ûÿÿ gìëÿÑUGüF­ÿ²ÿ¢ûÿá¬ýú*¨úý¢¿/ø[þ eÿDöÿÿÃYû:ÿôUQÿ«Á,¿èþßÿøk?g_þŠªþ¿kÇþ?|øgûNüø…ðã6—â {áOÅoÍá?ˆ>ðÏþ!|0Ô|Iá[ÉíåÕ¼3sã…ž)ð_­|?â;hDñf§xŽÏNñw…oõŸxžÛVð¶½­h÷ÿËüF­ÿ²ÿ¢ûÿá¬ýú*¨ÿˆÕ¿à–_ô@ÿoÿü5Ÿ³¯ÿEUW¿ ~xsàÿ4/‡^Ô¾ jþðïöŸö~¡ñKâÇÅ?Ž>;¸þ×Ö5 vëûwâ—Æ¿|@ø›â*÷S¹ƒLÿ„›ÅÚ¿ö&ŸáÝìÒ4­2ËÐ+ùÿˆÕ¿à–_ô@ÿoÿü5Ÿ³¯ÿEUñ·üËþˆíÿÿ†³öuÿ誠ßïˆßò”ߨßþÌþ Yÿ­ÿ¢¿ àðZ¿Ùcþ ÿMøaÿ ÑàÚÀÿðÎÿ°í·ÿ ¯ü/O ü:ðÏö§ü-¿Ú+þ Åÿçü"ßð€|Uø›öß±²׿¶ÿµ¿±>Íö½#ìÚ_h½û@ÿÙscons-doc-2.3.0/doc/python10/job-task.fig0000644000175000017500000000541712114661557020661 0ustar dktrkranzdktrkranz#FIG 3.2 Landscape Center Inches Letter 100.00 Single -2 1200 2 6 4200 3900 5100 4500 2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5 4200 3900 5100 3900 5100 4500 4200 4500 4200 3900 4 1 0 50 0 0 16 0.0000 4 165 465 4650 4275 Task\001 -6 6 4200 5100 5100 5700 2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5 4200 5100 5100 5100 5100 5700 4200 5700 4200 5100 4 1 0 50 0 0 16 0.0000 4 165 525 4650 5475 Node\001 -6 6 6300 2100 7200 2700 2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5 6300 2100 7200 2100 7200 2700 6300 2700 6300 2100 4 1 0 50 0 0 16 0.0000 4 225 330 6750 2475 Sig\001 -6 6 6300 3300 7500 3900 2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5 6300 3300 7500 3300 7500 3900 6300 3900 6300 3300 4 1 0 50 0 0 16 0.0000 4 165 735 6900 3675 Walker\001 -6 6 4200 2100 5700 2700 2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5 4200 2100 5700 2100 5700 2700 4200 2700 4200 2100 4 1 0 50 0 0 16 0.0000 4 165 1155 4950 2475 TaskMaster\001 -6 6 2400 3300 3600 3900 2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5 2400 3300 3600 3300 3600 3900 2400 3900 2400 3300 4 1 0 50 0 0 16 0.0000 4 165 765 3000 3675 Parallel\001 -6 6 600 3300 1800 3900 2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5 600 3300 1800 3300 1800 3900 600 3900 600 3300 4 1 0 50 0 0 16 0.0000 4 165 585 1200 3675 Serial\001 -6 6 1200 2100 3000 2700 2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5 1200 2100 3000 2100 3000 2700 1200 2700 1200 2100 4 1 0 50 0 0 17 0.0000 4 165 420 2099 2475 Jobs\001 -6 2 3 0 1 0 7 50 0 -1 4.000 0 0 7 0 0 5 2700 2700 2660 2775 2700 2850 2740 2775 2700 2700 2 3 0 1 0 7 50 0 -1 4.000 0 0 7 0 0 5 5700 2400 5775 2440 5850 2400 5775 2360 5700 2400 2 3 0 1 0 7 50 0 -1 4.000 0 0 7 0 0 5 5400 2700 5360 2775 5400 2850 5440 2775 5400 2700 2 3 0 1 0 7 50 0 -1 4.000 0 0 7 0 0 5 4650 2700 4610 2775 4650 2850 4690 2775 4650 2700 2 3 0 1 0 7 50 0 -1 4.000 0 0 7 0 0 5 1500 2700 1460 2775 1500 2850 1540 2775 1500 2700 2 3 0 1 0 7 50 0 -1 4.000 0 0 7 0 0 5 4650 4500 4610 4575 4650 4650 4690 4575 4650 4500 2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 0 2 1 1 1.00 60.00 120.00 1500 2850 1500 3300 2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 0 2 1 1 1.00 60.00 120.00 2700 2850 2700 3300 2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 0 2 1 1 1.00 60.00 120.00 4650 2850 4650 3900 2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 0 2 1 1 1.00 60.00 120.00 4650 4650 4650 5100 2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 0 3 1 1 1.00 60.00 120.00 5400 2850 5400 3600 6300 3600 2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 0 2 1 1 1.00 60.00 120.00 5850 2400 6300 2400 2 3 0 1 0 7 50 0 -1 4.000 0 0 7 0 0 5 3000 2400 3075 2440 3150 2400 3075 2360 3000 2400 2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 0 2 1 1 1.00 60.00 120.00 3150 2400 4200 2400 2 1 1 1 0 7 50 0 -1 4.000 0 0 -1 0 0 2 2100 2100 2100 1800 2 1 1 1 0 7 50 0 -1 4.000 0 0 -1 0 0 2 4950 2100 4950 1800 2 1 1 1 0 7 50 0 -1 4.000 0 0 -1 0 0 2 6750 2100 6750 1800 scons-doc-2.3.0/doc/python10/job-task.jpg0000644000175000017500000006244712114661557020702 0ustar dktrkranzdktrkranzÿØÿàJFIFPPÿÛCÿÛCÿÀÎ"ÿÄ ÿĵ}!1AQa"q2‘¡#B±ÁRÑð$3br‚ %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyzƒ„…†‡ˆ‰Š’“”•–—˜™š¢£¤¥¦§¨©ª²³´µ¶·¸¹ºÂÃÄÅÆÇÈÉÊÒÓÔÕÖרÙÚáâãäåæçèéêñòóôõö÷øùúÿÄ ÿĵw!1AQaq"2B‘¡±Á #3RðbrÑ $4á%ñ&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz‚ƒ„…†‡ˆ‰Š’“”•–—˜™š¢£¤¥¦§¨©ª²³´µ¶·¸¹ºÂÃÄÅÆÇÈÉÊÒÓÔÕÖרÙÚâãäåæçèéêòóôõö÷øùúÿÚ ?þþ(¢Š(¢Š(¢Š(¢Š(¢Š(¢ŠùƒÅžøËuûh|ñއsâýŸ4/Ùƒö»ðÏÄû;oAgáYþ2ø³â·ìEª| ¹Ö| ÚÌ>#ñ¯‚|ûFEáŸCáíRéמ,Ñî5 OÚØøéúùƒÅžøËuûh|ñއsâýŸ4/Ùƒö»ðÏÄû;oAgáYþ2ø³â·ìEª| ¹Ö| ÚÌ>#ñ¯‚|ûFEáŸCáíRéמ,Ñî5 OÚØøéú(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Šùƒà†~2è_¿mÝSâ}ψ'ðO?iÿ ø›ös‹Yñd#Ó¬þ ZþÅÿ²'ƒµËo èðë:¤žðû~Ð~øíysá;«Oy⫯xétií¼ioâwéúùƒà†~2è_¿mÝSâ}ψ'ðO?iÿ ø›ös‹Yñd#Ó¬þ ZþÅÿ²'ƒµËo èðë:¤žðû~Ð~øíysá;«Oy⫯xétií¼ioâwéú(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š+æ€^øË¡|Vý·uO‰÷> ŸÁ>4ý§ü'âoÙÎ-gÅxN³ø5kûþÈž×-¼'£Ã¬ê’x ÃíûAøOãµåÏ„î¬|9=犮¼Mã¥Ñ§¶ñ¥¿ˆuß§ëæ€^øË¡|Vý·uO‰÷> ŸÁ>4ý§ü'âoÙÎ-gÅxN³ø5kûþÈž×-¼'£Ã¬ê’x ÃíûAøOãµåÏ„î¬|9=犮¼Mã¥Ñ§¶ñ¥¿ˆuЧ袊(¢Š(¢Š(¢Š(¢Š+óá?ükâÇ…Ÿ >5ü-ÿ‚]~ßþ(øeñƒáÿƒ~)|:ñ7ü&ðL­þ/|@ðæ›âÏë¿ØÞ"ÿ‚Ži Ò?µü?«éú‡öf»¥išÅ‡Ú>Ë©éöW±Oméý|ÿÿ”YÁ4ÿìÀ?cýg_‡4Ãd~Ñ_ô‰ßÛÿÿ7üËÿ¦YGü6GíÿHý¿ÿðãÁ,¿úe•÷ýðü6GíÿHý¿ÿðãÁ,¿úe”Ãd~Ñ_ô‰ßÛÿÿ7üËÿ¦Y_Ñ@Ãd~Ñ_ô‰ßÛÿÿ7üËÿ¦YGü6GíÿHý¿ÿðãÁ,¿úe•÷ýùâ?Ú+öÝÔjƒ´ø&—íÿaðKÁß³ÿí-àˆ?¿áuÁ5-á*ø§ñ+â/ìâ/ƒ¾5ÿ„F×þ >þ×?áð—ÂŽšü$zÞ¡kâ? ÿÂÊþÌðµ•þ™ãO§{ÿü6GíÿHý¿ÿðãÁ,¿úe•÷ýðü6GíÿHý¿ÿðãÁ,¿úe”Ãd~Ñ_ô‰ßÛÿÿ7üËÿ¦Y_Ñ@Ãd~Ñ_ô‰ßÛÿÿ7üËÿ¦YGü6GíÿHý¿ÿðãÁ,¿úe•÷ýðü6GíÿHý¿ÿðãÁ,¿úe”Ãd~Ñ_ô‰ßÛÿÿ7üËÿ¦Y_Ñ@?û=|kð¯í)ð àíà]?ÄO‚~?|øiñ¯ÁÚ_‹-tëi¾ø©à½Ç^ÓüMc£êºö‘gâ =#]³·Ömt½sYÓ­õîa±ÕudŠòo`¯€?à“¿ò‹/ø&Ÿý˜ìoÿ¬ëðæ¾ÿ Š( Š( Š( Š( Ì„ÿðQ¯‰ÿ~|4ø×ð·þ uûø£á—Ƈþ ø¥ðëÄßð™ÿÁ2´OøH¼ ñÚo‹<#®ÿcx‹þ 9¤xƒHþ×ðþ¯§êٚkhû.§§Ù^Å=´^ÿ ‘ûEÒ'oÿü8ßðK/þ™eðIßùE—üOþÌö7ÿÖuøs_ÐÀðÙ´Wý"wöÿÿÃÿ²ÿé–Qÿ ‘ûEÒ'oÿü8ßðK/þ™e}ÿE|ÿ ‘ûEÒ'oÿü8ßðK/þ™eðÙ´Wý"wöÿÿÃÿ²ÿé–WßôPäÁ¿Ú+öÝðwÄ_ÚÇÄ_àš_·ÿм%ñ_ö€ðçþè_ðº¿àššçü+_…šwì±û4ü-Õüý™­ÿÁGítÏ}¿ãoÃ_Œ_áð|ú‡.¿á>ÿ„ºêö?ø«Å:}‡¿ÿÃd~Ñ_ô‰ßÛÿÿ7üËÿ¦Y_Ñ@Ãd~Ñ_ô‰ßÛÿÿ7üËÿ¦YGü6GíÿHý¿ÿðãÁ,¿úe•÷ýðü6GíÿHý¿ÿðãÁ,¿úe”Ãd~Ñ_ô‰ßÛÿÿ7üËÿ¦Y_Ñ@|&ý³õ¿ü}ðçìçñ+ö?ý§ÿf?xÓàÿÅ_~Õ>5ë?²?ˆü+âÏ üñ§Áü@ÓôûïÙ·ö«ý µ}3Äf¯ûA|9¸µµñF‡ iÚ¦uªÍaªÏs¥Mfßo×À¿å)¿±¿ý˜ü³ÿZ+þ ;_ÐEP_ ~пµµ×ÀïŠ > xGöký ?i‰¿>üfø¥£øgà^¡û8èŸðŽøàWˆþxOÆúïŠuŸÚCö†ýŸ þÑ_¶ïƒ¾"þÖ>"øƒÿÒý¿üUá/Šÿ´‡<ð/Bÿ…ÕÿÔ×?áZü,Ó¿eÙ§án¯à¯ìÍoþ ?k¦x;íÿ~übø‹ÿçƒçÔ|9uÿ ÷ü%×W±ø·Å^)Óì?_¨ €?á²?h¯úDïíÿÿ‡þ eÿÓ,£þ#öŠÿ¤Nþßÿøq¿à–_ý2ÊûþŠøþ#öŠÿ¤Nþßÿøq¿à–_ý2Êúözø×á_ÚSàÀÿÚ3Àºˆ4Ÿü~ø?ðÓã_ƒ´¿ZéÖ>*Ó|+ñSÁz/޼=§øšÇGÕuí"ÏÄzF»go¬Úézæ³§[ê1ÜÃcªê6ÉäÞÁ_Á'å_ðM?û0ØßÿY×áÍ}ÿEPEPEP_Á'å_ðM?û0ØßÿY×áÍ}ÿ_Á'å_ðM?û0ØßÿY×áÍ}ÿEPEP_Œ?±ïü6ü3ÿ‚Žü]ý«í |`øñãá[¨þxV×NÕ5OJ:oƒ4[h/®ôö£ø‰ñÛáGÀŸøÿöhý?ᬾ6èðŒÿÂû?ÿÂÝðwÀøO¿µÿÁP?bωÿðÑvÞø‹ñGñìŸÿ Š>ø—û6þÓ¿~)ø7Xý >ÝÀé¿ >/üð7Äßü@øÕ{gðŸá×ï ø·Çðî¯âø3M×¾ øÍû0üoø„Ÿ´í›àïÙ#Äø­ñ¿ãüë[¼ý”lµÿÙª×ö‡ñß…à›?·ž‘ñ×Ç_>(|CÒþ1ÚþÍÓü`ñ§Âïø‡Àþðl¿¼e§Xü$øðnMcãâŸ_|ø!ÏþØ?²÷Ä_ÛZûŴ߈¿`¿Úᯋ|'û?éÿ²Î…ðãÂeƒ¿·×ÄøLlŸÙ#öŠÓ?h†?>üuñßÁ/‡_ðÀºŸÀYþ<þËÞø‹ñúÿþ'Æ|Sðw‹¼ðOÂVº‰¿h0ÕÿÙ[öÉýž?mO üEñìçãx³HøGñƒÅß~'Yø³ágÅ¿ƒž*ð/Æ_iÞÕý 4=M&|Uø ð×UøŸñWörøk®|4Õü=ñƒÇ:¾¡¯x†ÿĶ>ûy|=ðÖ£¦ü*ðwÿ„Þ øðÅ~ øŸíÿ…¿ðHÙàÿ4/‡¾×ÿkÿxFþÓ¼}#áoí÷ûkþξ¸ñ‰5CÅ>4ñ…ð/ö_øñðKörøUÿ —Œõ­ÆŸ‚¾üø]ð³DÖ5ÝB×ÁðŸ‡ÖÇD²ý?¢¾ÿ‡iþοôQ¿oÿü[ü7ÿ£"øvŸìëÿEöÿÿűÁSú2(ïú+àøvŸìëÿEöÿÿűÁSú2(ÿ‡iþοôQ¿oÿü[ü7ÿ£"€>ÿ¢¾ÿ‡iþοôQ¿oÿü[ü7ÿ£"øvŸìëÿEöÿÿűÁSú2(íÿx³Â¾𯉼uã¯xÁ~ ð_‡õŸxÇÆ>,ÖtïxWÂ~ðæs¬x‡ÄÞ&ñ±sg¤h>д‹;ÍSYÖuKË];KÓ­no¯®`¶‚YWÏþ ~п¿iO ê:ýœþ8üøýà'Ä^Õ ñ7Â/~ØÏàŸŠžÑ®uhµgáÇŒQ¼+ã:ÚçÃ!VÒ5KÀ0cÏø4cþ £û1x«ÀŸüuãoÚöƒø­àßø^êßXÔ¾0êßü+ῌ¾Ô|1â~5ü$‹ösáŸÆO‡^ ÑüI ^]xJÕ>;xòéÚÔ‹}¨x£Å:F‡ãÙïø$ïü¢Ëþ §ÿfûÿë:ü9¯@ý°µÛŠßGø£~šgìÿÿ o‹h xwã—Ži ž-ð'ÂOÙÇþOˆúçþ"èþ ð'Å/„ž-ñÇÄ èŸü'àé^&û.·¬x¶;oOá/ oüIð?ŸÿÁ'å_ðM?û0ØßÿY×áÍ}ÿ@Îì{ûjÁb?jc㎳á?ø`xKöJÿ‚¿ø—þ íñïÀÚgÀ?Ž_ øûÀ¿uø7àæ„ÞÖ>üSñ›ðãLð<_4>2ÊèðQOø*ÏŽþÁ,¾þݱ·ÂOøXi¿‡þø¥û?øã.“£¯‡<;àIg_þÛßµßÚv‹ñ—ÀSYÿ û"üøÓª¦™ðóâŠ5‹ÿŠzw„¼7áý?ÅÖZœ­/¿þÎðQ?…šGì;û~ÐÿðP/Úƒö@ýŸþ&þ׳ÿ‚~:iðx‹Æžýš¼ «ÿÂuá/ üEºð·Ã¯ükøËã/kßð¬é ðÿÆÏ…>&ø?û\þÎ?ðFß‚_ ,~|Ô~|3ÿ‚‘Ï{ñ³ö¥ð„zç‹þ|6Òþøcã·Š¾x_MÕþ,üFÔ~!i¾ý•|{ðcᯀ<-ã ü?øŸÏøçö^ÿ‚‡Gûø‡ö)Ö¿`¿ˆ"ÿ„‹þƒû.~Æ~ ñ—ì¿ñçöJðŸˆõÏÚcÂüM¦øÃ¿¶wÄ¿üuýŸ~-Yü?ýœþ-,ZÆ_³ÿо6~Ï~>øYñ³ö˜Õ~+Kñ‹þ—Âo…Ð÷‰¿mߨ¿Ám¿g?þ׳„ÿh;ÏxOÂv|Mñ÷áNƒñ–ëÅ^=ƒFºð/†m¾jž,µñ´þ ñ¥·ˆü=qá=- µÁ®èÓhöבê–-?Ïÿ¿à­¿ðOOÚ3¿µ?ÄO‡ßµÁû?…?±÷Æ /àßÅÿ‹¾6øà|3‡Q×´ï  xûCñV¿â‹[iþøÓÆÞ#Õ¾|6ø¡­G øWâÇÄ/xæßáeïŒü-g¡x«Äxïöaý¤~(~ß¾øáàoÙ#ãÂß x£öŸý?h_‹ºíM¯þÂß´×ìKã>þΞ𿊾8Øø|cñíGûÁGþ èzö«ðáÏÄ¿ØúËÇ_¼yãÙ—à†£ãýkÅ ~$x«âo€8>…ÿ/gë_‰? >3x·á~‘ñ›B½ñ/Ä/j Ó÷ˆ¿à¡?°/„< ðëâ—‹?nÙÂÿ ¾0Â]ÿ “â/ˆ¿io‚ú'>(ÿÂ¿Ö ðïá]x»Rñ­·‡ümÿOˆ.m´/Â3¨jðëišÏدeŽúþ¿žØ›öZø™û#ü[ýõo„ßðLÿŒ³ÏìÝá¿ÿÁD¾ÛüÓmO‡¿´ïŠ¿g¿ þÑ_´?ìâ†~-¿ÆÿÚ #áׇüw¤~Ï>)ø÷àOìƒñö¢Ó¼ ¨ßÇ­XKâïŠ?<_§AýPçÇ?xWÀ_ðROÙsÇ^:ñ7‡üàŸÿÁ8?ਾ,ñŒ|Y¬éÞð¯„ü+áÏ?ðJmcÄ>&ñ7ˆu‹›=#Aðþƒ¤YÞjšÎ³ª^ZéÚ^ks}}s´Ê¿P| ý¨þþÒð”ÿ˜ñÏü%¿ð‰b_^}«Ã>1ð—ö[ý¯ÿÅ߇ßðšø{ßð´ÿgÿŠðŽx£þïíðÏþß?¿áñ‡ü*ÿˆž-ÿ„KÄÙœ·‡ìŸð÷ö´ÿ‚’Á<´_êÞ ð‡Š~ ~Ì·÷Çï‚_|'£ü3ñ/оüeðOÇŸø%þƒ |BðÏ…þ6|;ø½ðo\ñ›á¿ø·IѦø‡ð³ÆÐxKQÖí¾ øßü!ðÿǾõÿÁ:µÚ?Ǿ+þÞ_ÿáo|@ø#ÿ töKðŽÿ`á5×ôx9üJý¶¬> xÇÃWZ÷~!èú/훡~Î>?ð#ø:þÍóü@ð—ñìôþŠøþ§û:ÿÑFý¿ÿñl_ðTßþŒŠ?áÚ³¯ýoÛÿÿÅÿMÿèÈ  <ûXþË >)ø+à_Å/Ú[öøkñ·âWü#Ÿð®¾øÿã'ï|Sñ÷ü&>#½ð„á ø{â/éÞ-ñWü%^-Óµ xsû H¿þÜñ…}«SµžÕ>øÿ)MýÿìÀ?य़úÑ_ðIÚüýµ¿àÖÙ;öèý©ôŽ_¿i¯ÚÿHøeáßÙÿJøQgð×þwÄŸŽ?ßÇzGÄ_ø¾ßÇ?ðÑß¶/Ži¿ißÛÃþ.Ô4øRZ‚4ÓÄqÂw¦xªÂ÷Wñ>‘¯ûÿìwÿËý“¿à˜·×ì“ð“öMð÷ĸŰü SñýÏ~.üIøÿ ߎü5ñ£þ -á›ßŠW¾ñ'‰.~x'⬭­$ñþ¡ð‡À_ ôÿfxsM¸Ð¢ðÿƒ<£xt÷úŠ( À‡·ÏíõûVk·×ˆc/~À>ñì/û_üKýŸî¿àŸþ)ð÷Æ‹OÚ;Ç^ø#ã±à½B_~Õz¯Æ|2ø)ñöœ²ðGÅ~ÏÔ?²çÅ¿>º xÇ_µÿxCã]×€?Gü ûBøªÇö‡ý½tŒßb ?ÙóökðÿÀßxWKð7ĽFßö‡øá]wá&»ã¯‹¾&ý½l|U­'‚~øY¹Ò_Æ?u:-NÔ~iºïˆ|Os4–fò?ÄÛÇöwý¿kïk_<ÿßñÀOø)ïìáûOø‡áïüÃþ }ðûö‚ý4Oƒ^ý‘Ûö Ð/´ˆ<3í7yñ§]ðþ½û4Þ|CðÿÄ?€~3ýšÿi æ|BÔ|%ðûíñÇÆ ¼7ôÿü—áŽüykÿ[“Åßþ x/áoí/û Á8¿`_ƒ~?Ó>$hþÖ>.|]ñÇíûVxZô|-ÒþüO·ñmçö/‹m„^Óþ~Òº—ìÛðOã÷ŽÆ»ð“ãgÄ¿‡Ÿ²—‰¼}ñßHý_ø)ûnþÅÿ´§ŠµþεßìÁñûÆÚO‡î¼Yªx;à§Çß…?ø‚ûö¼ñü5wì=â¯Ø³áŸÅ½;áçÃßÙÓöNñ ïìËàÝcã/í%s¦øá/‚<û3ØüRÐ|wñËÅ>*Ô¾!|T¸ñ¯„åOkÒø+á‡îõðüwþQeÿÓÿ³ýÿõ~×ßõðüwþQeÿÓÿ³ýÿõ~ÐßôQEQEQEðüwþQeÿÓÿ³ýÿõ~×ßõøÃû|^ý­?f¿Ø¿öDýœüuÿ­ý·õo|ý˜>|ñŽ©á?Š?ðL;ï ê^*øWð§Â~ñ¡á›ícþ / ê÷ž¼Õô+ËëTÐômFãN’Úkí+N¹yláýž¢¾ÿ†Èý¢¿é¿·ÿþoø%—ÿL²¼Æ_ðVÍSÀ?¾þÍ(ÿ‚kþßö¿~6iÂà­7ÄðN?oûƒ¾%øþÏþ/øsþ «xKáwü%þø-ñŸVøiÿ C^ðwü-øS¿,>ÿÂY©ü5ñ­–†úýE|ÿ ‘ûEÒ'oÿü8ßðK/þ™eðÙ´Wý"wöÿÿÃÿ²ÿé–PßôWÀðÙ´Wý"wöÿÿÃÿ²ÿé–Qÿ ‘ûEÒ'oÿü8ßðK/þ™e}ÿE|ÿ ‘ûEÒ'oÿü8ßðK/þ™eðÙ´Wý"wöÿÿÃÿ²ÿé–PßôWÀðÙ´Wý"wöÿÿÃÿ²ÿé–Qÿ ‘ûEÒ'oÿü8ßðK/þ™e}ÿE|ÿ ‘ûEÒ'oÿü8ßðK/þ™exŒ¿à­š§€~;|'ýš­ðÓþ†½àïøZÿð§~,X|3ÿ„³Søkã[- õúŠøþ#öŠÿ¤Nþßÿøq¿à–_ý2Ê?á²?h¯úDïíÿÿ‡þ eÿÓ, þ ;ÿ(²ÿ‚iÿÙ€~Æÿúοkïúùþ íð·Ç¿`_Ø{à§Å- þ‰¿ÿdÙ§áoÄ_ ÿièúßü#¾;øð_Á^ñv…ý³áÝCWðþ¯ý‘â #PÓÿ´ô-WSÑïþÏö­3P½²– ™~¿ Š( Š( Š( Š( €?à“¿ò‹/ø&Ÿý˜ìoÿ¬ëðæ¾ÿ¯ÆØ‹â÷íiû5þÅÿ²'ìçã¯ø%oí¿«xÛàìÁð à§ŒuO üQÿ‚aßxWRñW¿…>ð/ˆu ßkðQ}W¼ðý毡^\h×Z¦‡£j7t–Ó_iZuËËgÓÿðÙ´Wý"wöÿÿÃÿ²ÿé–PßôWÀðÙ´Wý"wöÿÿÃÿ²ÿé–W€~ÒÿðVÍSö?øY«|gý¡ÿàšÿ·ÿ>èÿoóïlüAÿãñ߈õìkÞ5ñü#žøuÿ ñoÄÿÂ%ðÿÂ^4ø™ã_øE¼/¬Âð³Àž?ø¡â¿ì‡þñwˆôP×ê+àølÚ+þ‘;ûÿáÆÿ‚YôË(ÿ†Èý¢¿é¿·ÿþoø%—ÿL²€>ÿ¢¾ÿ†Èý¢¿é¿·ÿþoø%—ÿL²ølÚ+þ‘;ûÿáÆÿ‚YôË(ïú+àølÚ+þ‘;ûÿáÆÿ‚YôË+ŸñgíëñƒÀ^ñ7޼uÿÃý·üàŸøYñgŒ|câÏ‹ÿðJxWÂ~ðæs¬x‡ÄÞ&ñ±ÿ5³Ò4èZE橬ë:¥å®¥éÖ·7××0[A,ªú?E~P|ÿ‚ xûö”øg¥ü]øEÿ±ÿ‚êÞ Õ¼Aãÿ Ç'‹'ÿ‚z|+ñV›â¯…¼Sð£â'†|Mðïâ¿üÁ?þËß?iÏø/ö`ýµþ xßKø)âï٫Þ*🊾4|Vý‚üuðÿPÔ,i/Úö}Ò5?êzGìûñÞêëÂú濨éz®• þ•¶« â~Ñ_Ãd~Ñ_ô‰ßÛÿÿ7üËÿ¦YGü6GíÿHý¿ÿðãÁ,¿úe”÷ýxÿÇï€?¿j_ƒ_¿gÏÚáï‡þ*|ø©áù¼3ã¯øšßNÕôçžÞúÎæÚòÆâÏWмA¡jözwˆ¼'âÏj:Oмâ­'Fñ_…5Äš6—ªZ|Áÿ ‘ûEÒ'oÿü8ßðK/þ™eðÙ´Wý"wöÿÿÃÿ²ÿé–PгßìÅð‹ö_Ñüy£|$´øÿCâ|Rø…âŠ_>6~Ð;ñ‡Žÿáð'ÃuÝwâ?Çïˆ_~ ^ý‹áÿÃ/øOLÓ$ñ7ö>•£øgO¶ÓtûOô†›ßëàølÚ+þ‘;ûÿáÆÿ‚YôË(ÿ†Èý¢¿é¿·ÿþoø%—ÿL²€>ÿ¯€?à“¿ò‹/ø&Ÿý˜ìoÿ¬ëðæølÚ+þ‘;ûÿáÆÿ‚YôË+Ð?àžß |wð;öý‡¾ |Rпáø›ðö@ýš~üEðÏöž­ÿÂ;㿇ÿüá?h_Û>Ô5êÿÙ Ò5 ?ûOBÕu=ÿìÿjÓ5 Û)`¹”ëú(¢€ (¢€ (¢€ (¢€ ü!ý©ü'â«Úƒã·ì{/†|AâOÁOüAû?xûà¯ík£j:–­û5üý˜¼?àÍöŸ²ðÿŒ´koêÿ ñ'¿à§þ ýŸ¼}ðWö‰µÑµKVýšþ~Ì^ðf‹ûOÙxÆZ5·Šõ€ ý‰µhÿ´Ïì5ñ“Ä)aà¸à¥ßðR¯…-àï…ò\øGãïÅ K÷z¾øÿ)MýÿìÀ?य़úÑ_ðIÚûþŠ( Š( Š( Š( Š( Š( Š( ¿0?à¡>?ÿ†dñßìÓûpø³Á_>3|ø ÿ —áŠ>ü7ðçü';Ó¾)þÒú?‚¼û<|gøCðø^Ë{ñö€Ôþ&ø^Ûöøaá 躈×Gÿ‚‡ü@ñ³ñ3á÷Áÿ|\µñwéý|ÿ,ÿ“uøsÿgÿÿÿצþÆôì±ÁO~ϲÿ„~:Ô–ò§è¢€ (¢€ ùöõø[㿌_²wÅoü6пá8ñm¿ü ¾=¶ø=>§£èš?í'£ü$ø“àï‹,ý‘¼]­ø›PÓü%¢ü?ý°<%à­sö_ø‹«øÙ5¿i~ø·â+ïøCÇ·Ö|®ýE~pÁ?üM§~ÐúÇßÛÛÁÖÞ ð×Á¿Ú÷Ä?ý«> ü2ñ~¦êðü`ý¥5{ÏቲÎ'ø‹û|;ý4ýcÃþñ'õ½Ñúøþ ;ÿ(²ÿ‚iÿÙ€~Æÿúοkïú(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š+àˆßò”ߨßþÌþ Yÿ­ÿ¯¿ëù‚ý·ÿà¦ÿÿgø8[þ ùû%éÿ²?ü-ßøY¿³ÿ‹>øGÅÞø½â;ßÚ|,ý³~>~ÎsüEøÏã_‡P|¾ÿ„sþŸþgâ—‰¼Gá}3Æ>'ð牾^ÙüC×~&|2þÇñ‡tÐéöŠ( Š( Š( Š( ¾øÿ)MýÿìÀ?य़úÑ_ðIÚûþ¿˜/Ûþ oñOöwÿƒ…¿àŸŸ²^Ÿû#ÿÂÝÿ…›û?ø³á„|]à‹Þ#½ñ§ÂÏÛ7ãçìç?Ä_Œþ5øuÀ[ïøG?á™ÿá†~)x›Ä~Ó|`øàߊ_¼Mÿ Ÿü+Dÿ„‹ÀŸ<9¦ø³Â:ïö7ˆ¿à£šGˆ4íêú~¡ý™®éZf±aö²êz}•ìSÛEèðÙ´Wý"wöÿÿÃÿ²ÿé–Qÿÿ”YÁ4ÿìÀ?cýg_‡5÷ý|ÿ ‘ûEÒ'oÿü8ßðK/þ™eðÙ´Wý"wöÿÿÃÿ²ÿé–WßôPÀðÙ´Wý"wöÿÿÃÿ²ÿé–Qÿ ‘ûEÒ'oÿü8ßðK/þ™e}ÿE|ÿ ‘ûEÒ'oÿü8ßðK/þ™e~p|nð7ÆŒ¿ðRïØ[öÿºÿ‚KþÛú&¯û|ý¬<'qªhß¿à”oÄ/ø«ã>“ðÿÀ¿ |3âËé?oRçÅ_þx'Äÿµ/ˆ-´gñŸ†5 |Lñ¿†uo ÛjºG‰~"YÜCÔPÀðÙ´Wý"wöÿÿÃÿ²ÿé–Qÿ ‘ûEÒ'oÿü8ßðK/þ™e}ÿExÿìõñ¯Â¿´§À/ÿ´gtÿi> øýðá§Æ¿i~,µÓ¬|U¦øWâ§‚ô_x{Oñ5ŽªëÚEŸˆ,ôvÎßYµÒõÍgN·Ôc¹†ÇUÔm’+ɽ‚¾ÿ‚NÿÊ,¿àšö`±¿þ³¯Ãšûþ€ (¢€ üÀøOÿøŸñÇágÃO à—_·ÿŠ>|`øàߊ_¼Mÿ Ÿü+Dÿ„‹ÀŸ<9¦ø³Â:ïö7ˆ¿à£šGˆ4íêú~¡ý™®éZf±aö²êz}•ìSÛEú_Á'å_ðM?û0ØßÿY×áÍðÙ´Wý"wöÿÿÃÿ²ÿé–WçÆï|`øËÿ.ý…¿oû¯ø$¿í¿¢jÿ±×ÁÿÚÃÂw¦ñ þ A¦üBñÿо3é?ü ð§Ã>,¾“öùÕ.|Uðáç‚|OûRø‚ÛFøcQð—ÄÏøgVðݶ«¤x—â%Çô=E|ÿ ‘ûEÒ'oÿü8ßðK/þ™eðÙ´Wý"wöÿÿÃÿ²ÿé–WßôPÀðÙ´Wý"wöÿÿÃÿ²ÿé–Qÿ ‘ûEÒ'oÿü8ßðK/þ™e}ÿE|ÿ ‘ûEÒ'oÿü8ßðK/þ™eðÙ´Wý"wöÿÿÃÿ²ÿé–WßôPÀðÙ´Wý"wöÿÿÃÿ²ÿé–WÓÿ³×Æ¿ þÒŸ¾þÑžÓüA¤ø'ã÷Áÿ†Ÿü¥ø²×N±ñV›á_Šž Ñ|uáí?ÄÖ:>«¯i~ ³Ò5Û;}f×K×5:ßQŽæWQ¶H¯&ö øþ ;ÿ(²ÿ‚iÿÙ€~Æÿúοhïú(¢€ (¢€ (¢€ øþ Yÿ&ëðçþÏÿþ ;ÿ¯Mýëïúøþ Yÿ&ëðçþÏÿþ ;ÿ¯Mýèïú(¢€ (¢€ (¢€>ÿ‚NÿÊ,¿àšö`±¿þ³¯Ãšûþ¾ÿ‚NÿÊ,¿àšö`±¿þ³¯Ãšûþ€ (¢€ (¢€ ùö…ý­®¾|SøIðSÂ?³_íûL|MøÁðÿã7Å-Ã?õÙÇDÿ„wÀŸ¼GðCÂ~7×|S¬þÒ´7ìùáøüÏ~дýLðî«â=bÿí½Ôú}•–—%Ì¿_×À¿å)¿±¿ý˜ü³ÿZ+þ ;@ü6GíÿHý¿ÿðãÁ,¿úe”Ãd~Ñ_ô‰ßÛÿÿ7üËÿ¦Y_Ñ@Ãd~Ñ_ô‰ßÛÿÿ7üËÿ¦YGü6GíÿHý¿ÿðãÁ,¿úe•÷ýðü6GíÿHý¿ÿðãÁ,¿úe”Ãd~Ñ_ô‰ßÛÿÿ7üËÿ¦Y_Ñ@Ãd~Ñ_ô‰ßÛÿÿ7üËÿ¦YGü6GíÿHý¿ÿðãÁ,¿úe•÷ýðü6GíÿHý¿ÿðãÁ,¿úe•Ð|&ý³õ¿ü}ðçìçñ+ö?ý§ÿf?xÓàÿÅ_~Õ>5ë?²?ˆü+âÏ üñ§Áü@ÓôûïÙ·ö«ý µ}3Äf¯ûA|9¸µµñF‡ iÚ¦uªÍaªÏs¥Mfßo×À¿å)¿±¿ý˜ü³ÿZ+þ ;@üwþQeÿÓÿ³ýÿõ~×ßõðüwþQeÿÓÿ³ýÿõ~×ßôàµÇOøeÿÙcö–ý¥ÿáÿ„ãþßöøÉñÓþ¯í¿øFá0ÿ…IðëÄ~?ÿ„[þ?ìÂ?ÿ ü#ÿÙ?ÛØ:ßöWÚþßý‘©}Ÿìs|ûþп·×Ç}ö5øùsñ#öý°¿d_Úcáÿˆï>*øÿöKðÆZÇìóã< qâ?xÃ7¿¾;ü\½øÿðþÛâo…|pñìçá?ø÷öƒÐ~üKÖ~øÅ—0YøWÆŸt¿ëWß <'âk˯x.Ú×Ãþ#ñ´¬ÜÜxÇÂpA§^\Ë7‰´(ÕµK_ç Ëöý°¾.þØRülý˜ÿeŸŒðF ŸÚóö`ý¯4ø*‡Ä9¾;ü ø‘á_‰Ÿ~'| Ð|û0xßàÿg_ÚWÇ’jÿ?gÏÚÄ^6øÛoñÊÓáÿìkãOiÃâ¿â/ÚxÛâç‰|%â ßí7öÝý‹õŸ|[ð.û]þÌ·¾øǾ,øíàí7ãï›ï|ð¯Â½F=≾-øz×Å’êÿx[Ç^ðÏìcðÏÅš§‚|U⇗>'Ó ~Ç_¿diÙ5} 5Ox#ÂÝ~ÿ|MƒãÇŸŠßðLAû/|`øa¤|8ý§þ7|køßoñ'Åß³TºÀß é?±í‹û9øCOñ´? ÿh‰6Þ)ñÅ|vðN©àk_‚÷Ÿ´í+±k—ßu_‡½…¾‡|õü5ì±ÿ Ûþsþ[öÿ†šÿ£tÿ…Éðëþ·ü‰ßð±?ä‘ÂGÿ þIÿüW?ò/Èÿ7üÓ«ÏÿaŒ_þ8þÏóø»ã]×ÃýGâo…ÿhÛà_‰µ…¾ñÃÿx‹þ“ö¿øéû7èÞ)мâψ_¼AáoøJ|?ð§Jñ§¢j|]ö cSÔ µÕå²[hâü ð‚nOþÐÿ±Wо.Á?>0xWøÿý­>0øëSøñ¿ö9‹ö%ð_ÁÚá'íYð*ÏÆ_ <#cûE|$øÉñÃÄ÷‰>(ü5ý§~,|Xý ¿eè?iYüqñöºÕ¾èžÒ>"é³…×éÿüËÀß<û7_Ùü]ømâ„6ñ§í?ûx|k“á·‹5χ¾#ñW„ü+ûBþÝ?´oLJz‰µo…6øðî_Kðïâ?…oµ›_ øëÅv—¨Ý\égUžæÊà(ÛôQE|ÿÿ”YÁ4ÿìÀ?cýg_‡5÷ý|ÿÿ”YÁ4ÿìÀ?cýg_‡5÷ýQEðüwþQeÿÓÿ³ýÿõ~×ßõðüwþQeÿÓÿ³ýÿõ~ÐßõüÁ|/ÿ‚“ÁS|Yû8ÿÁO?j{wöñO‡ÿà•ÿµÿí[ð/ÇŸ ý›ÿh¯…ÚÇíð³ö)µðŽ>-ø§Â?dý±>2Y|øãï†Z—‹áÖ‰¬üøÓáÍ Çzw‡l|M«ê>Õµ-WFþŸkù‚ý†?à“ÿÛ?à¤Þÿ‚€~È¿´¯ðËãüÿöžýµ¾júŸíoöÿØwãÂÍ_âïÃüÐ>)~Èß ¿ké|?ñâ§â ÜüCÔ4¯¿²^¯áËß xwBðÏüW-î‘áÏ[€~Ï|9ÿ‚Ž~Åþ=ð¯ìµ¬k´7Áÿ„6ý±þüø×ð'àOÆ¿‹_ ~ü}ñg…h]:Æëᆟ§ü0ºñÅæ¯¯xƒ^Õï%ðu­¯ƒ¥ñFªxÓLÕ|=áíW[¹²fo`ðícû,|Wø§ã_ ioÙÿâWÆß†¿ð‘ÿÂÅø;àŒŸ¼cñOÀ?ð‡xŽÏÁþ.ÿ„×áï‡|G¨ø·Â¿ðŠø·QÓü-â?íÝ"ÃûÄwöz&§ö]Nê Wü`ý«|ûrxóâƽ&ø'çÆi7üöý¦þx‹öRøßûø?à×Å„Ÿ²wÄÏÙ³ÆÞ/ø¥ñîïâgíû/üvø±ûOüEømàÏ|,Õ¾ühø}ãÙãÀ3üý“t_…ׇWøSqûNxÇè øWöÂðWì5ÿø!áOØÃþ>øÉÆø(Ÿ¾ø'ö ñ×À˯ÙöÁð¯ícûc~Ôÿ<%¡¤ø™ñÅ2ø~_…?ü=oãO†ß´„?g½;Æ~ ñÃ=GÇ> ðÞ£ãŠ>õÿÚ?þ 'ð³Wý‡m¿Úþ ûûP~È´ÄßÙöñ·ÇMBøÓß´¯4øA|%⯈¶¾ø‹áß‚Ÿ|â þ‡þøÏÂÞÖæñ¦ý‘¬$þ+Hñ¥—„µ_êÿ@|:øÅñOWý¸¿jŸÙãÅ—_ïþ|7ýŸÿc¿ŽŸ 'ðïƒüGáÿéð¾ü[û\|:ñï…¾"ø‹Rø…âŸøóìž ý˜m¼SáoÃ> øcý‘£øÎ k:G‰otü]«þ0|Sø5ûf|Jð¯ü°é?°÷í¿¨x§öÅÿ‚P|ý“ü®þÐ_¿àŸÚ—о#þÐÿ´ïÚ×Ὣ^h ¿m WàßÁëþý§>|]×´…žøMð’óQðGíâ­áÞ•ñ[Å~ðׯ? ?à °§í?ûrÏÿdøUðËUñìÇmûCþİwìãðOãçˆ|SðWÀ¿Š_´·ìÿð×ãoįøG?á]|ñÿÆO‡^ø§ãïøL|G{àÿÿÂð÷Ä^#Ó¼[â¯øJ¼[§jðçö‘ý¹â; ÝLûV§k=ª|ÿñþR›ûÿÙ€ÁK?õ¢¿à“µùûkÁ¬ÿ²wíÑûSè¾)~Ó_µþ‘ðËÿ³þ•ð¢Ïá¯ü.ï‰?~)¿Žôˆ¾+ñ}¿Ž᣿l_þÓ~ Ó¾·‡ü]¨hð¤´/i=§ˆâÿ„ïLñU…î¯â}#_÷ÿØïþ —û'Á0?o¯Ù'á'ì›áïˆðÿŠ?`ø(§ãûŸü]ø“ñþ¿økãGü[Ã7¿¯|'âO\ü2ðOÄY[ZIãýCှèþ'þÌðæ›q¡Eáÿx+Fðèïõ|ÿAøÅûSþÎ_°ïÇïÚCöCºýŸÛâoìëðÿÅß-üø‘ðÇögøßðïã.½ñ àïìéáŒ^/“Ä??iýCHøMðâ‘⻊5+Ï|Oø™ Áx¿³·Ž¼m¨é·´çÓÿ²×íuñNëá×íuûKþÝ?d‚Ÿ³/€kÿÚOà_À]rÁ¼Gð£þï…Ÿ³ßíOñ;öV´ñOí/ñkãÅ;ÿ‡÷_>%|@ðuŽ“ èž Ð|áË/I¿‹W×5¯‰0øá¯à„¿à€_µÁø'íMá?…!þßý®¾~×þ ø¥ÿ‚Óþ!x÷á´ßð«> þÃ_µ¯íñ¯öe×|auáχ^øeâÚã…—íMû^xšëLøÕsã„_ñyþèÞ6Óþ MàÍsJøuû½ñƒáÆÿÙWá'ügàŸì—ûüýž7þÌ¿´·íÿðGÄŸøßâß앨øsö4ðçÆÛt~ПjïÙEµï‹÷4ßÙÿöˆøâoÃûI~;´øsñNóâŸÂïü`ø‹àïŠÚ×…?f¯(ï÷ü5ì±ÿ Ûþsþ[öÿ†šÿ£tÿ…Éðëþ·ü‰ßð±?ä‘ÂGÿ þIÿüW?ò/Èÿ7üÓ¨øûXþ˵ü%?ðÍ´·ìÿûDÂý‰ÿ ¯ü(¿ŒŸ¾-ÿÂÿ 7ö¿ü#ð”ÿÂâ?Â?ÿ ü#ú÷ö'ö·Ù?µ±5°}£û6óÉþpbÏø%ÇÆÏÙ¿âŸìÉàÿ_²íû\üzý˜kÿ/ðü'Å?ðT‹¾øßþ:xÅ_~"þÐ:Gìáíã?‰¾ý >$^ø‚Óà·ÇOÙZÙ³SøEûCxÆÓ[ñ?ÆÚ‡Å>ñ—ˆ¼Ysû}ÿ·ðÿÅ߇ÿ°/ì§ðSã§À¯ˆ>&þο³ÿÀÏÙïÅ>ñÿ‰~ ø³þcáÁ‡ÞÖüyà­gà_Å¿Œ¸ø­ø‚ËWÓü8Þ)Õ|)ã¹±¯nµ¿hVW:EΦ÷ý|ñþR›ûÿÙ€ÁK?õ¢¿à“µ÷ý|ñþR›ûÿÙ€ÁK?õ¢¿à“´Á'å_ðM?û0ØßÿY×áÍ}ÿ_Œ?±ÅïÚÓöký‹ÿdOÙÏÇ_ðJßÛVñ·ÀÙƒàÁOêžø£ÿþð®¥â¯… |'à_ê¾Ö?à¢ú¯yáûÍ_B¼¸Ñ®µMFÔn4é-¦¾Ò´ë—–Χÿá²?h¯úDïíÿÿ‡þ eÿÓ, ¿è¯€?á²?h¯úDïíÿÿ‡þ eÿÓ,£þ#öŠÿ¤Nþßÿøq¿à–_ý2ÊûþŠøþ#öŠÿ¤Nþßÿøq¿à–_ý2Ê?á²?h¯úDïíÿÿ‡þ eÿÓ, ¿è¯€?á²?h¯úDïíÿÿ‡þ eÿÓ,£þ#öŠÿ¤Nþßÿøq¿à–_ý2ÊûþŠøþ#öŠÿ¤Nþßÿøq¿à–_ý2Ê?á²?h¯úDïíÿÿ‡þ eÿÓ, þ ;ÿ(²ÿ‚iÿÙ€~Æÿúοkïúùþ íð·Ç¿`_Ø{à§Å- þ‰¿ÿdÙ§áoÄ_ ÿièúßü#¾;øð_Á^ñv…ý³áÝCWðþ¯ý‘â #PÓÿ´ô-WSÑïþÏö­3P½²– ™~¿ Š( ¾ÿ‚NÿÊ,¿àšö`±¿þ³¯Ãšûþ¿b/‹ßµ§ì×ûþÈŸ³ŸŽ¿à•¿¶þ­ão€?³À/‚ž1Õ<'ñGþ ‡}á]KÅ_ þøOÀ¾!Ô<3}¬ÁEô^óÃ÷š¾…yq£]jš¨ÜiÒ[M}¥i×/-œ ³ÔWÀðÙ´Wý"wöÿÿÃÿ²ÿé–Qÿ ‘ûEÒ'oÿü8ßðK/þ™e}ÿE|ÿ ‘ûEÒ'oÿü8ßðK/þ™eðÙ´Wý"wöÿÿÃÿ²ÿé–PßôWÀðÙ´Wý"wöÿÿÃÿ²ÿé–Qÿ ‘ûEÒ'oÿü8ßðK/þ™e}ÿE|ÿ ‘ûEÒ'oÿü8ßðK/þ™eðÙ´Wý"wöÿÿÃÿ²ÿé–PßõðüwþQeÿÓÿ³ýÿõ~Ñÿ ‘ûEÒ'oÿü8ßðK/þ™ezüÛáoŽþ~À¿°÷ÁOŠZü"ÿ~þȳOÂ߈¾þÓÑõ¿øG|wðÿà¿‚¼'âí ûgú†¯áý_û#ÄF¡§ÿièZ®§£ßýŸíZf¡{e,2€}EPEPE~`|'ÿ‚|Oøãð³á§Æ¿…¿ðK¯ÛÿÅ ¾0|?ðoÅ/‡^&ÿ„Ïþ •¢ÂEàOˆÓ|YáwûÄ_ðQÍ#ÄGö¿‡õ}?PþÌ×t­3X°ûGÙu=>Êö)í¢ôølÚ+þ‘;ûÿáÆÿ‚YôË(ïúþxàä¯ÛÃÅ_°ì{ðâmŸÁ_ü_ðN¿ûoþÉW>!kŸ‹šÃ¯h~*ýž¾9øöÑðv£hq|%ñö‘â?üIÒ?f?x ÄÞ&¾ñG…õ†Úޱá={KðŸÄËkÝcLÐÿGÿá²?h¯úDïíÿÿ‡þ eÿÓ,¯Îø*Ͼ0ÁM?cÝcöRñWü_ößµ¶ñÆÙÛÅãKâü‚|=ð¯‚þ9ø XøÍâo„ž!ÔoŸÇá?Œ§ìøŸ|à-dør}:÷Qñ¤žñUͯ‚|Gây@í÷ìõñÅ_þ|ø»ã¯†> ø'ãoŠŸþ|GñÁŸ6¢þ*øGâ¯x/Eñ?ˆ~ø™õøOWox WÕ/<+¬¶©á_ j-¨é7&ûÃú5É—N¶ö øþ#öŠÿ¤Nþßÿøq¿à–_ý2Ê?á²?h¯úDïíÿÿ‡þ eÿÓ, ¿è¯€?á²?h¯úDïíÿÿ‡þ eÿÓ,®ƒá7ퟭø÷ãï‡?g?‰_±ÿí?û1øÛÆŸþ*ükðF©ñ¯Yý‘üGá_xWà¿>øâŸ§ß~Í¿µ_í«éž Ó5Ú áÍÅ­¯Š4=NÕ4ë­Vk V{*k6û~Š( Š( Š( Š( ¾øÿ)MýÿìÀ?य़úÑ_ðIÚûþ¿8?j>7ø öÐý–hφ¿²÷ÆÚsÁ> ý˜?m‚ž7Òþ x»öjð犼'⯿`¿|?Ô5 ÚKöýŸtOÃúž‘û>üF·ººð¾¹¯ê:^£k¥C¥AmªÃx€£ôWÀðÙ´Wý"wöÿÿÃÿ²ÿé–Qÿ ‘ûEÒ'oÿü8ßðK/þ™e}ÿE|ÿ ‘ûEÒ'oÿü8ßðK/þ™eðÙ´Wý"wöÿÿÃÿ²ÿé–PßôWÀðÙ´Wý"wöÿÿÃÿ²ÿé–Qÿ ‘ûEÒ'oÿü8ßðK/þ™e}ÿE|ÿ ‘ûEÒ'oÿü8ßðK/þ™eðÙ´Wý"wöÿÿÃÿ²ÿé–PßõðÄoùJoìoÿfÿ,ÿÖŠÿ‚NÑÿ ‘ûEÒ'oÿü8ßðK/þ™eyÿ€5?ÚãíõðGã_‹¿cÚögøeðö@ý±¾ë&øéãoÙ[ÿ„‹Ç~4~Á¾,ðF…ámöoý©¿h?Iåøö|ø‹¨kzŸˆ´¯högÒ-`Ô/ouHí¢ý?¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(àø$ïü¢Ëþ §ÿfûÿë:ü9¯¿ëàø$ïü¢Ëþ §ÿfûÿë:ü9¯§ÿh]gã/‡>|pñìçá?ø÷öƒÐ~üKÖ~øÅ—0YøWÆŸt¿ëWß <'âk˯x.Ú×Ãþ#ñ´¬ÜÜxÇÂpA§^\Ë7‰´(ÕµKP`¢¿œøk/ø-ü7×ü;£þ/üþoü;þçþŸøeOÚ·þgü-?ø]ð¢ÿáŸ?¶ÿá¶ÿá-ÿ…ÿ oüLá áÿ„þÏô¿øgŸí?ø”WêÿßÛ+áï‚ü+û-|,ý·~5~̳ÇíáñŸàÿÁ gÆ?³¥ñ·áŸ†¼Uuñ—â.cáßøOá'‚u‰>#ñ'ðφµ¯Áâ=GD“IѼMâ{ø%»œíú+óö¸ÿ‚¤~Ëþ þØŸð¥?jÏÙâíuû:þÏÿµOü3û:OñÏá׋ø5ñCá'ìñ3ölñ·‹þ)|{»ø™ûE~Ëÿ¾,~Óÿ~x3Æß 5o†>xÇöxð ÿdÝáuáÕþÜ~Óž1þ‡¨ å ö…ÿ‚{þ×|GûiiŸ¿eˆ𗎾ÿÁd¢ðç?jþžY|Eý°þ|Að‚¾+Á1?hK/‰³~Øÿ²/ÄÚëâD>ø¥û@~Ïÿ´/ ~Ë^øõûDéï­xžðüMûÿþ ð‡öÓý•üGûP~Î_´‡ÃÿÚâ'Áý{âûDþÏ¿¶÷íñ#öbñ—Çoèÿ>|!Óµ¯Ù»ö­_…_ükñ7Ç_´ìå{¦IðªÃãö±ñÀŸüðîãÃ~ñÃÿþxÄ?·ÔP_|Fÿ”¦þÆÿö`ðRÏýh¯ø$í}ÿ_|Fÿ”¦þÆÿö`ðRÏýh¯ø$í}ÿEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPãìEñ{ö´ýšÿbÿÙösñ×ü·ößÕ¼mðö`øðSÆ:§„þ(ÿÁ0ï¼+©x«á_Ÿ øÄ:‡†oµø(¾ƒ«Þx~óWЯ.4k­SCѵ:Ki¯´­:å峇éÿølÚ+þ‘;ûÿáÆÿ‚YôË+ïú(àølÚ+þ‘;ûÿáÆÿ‚YôË+À?iø+f©ûü,Õ¾3þÐÿðMÛÿÀŸô·ù÷¶~ ÿ‚qøïÄz‡ö?‡5ïø‹þÏ|:ÿ‚…ø·â‹áøá/|Lñ¯ü"ÞÖ?á øYàOüPñ_ö?Ãÿx»Äz/ëõ~`ÁB|ÿ Éã¿Ù§öáñg‚¾ |fø?ðþ/Âÿ|øoáÏøN jþ%¼øKû-ø TÓ`Ô~~Ï øgà;é.®|9-äÿOÐÀðÙ´Wý"wöÿÿÃÿ²ÿé–WŸøSý >8þß_~5ø»ö1ý ?f†_ÿdÛán±âoŽž6ý5¿øH¼wñ×ãGìâÏh^Ñ¿fÿÚ›öƒñž_‡ÿgψº†·©ø‹Jðæaö}"Ö Bö÷TŽÚ/Óú(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¯€?य़òn¿ìÿÿà“¿úôßØÞ¾ÿ¯€?य़òn¿ìÿÿà“¿úôßØÞ€>ÿ¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢¿0>ÿÁF¾'üqøYðÓã_Âßø%×íÿâ†_>ø7â—ïÂgÿÊÑ?á"ð'Äi¾,ðŽ»ýâ/ø(æ‘â #û_Ãú¾Ÿ¨fkºV™¬X}£ìºžŸe{öÑzü6GíÿHý¿ÿðãÁ,¿úe”÷ý{¨øÒO xªæ×Á>#ñ< öûözøâ¯Œ¾ü]ñ×Ã|ñ·ÅOƒÿ >#øÇàÏ‹Q|#ñWŽ<¢øŸÄ? |LúLJü'«·ˆ<«ê—žÖ[T𯆵Ôt›“}áýä˧[{|ÿ ‘ûEÒ'oÿü8ßðK/þ™eðÙ´Wý"wöÿÿÃÿ²ÿé–PßôWÀðÙ´Wý"wöÿÿÃÿ²ÿé–WAð›öÏÖü{ñ÷ß³ŸÄ¯ØÿöŸý˜ümãOƒÿ~5ø#Tø×¬þÈþ#ð¯‹<+ð_ÆŸ| ñOÓï¿fßÚ¯ö‚ÕôÏiš¿íðæâÖ×ŧjšuÖ«5†«=Ε5›}¿EPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPÀðIßùE—üOþÌö7ÿÖuøs_×ÀðIßùE—üOþÌö7ÿÖuøs]üsâÏÇßÙóö/ý¡¿hÏÙÏÄô?~Îþ-||Õ4¿ ¼iñc¾<ð¯Â…>8ñÕ÷ÃÝ>ÇÀ¿¾êþ ñŠ5}C·µøƒq®xÇNðî« ß+ÿ‚…ü}øûé~ øÃÿ²ŸíGàÛöXý‰¿j¿€µ·Â|eƒà¯ÄÏþ|ñf…áíGá·Æ?ÙûHø¡áý+Hý¦~~Ñ>7ø'Æš×Ãß_®4+X4 kŸ.ï~þøÿö±ý–>|SðWÀ¿Š_´·ìÿð×ãoįøG?á]|ñÿÆO‡^ø§ãïøL|G{àÿÿÂð÷Ä^#Ó¼[â¯øJ¼[§jðçö‘ý¹â; ÝLûV§k=ª{ýùÿAÿ‚‰ü,ý‡¿gÚžûP~È k¯þÏþ.øéðSà·í!ãO\ë?áµ×õ]+ÂÚ?ÁË_Œ¿>&ø³þÍï‚|Mð·Áß„õÖþÏñÝÌz…¶‘ãi¼7¨xZûÇÿµì±ð£âŸ‚¾üRý¥¿gÿ†¿~%Â9ÿ ëàïþ2|:ðwÅ?Âcâ;ßøGþ¯‡¾"ñâßÂUâÝ;Pð·‡?°´‹ÿíÏØ^èšgÚµ;YíPßëàˆßò”ߨßþÌþ Yÿ­ÿ¯¿ëàˆßò”ߨßþÌþ Yÿ­ÿ ¿è¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Šøþ ;ÿ(²ÿ‚iÿÙ€~Æÿúοk ÿ‚—øâgÅOø'§í±ð‹àÏÃo|\ø­ñŸö`øÕðSá÷€¼3®|=ðÖ£«x«ã€uφZN¡s¯|Sñ·ÃÏé~ðÅÏŠ“Å^,ºÔ|Um¨§…tMdxcJñ?Š[Eð¾³ÏÿÁ'å_ðM?û0ØßÿY×áÍ}ÿ@„?·?üÇâgÆÿ‹²gí¯û)xWÄ~2xÇöŸÿ‚wxƒþ 1ðK[ñÃÝŽß³À_Úá_ÆË&ø± h7~?øSã?Úö>ñO‚¼9sà߉Þøcâ ¯ƒZoÆ„>øñ{Ãz¯Ã?‡úŸþÜ?³Ÿí…ã¿ÿÁZÿdß~ÂÞý¤>ÿÁJüAð¿ÅŸ>?jÿ¾h¾x«ÅŸ²ÿìíû$x¿Äß´w¾&]Z|dðψ?e/~ͺ'íYð×Yøð«ö‰Ô~!O¨øCDðåÏÃoˆž¸½±þ‡¨ ç‡ö¹øûUh/ÿ»øwð³öñÇ«Ÿø)×Áøí~þÒÿ¾&þÊ>ÔlõWö ðïì§üý¤ô¿ÚâçÀˆžðÿÁÿˆŸõ¯ŠþÕ~IñË·žý ¼Oq¤éþñµŠô {æˆðKš7íûZ\|\ý¿hø)'ü.ÛÿÁß·wì‡ñóJÿ‚£ü]ýŽþ þÏzÇ.¼3ðú/|iøy þÑoÄ…Ÿ?a?‡þ0üøùû<|ý¦üwñá¾ø{á{?„ßðŠÙ|=ðÇõ{EðÄoùJoìoÿfÿ,ÿÖŠÿ‚N×ßõðÄoùJoìoÿfÿ,ÿÖŠÿ‚NÐßôQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE ±ü×ÿÛýšÿbÿÙösñ×Á/ÛVñ·ÀÙƒàÁOêžømðûº—оü)ðŸ|C¨xfûXý¥t^óÃ÷š¾…yq£]jš¨ÜiÒ[M}¥i×/-œ?OÿÄjßðK/ú ·ÿþÏÙ×ÿ¢ªŠ(ÿˆÕ¿à–_ô@ÿoÿü5Ÿ³¯ÿEUñ·üËþˆíÿÿ†³öuÿ誢Š?â5oø%—ý?Ûÿÿ gìëÿÑUGüF­ÿ²ÿ¢ûÿá¬ýú*¨¢€ø[þ eÿDöÿÿÃYû:ÿôUWÐðN¿ø-_ì±ÿƒÿ‚¦ü0ÿ†hðíàøgØöÛÿ„×þ§…~xgûSþßíÿâÿ„sþoø@>*üMûoØ¿áYkßÛÚߨŸfû^‘öí/´^ý€¢€?§Ú(¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€?ÿÙscons-doc-2.3.0/doc/python10/abstract.xml0000644000175000017500000000222712114661557021001 0ustar dktrkranzdktrkranz &SCons; is a software construction tool (build tool, or make tool) implemented in Python, which uses Python scripts as "configuration files" for software builds. Based on the design which won the Software Carpentry build tool competition, &SCons; solves a number of problems associated with other build tools, especially including the classic and ubiquitous &Make; itself. Distinctive features of &SCons; include: a modular design that lends itself to being embedded in other applications; a global view of all dependencies in the source tree; an improved model for parallel () builds; automatic scanning of files for dependencies; use of MD5 signatures for deciding whether a file is up-to-date; use of traditional file timestamps instead of MD5 signatures available as an option; use of Python functions or objects to build target files; easy user extensibility. This paper discusses the goals of the &SCons; project, gives an overview of the design of &SCons; itself, describes the development process used, and discusses future plans and directions for the tool. scons-doc-2.3.0/doc/python10/node.fig0000644000175000017500000001220112114661557020061 0ustar dktrkranzdktrkranz#FIG 3.2 Landscape Center Inches Letter 100.00 Single -2 1200 2 6 2700 1200 4500 1800 2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5 2700 1200 4500 1200 4500 1800 2700 1800 2700 1200 4 0 0 50 0 0 16 0.0000 4 165 1290 2925 1575 Environment\001 -6 6 2700 3600 4500 4200 2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5 2700 3600 4500 3600 4500 4200 2700 4200 2700 3600 4 0 0 50 0 0 16 0.0000 4 165 525 3375 3975 Node\001 -6 6 5700 1800 6900 2400 2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5 5700 1800 6900 1800 6900 2400 5700 2400 5700 1800 4 0 0 50 0 0 16 0.0000 4 165 735 5925 2175 Walker\001 -6 6 2100 2400 3300 3000 2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5 2100 2400 3300 2400 3300 3000 2100 3000 2100 2400 4 0 0 50 0 0 16 0.0000 4 165 750 2325 2775 Builder\001 -6 6 3900 2400 5100 3000 2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5 3900 2400 5100 2400 5100 3000 3900 3000 3900 2400 4 0 0 50 0 0 16 0.0000 4 165 780 4125 2775 Scanner\001 -6 6 0 5100 3300 6900 6 2400 6300 3300 6900 2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5 2400 6300 3300 6300 3300 6900 2400 6900 2400 6300 4 0 0 50 0 0 16 0.0000 4 165 345 2700 6675 Dir\001 -6 6 0 6300 900 6900 2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5 0 6300 900 6300 900 6900 0 6900 0 6300 4 0 0 50 0 0 16 0.0000 4 225 555 150 6675 Entry\001 -6 6 1200 6300 2100 6900 2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5 1200 6300 2100 6300 2100 6900 1200 6900 1200 6300 4 0 0 50 0 0 16 0.0000 4 165 390 1425 6675 File\001 -6 6 1050 5100 2250 5700 2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5 1050 5100 2250 5100 2250 5700 1050 5700 1050 5100 4 0 0 50 0 0 16 0.0000 4 165 855 1200 5475 Node.FS\001 -6 6 450 5700 2700 6300 2 3 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 4 1650 5700 1575 5850 1725 5850 1650 5700 2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 4 450 6300 450 6000 2700 6000 2700 6300 2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 2 1650 6300 1650 5850 -6 -6 6 3900 5100 7500 6900 6 5100 6300 6300 6900 2 2 1 1 0 7 50 0 -1 4.000 0 0 -1 0 0 5 5100 6300 6300 6300 6300 6900 5100 6900 5100 6300 4 0 0 50 0 0 16 0.0000 4 165 705 5325 6675 Record\001 -6 6 6600 6300 7500 6900 2 2 1 1 0 7 50 0 -1 4.000 0 0 -1 0 0 5 6600 6300 7500 6300 7500 6900 6600 6900 6600 6300 4 0 0 50 0 0 16 0.0000 4 165 510 6750 6675 Field\001 -6 6 4950 5100 6150 5700 2 2 1 1 0 7 50 0 -1 4.000 0 0 -1 0 0 5 4950 5100 6150 5100 6150 5700 4950 5700 4950 5100 4 0 0 50 0 0 16 0.0000 4 165 930 5100 5475 Node.DB\001 -6 6 4350 5700 7050 6300 2 3 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 4 5550 5700 5475 5850 5625 5850 5550 5700 2 1 1 1 0 7 50 0 -1 4.000 0 0 -1 0 0 4 4350 6300 4350 6000 7050 6000 7050 6300 2 1 1 1 0 7 50 0 -1 4.000 0 0 -1 0 0 2 5550 5850 5550 6300 -6 6 3900 6300 4800 6900 2 2 1 1 0 7 50 0 -1 4.000 0 0 -1 0 0 5 3900 6300 4800 6300 4800 6900 3900 6900 3900 6300 4 0 0 50 0 0 16 0.0000 4 165 555 4050 6675 Table\001 -6 -6 6 5700 3000 6900 3600 2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5 5700 3000 6900 3000 6900 3600 5700 3600 5700 3000 4 0 0 50 0 0 16 0.0000 4 225 870 5850 3375 Wrapper\001 -6 6 900 1200 1800 1800 2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5 900 1200 1800 1200 1800 1800 900 1800 900 1200 4 0 0 50 0 0 16 0.0000 4 165 270 1200 1575 FS\001 -6 2 3 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 4 3600 4200 3525 4350 3675 4350 3600 4200 2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 4 1800 5100 1800 4800 5550 4800 5550 5100 2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 2 3600 4800 3600 4350 2 3 0 1 0 7 50 0 -1 4.000 0 0 7 0 0 5 4200 1800 4160 1875 4200 1950 4240 1875 4200 1800 2 3 0 1 0 7 50 0 -1 4.000 0 0 7 0 0 5 3000 6150 2960 6225 3000 6300 3040 6225 3000 6150 2 3 0 1 0 7 50 0 -1 4.000 0 0 7 0 0 5 6300 3600 6260 3675 6300 3750 6340 3675 6300 3600 2 3 0 1 0 7 50 0 -1 4.000 0 0 7 0 0 5 6300 2400 6260 2475 6300 2550 6340 2475 6300 2400 2 3 0 1 0 7 50 0 -1 4.000 0 0 7 0 0 5 3000 4200 2960 4275 3000 4350 3040 4275 3000 4200 2 3 0 1 0 7 50 0 -1 4.000 0 0 7 0 0 5 4200 3450 4160 3525 4200 3600 4240 3525 4200 3450 2 3 0 1 0 7 50 0 -1 4.000 0 0 7 0 0 5 3000 3450 2960 3525 3000 3600 3040 3525 3000 3450 2 1 0 1 0 7 50 0 -1 0.000 0 0 7 1 0 3 1 1 1.00 60.00 120.00 3000 6150 3000 5400 2250 5400 2 1 0 1 0 7 50 0 -1 0.000 0 0 7 1 0 5 1 1 1.00 60.00 120.00 3000 4350 3000 4500 1800 4500 1800 3900 2700 3900 2 1 0 1 0 7 50 0 -1 0.000 0 0 7 1 0 3 1 1 1.00 60.00 120.00 6300 3750 6300 3900 4500 3900 2 1 0 1 0 7 50 0 -1 0.000 0 0 7 1 0 2 1 1 1.00 60.00 120.00 4200 3450 4200 3000 2 1 0 1 0 7 50 0 -1 0.000 0 0 7 1 0 2 1 1 1.00 60.00 120.00 3000 3450 3000 3000 2 3 0 1 0 7 50 0 -1 0.000 0 0 7 0 0 5 3000 1800 2960 1875 3000 1950 3040 1875 3000 1800 2 1 0 1 0 7 50 0 -1 0.000 0 0 7 1 0 2 1 1 1.00 60.00 120.00 3000 1950 3000 2400 2 1 0 1 0 7 50 0 -1 0.000 0 0 7 1 0 2 1 1 1.00 60.00 120.00 4200 1950 4200 2400 2 1 0 1 0 7 50 0 -1 0.000 0 0 7 1 0 2 1 1 1.00 60.00 120.00 6300 2550 6300 3000 2 1 0 1 0 7 50 0 -1 0.000 0 0 7 1 0 2 1 1 1.00 60.00 120.00 5100 6600 4800 6600 2 1 0 1 0 7 50 0 -1 0.000 0 0 7 1 0 2 1 1 1.00 60.00 120.00 6600 6600 6300 6600 2 3 0 1 0 7 50 0 -1 4.000 0 0 7 0 0 5 1350 1800 1310 1875 1350 1950 1390 1875 1350 1800 2 1 0 1 0 7 50 0 -1 0.000 0 0 7 1 0 2 1 1 1.00 60.00 120.00 1350 1950 1350 5100 2 1 1 1 0 7 50 0 -1 4.000 0 0 -1 0 0 2 1350 1200 1350 900 2 1 1 1 0 7 50 0 -1 4.000 0 0 -1 0 0 2 3600 1200 3600 900 scons-doc-2.3.0/doc/python10/design.xml0000644000175000017500000005577712114661557020471 0ustar dktrkranzdktrkranz The &SCons; architecture consists of three layers: The &SCons; Build Engine, a package of Python modules that handle dependency management and updating out-of-date objects. The &SCons; API (applications programming interface) between the Build Engine and the user interface. The &scons; script itself (note lower case sc), which is the pre-provided interface to the Build Engine. Notice that this architecture separates the internal workings of &SCons; (the Build Engine) from the external user interface. The benefit is that the &SCons; Build Engine can be imported into any other software package written in Python to support a variety of user interfaces—or, to look at it in reverse, other software interfaces can use the &SCons; Build Engine to manage dependencies between their objects. Because the &SCons; package itself is modular, only those parts of the package relevant to the embedding interface need be imported; for example, a utility that wants to use only file timestamps for checking whether a file is up-to-date need not import the MD5 signature module.
The &SCons; Build Engine The Build Engine is a package of Python modules that form the heart of &SCons;. The Build Engine can be broadly divided into five architectural subsystems, each responsible for a crucial part of &SCons; functionality: A node subsystem, responsible for managing the files (or other objects) to be built, and the dependency relationships between them. A scanner subsystem, responsible for scanning various file types for implicit dependencies. A signature subsystem, responsible for deciding whether a given file (or other object) requires rebuilding. A builder subsystem, responsible for actually executing the necessary command or function to build a file (or other object). A job/task subsystem, responsible for handling parallelization of builds. The rest of this section will provide a high-level overview of the class structure of each of these Build Engine subsystems.
Node Subsystem The node subsystem of the Build Engine is responsible for managing the knowledge in &SCons; of the relationships among the external objects (files) it is responsible for updating. The most important of these relationships is the dependency relationship between various &Node; objects, which &SCons; uses to determine the order in which builds should be performed. The &scons; script (or other user interface) tells the Build Engine about dependencies through its &consenv; API. The Build Engine also discovers dependencies automatically through the use of &Scanner; objects. Subclasses of the &Node; class maintain additional relationships that reflect the real-world existence of these objects. For example, the &Node_FS; subclass is responsible for managing a representation of the directory hierarchy of a file system. A &Walker; class is used by other subsystems to walk the dependency tree maintained by the &Node; class. The &Walker; class maintains a stack of &Node; objects visited during its depth-first traversal of the dependency tree, and uses an intermediate node &Wrapper; class to maintain state information about a &Node; object's dependencies.
Scanner Subsystem The scanner subsystem is responsible for maintaining objects that can scan the contents of a &Node;'s for implicit dependencies. In practice, a given &Scanner; subclass object functions as a prototype, returning clones of itself depending on the &consenv; values governing how the &Node; should be scanned.
Signature Subsystem The signature subsystem is responsible for computing signature information for &Node; objects. The signature subsystem in &SCons; supports multiple ways to determine whether a &Node; is up-to-date by using an abstract &Sig; class as a strategy wrapper: By default, &SCons; tracks dependencies by computing and maintaining MD5 signatures for the contents of each source file (or other object). The signature of a derived file consists of the aggregate of the signatures of all the source files plus the command-line string used to build the file. These signatures are stored in a &sconsign; file in each directory. If the contents of any of the source files changes, the change to its MD5 signature is propogated to the signature of the derived file(s). The simple fact that the new signature does not match the stored signature indicates that the derived file is not up to date and must be rebuilt. A separate &TimeStamp; subclass of the &Sig; class supports the use of traditional file timestamps for deciding whether files are up-to-date.
Builder Subsystem The &SCons; Build Engine records how out-of-date files (or other objects) should be rebuilt in &Builder; objects, maintained by the builder subsystem: The actual underlying class name is &BuilderBase;, and there are subclasses that can encapsulate multiple &Builder; objects for special purposes. One subclass (&CompositeBuilder;) selects an appropriate encapsulated &Builder; based on the file suffix of the target object. The other (&MultiStepBuilder;). can chain together multiple &Builder; objects, for example, to build an executable program from a source file through an implicit intermediate object file. A &BuilderBase; object has an associated &ActionBase; object responsible for actually executing the appropriate steps to update the target file. There are three subclasses, one for externally executable commands (&CommandAction;), one for Python functions (&FunctionAction;), and one for lists of multiple &Action; objects (&ListAction;).
Job/Task Subsystem &SCons; supports parallel builds with a thread-based tasking model, managed by the job/task subsystem. Instead of performing an outer-loop recursive descent of the dependency tree and then forking a task when it finds a file that needs updating, &SCons; starts as many threads as are requested, each thread managed by the &Jobs; class. As a performance optimization, the &Jobs; class maintains an internal distinction between &Serial; and &Parallel; build jobs, so that serial builds don't pay any performance penalty by using a multi-threaded implementation written for &Parallel; builds. Each &Jobs; object, running in its own thread, then requests a &Task; from a central &Taskmaster;, which is responsible for handing out available &Task; objects for (re-)building out-of-date nodes. A condition variable makes sure that the &Jobs; objects query the &Taskmaster; one at a time. The &Taskmaster; uses the node subsystem's &Walker; class to walk the dependency tree, and the &Sig; class to use the appropriate method of deciding if a &Node; is up-to-date. This scheme has many advantages over the standard &Make; implementation of . Effective use of is difficult with the usual recursive use of Make, because the number of jobs started by multiply at each level of the source tree. This makes the actual number of jobs executed at any moment very dependent on the size and layout of the tree. &SCons;, in contrast, starts only as many jobs as are requested, and keeps them constantly busy (excepting jobs that block waiting for their dependency files to finish building).
The &SCons; API This section provides an overview of the &SCons; interface. The complete interface specification is both more detailed and flexible than this overview.
&ConsVars; In &SCons;, a &consenv; is an object through which an external interface (such as the &scons; script) communicates dependency information to the &SCons; Build Engine. A construction environment is implemented as a dictionary containing: construction variables, string values that are substituted into command lines or used by builder functions; one or more &Builder; objects that can be invoked to update a file or other object; one or more &Scanner; objects that can be used to scan a file automatically for dependencies (such as files specified on #include lines). &Consenvs; are instantiated as follows: env = Environment() env_debug = Environment(CCFLAGS = '-g')
&Builder; Objects An &SCons; &Builder; object encapsulates information about how to build a specific type of file: an executable program, an object file, a library, etc. A &Builder; object is associated with a file through an associated &consenv; method and later invoked to actually build the file. The &Builder; object will typically use construction variables (such as &CCFLAGS;, &LIBPATH;) to influence the specific build execution. &Builder; objects are instantiated as follows: bld = Builder(name = 'Program', action = "$CC -o $TARGET $SOURCES") In the above example, the action is a command-line string in which the Build Engine will interpolate the values of construction variables before execution. The actual action specified, though, may be a function: def update(dest): # [code to update the object] return 0 bld = Builder(name = 'Program', function = update) Or a callable Python object (or class): class class_a: def __call__(self, kw): # build the desired object return 0 builder = SCons.Builder.Builder(action = class_a()) A &Builder; object may have the prefix and suffix of its target file type specified as keyword arguments at instantiation. Additionally, the suffix of the source files used by this &Builder; to build its target files may be specified using the src_suffix keyword argument: bld_lib = Builder(name = 'Library', action = "$AR r $TARGET $SOURCES", prefix = 'lib', suffix = '.a', src_suffix = '.o') The specified prefix and suffix will be appended to the name of any target file built by this &Builder; object, if they are not already part of the file name. The src_suffix is used by the &SCons; Build Engine to chain together multiple &Builder; objects to create, for example, a library from the original source files without having to specify the intermediate .o files. &Builder; objects are associated with a &consenv; through a &consvar; named &BUILDERS;, a list of the &Builder; objects that will be available for execution through the &consenv;: env = Environment(BUILDERS = [ Object, Library, WebPage, Program ])
&Scanner; Objects &Scanner; objects perform automatic checking for dependencies by scanning the contents of files. The canonical example is scanning a C source file or header file for files specified on #include lines. A &Scanner; object is instantiated as follows: def c_scan(contents): # scan contents of file return # list of files found c_scanner = Scanner(name = 'CScan', function = c_scan, argument = None, skeys = ['.c', '.C', '.h', '.H') The skeys argument specifies a list of file suffixes for file types that this &Scanner; knows how to scan. &Scanner; objects are associated with a &consenv; through a &consvar; named &SCANNERS;, a list of the &Scanner; objects that will be available through the &consenv;: env = Environment(SCANNERS = [ CScan, M4Scan ]) For utilities that will build files with a variety of file suffixes, or which require unusual scanning rules, a &Scanner; object may be associated explicitly with a &Builder; object as follows: def tool_scan(contents): # scan contents of file return # list of files found tool_scanner = Scanner(name = 'TScan', function = tool_scan) bld = Builder(name = 'Tool', scanner = tool_scanner)
&BuildDir; &SCons; supports a flexible mechanism for building target files in a separate build directory from the source files. The &BuildDir; syntax is straightforward: BuildDir(source = 'src', build = 'bld') By default, source files are linked or copied into the build directory, because exactly replicating the source directory is sometimes necessary for certain combinations of use of #include "..." and search paths. An option exists to specify that only output files should be placed in the build directory: BuildDir(source = 'src', build = 'bld', no_sources = 1)
&Repository; &SCons; supports the ability to search a list of code repositories for source files and derived files. This works much like &Make;'s VPATH feature, as implemented in recent versions of GNU &Make;. (The POSIX standard for &Make; specifies slightly different behavior for VPATH.) The syntax is: Repository('/home/source/1.1', '/home/source/1.0') A command-line option exists to allow repositories to be specified on the command line, or in the &SCONSFLAGS; environment variable (not construction variable!). This avoids a chicken-and-egg situation and allows the top-level &SConstruct; file to be found in a repository as well.
&Cache; &SCons; supports a way for developers to share derived files. Again, the syntax is straightforward: Cache('/var/build.cache/i386') Copies of any derived files built will be placed in the specified directory with their MD5 signature. If another build results in an out-of-date derived file with the same signature, the derived file will be copied from the cache instead of being rebuilt.
The &scons; Script The &scons; script provides an interface that looks roughly equivalent to the classic &Make; utility—that is, execution from the command line, and dependency information read from configuration files. The most noticeable difference between &scons; and &Make;, or most other build tools, is that the configuration files are actually Python scripts, generically called "SConscripts" (although the top-level "Makefile" is named &SConstruct;). Users do not have to learn a new language syntax, but instead configure dependency information by making direct calls to the Python API of the &SCons; Build Engine. Here is an example &SConstruct; file which builds a program in side-by-side normal and debug versions: env = Environment() debug = env.Copy(CCFLAGS = '-g') source_files = ['f1.c', 'f2.c', 'f3.c'] env.Program(target = 'foo', sources = source_files) debug.Program(target = 'foo-debug', sources = source_files) Notice the fact that this file is a Python script, which allows us to define and re-use an array that lists the source files. Because quoting individul strings in long lists of files can get tedious and error-prone, the &SCons; methods support a short-cut of listing multiple files in a single string, separated by white space. This would change the assignment in the above example to a more easily-readable: source_files = 'f1.c f2.c f3.c' The mechanism to establish hierarchical builds is to "include" any subsidiary configuration files in the build by listing them explicitly in a call to the &SConscript; function: SConscript('src/SConscript', 'lib/SConscript') By convention, configuration files in subdirectories are named &SConscript;. The &scons; script has intentionally been made to look, from the outside, as much like &Make; as is practical. To this end, the &scons; script supports all of the same command-line options supported by GNU &Make;: FILE, , , , etc. For compatibility, &scons; ignores those GNU &Make; options that don't make sense for the &SCons; architecture, such as , , , and . The intention is that, given an equivalent &SConstruct; file for a &Makefile;, a user could use &SCons; as a drop-in replacement for &Make;. Additional command-line options are, where possible, taken from the Perl &Cons; utility on which the &SCons; design is based.
scons-doc-2.3.0/doc/python10/builder.jpg0000644000175000017500000014756312114661557020621 0ustar dktrkranzdktrkranzÿØÿàJFIFPPÿÛCÿÛCÿÀBæ"ÿÄ ÿĵ}!1AQa"q2‘¡#B±ÁRÑð$3br‚ %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyzƒ„…†‡ˆ‰Š’“”•–—˜™š¢£¤¥¦§¨©ª²³´µ¶·¸¹ºÂÃÄÅÆÇÈÉÊÒÓÔÕÖרÙÚáâãäåæçèéêñòóôõö÷øùúÿÄ ÿĵw!1AQaq"2B‘¡±Á #3RðbrÑ $4á%ñ&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz‚ƒ„…†‡ˆ‰Š’“”•–—˜™š¢£¤¥¦§¨©ª²³´µ¶·¸¹ºÂÃÄÅÆÇÈÉÊÒÓÔÕÖרÙÚâãäåæçèéêòóôõö÷øùúÿÚ ?þþ(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢ŠùƒÅžøËuûh|ñއsâýŸ4/Ùƒö»ðÏÄû;oAgáYþ2ø³â·ìEª| ¹Ö| ÚÌ>#ñ¯‚|ûFEáŸCáíRéמ,Ñî5 OÚØøéúùƒÅžøËuûh|ñއsâýŸ4/Ùƒö»ðÏÄû;oAgáYþ2ø³â·ìEª| ¹Ö| ÚÌ>#ñ¯‚|ûFEáŸCáíRéמ,Ñî5 OÚØøéú(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š+óá?ükâÇ…Ÿ >5ü-ÿ‚]~ßþ(øeñƒáÿƒ~)|:ñ7ü&ðL­þ/|@ðæ›âÏë¿ØÞ"ÿ‚Ži Ò?µü?«éú‡öf»¥išÅ‡Ú>Ë©éöW±Oméý|ÿÿ”YÁ4ÿìÀ?cýg_‡4Ãd~Ñ_ô‰ßÛÿÿ7üËÿ¦YGü6GíÿHý¿ÿðãÁ,¿úe•÷ýðü6GíÿHý¿ÿðãÁ,¿úe”Ãd~Ñ_ô‰ßÛÿÿ7üËÿ¦Y_Ñ@Ãd~Ñ_ô‰ßÛÿÿ7üËÿ¦YGü6GíÿHý¿ÿðãÁ,¿úe•÷ýùâ?Ú+öÝÔjƒ´ø&—íÿaðKÁß³ÿí-àˆ?¿áuÁ5-á*ø§ñ+â/ìâ/ƒ¾5ÿ„F×þ >þ×?áð—ÂŽšü$zÞ¡kâ? ÿÂÊþÌðµ•þ™ãO§{ÿü6GíÿHý¿ÿðãÁ,¿úe•÷ýðü6GíÿHý¿ÿðãÁ,¿úe”Ãd~Ñ_ô‰ßÛÿÿ7üËÿ¦Y_Ñ@Ãd~Ñ_ô‰ßÛÿÿ7üËÿ¦YGü6GíÿHý¿ÿðãÁ,¿úe•÷ýðü6GíÿHý¿ÿðãÁ,¿úe”Ãd~Ñ_ô‰ßÛÿÿ7üËÿ¦Y_Ñ@?û=|kð¯í)ð àíà]?ÄO‚~?|øiñ¯ÁÚ_‹-tëi¾ø©à½Ç^ÓüMc£êºö‘gâ =#]³·Ömt½sYÓ­õîa±ÕudŠòo`¯€?à“¿ò‹/ø&Ÿý˜ìoÿ¬ëðæ¾ÿ Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( ¾ÿ‚NÿÊ,¿àšö`±¿þ³¯Ãšûþ¾ÿ‚NÿÊ,¿àšö`±¿þ³¯Ãšûþ¿žØÇöÍÿ‚’~Ô¾ý¾¼uã¯Úwþ Áû=ø'ö ý·ÿjÙÆ>1ñgìEñçSð®¯á_ÙsNð¶±âŽþ&ñ±ÿ=ð6‘ðÓÃú†‘âÍSYеKÍsNðn¢Üß_xßS¶’Ym?¡êþH¾ÿÁ5´Ÿ ø«þ ‰£þÛ¿ðCo~Ù·?µ_üöÙø×ð—ã·„ÏüG_ñRþ̵"×Àz†~'ü\ý²~~Ðü@²[x³Æ:5¯†¢ð§ˆ<¨ø¦ÛÄ:6«¡øÚmIt°ßíKöÝøû5øWá'oÿÚïö øûQêßüâωÞÔ¾>ø/á_…u/_iÒhþ4ñ7ÂO|jñfƒñóàýçÄM Æz_€õŸYɨÜiÚ4–:µËëšv«>ÿ£~п¼Gñ—ÅŸ³Ÿ‡¾8ü×h?x~ÛÅž:ø£|Kð^©ñ—Á~¼ƒÃ7V~&ñgà jxsÃ÷VÞ4ðuŶ³¬hvztðx³Ã3ErÑëÚ[]~`üð¯í…âÏ‹´ÅßÛ‡öðþ­mûIÁ8>xWÅ_~øëàgů†o­ü+ý¡ÿà¡þ'°ÿŠßâïÄÏGñã¹û>~Ôü+ã?j^Ñ¿düBÓ~/ñ3Á^  éÚ§Ä¿à•?¶N¯û Á$|[û/ø“þŸöúýœ¿dƒ¿ðOOÚ“Å:?‹ìtÍgHý–>:þÏþøûAjrk ¼Wà»ß?b‰²XþÔß³…Söðçí#Ç~ ñ¿<޾"êžÒ5p×ïøkøÊù9oØþ—þþÇþK'üe?ü”_ù9oùÿáRÿÃÂ¥ÿšÉÿEþf?øF«Ð?áá?°/ü*Ïø^ŸðÜ?²ü)/øXð©ÿáqÃK|ÿ…Yÿ OþÏøL?áZÂÂÿ„×þ/øXð‰ÅSÿoö¿ü$ðŽÄïû7û3ý*¾ÿ‚„þÉ~(ÿÂ}ð“öhý?³¾x_þƒÿ9ý’>ÜøsVø'ðÿác|]ý¦ÿᓼ3û?þΞðœ¾=мAá?ìoþκô—º¶­à/üð‡‡5?i¶=–õµ­Ã~¿ñ{[ýªŸãïìSûkxöøÁã-#Ã_ÿlßÙËã—ìÛsñsöQðÇíAð¯NøéãOÙ·âÃ47ß®ÿeω:çì‡g x›Ãz_íU¢ø«FðÿÆ ø¦ÆÃ[Õü/ã_i `|Gý·bÿƒžøc㯋¿µßìÁð¯Á?<>Þ,ø3ãˆÿ~ø¿<*šw‡õ‡ñ7ÃøŸÅš^‘ãß®‘âÏ ê¬øVóVÓ—Nñ/‡ïȶÖténOˆÿ¶ïì_ðs¿ |uñwö»ý˜>ø'ãg‡ÛÅŸ|cñãïŸøWâç…SNðþ°þ&øcâø³KÒ<{áõÒð'‚|â€÷Z¯ìùðÛÆžÒ>!h:PÓ´û?…7Ö¾<¶çÿcŸÙëö€ýš¼ûøšÃöøà¿†ZGü>WáÖ½ð«À(ý4ød~ÛßðSÿ€ÿ´oìÑ?|7¤þÑgeøá€¿õkŸxwöhÖ~4k þȳðžƒá^öK ý?ø[ÿ ý~8øïBø[ðSöáý>0|MñGöŸü#?¾þÒßþ xïÄ_Øš>¡â-gû Â>ñ®¯â _û#ÃúF«®êÙú}ÇØ4}3PÔ++™âôøkÙcþ·ü2çü4·ìÿÿ 5ÿFéÿ “á×ü/où¿ábÉ"ÿ„þü“ÿø®ä^ÿ‘;þ*oùÿ§WäÁß~×v?ðÁÿð‘þ¿´…?á]Á_ÿà¢ßµÅí‰_±E÷ü+Ÿ?µGü<þŸŽu¿øEÿkoÿný›þïÁ_ð”øgÀð™xÇJÿ…/ñÓì~Õ|Ÿƒðº>@ý‹?à—?fÿг'ƒþ=~È_´ísñëöaý¯þ4x¿Â?ðTŸÿÁQþ.øàK|øéâ?|mø‹û@鳄´GŒþ&økö€ø‘{â O‚ß?ehfÍOáí ãMoÄÿ?jøÆ^"ñeÈï÷€?à¡?°/ÅøMáVþÜ?²įøV¿üGñcâ/ü ´·ÁÿÂð³Áßcÿ„»â_áñ®£ÿ¯Ãÿ ÿhéÿð‘øË]û‡4?·ÙÿiêV¿jƒyûþÝ?³üKàL´oì¹ã?øLþKñâWï´ÞEk¦xŽÇXøoãSÃ>‰<'öëŸx'þoÛx{⯂¼;ã› xîo…Ÿ<âOxGÂ÷¾ þÆ´üàýž?fö? ÿàÞÍÇß²GˆðVâŽvß ¼Sá Ñúüùû:þÐÿ²ü¿öý–¿j_ƒ> ø'ñ“àŸˆ>5hÚî…¬ø¿á'Ž4ïiÞ8øÓãÏ‹ú‹<'¯ü øñH—Ãòéí|7smâK¯ xªÛÅ^ñ2?†O†Ï†¼Kâ@Ùê(¢€>ÿ‚NÿÊ,¿àšö`±¿þ³¯Ãšûþ¾ÿ‚NÿÊ,¿àšö`±¿þ³¯Ãšûþ€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ øþ ;ÿ(²ÿ‚iÿÙ€~Æÿúοkïúüaýˆ¾/~ÖŸ³_ì_û"~Î~:ÿ‚VþÛú·¾þ̾ xÇTðŸÅø&÷…u/|+øSá?ø‡PðÍö±ÿÐu{ÏÞjúåÆuªhz6£q§Im5ö•§\¼¶p€~ÏQ_Ãd~Ñ_ô‰ßÛÿÿ7üËÿ¦YGü6GíÿHý¿ÿðãÁ,¿úe”÷ýðü6GíÿHý¿ÿðãÁ,¿úe•Ïø³öõøÁà/ ø›Ç^:ÿ‚aþÛþ ðO‚ü?¬ø³Æ>1ñgÅÿø%‡<+á? øsN¹ÖÁP<}ûJ|3Òþ.ü"ÿ‚XÿÁGõoêÞ ñÿ„ã“Å“ÿÁ=>ø«MñW¿ˆ^)øQñÃ>&øwñ_þ àŸˆžñ„>"x'Å^ÖtoxWFÔmõä‹g¶{{‰½ƒþ#öŠÿ¤Nþßÿøq¿à–_ý2ÊûþŠøþ#öŠÿ¤Nþßÿøq¿à–_ý2Ê?á²?h¯úDïíÿÿ‡þ eÿÓ, ¿è¯€?á²?h¯úDïíÿÿ‡þ eÿÓ,¯øÿFñ7íKðkáïíû>Á7ÿmÿŠŸþ*x~xÇ^ø™ÿ¹};WÓž{‹Ëk›;ïø)%ž¯¡xƒBÕìõø³Â~!Ó´Ÿx7ÅZN³áOèÚ7‰4mSK´ý_¢¾ÿ†Èý¢¿é¿·ÿþoø%—ÿL²ølÚ+þ‘;ûÿáÆÿ‚YôË(ïú+àølÚ+þ‘;ûÿáÆÿ‚YôË(ÿ†Èý¢¿é¿·ÿþoø%—ÿL²€ø$ïü¢Ëþ §ÿfûÿë:ü9¯¿ëäø'·Âßüýa´/øEþ&üý?fŸ…¿|3ý§£ëðŽøïáÿÁxOÅÚöχu _Ãú¿öGˆ4COþÓе]OG¿û?Ú´ÍBöÊX.eúþ€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ ùöõø[㿌_²wÅoü6пá8ñm¿ü ¾=¶ø=>§£èš?í'£ü$ø“àï‹,ý‘¼]­ø›PÓü%¢ü?ý°<%à­sö_ø‹«øÙ5¿i~ø·â+ïøCÇ·Ö|®ýE~pÁ?üM§~ÐúÇßÛÛÁÖÞ ð×Á¿Ú÷Ä?ý«> ü2ñ~¦êðü`ý¥5{ÏቲÎ'ø‹û|;ý4ýcÃþñ'õ½Ñúøþ ;ÿ(²ÿ‚iÿÙ€~Æÿúοkïú(¢Šøþ ©ãÿŠ àœ¶ˆ¾ø+ö€ñ÷ÆÝGöñ÷€>è_²ç‡öÃŶ¿ð­|[ã#â/‰¼GáißÄ~ðw…|CâíËQÔô;]>ëòƒþ 3Ô¾>§üûÀ^øçð“Ä t†Ÿ>%i¿.5Þ|<ñ7ÄGán⟠ü0Ñ.¼gã­>ÛÇ_ÃÏŠ—:OˆÁ:7ˆnü'k/‚oôíÅVÚ7‡uWšF­}âÝ Çÿà“¿ò‹/ø&Ÿý˜ìoÿ¬ëðæ¾ÿ¯Âø6óö»ñWíÿ ýžõüñÁ-#ö|ðÿƒdOêšÎ»¨ø‹Nøïá_ÙËàÿÂÏÞ|wðõ÷‚<mkáÿøÚø:çBÑåñ–á¯xÄÞ—Æú¾¯¦j–zgîõQEðüwþQeÿÓÿ³ýÿõ~×Óÿ¾5øWötø5ñ ãWŒtÿkÚG€¼?6¥gàïZéÚ§Ä/‰>*¼žßGð/ÂO…~Õ5]ÛÆ?>/xÛRð÷Ä>‹U³Ô|ñ3Åžð^#júíŒR|ÿµ¹ñWÃÏÙà×ìqñ›Fðÿ¿io؇àÿÂÏÙçâÏ€´?j>)ÓµO ü;Ñ/~|ý ü ¯jþðEÏŠ~þÒÞ ølÿüâ;B—NðÇŠ£ø“ðÄúœ~üað¿…@?G袊(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢ŠüÀøOÿøŸñÇágÃO à—_·ÿŠ>|`øàߊ_¼Mÿ Ÿü+Dÿ„‹ÀŸ<9¦ø³Â:ïö7ˆ¿à£šGˆ4íêú~¡ý™®éZf±aö²êz}•ìSÛEèðÙ´Wý"wöÿÿÃÿ²ÿé–Qÿÿ”YÁ4ÿìÀ?cýg_‡5÷ý|ÿ ‘ûEÒ'oÿü8ßðK/þ™eðÙ´Wý"wöÿÿÃÿ²ÿé–WßôPÀðÙ´Wý"wöÿÿÃÿ²ÿé–Qÿ ‘ûEÒ'oÿü8ßðK/þ™e}ÿE|ÿ ‘ûEÒ'oÿü8ßðK/þ™eðÙ´Wý"wöÿÿÃÿ²ÿé–WßôPÀðÙ´Wý"wöÿÿÃÿ²ÿé–Qÿ ‘ûEÒ'oÿü8ßðK/þ™e}ÿExÿìõñ¯Â¿´§À/ÿ´gtÿi> øýðá§Æ¿i~,µÓ¬|U¦øWâ§‚ô_x{Oñ5ŽªëÚEŸˆ,ôvÎßYµÒõÍgN·Ôc¹†ÇUÔm’+ɽ‚¾ÿ‚NÿÊ,¿àšö`±¿þ³¯Ãšûþ€ (¢€ üÀøOÿøŸñÇágÃO à—_·ÿŠ>|`øàߊ_¼Mÿ Ÿü+Dÿ„‹ÀŸ<9¦ø³Â:ïö7ˆ¿à£šGˆ4íêú~¡ý™®éZf±aö²êz}•ìSÛEú_Á'å_ðM?û0ØßÿY×áÍðÙ´Wý"wöÿÿÃÿ²ÿé–Qÿ ‘ûEÒ'oÿü8ßðK/þ™e}ÿE|ÿ ‘ûEÒ'oÿü8ßðK/þ™esþ,ý§>0x÷¾&ð/Ž¿à¶ÿ<ãOë>ñƒ¼Yâßø%ˆü+âÏ øN¹ÑüCáŸx{Xÿ‚‘Þi÷‡õÝ"òóKÖtmRÎëNÕ4ë«›ëií§–&ý¢€?à–6_´Wü·þ ÷û/~ÇW?ðKÏÛÿÅ ø?ðþøO5Ø>-ÿÁ,µ½ûâŸÄëÿ¾-Â#©ÉÿðMíÏÃûo‰¾6ñeŸÃ¯íŸ iž#‡À–þ·ñ7Û|A¥¨]ýÿÿ ‘ûEÒ'oÿü8ßðK/þ™e}ÿE|ÿ ‘ûEÒ'oÿü8ßðK/þ™eðÙ´Wý"wöÿÿÃÿ²ÿé–WßôPäíGñWöŠý£þøçá=—üöÿð‹uøF|að§â_ü%ðK/¦øíð£Æ>ø³û?übÿ„6_ø*…¦xóþÆßxâ_ü+ßêIàïÿÂ+ÿomu kšÖ›wçÿ²oÆoÛwÁßð·¾3þÒðIOÚþ?Úkö„øq­øîÏáÇoø&§ÄŸ‡_ ~xí~ ýžþü,ñÿ?n¯†¿/þø;áý¬Ÿs‡µŸx;Gñg€µxKÕÚÎûYðΗã¯ê:îΗcâÏ\ÝE¬Yþ@þÂ?ðP¿¿´Ÿˆ?hOÙOã‡#7ƒ¾*ÛÞü?ýž¯Àö ø»ûGþÅž)ºÖ~ü@ýˆ¿à ÿ?kÿø(í;û|fñ޾ Éã¿„Þ;ý¤ÿißÿ´WÃ+½_ÅÿücûNøKTýŸþ*xKâï†~ ~×?üY§ø³Gñþ ø÷J¹ø{â;} áÄk°·ÿf۟¶¿ðOOØ÷öÁý½¾=~ÌuÚ[àÿÂOˆúеoNýœ~ Aâ¯Þo‹¾øcá1ñ›âç.n¼AáÏÏ>•rÓøÿQÔ|i?ƒ¼Mã?ÃþÒ.ŸÂ¾úƒÇÿµì±ð£ág‚¾:|Rý¥¿gÿ†¿¾%Â9ÿ ëãþ2|:ðwÂÏÂcáËßxGþ¯ˆ^"ñá/ÂUá-;PñO‡?°µ{ÿíÏX^ëzgÚ´ËYî“òƒá/ÁoÛ öPø{ÿeñî—û*xƒãæ¯û1ÿÁ8Ò4gNoøû#þӲůüWãÄŸðO‡þ?½ø/ÿUÿ„ËöPýŸ~(þÏcǰïü%ÿÁI|ñkö…ý›~<øs¾ð¯‡><ë_ü=ð‡ã‡4Û’ÏHÐ|?¡iv~%ø—ûMi~µÓ´½:×ZÖl~ÁmZjýÿäÿ‚¶|ýºÿc߆_´ç޵ÿƒÿ³¼iðã7í ã€~,ý |â?|.øð_ãŸÄ¯þ!øãâmGXÒþjñ|‹Wøqyw¬üKÕ< x/ÃzÕχoµ©ît©on~ý¯c¿|Tÿ‚Ù^þÕÿÿà”ž ý»ÿcÏ ÿÁ84Ù[ÃvúÊÿÁ=<{§jßOí/Æôø‹á?†µ/í9ðòçKðÿ†<â¯ü8¹ñv»£xgƉâ¦ñ6› xwTð«kâýgö?ýœà¤ÿ°¦±û øçÄß²ïÄÚwáÿ‡þÿÁF¾\~Íÿ h¯€¾ ñßü;öqý£¼wû|Qý”d½ ãí5ñcá'ü6_ü)oøfýgá¶§ãŸhÖ¾ÑüA¨h~ñ7ˆ>ü'ø[§üFý~Õ?køX_¿a?øfÚ[öñÇìËûDÃOÂký©ñ“þoŽßÿáRx:?øFÿá„ÿáñïÃÿ‰¿ð¬¾ YkßðÓßÚßÛð†x:Ò?°fëV÷›…ßµ×ü"÷_ðP=Oö¼ø«û?ü0øeû"þ×úgÁßü[ÔÛþ_4Ï…Ÿ?gÙ3ã¯Ã‹_Š^ øñOÅÞoˆwˆ?i¸¾ê0²×|áψÆ™¡jÃßÞëËᨾ@ø#û>þÖ:'ü;³Jø™ðþßx þ ÿKý±h gÁ_~xçágÁÿ|qÿ‡¡[|)³³ñ6µ­|>ø›ñ#þ…ïíð¾ëÀø)±eáË?êü1ðWÄxNãÏþ/~Í_´Æ·ñ?âíkûÿÂä²økÿ¾øoûcøOö{ñÇÄOÙïKñ߯>ÿ‚e|0ý€í¾<|¼Ö|oâƒúgćÿ"Ô?iÏ‚Þ ø×ñà‡Œot„ÚTš®«ð‹ân¥á› p¯ÿeÏø(ŸÂω^ý¹¾2üRý¨?d Göeý›kÿøS¿¿jxÓÞ øð³Å_eߊ~µñ¯Äï|eø‹ðÿYøáˆ´&¡ðGÄ~0м[á_xÇÆ>³M3áïõ­F Úýâ/ø(Oì á|:ø¥âÏÛ‡ö@ð¿Ã/Œð—¤ø‹â/Ú[ྉàOŠ?ð¯õˆ<;ãßøW^.Ô¼kmáÿÂâ ›m ÅßðŒêŸü#zÅÄf³ö+Ùc¿~<þÿ¶×Çß6Ÿ¶ú|ý§üàŸÁGüQñ³ÄŸðOýö½ð?ìÓûCülø5ñCþ ·û~ÃOñÓÀße¯ÚÞçáO…>0~Ï^)ø[ñ›Ä^ øuâ^ŸâŸÂOüIð6£ñGáµ·.¼)ñ ßÿboÙkâgìñoö?Õ¾Á3þ0~Ï?³w†ü?ÿøoðgMýµ>þÓ¾*ýžü+ûE~Ðÿ°‰þüeø¶ÿÿhx4‡^ñÞ‘û<|\ø§ãß?²ÄÚ‹Nð&£µa/‹¾(üLñ~°j¿µüJçöúýµ¿`¿üXý€4ωº'ìá_Ûþ ÷¤x›öqý¨¯á6ð'Š>4kÿ u»?ÚÅúí¾ðßü*ïxB…>!¸øu§K¬xÛþ¿hoøbÞËáÿÄÙÆçÇþÁKÿmÛþ éÿžø÷û>Iû0x{öƒÿ‚€þÓþ ø)ñGZøÉðWâµ§Á¯†þøiàÛ+âÆCÀ? <ûLkž6×üAákoÙSQÐþÝx‡ãnŸ§|[ŸìZ¿Šô¯‘øæ}'ág Ái¿à—ÿ?à¡þ4ý€uÿ„/ñ‚5†>6|øýâ?_ø{¤éÞýƒ?l_€^,økûfêU‡Ž>üC¹Õ>0xŸÁ>ðׂ—Z…ª§†|UñZëĺþ•a¤i²|FøoÐ~ÅßðMþÍðUø(Gí3;bþÌ¿üÿŠ_²Ç„ü;â\ÑÇÅßÛF?„ZÏüg]ø‹mâ-ãâÕŸÄ kâ×ì]ðÆ>Ó!ñ…ÇÁ? øÇÓéßtøõ«Ïøgáàèþ›ûnþÅúÏŠ¾-øGý®ÿf [Æß|?ãß|vðv›ñ÷áM÷о øWá^£ñ?Äßü=kâÉu‡øq«Ë—ãÝgÆ6z6àýFHì|Cs§\ºÄyÿøxOì ÿ ³þ§ü7ìÿ Kþü*ø\_ðÒßÿáVÂÓÿ„sþøVŸð°¿á5ÿ„Kþü"_ñTÿÂý¯ÿ ü#Ÿñ;þÍþÌÿJ¯Î |+ûrx'öKøíðWÀ?°ÿˆ*\ü5¼øOão.Áw´¿ÚóàÏÁ_ŠŸ¿bÿŒ<}¨§ü7ã¿‚Þ÷š¯‰ÿmÏü6ðWˆ>üvýõ¯‹ž Ð|eñkÀ^ø_ðöÛá›û;JÚ¿„ôï†>ýþÖmߨ¿ÃŸ¼'ûFx‡ö»ý˜4/ÙóǾ ¹ðŸ~;k?~é¼iâ«9üMky០üO¾ñd ñˆ-n|ã{G×/5'🉡–ÙdÐuEµè?á¬eøQ?ðÔðÒß³ÿü3/ý_ü.O‡_ð¢äqÿ…wÿ%wþ?øWÿòP?â†ÿ‘‡þGø¦ä5þƒ_€>&øSñá¯þükø¥ÿúþÎÔÿi/ø/÷ÄÚëá×ì;àþË4ø§aÿ¯ü?öŠøkáо5Õ|{ý–>þÅ ûCËûBþÏ>|ðçíáÍOágÅ/Ú»Zøm¦|r—ß<ûG|VøW®þÕ–¾-ñ?ŽôŸˆ¿Þ"ÿ‚„þÀ¾ð'ïŠ^,ý¸d ü2øÁÿ wü*Oˆ¾"ý¥¾ èžø£ÿ ÿXƒÃ¾=ÿ…uâíKƶÞñ·ü!> ¹¶Ð¼]ÿΡ©ÿÂ7¬\A¦k?b½–8ëúþxboÙkâgìñoö?Õ¾Á3þ0~Ï?³w†ü?ÿøoðgMýµ>þÓ¾*ýžü+ûE~Ðÿ°‰þüeø¶ÿÿhx4‡^ñÞ‘û<|\ø§ãß?²ÄÚ‹Nð&£µa/‹¾(üLñ~ô=@Á'å_ðM?û0ØßÿY×áÍ}ÿ_Á'å_ðM?û0ØßÿY×áÍ}ÿ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Á'å_ðM?û0ØßÿY×áÍ}ÿ_Œ?±ÅïÚÓöký‹ÿdOÙÏÇ_ðJßÛVñ·ÀÙƒàÁOêžø£ÿþð®¥â¯… |'à_ê¾Ö?à¢ú¯yáûÍ_B¼¸Ñ®µMFÔn4é-¦¾Ò´ë—–Χÿá²?h¯úDïíÿÿ‡þ eÿÓ, ¿è¯€?á²?h¯úDïíÿÿ‡þ eÿÓ,£þ#öŠÿ¤Nþßÿøq¿à–_ý2ÊûþŠüàñgíëñƒÀ^ñ7޼uÿÃý·üàŸøYñgŒ|câÏ‹ÿðJxWÂ~ðæs¬x‡ÄÞ&ñ±ÿ5³Ò4èZE橬ë:¥å®¥éÖ·7××0[A,«çÿ?à¨>ý¥>é~Á,à£ú·‚uoxÿÂqÉâÉÿàžŸ üU¦ø«á_Ä/ü(ø‰áŸ|;ø¯ÿðOÄOxƒÂ<⯠ë:7м+£j6úŽrE³Û=½ÄÀ«ôWÀðÙ´Wý"wöÿÿÃÿ²ÿé–Qÿ ‘ûEÒ'oÿü8ßðK/þ™e}ÿE|ÿ ‘ûEÒ'oÿü8ßðK/þ™eðÙ´Wý"wöÿÿÃÿ²ÿé–Pÿÿ”YÁ4ÿìÀ?cýg_‡5÷ý|ÿöø[ã¿ß°/ì=ðSâ–…ÿ¿Ä߃ÿ²ìÓð·â/†´ô}oþßü?ø/௠ø»BþÙðî¡«øWþÈñ‘¨iÿÚz«©è÷ÿgûV™¨^ÙKÌ¿_ÐEP_Á'å_ðM?û0ØßÿY×áÍ}ÿ_Œ?±ÅïÚÓöký‹ÿdOÙÏÇ_ðJßÛVñ·ÀÙƒàÁOêžø£ÿþð®¥â¯… |'à_ê¾Ö?à¢ú¯yáûÍ_B¼¸Ñ®µMFÔn4é-¦¾Ò´ë—–ÎÙê+àølÚ+þ‘;ûÿáÆÿ‚YôË(ÿ†Èý¢¿é¿·ÿþoø%—ÿL²€>ÿ¢¾ÿ†Èý¢¿é¿·ÿþoø%—ÿL²ølÚ+þ‘;ûÿáÆÿ‚YôË(ïú+àølÚ+þ‘;ûÿáÆÿ‚YôË(ÿ†Èý¢¿é¿·ÿþoø%—ÿL²€>ÿ¢¿ |eÿlÕ<ñÛá?ìÑâø&¿íÿkñ·ãgö—ü  Ó|Aÿãñÿ°x;â_ìÿá2ñ‡?à¡z·„¾Â_á/‚ßõo†Ÿð´5ïÂ×ÿ…;ñbÃáŸü%šŸÃ_Ùh~ÿÿ ‘ûEÒ'oÿü8ßðK/þ™e}ÿ_0~Ñ¿±ïÁÚ¯QøU¬|__Œ «üñ‰)h_ð‹üMø?û ~Í? ~"øgûOGÖÿáñßÃÿ‚þ 🋴/íŸê¿‡õìi†Ÿý§¡jºžöµiš…í”°\ËõýQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE|ûzü-ñ߯/Ù;â·‚þh_ðœx¶ßþ_Û|ŸSÑôMö“Ñþ|IðwÅ~ÈÞ.ÖüM¨iþÑ~þØðV¹û/üEÕülšß4¿|[ñ÷Ž|!ã [ë> ×|þ ÿâm;ö‡Ô~>þÞÞ¶ñ†¾ þ×¾ ðøá}GÂ~*øbþ;ø5ðO·>ðçíYñá—‹ôm7W‡ãí)«Üx† üM–q?Ä_Ø3áßì §ëð‡‰<­è0~×ÀðIßùE—üOþÌö7ÿÖuøs@ÑEQEQEQEQEQEQEQEøCûSøOÅWµÇoØö_ øƒÄž6ÿ‚Ÿøƒö~ñ÷Á_Ú&×FÔu-[ökøû1xÁš/í?eáÿhÖÞ+Õþxƒö&Õü!£þÓ?°×ÆO¥‡‚áÿ‚—ÁJ¾·ƒ¾Ésá¿5/Ýêøâ7ü¥7ö7ÿ³ÿ‚–ëEÁ'kïú(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š+àø$ïü¢Ëþ §ÿfûÿë:ü9¯¿ëàø$ïü¢Ëþ §ÿfûÿë:ü9 ¿è¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Šøâ7ü¥7ö7ÿ³ÿ‚–ëEÁ'kïúþ`¿mÿø)¿Å?Ùßþÿ‚~~Ézìÿ wþoìÿâÏ…þñw€>/xŽ÷ÄvŸ ?lߟ³œÿ~3ø×áÔo¿áÿ†gÿ†ø¥âoø_Lñ‰ü9âo…—¶õ߉Ÿ ¿±üEáÝ7ú} Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( ¾ÿ‚NÿÊ,¿àšö`±¿þ³¯Ãšûþ¾ÿ‚NÿÊ,¿àšö`±¿þ³¯ÃšûþŠ( Š( Š( Š( ¿0>ÿÁF¾'üqøYðÓã_Âßø%×íÿâ†_>ø7â—ïÂgÿÊÑ?á"ð'Äi¾,ðŽ»ýâ/ø(æ‘â #û_Ãú¾Ÿ¨fkºV™¬X}£ìºžŸe{öÑ~Ÿ×ÀðIßùE—üOþÌö7ÿÖuøs@ü6GíÿHý¿ÿðãÁ,¿úe”Ãd~Ñ_ô‰ßÛÿÿ7üËÿ¦Y_Ñ@Ãd~Ñ_ô‰ßÛÿÿ7üËÿ¦YGü6GíÿHý¿ÿðãÁ,¿úe•÷ýðü6GíÿHý¿ÿðãÁ,¿úe”Ãd~Ñ_ô‰ßÛÿÿ7üËÿ¦Y_Ñ@ÏÆï|`øËÿ.ý…¿oû¯ø$¿í¿¢jÿ±×ÁÿÚÃÂw¦ñ þ A¦üBñÿо3é?ü ð§Ã>,¾“öùÕ.|Uðáç‚|OûRø‚ÛFøcQð—ÄÏøgVðݶ«¤x—â%Çèÿü6GíÿHý¿ÿðãÁ,¿úe•÷ýðü6GíÿHý¿ÿðãÁ,¿úe”Ãd~Ñ_ô‰ßÛÿÿ7üËÿ¦Y_Ñ@Ãd~Ñ_ô‰ßÛÿÿ7üËÿ¦YGü6GíÿHý¿ÿðãÁ,¿úe•÷ýðü6GíÿHý¿ÿðãÁ,¿úe”Ãd~Ñ_ô‰ßÛÿÿ7üËÿ¦Y_Ñ@?û=|kð¯í)ð àíà]?ÄO‚~?|øiñ¯ÁÚ_‹-tëi¾ø©à½Ç^ÓüMc£êºö‘gâ =#]³·Ömt½sYÓ­õîa±ÕudŠòo`¯€?à“¿ò‹/ø&Ÿý˜ìoÿ¬ëðæ¾ÿ Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( ?h_ÚÚëàwÅ?„Ÿ<#û5þдÇÄߌþ3|RÑü3ð/PýœtOøG| ð+Ä<'ã}wÅ:Ïí!ûC~ÏžÌñíðëOÑ4Ïê¾#Ö/þÑ«ÝO§ÙYir\ËçÿðÙ´Wý"wöÿÿÃÿ²ÿé–QñþR›ûÿÙ€ÁK?õ¢¿à“µ÷ý|ÿ ‘ûEÒ'oÿü8ßðK/þ™eðÙ´Wý"wöÿÿÃÿ²ÿé–WßôPÀðÙ´Wý"wöÿÿÃÿ²ÿé–Qÿ ‘ûEÒ'oÿü8ßðK/þ™e}ÿE|ÿ ‘ûEÒ'oÿü8ßðK/þ™eðÙ´Wý"wöÿÿÃÿ²ÿé–WßôPÀðÙ´Wý"wöÿÿÃÿ²ÿé–Qÿ ‘ûEÒ'oÿü8ßðK/þ™e}ÿE|ÿ ‘ûEÒ'oÿü8ßðK/þ™eðÙ´Wý"wöÿÿÃÿ²ÿé–WßôPÀðÙ´Wý"wöÿÿÃÿ²ÿé–WÓÿ³×Æ¿ þÒŸ¾þÑžÓüA¤ø'ã÷Áÿ†Ÿü¥ø²×N±ñV›á_Šž Ñ|uáí?ÄÖ:>«¯i~ ³Ò5Û;}f×K×5:ßQŽæWQ¶H¯&ö øþ ;ÿ(²ÿ‚iÿÙ€~Æÿúοhïú(¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ øþ ;ÿ(²ÿ‚iÿÙ€~ÆÿúοkïúüàðŸüŸöKð…|3à_ø‡ößð_‚|áýÂ~ðw„ÿà©?ðSÏxWÂ~ðæm£ø{Ã>ðöû`Yé‡ô-"ÎÏKÑ´m.Î×NÒôë[kh- Š%ý¢¾ÿ‡iþοôQ¿oÿü[ü7ÿ£"øvŸìëÿEöÿÿűÁSú2(ïú+àøvŸìëÿEöÿÿűÁSú2(ÿ‡iþοôQ¿oÿü[ü7ÿ£"€>ÿ¢¾ÿ‡iþοôQ¿oÿü[ü7ÿ£"øvŸìëÿEöÿÿűÁSú2(ïúçüYâÏ ø ¾&ñ׎¼Máÿø'Á~Ö|Yãø³YÓ¼9á_ øWÚuαâx›Ä:ÅÍž‘ øBÒ,ï5MgYÕ/-tí/Nµ¹¾¾¹‚Ú e_ˆ?áÚ³¯ýoÛÿÿÅÿMÿèȯý¡à¿¾6|øáðgGøÍûoøOWø¹ðâ_Ã/Å^,ÿ‚ÁI>1øWÃZ|­xVÇÄ&øEã¯ÛüñSÃú5έ£¬ü8ñŠ7…|q§[\øcÄ*ÚF©xßÿ?h_€_´§…u~Î~ü~ðN“â ¯ êž1ø)ñ/Á<+¦øªÇNÒµ‹ï ê!ð.µ¯i~ ³Ò5ÝTºÑ®/#ÔmôígJ¾šÙ-µ9fùƒþ ;ÿ(²ÿ‚iÿÙ€~Æÿúοkñ‡ö<ÿƒF?àš?³м ñ?Ç^6ý§ÿh?ŠÞ ðÿ…î­õKã­ð/¾øËáÝGÃ!‡ã_ÂH¿g8þüdøuâ Äšå×€ô­Sã· ðŽ­H·Ú‡Š~Åÿ´7íû9øàþ‡ãoÙÏàÿůš¦—ñ¯áW>,xWÇžøCð§Ç:¾ø{§ØøãwÀ½_Á¾ ñF¯¢èvö¿n5ÏéÞÓ¡Õa›áçˆ.u+;Í#íúøƒþ _ào‰Ÿ?àžŸ¶ÇÂ/ƒ? ¼Añsâ·ÆÙƒãWÁO‡Þðιð÷ÃZŽ­â¯Œ~×>i:…νñOÆß<¥øÃ>*Ox²ëQñU¶¢žÑ5‘á+Äþ)mÂúÈåì¡ÿmý©õOÚcþ _û=þÒö߲Ÿ{'ì§~Õ> ›öz‡â/ÁвwöOì÷ã^$Ðþ-üñÿÄŸÚ?þÏÃÿý“AðÁŸ‹ZOŽþ lxÁÚÿÀ·ðˆYÚMû?kûnþÅ÷ÚwÁMbÇö»ý˜/4ÚSį„ÿgMR×ãï›;ã÷Š´/iþ×<3ðRú=·ÅOhÞ6Õ´¿jº7e×µ;ÅZ–ŸáëËhu{Ë{9?žØçþ Éû@x'ÄðIHþ þÀ¿ð쌲GÃÿØÿÁAlïíŸÙÃ[xà >øKã?ì±ÿ ¯ö[øÕñZ÷ö–ÿ†©ø›¤Zx÷þ‡í;¦øXý¿á\ÿÂÛøzú‡Æ[øjçéýáÇíûª|7ñgì}ûøƒÃžðçüzÛö«ÑÿhgöŒý'ø{ãßÙø,φo)|á?ø{Ç:ïÅk?Œ~×µO´üñׂ¼¢7Ã?ø›ÆVn¾2]x;öiñ`ÔÏíuûSøá×ÄÿÂÕý€>$Âÿø+û"ÿdüoˆ¿ÿáý–>2þÔÿ¿føU_¯ÿáiøWþ÷íÿðçþü&¿×ì~5øuáÿìMßð«.?á.þÞðÿÿAÿ‚‰ü,ý‡¿gÚžûP~È k¯þÏþ.øéðSà·í!ãO\ë?áµ×õ]+ÂÚ?ÁË_Œ¿>&ø³þÍï‚|Mð·Áß„õÖþÏñÝÌz…¶‘ãi¼7¨xZð|ý¦6\}áþµÿ ]ñKþ¯éŸÛ´&£ý¿àíß‚~þÔñü"¾?û\üýª´ÿ‚Ýü;øYûxƒãÕÏüëàüv¿i‚ßeê6zŽ«ûxwö?Óþ ~Òz_íñsàÄOøàÿÄO‡ú×Å꿤øåá[Ï þÐ^'¸Òtÿ xÚÇÅz¼û?ãÿÚÇöXøQñOÁ_þ)~Òß³ÿÃ_¿¿áÿ…uðwÇÿ>x;⟿á1ñïƒü#ÿWÃßxNñoŠ¿á*ñn¨x[ߨZEÿöçˆì/tM3íZ¬ö©ðÿí»ÿ ð¯„þ~×zÇìEû\þÄ.ý¨ÿbŸƒÿ~>|ZøâËý;ö‰ñRxWöpð_‹.¼yð÷Äß >þÒ?>"|#ñüD¶ðŸƒµŸˆ>%—ÄZw€µ›Ÿk?5}s[Ó[Kü¡ø‡ÿ¸øÙ£~Ñßµ¥ÇÅÏÙ ö€ÿ‚’Âçý¿üûw~È4¯ø*?ÅߨïàŸì÷¬xÒëÃ?¢ðçÆŸ‡ší¦ü@øYñöøá#ÁÏŸ³ÇÀÚoÇ~Kà‡¾³øMÿ­—Ãß }¿âÏ„ÿµU÷üþ ¥û#ØþÈ?.þ+|GñüFø,e¼+ñûNý¾jOÚçÆ? (ÿÃÙ?d ;ö©ðTß³Ô?~üSý“¿²g¿üzñ&‡ñoàÿþ$þÑÿð¶~øÿ욀þ üZÒ|wð[ûcÄ~øÞ×þ»ÿ„BÎÒoÛÿøkÙcþ·ü2çü4·ìÿÿ 5ÿFéÿ “á×ü/où¿ábÉ"ÿ„þü“ÿø®ä^ÿ‘;þ*oùÿ§WóƒûÿÁ9?høþ )Á?ØþñƒöHøà[ø(/íý³û økoxcágÃß |gý–?áUþ˾+^þÒßðÕ?t‹OÿÂðý§tßk³·ü+Ÿø[_PøÁâ+ \ý?àÿþÜž#ý¡ÿb¯|\ÿ‚~|`ð&¯ðþ ?ûZ|añÖ§ð3ãìsìKà¿‚´?ÂOÚ³àUŸŒ¾xFÇöŠøIñ“㇈5ï|QøkûNüXø±ûA~ËÐ~Ò³øãâ/íu«|3Ñ< ¤|EÒÿg  ÕÿÁB`_Šÿðšÿ­ý¸d‰_ð­~øâÇÄ_ø@?io‚þ1ÿ„ágƒ¾Çÿ wÄ¿ÿÂ;ã]Gþ_‡þþÑÓÿá#ñ–»öho³þÓÔ­~Õÿ_ø)ûBüý¥<+¨xëösøãðã÷‚tŸ]xOTñÁO‰~ ø©á]7ÅV:v•¬_xgPñu­{H³ñž‘®èz¥Öqy£o§k:UôÖÉm¨ÙË7òâÿø$Ÿíûñ+Á uO„?~0| ðOì¹áÿÙÇâ‰û*~Ó¿gHÿi_iß¾>êÿ-¿à™±ÿüwö7øµ¯~Ðv±œšíÍçÂíCöÖ±±ñWÁÿ³ßìg㯆:ž‡mªøÿÄ¿³¯ï÷üömÓ¿gíGö²Ô|+û|`ý‰üñkã‡ãû{?ß·'Š¿kOŒ¿þ!jÞTø­ñßâcø×ûL|)ø7â oÅ:Èð¤Þ)ð¯íã߉Ÿ`𕿈¾.é~¶ðŸÃ»ÐÕú(¢€?`ÛCö`ýž?à›ðLOü\ø¿áÿxÚëþ ÁûxëTðv›eâx«ÀŸ 'ø àíûö‚ø·áïèþ%Õþ ~ÌÕôMKKñïíIñjÏÁ³ÇíFì|yñ7×7VqOû=_Ïü/öø™ðcþ ·ûKû~ÑÞýž~~Ò_³ìçñ»ã·‚üIû7|=øµâ­âgÅ_€ß u_‰ÿg/‰ÖºçÃM_ÃßøBø{àMö¿ð‡‡ôoí;ÇÒ>þß¶¿ìëàKø“XÔÿ¢¾ÿ‡iþοôQ¿oÿü[ü7ÿ£"øvŸìëÿEöÿÿűÁSú2(ïú+àøvŸìëÿEöÿÿűÁSú2(ÿ‡iþοôQ¿oÿü[ü7ÿ£"€>ÿ¢¾ÿ‡iþοôQ¿oÿü[ü7ÿ£"øvŸìëÿEöÿÿűÁSú2(ïú+àøvŸìëÿEöÿÿűÁSú2(ÿ‡iþοôQ¿oÿü[ü7ÿ£"€>ÿ¢¾ÿ‡iþοôQ¿oÿü[ü7ÿ£"øvŸìëÿEöÿÿűÁSú2(ïú+àøvŸìëÿEöÿÿűÁSú2(ÿ‡iþοôQ¿oÿü[ü7ÿ£"€>ÿ¢¾ÿ‡iþοôQ¿oÿü[ü7ÿ£"øvŸìëÿEöÿÿűÁSú2(ïú+àøvŸìëÿEöÿÿűÁSú2(ÿ‡iþοôQ¿oÿü[ü7ÿ£"€>ÿ¢¾ÿ‡iþοôQ¿oÿü[ü7ÿ£"øvŸìëÿEöÿÿűÁSú2(ïú+àøvŸìëÿEöÿÿűÁSú2(ÿ‡iþοôQ¿oÿü[ü7ÿ£"€>ÿ¢¾ÿ‡iþοôQ¿oÿü[ü7ÿ£"øvŸìëÿEöÿÿűÁSú2(ïú+àøvŸìëÿEöÿÿűÁSú2(ÿ‡iþοôQ¿oÿü[ü7ÿ£"€>ÿ¢¾ÿ‡iþοôQ¿oÿü[ü7ÿ£"øvŸìëÿEöÿÿűÁSú2(ïúðþÖ?²ÇŠ~ øñKö–ýŸþümø•ÿçü+¯ƒ¾?øÉðëÁßü}ÿ ˆï|áøB¾ø‹Äzw‹|Uÿ W‹tíCÂÞþÂÒ/ÿ·¬ÿ…éÿ /áÿü)/øWÿð¶?áqÂeáÏøUŸð«?áÿ„Ãþ_ü,/í/øD¿á_ÿÂ%ÿOü&_ÚÿðŽÂ9ÿ¿í/ìÏôªüøËû,~Ô_¶üsãrüVOÚöpý‘~~È û<~Îÿ>ßÁ;~/ü,ý¢ôÚTð—Œÿmï |gøCûIüý >&èÿðÞøKöøqá k_ o¼ ?‡?g?‰ž —Æš,?­|)⯈?gÿÙöý¹ÿ‚F|xý‚¾/þÆþ ñ­·ìÅûOü&ñ§ìKð·ö¿øãû:x»Qý¯¿aoµçÃ/Ú{áïì¿ñËâߊ´Wýãÿþx—öt¿±×~øönðß…|Ið[š_ø'Bø¬xlõ{ãoükþ •ð3ÀŸ >#ë?¶Oìÿã¿ |hý <+û7øYøIñƒá‡Ämüw¬k¶ñ¶±â_h~1 x3áÿÀÏ |BðÏÄ7ñgˆ4}áï5_µÌ—Þ-ñßÃo øÏØ< ûBøªÇö‡ý½tŒßb ?ÙóökðÿÀßxWKð7ĽFßö‡øá]wá&»ã¯‹¾&ý½l|U­'‚~øY¹Ò_Æ?u:-NÔ~iºïˆ|Os4–fò?Êø(?ÁÏÚïö¶ðwìñWáoü[âÂÿþÏÿð[ïØóöÇø‹ð¶óâ·ìQiñÛãÀŸÙÿàM–™âï$Ô<;û@ÿÂ’ÿ…ý§ýŸû>ø+ÁšïÇÿxÆÿÁß ü­jz¯†|%w…àŸ ?loÙ÷ö±ø™ñ‹öâø¡ðkàü$w¾øÿjñìÿ¥xÿâ¯Ão‡þýª¼wÿ÷ý»><~ݾx+Äš.µñÄ ?·¼?ã/ |ð狾7xÀ;üSÔïo^ ~iRü@œôFý·bÿüñgíáïÚïö`×gÏx‚ÛÂ~:øí£|}øSª|ð_Н'ðÍ­Ÿ†|Yñ>ÇÅ“ø'Þ º¹ñ§ƒ­í´mc\³ÔgŸÅž†+f“^ÒÖëåÿÚÇöÞþÑýioÛþ ÑûIþÈ¿á—~üdø¡âwì¿ðÕŸ &þÊ3þÔ´fÿëý¶´ïÚGã‡Æ¿Þø?âäÿ³w‡±ð[öHñ©«þØ_ðNÙßöVøeªxg_ýš¼'¨þÓ¿W¿¶×|Qñæû^øÇá{›/ü ðOí ð;ÁÞ,ñwÇi|ãKÿ ü,Ö|=ð¯Ã¿´ ø ÏÄ€?û(Á[j}Sö˜ÿ‚WþÏ´½·ìñGþÉû ißµO‚¦ýž¡ø‹ðGâŸìý“û=øÇã׉4?‹üñ'öÿ…³ðÿÇÿdÐ|ðgâÖ“ã¿‚ßÛ#ðGÆö¿ð-ßü"v“~ßÿÃXþËð½¿á—?᥿gÿøi¯ú7Oø\Ÿ¿á{Èÿ þIü$ð°?äŸÿÅsÿ"÷ü‰ßñSÈý:¿œØçþ Éû@x'ÄðIHþ þÀ¿ð쌲GÃÿØÿÁAlïíŸÙÃ[xà >øKã?ì±ÿ ¯ö[øÕñZ÷ö–ÿ†©ø›¤Zx÷þ‡í;¦øXý¿á\ÿÂÛøzú‡Æ[øjçéÿø'öäñíûx«âçüóã5€ÿðQÿÚÓã޵?Ÿÿc˜¿b_üý¡þ~ÕŸ¬üeðÓÂ6?´WÂOŒŸ"ÿÂûK|ñü ? <ö?øK¾%ø×þßê?ðŠü?ð¯öŽŸÿ Œµß°xsCû}Ÿöž¥kö¨7úÿÁOÚàí)á]CÇ_³ŸÇƒÿ¼¤ø‚ëÂz§Œ~ |Kð_ÅO é¾*±Ó´­bûÃ:‡ˆ| ­kÚEŸˆ,ôwCÕ.´k‹Èõ};YÒ¯¦¶KmFÎY¿/ÿÁ$ÿo߉^ økª|!ø+ñƒào‚eÏþÎ?ôOÙSöøóû:GûJø»Nø ñ÷WøñmÿÈýÿà«¿±¿Å­{öƒ³ýˆ,ä×no>j¶µŠ¾üoýžÿc?|1Ôô;mWÇþ%ý¿à˜³nû?j?µ–£á_؃ãìOàŸ‹_4?ÛÙü~ý¹2]~ıޥqñ´÷Åxª ÏðL׿øI០~ÑšŸÇ…?¾ÅâŸj¾(ѼðÇáÏ‚ôïê0[Xx.Oxnë\е€êwáoÅ…Ÿ| ¡|Rø)ñ/áÿƆ^(þÓÿ„gâ/Âßxsâ,ñ7|à;âGŠ´gâ]ç„ü;«ø¿QÒm>Ÿÿ‡iþοôQ¿oÿü[ü7ÿ£"€>ÿ¢¾ÿ‡iþοôQ¿oÿü[ü7ÿ£"øvŸìëÿEöÿÿűÁSú2(ïú+àøvŸìëÿEöÿÿűÁSú2(ÿ‡iþοôQ¿oÿü[ü7ÿ£"€>ÿ¢¾ÿ‡iþοôQ¿oÿü[ü7ÿ£"øvŸìëÿEöÿÿűÁSú2(ïú+àøvŸìëÿEöÿÿűÁSú2(ÿ‡iþοôQ¿oÿü[ü7ÿ£"€>ÿ¢¾ÿ‡iþοôQ¿oÿü[ü7ÿ£"øvŸìëÿEöÿÿűÁSú2(ïú+ñ‡öµý’¼û5øá/Åß„_¿mý'ÆÚOí¿ÿÝðœrx³þ Eÿ ø©á]K¿?ࡳˆžñ7ÿŠÿ´ÿ¾ø¿Ãþ/øwãoxWYѼUá]gN¸Óµ›’-’å-î!ýž Š( Š( Š( Š( Š( Š( Š( Š( Š( ¾ÿ‚NÿÊ,¿àšö`±¿þ³¯Ãšûþ¿b/‹ßµ§ì×ûþÈŸ³ŸŽ¿à•¿¶þ­ão€?³À/‚ž1Õ<'ñGþ ‡}á]KÅ_ þøOÀ¾!Ô<3}¬ÁEô^óÃ÷š¾…yq£]jš¨ÜiÒ[M}¥i×/-œ ³ÔWå?à¨Þ&ø™ñ3ã—ÁŸÿÁ7ÿmÿüVýšüAà ürø}mñ3þ so⯇ZÄ¿‡ºÅ?‡×:Γ}ÿ$µ¹ŸÃþ4ðOˆìõ ø³KMCºƣ¦ø³Ã:ÌÞ)ð7t]Ø?á²?h¯úDïíÿÿ‡þ eÿÓ, ¿è¯€?á²?h¯úDïíÿÿ‡þ eÿÓ,£þ#öŠÿ¤Nþßÿøq¿à–_ý2ÊûþŠøþ#öŠÿ¤Nþßÿøq¿à–_ý2Ê?á²?h¯úDïíÿÿ‡þ eÿÓ, ¿è¯ÈÁ[5OüvøOû4x£þ ¯ûÚümøÙý¥ÿ‚´ßÁ8üE¿ìø—ãû?øL¼GáÏø(^­á/…ßð—øKà·Æ}[á§ü- {ÁßðµÿáNüX°øgÿ f§ðׯ¶Z¿ÿÃd~Ñ_ô‰ßÛÿÿ7üËÿ¦Y@Ñ_Ãd~Ñ_ô‰ßÛÿÿ7üËÿ¦YGü6GíÿHý¿ÿðãÁ,¿úe”÷ýðü6GíÿHý¿ÿðãÁ,¿úe”Ãd~Ñ_ô‰ßÛÿÿ7üËÿ¦Y@Ñ_”ÿà¨Þ&ý˜¾êŸ¾?ÿÁ7ÿmÿ…? txÃ>!øƒâωŸðK›? øoQøŸñ Âß <sâmZ/ø)$öÞðý×¼iáÍ;Yñf¶öðŽysâë:…´cZ°öølÚ+þ‘;ûÿáÆÿ‚YôË(ÿ‚NÿÊ,¿àšö`±¿þ³¯Ãšûþ¾@ÿ‚{|-ñßÀïØöø)ñKBÿ„_âoÁÿÙöiø[ñÃ?Úz>·ÿïŽþüðW„ü]¡løwPÕü?«ÿdxƒHÔ4ÿí= UÔô{ÿ³ý«LÔ/l¥‚æ_¯è¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(àˆßò”ߨßþÌþ Yÿ­ÿ¯¿ëóƒö ƒã€¿mÙgöŒøkû/|`ý§<à¿Ùƒö×ø)ã}/à§‹¿f¯x«Â~*øÑñ[ö ñ×ÃýCP±ý¤¿hÙ÷HÔü?©é³ïÄk{«¯ ëšþ£¥ê6ºT7úTÚ¬7‰ÐÃd~Ñ_ô‰ßÛÿÿ7üËÿ¦Y@Ñ_Ãd~Ñ_ô‰ßÛÿÿ7üËÿ¦YGü6GíÿHý¿ÿðãÁ,¿úe”÷ýðü6GíÿHý¿ÿðãÁ,¿úe”Ãd~Ñ_ô‰ßÛÿÿ7üËÿ¦Y@Ñ_?´¿ü³Týþjßÿhø&¿íÿàO‡ú?ÛüûÛ?Á8üwâ=CûÚ÷|Eÿç€>ÁBü[ñÅ¿ð‰|?ð—>&x×þo ëð…|,ð'þ(x¯ûáÿ<]â=ßÿá²?h¯úDïíÿÿ‡þ eÿÓ, ¿è¯€?á²?h¯úDïíÿÿ‡þ eÿÓ,£þ#öŠÿ¤Nþßÿøq¿à–_ý2ÊûþŠøþ#öŠÿ¤Nþßÿøq¿à–_ý2Ê?á²?h¯úDïíÿÿ‡þ eÿÓ, ¿ëàø$ïü¢Ëþ §ÿfûÿë:ü9£þ#öŠÿ¤Nþßÿøq¿à–_ý2Êôø'·Âßüýa´/øEþ&üý?fŸ…¿|3ý§£ëðŽøïáÿÁxOÅÚöχu _Ãú¿öGˆ4COþÓе]OG¿û?Ú´ÍBöÊX.eúþŠ( Š( Š( Š( Š( Š( €?य़òn¿ìÿÿà“¿úôßØÞ¾ÿ¯€?य़òn¿ìÿÿà“¿úôßØÞ¾ÿ Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( â‹þ ãø£ûhxçþ 3ÿ—ñ×ÅÙ¿öŸøuð'öñˆ>(ÝxÇö¬ø?ñ[Â?>x«áŸÄk²×ìßâø“P»ðwƒ|A¤~Éßµ-¾¨>Éy¯ê:Ãß ü2¾øw¨X|6ÑÒ_ÿkµðìoÿ'ÿbÿ³ÿøqÿ®²ÿ‚i×ßôQEW?âÏxWÀ^ñ7޼uâoø/Á> ðþ³âÏøÇÅšÎáÏ øO¾Ó®uø›ÄÞ!Ö.lôÃú‘gyªk:Ωyk§izu­ÍõõÌÐK*ôPóCàOÛ þ ·ûT~Ëÿ¾*üJÿ‚—þÄ¿iÛSÄý ~ë^7ý¥>^øãö;ƒà§ˆ4ˆ?ðM¯ë¿ jZÇ‚|G j×¾?ñþR›ûÿÙ€ÁK?õ¢¿à“µ÷ýQEQE$_ðxN¥ñ÷ÅŸ°gÀOÙÏà'ÂOÚâÏÆ_ÚÁš—Ä=Sà€¼iâ߆oá_ "øÀÿ >2ßx2K™Ä?h?Œ®>ø \ÐõM;Ç¿¼,ÚD–þ6𯄬õOé{öNñÿÅ?Šÿ²ÇìÓñKã§‚¿áZümø•û?üñÿÆ/‡_ðŽxÁßð€üSñïx‹â‚¿áñ…î£âß ÿÂ+âÝGWпáñN¡â=ìÙšÝíÖ§ku;üÿÿ,ÿ“uøsÿgÿÿÿצþÆõ÷ýQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE~ êðPŸØÅðPOŠWßnÙᇇÿ`oí€_ ~|Tý¥¾ ø+XÔÿjˆžмEûG|j—Â>/ñ­Å•ïü)ÿ†Zß‚?eƒ¿ôðoÄ_‡þ;ñoü«àŽôkïëÚDòúÿü·ö…øâÏ üaýþüqø?ñçÁ?±'ˆ<'á?ƒ^1ø)ñ/Áü*ÿ±ïÄ­;YÖ?e ê!ø}­xËHÑ|Að/Hð¿cû­Ç~=ñÇ?éß³•ûMüQ¶Òí¿h¿˪{ü³þM×áÏýŸÿüwÿ^›û×ßôQEQEQEQEQEQEQEQEQEðü³þM×áÏýŸÿüwÿ^›û×ßõðü³þM×áÏýŸÿüwÿ^›û×ßôQEQEQEQEQEQEQEQEQEQEðìoÿ'ÿbÿ³ÿøqÿ®²ÿ‚i×ßõùû ütûí1û[xƒ]ð·ü"¿oÚÛã§ì?ñ{VÖüûÚ'þïì÷ðÿöMø¡ámÞÃH¸ð•Ÿöׄ¿bí3öÉýš5½?∿áªÿcoÖ~é¦|øÿiðïõú€>ÿ‚™Ï¬Ã.èú6âïˆÿ„ãö¿ÿ‚q|-ñˆ>üEñßÂOÿ ñoþ %û,ü1ø¡h_þø‹Â_<%ÿ oÃÿø›Âzž§á?hšÇö>·¨[[jÿhf£þ§û:ÿÑFý¿ÿñl_ðTßþŒŠ?य़òn¿ìÿÿà“¿úôßØÞ¾ÿ €?áÚ³¯ýoÛÿÿÅÿMÿèÈ£þ§û:ÿÑFý¿ÿñl_ðTßþŒŠûþŠüà¹ÿ‚SþÉwž*ѼuyâÛ~ëÆÞðÿ‰¼'áïÜÿÁRà§“ø«Bð¯5 ë1ðÎâlÕô¿ø³WðuOhÖ7iÚö£à¿ ßj–×W>Ñå³è?áÚ³¯ýoÛÿÿÅÿMÿèȯ¿è €?áÚ³¯ýoÛÿÿÅÿMÿèÈ£þ§û:ÿÑFý¿ÿñl_ðTßþŒŠûþŠüàÿ‚hø ÇßðQŸ„V~7øÁãOü ý·ü)á?†Ñükøåñ—ö…ñW„ü+â?ø'§ìñ_VðΟñãÇŽþ#üD—ÃòüDøã¯Zè×Þ*ºÓ´½GŨÒí¬­§ëú?_~Æÿòq_ðV/û?ÿ‡úë/ø&}ÿ@ÁK?äÝ~ÿÙÿÿÁ'õ鿱½}ÿ_˜ðTÿxïQøEð·à·À‡_ð»i¯~Ðþ=|$ø3‹´Úø«Gý‚þ6|2ý¶4xBø“ðÛ]ÿ„ƒÂ^ þÓ‚Úæ}3Xðþ±¥kÖ5 ø³Â>.🉴ýžøàOèúç‚~"ü:ñ¶‡áÿü9ñ߇üEàoøwÃþ-ðþ³£X€zQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@~@ü:ýœ<%ûP~Ô_ðRmgâßÅÚÿþ-íðßáoÃßü-ý½¿n/ÙÿÀžð'ü;·öø6…¡|8øûC|2øeö߈|yâÍOSÃ?Û®±âmBçRÔ.ÿÑÖ×êøö7ÿ“Šÿ‚±Ùÿü8ÿ×YÁ4èÿ‡iþοôQ¿oÿü[ü7ÿ£"øvŸìëÿEöÿÿűÁSú2+ïú(àøvŸìëÿEöÿÿűÁSú2(ÿ‡iþοôQ¿oÿü[ü7ÿ£"¾ÿ¢€>ÿ‡iþοôQ¿oÿü[ü7ÿ£"øvŸìëÿEöÿÿűÁSú2+ïú(àøvŸìëÿEöÿÿűÁSú2(ÿ‡iþοôQ¿oÿü[ü7ÿ£"¾ÿ¢€>ÿ‡iþοôQ¿oÿü[ü7ÿ£"øvŸìëÿEöÿÿűÁSú2+ïú(àøvŸìëÿEöÿÿűÁSú2(ÿ‡iþοôQ¿oÿü[ü7ÿ£"¾ÿ¢€>ÿ‡iþοôQ¿oÿü[ü7ÿ£"øvŸìëÿEöÿÿűÁSú2+ïú(àøvŸìëÿEöÿÿűÁSú2(ÿ‡iþοôQ¿oÿü[ü7ÿ£"¾ÿ¢€?à ßðOŸ€Þø àSKñ÷í¿usuûoÿÁ2|3,^&ÿ‚›ÁIÿ‡iþοôQ¿oÿü[ü7ÿ£"øvŸìëÿEöÿÿűÁSú2+ïú(àøvŸìëÿEöÿÿűÁSú2(ÿ‡iþοôQ¿oÿü[ü7ÿ£"¾ÿ¢€>ÿ‡iþοôQ¿oÿü[ü7ÿ£"øvŸìëÿEöÿÿűÁSú2+ïú(àøvŸìëÿEöÿÿűÁSú2(ÿ‡iþοôQ¿oÿü[ü7ÿ£"¾ÿ¢€>ÿ‡iþοôQ¿oÿü[ü7ÿ£"øvŸìëÿEöÿÿűÁSú2+ïú(àøvŸìëÿEöÿÿűÁSú2+æÚ×öJðì×à„¿~|Zý·ôŸi?¶ÿüwÂqÉâÏø)ü/â§…u/ üTÿ‚…þÌ >"xgÄßþ+þÓþ6øwâÿø¿áß¼Uá]gFñW…u:ãNÖnH¶K”·¸‡öz¾ÿ‚–ɺü9ÿ³ÿÿ‚NÿëÓczûþŠ( Š( €?य़òn¿ìÿÿà“¿úôßØÞ¾ÿ¯Ìø*ˆ¼w¨ü"ø[ð[àGïø]¿´×?h¾|ÅÚ?í|U£þÁ>~Û<›Æþ5Ô Õ¿áW|?ñü)o ~Íš?Æ SÃ×<+ûG~Ò_³ƒüwáâD~$Ó¾ÿø[ñKÀŸ< ¡|Iøm®ÿÂAá/iÁms>™¬xXҵ뇆|YáxOÄÚ~âß|@ð'‹t}sÁ?~xÛCðÿŽþøïÃþ"ð7Ž|;áÿøYѬ@=Š( Š( Š( Š( Š( ¾@ý¡kk¯ßþ|ðì×û@~Ó~0|?øÍñKGðÏÀ½CöqÑ?áð'À¯üðŸõßë?´‡í û>x~?3Ä´í?DÓ<;ªøX¿ûF¯u>Ÿee¥És/×õðÄoùJoìoÿfÿ,ÿÖŠÿ‚NÐÿ ‘ûEÒ'oÿü8ßðK/þ™eðÙ´Wý"wöÿÿÃÿ²ÿé–WßôPÀðÙ´Wý"wöÿÿÃÿ²ÿé–Qÿ ‘ûEÒ'oÿü8ßðK/þ™e}ÿE|ÿ ‘ûEÒ'oÿü8ßðK/þ™eðÙ´Wý"wöÿÿÃÿ²ÿé–WßôPÀðÙ´Wý"wöÿÿÃÿ²ÿé–Qÿ ‘ûEÒ'oÿü8ßðK/þ™e}ÿE8?²å—í»ð‡âŸ´‰ŸðKÏÚÿÄ¿³/ì]ðÿÄß ?àžžðçÅ¿ø&¥çˆô/|GñˆlàÔ¾2x+Å_ðP´]âì›û2é ÿbÙ§âÇ…þ#xçâŸþk_µÄOŒþ2¸Ö¿jMOÀý?ÿ†Èý¢¿é¿·ÿþoø%—ÿL²¾ÿ¢€?k¿‹ßµ§Çï…>ð/ƒ¿à•¿¶þ›«è_´ÿìEñ®òãÄßà˜vztžýšÿm€_´gŽ´ûi´¿ø(¾³rþ Õ¼ð¯Ä:_„íe³‡N¾ñUæc¬jº‘q}®ißOÿÃd~Ñ_ô‰ßÛÿÿ7üËÿ¦Y_Ñ@?û=|kð¯í)ð àíà]?ÄO‚~?|øiñ¯ÁÚ_‹-tëi¾ø©à½Ç^ÓüMc£êºö‘gâ =#]³·Ömt½sYÓ­õîa±ÕudŠòo`¯€?à“¿ò‹/ø&Ÿý˜ìoÿ¬ëðæ¾ÿ Š( ¿0>ÿÁF¾'üqøYðÓã_Âßø%×íÿâ†_>ø7â—ïÂgÿÊÑ?á"ð'Äi¾,ðŽ»ýâ/ø(æ‘â #û_Ãú¾Ÿ¨fkºV™¬X}£ìºžŸe{öÑ~Ÿ×ÀðIßùE—üOþÌö7ÿÖuøs@0|ø½ûZ|+ø­ûnøëÄ?ðJßÛ~óHý¥?iÿ ükð-¾ñGþ ‡q¨é>ÐbÿÙös¼ÓüY ÷ü_N¶±ñž6øãRÚ×G¼×´çð®¥á›éuXu{ÍSCѾŸÿ†Èý¢¿é¿·ÿþoø%—ÿL²¾ÿ¢€?l/‹ßðPê? ÿh/Øóþ [ûOéßµÁ/ücð7ƒ,~?|Qÿ‚rÙ|ø‡ðÏã·…t«?ü6ø…ªxþ /â¯xcÃú_ÆO‡Ÿ³—í6¹àÏ]|BñTÿ³”ôo|-ðwÆÿˆ~:ÒýÿàÅ§Á¯‡¿|ÿ­ÿ‚ëÚG€¼?›yãüQÿ‚PjŸ¾$øªò{cÇ_þ*x‡Kÿ‚‹èvÞ1øÁñ{ÆÚ—ˆ~'|^ñìºUž£ãÿ‰ž,ñ_5ˆÛW×o¥“õ~Šøþ#öŠÿ¤Nþßÿøq¿à–_ý2Ê?á²?h¯úDïíÿÿ‡þ eÿÓ,¯¿è €?á²?h¯úDïíÿÿ‡þ eÿÓ,£þ#öŠÿ¤Nþßÿøq¿à–_ý2ÊûþŠøþ#öŠÿ¤Nþßÿøq¿à–_ý2Ê?á²?h¯úDïíÿÿ‡þ eÿÓ,¯¿è €?á²?h¯úDïíÿÿ‡þ eÿÓ,®ƒá7ퟭø÷ãï‡?g?‰_±ÿí?û1øÛÆŸþ*ükðF©ñ¯Yý‘üGá_xWà¿>øâŸ§ß~Í¿µ_í«éž Ó5Ú áÍÅ­¯Š4=NÕ4ë­Vk V{*k6û~¾øÿ)MýÿìÀ?य़úÑ_ðIÚûþŠ( Š( Š( ¿0>ÿÁF¾'üqøYðÓã_Âßø%×íÿâ†_>ø7â—ïÂgÿÊÑ?á"ð'Äi¾,ðŽ»ýâ/ø(æ‘â #û_Ãú¾Ÿ¨fkºV™¬X}£ìºžŸe{öÑ~Ÿ×ÀðIßùE—üOþÌö7ÿÖuøs@ü6GíÿHý¿ÿðãÁ,¿úe”Ãd~Ñ_ô‰ßÛÿÿ7üËÿ¦Y_Ñ@Ãd~Ñ_ô‰ßÛÿÿ7üËÿ¦YGü6GíÿHý¿ÿðãÁ,¿úe•÷ýðü6GíÿHý¿ÿðãÁ,¿úe•óÀ/‹ßµ§Â¿Šß¶ï޼Cÿ­ý·ï4ÚSöŸðŸÆ¿Ûèßà˜wŽ“á]ö/ý‘?g;Í?ÅßÁEôëkIão€>1Õ-­t{Í{N ê^¾—U‡W¼Õ4=özŠøþ#öŠÿ¤Nþßÿøq¿à–_ý2Ê?á²?h¯úDïíÿÿ‡þ eÿÓ,¯¿è €?á²?h¯úDïíÿÿ‡þ eÿÓ,£þ#öŠÿ¤Nþßÿøq¿à–_ý2ÊûþŠøþ#öŠÿ¤Nþßÿøq¿à–_ý2Êè>~Ùúß~>øsösø•ûþÓÿ³¼iðâ¯Æ¿jŸõŸÙÄ~ñg…~ øÓà~ iú}÷ìÛûUþÐZ¾™â 3Wý ¾ÜZÚø£CÐ4íSNºÕf°Õg¹Ò¦³o·ëàˆßò”ߨßþÌþ Yÿ­ÿ ¿è¢Š(¢Šùö…ý­®¾|SøIðSÂ?³_íûL|MøÁðÿã7Å-Ã?õÙÇDÿ„wÀŸ¼GðCÂ~7×|S¬þÒ´7ìùáøüÏ~дýLðî«â=bÿí½Ôú}•–—%̾ÿ ‘ûEÒ'oÿü8ßðK/þ™e¿å)¿±¿ý˜ü³ÿZ+þ ;_ÐÀðÙ´Wý"wöÿÿÃÿ²ÿé–Qÿ ‘ûEÒ'oÿü8ßðK/þ™e}ÿE|ÿ ‘ûEÒ'oÿü8ßðK/þ™eðÙ´Wý"wöÿÿÃÿ²ÿé–WßôPãíwñ{ö´øýð§Â~ðwü·ößÓu} öŸýˆ¾5Þ\x›âüÏN“¿³_í¡ð öŒñÖŸm6—ÿÖn_Ä·‚~ø‡Kð¬¶pé×Þ*¼Ñ¬uWAÒ./µÍ;éÿølÚ+þ‘;ûÿáÆÿ‚YôË+ïú(àølÚ+þ‘;ûÿáÆÿ‚YôË(ÿ†Èý¢¿é¿·ÿþoø%—ÿL²¾ÿ¢€>ÿ†Èý¢¿é¿·ÿþoø%—ÿL²ølÚ+þ‘;ûÿáÆÿ‚YôË+ïú(àølÚ+þ‘;ûÿáÆÿ‚YôË+éÿÙëã_…iO€_ÿhÏéþ Ò|ñûàÿÃO~ÒüYk§Xø«Mð¯ÅOè¾:ðöŸâkU×´‹?Yéí¾³k¥ëšÎo¨Çs Ž«¨Û$W“{|ÿÿ”YÁ4ÿìÀ?cýg_‡4÷ýQ@Q@˜ ÿà£_þ8ü,øiñ¯áoüëöÿñGÃ/ŒüñKá׉¿á3ÿ‚ehŸð‘xâ‡4ßxG]þÆñüsHñ‘ý¯áý_OÔ?³5Ý+LÖ,>Ñö]OO²½Š{h½þ#öŠÿ¤Nþßÿøq¿à–_ý2Ê?à“¿ò‹/ø&Ÿý˜ìoÿ¬ëðæ¾ÿ €?á²?h¯úDïíÿÿ‡þ eÿÓ,¯˜?k¿‹ßµ§Çï…>ð/ƒ¿à•¿¶þ›«è_´ÿìEñ®òãÄßà˜vztžýšÿm€_´gŽ´ûi´¿ø(¾³rþ Õ¼ð¯Ä:_„íe³‡N¾ñUæc¬jº‘q}®iß³ÔPÀðÙ´Wý"wöÿÿÃÿ²ÿé–Qÿ ‘ûEÒ'oÿü8ßðK/þ™e}ÿE|ÿ ‘ûEÒ'oÿü8ßðK/þ™eðÙ´Wý"wöÿÿÃÿ²ÿé–WßôPáí…ñ{þ â}Gá¿íûÁ+iý;ö±ø%áÿŒ~ðeÇïŠ?ðN[/ƒ_þüvð®•gâ߆ßµOÿÁEüUâO xKøÉðóörý¢æ×<à믈^*Ÿörƒà>ão…¾øßñÇZ_¿üø½ñƒötø5ð÷௃¿à•¿ðQý{Hð‡áÓo/xÛRñÄï‹Þ=—J³Ô|ñ3Åž+ñ¦±júíô²~¯Ñ@Ãd~Ñ_ô‰ßÛÿÿ7üËÿ¦YGü6GíÿHý¿ÿðãÁ,¿úe•÷ýñÂoÛ?[ñïÇß~οcÿÚöcñ·>üUø×àSã^³û#øÂ¾,ð¯Á|ð/Ä ?O¾ý›j¿Ú WÓþвÿÃÿþÏúWÄ|Wàÿ‡ôŒ_´gü.ÿìífëUѾk^Ô¼-â¯×êü!ý…>þÕ_ðI/†z¯ìáÙãí¥û'|'ñ‰ø™/Ãß‹ÿ¾ |KñoÂÚŸÀ?´Ÿí#û9ÛCñƒá?üC¨Á¥üTø3­ðÏâïÃßøSP|'ñ‡|Qá›Ð§þ~Ýÿðª?f…þ"ÿ‚¶|aý?cŸÚkÄÿ?i¯jÚ·ñþïág¿áAþО<ø[aã_zgí/ã ;âоø«áþðÛâ.‘â;™î?·<9ñÂÞ.†ËFÑ|] ééõÆ¿Ûwö/ýšüU§øöŒý®ÿf€>6Õ¼?kâÍ/Áßþ>ü)øWâ­K·ڎ«£Øø›Oð÷޼Y ê÷ž¼Õô-sKµÖmìäÓ®5U±†åîtëÈ¡üÀñ]×íû§üBø‰ûAüDÿ‚Txö…¶ý²¿àœ > üPý”¾~Ö³§4ï„ß~|Lý¬üS¢ü øùâ?Ú'KøðïÇßþ9ü;ý¯ãð÷Ä?ˆ¿ tïŒ|>ñW߈^·ø]ñwÁ:ǃ|{⾃ö;øûL~Âþ;øuá~Í¿>=xÃðHø'G칪|Rø ãÙí|Ÿ¿a=ößÔüàhtÿŽÿ>|M›þmïÅ/‡ø?âeøuÿuÞ±âûIþ'x‡áo‡ôëºèÿÁOÛwö/ý¥>ü)ø©â­7¶:Ž•£ßx›Pð÷|Y¯jö~³ÕõÝKºÖn,ãÓ­õgJ±šå.u8¦> ~Û¿±í)â­CÀ¿³Ÿíwû0|~ñ¶“áû¯jžø)ñ÷áOÅOi¾±Ôt­ûÄÚ‡‡¼ âÍ{W³ðýž¯®èz]Ö³qgo¨ë:UŒ×)s¨ÙÅ7æì+ðŸöªøkâ¯ø#&þÈ?<¤~ÌÿðJ‹_²ÇŸj¿eo¿>2êšìYc Y]Yø#ö‘ñ_¼SáÿÛ~Ä~(Ö4ý{À^ñdzwÆO‚‘kÑhÚ»|cÒþ|ÁÿÈøSñâßÁßø ÿƒÿàŸ_ð ~þÆ_³úütñOíSñÄ?²Æÿ Çü.ŸØOÅ? u¿ ~ΞøñâÿÅ­Cþ«â×Ç›_Ú_âî·ñ‹IýŸ>Ñÿ ŠÇ^ø…¤xŸâÖ¹¤hz ï÷€?kÙcâ¿Å?ü ø[ûK~Ïÿ¾6ü5ÿ„þ/Áß|døuãŠ~ÿ„;Äv~ñwü&¿|;â=Gžÿ„WÅºŽŸáoÿnéØ~#¿³Ñ5?²êwPZ¿Ãÿ´WüÛàˆÿgŒÚÇìÿÿ‚Pk¿´€¼?áiÚ§íûWø/Týž<á[Ï‹| â|f¾ø3ãéümáÏÝ[xÒxCY‚[=:‹~,øsáíBå£×’ÎëäÙ7ösý°¥ÖÿàŽ_þ6~ÂÞøKsÿ¦ðÿÄŸ üCý±5_> xWâ?…|û#üDý‰¼áŸÙ>ÓÁ—Z¿íwáÿÚ–O|?ý£þ"hß~~ͺwÃ;àî“á~Ûâ?´ß OÈý„?lÍ ö`ý³¼àoد㉤|@ÿ‚P|SýŠ~üý¥üeÿþøÁûP| ø…ñŸÄ%ýœ¿d¯ÛûÀŸìümñ+þ ÁðrÛâ?ˆ¥ñWÿmÿøcâg¼?ðàž±ð×â¯â_|-ðPï÷Áoø(¯ìûBþ×´ŸìKðƒâLJüiñÛöPðÿƒõŸ‹º>—ªè’éÖÚˆõ¿ø{Å^ð´£W:¿ŒtO|eø‘ñâgüûößý…5‰þë_¾3x›þ ‡û\xçã/ÆÏ¾ƒUðÅ—ÃKdý›¿lˆ7?þüðL7øãñ¿âWÂï&øáo‰5ýsXðÿÄ_ÙsâgÂOx[Yñ¿Œü=§x»ÅZ¾©«h·6Ÿõ øŽÿöûŚΣáÏ ø›Ä:?„üAãÝ_Aðþ³¬é~ðÏ…lüUãMGKÓ®o¬|'á›Ïx›Á~ µñˆî`‹GÑ®|cã øV FòÚ_ø›BÒóTµþ`¿dßø%çí…áÏŒ¿ðKßë~ ðÿÁÙòßö ý“¾ÁT¿gŸXü ‡Æž4ý§ÿà“Ð#~Â_­¾/|ñÄÏ|Eñ±ñ“_ð‡Ä? ¯‡<[á/Áð ökѼñÿÄ”ž ðïìójôÿüoþ }ûG~Øz?ÆÏ ÿÁEuŸÙàí5á/Úÿãìuð×ö_øZn¼%ã¿xïöQð'ƒ#ý ?á‚þ xÞ ÿƒ¿kø(/ü3¯ü/ŸÙ+Ãÿí?coÚƒö6øÉðþü$ßð½u¿‚W<©ü[óáTÿÂÖ†ûÄÞ#ðÿö/ü$>ð–«ÿ MûRø-ûaxç¿´f±âOÙSÄñ'í3ÿžÿ‚nþÓ~ð†›ñwàgЧøiû0~ÏwüûÄ~1ø¥ño[—ÇþÒ-¼Aá½#öAø‡àïü3øW/ůiÿ^?|)¼ø×ðoûãÿˆ@=öŒÿ‚¶üÕ'ýºgØ×ö£ýˆ<'ûaþÅ>ø+¬øãÅ¿·ÄyüûøWQñïÆ[_‡Ÿü'âßøKÅOµŸ|+¶KøúçÂV­á_‡Ÿþ0üøUâŸ?5OøÃí ûkül“þ kà?ø&×Àߌ¿²ì÷âÝö@_Ú‹M×?i_…ÿ~;øïã¾±¬üOñß‚cøkðGá·€¾3~Ì^ÒÿáUøKáþ"üIÕ5ÿ'ñ߈ôè7øG'„¾|[ñÎðÆßØóöÓµý ?àåýg²‡Ä‰^ÿ‚–~ȳÏÂßÙ3Æ>ø—û1iš?Œ|wàïÙXý™¼W¡x‹Nø‰ñóÀ~-ðGö‹~/\ø²}OÄÞ°Ñïü ð·â-Ψj~-ºøaàÿ‰_oÿÁG¾øÓöžø…«þÊŸ´Oü«ÄðP/ø'~¿ðá—Äí Ç_ >/üøuñ—áçíq቟¬uÿÇgñö§ýšüS¦ø~ãáLÞ »Ó~#ü/ñg…5½" cÇ¿ µ¦ø™áо%Òþ€wþÿ‚›øWொ¿aÿÙ/þ ©üø!ÿý­(ÿ‚wÿÁ^¾%ø;þMsñ×^øâoÚ7àìÿÿ<ø%ûW~Ôÿ >(ü'Õþ)þÊúÇí¯ð&ßà7ìÉñ_RÕ2ü@ýž,|ñWÃß…_³wŽ|ð»ÂŸ²ÿì÷ƈº¿Ž~üxÕ司ácðãÀ?Ù7áŸì­<){á /Ä?|à›+ðë{à_ícû,~Ôð”ÿÃ4~Òß³ÿíÿ?ö'ü&¿ð¢þ2|:ø·ÿü$ßÚÿðÿÂSÿˆüAÿÿü$ðëߨŸÚßdþÕþÄÕþÁöìÛÏ'ßëñÁ²÷ˆÿiÚÿž;ñ7ìñþ ÿû9|ý€8xÆŸ´OÃ?ˆ?|Oáý[Ä^<ýš~ |-øGñ_àN‘¨øƒàŽ‘uá?ø^Þ>ñ/í-¥øÓáÄ+˜¾ë?³N¯mâ­7⎅ú¿_ˆ?~~ÓðLŸÚ;ö¼ƒàßìÛñöÙý‘n_Úâ¿íÎu?…8ýžüûG~Îßµ?Å[¯éÿ>xçBý¡þ6~ÏŸ ¾%þÏþ9²Òl¼]ðKÆÞñ—Ä_†×Zо|PðŽ!Ô¼/ñJäéÿÙ×öŽøûðÏáïíuâOø*G‹ÿfƒGìíûOÜü8ð_íá#ÆŸ³ìññà‰~üñ_¯‰×:íñ+Æ6Úoˆ5Ï|^Õþø±tŸˆzß‚ô‹~Ö~øsÄ,Õü1sâ-è göÝý‹ü9ðk´gˆk¿ÙƒBýŸ<{â Ÿ øã¶³ñ÷áN—ðkÆž*³ŸÄÖ·žðŸÄûïAàŸø‚ÖçÁ~1·¹Ñ´}róQ‚ øšm–MT[_ˆ>2jŸ·gÅ¿~Éß´ßü0—ü#ÿ?eÿÛÿÄ?ᑿ᧾꿼qû,xƒàOí-û!Âyÿ —¦þ̾ý -4_Ú#þÏü(Oø_Þ øuqàïÿÂÿ 5ÄbhŸ/ø7àíUà/hŸµ}ìâ \ø·þ =ñ‡öàÖ?dƒeŒ¾ø5â¯ø%ÏÅØF/‰ž>ÕüCñsáìÓsñƒãÆ•ÒÿhOŠð/Ljsép|e½¹¼ñ×üm¥øÆUý?ðŸüÛþ ·ãßxgÀ¾ÿ‚ƒ~Ä4ñ·*x'àŸ‡×ÅŸ¼cðããïŸxWá…_Nñ°ž&øâ x³TÒ<áöÒ<'â­QuŸ^i:siÞñð¹6Ú6£-·?ÿ ýáVÂôÿ†áý?áIÂÀÿ…Oÿ ‹þ[à¿ü*ÏøZðŽÂaÿ Óþü&¿ð‰ÂÀÿ„Kþ*ŸøCµÿá#ÿ„sþ'ٿٟéUùûKiß··ŠhOÚâwÁ¯ø'ŸÄ cÃÿ¿gÿø%g„S_ø‘ñö¼ñ€î¿gÿÚcö‚øåñ»Ä_| âߎ?~x›ö¿ýž,¿iÿ xƒözŸãrøKöq´øíðÇŸßâ/Ä?øàׂ?iîá'Á¯Ú©i¸þ/Ü~Ãß´ÿ€<ªÁg´ßÛ&òÏâßÇoÙGâ4¿ƒ_à‘=ý†|uã/t¿ÛCâä–Þø9ûA´:î¥ð³Cñ-Äþøâ¯è³Ÿ|Omá+ï„þýŸÿ†±ý–?áDÿÃQÿÃK~ÏÿðÌ¿ôqð¹>‰ÿ‘Çþßü•ßøHÿá_ÿÉ@ÿŠþFùâ™ÿ×ú |ÿûþ×_ð×w_¶N§£|UýŸþ8ü2ø+û_ê~ |[ý›Ûížñ‡ÂËÿÙÇömøë¥Zë µø§ñoÃþ/øà|nñ7ÂßøÃÂzï‡|9â-cÀÒjß|{.¡á«ÎþÇßôo|IøïàOØ'Ãþ8ð߀à»ÚíçáïÙ›Rñ_ìÕàï|jøf?àžž ý|cûNü$Y|]¯|³øÁgûKkÞ0ý³|¢üpñßÁ?ˆ^0Ô|'‰|[®ü.øÉâ]èÿì/áÿ‹¿ð–þÝ¿þ-ü øû;ÿÃD~×þø¥ð÷áïÅ/üñ7ŽÿáðÏì;û|›]×føñoãoÃý3ûOâÁ/G¦i‘øöïXþÇ´Óõ-KOÓ´­íèïúøâ7ü¥7ö7ÿ³ÿ‚–ëEÁ'kïúøâ7ü¥7ö7ÿ³ÿ‚–ëEÁ'hïú(¢€ (¢€>øÿ)MýÿìÀ?य़úÑ_ðIÚûþ¾øÿ)MýÿìÀ?य़úÑ_ðIÚûþ€ üAø¥ûY~ßZ¿ü#]ÿ‚püø‹û |?øeqûiŸ·?†|yñKöTøÑñƒÇzG•ñËOýžõŸ…ÚêøOöÛøáÿ}¿Äj¾?ÓþÈÑïtÿÝxG[½Ó.|w«þßWóÃûTþÁ:߯?ø.WïÚsãWü÷Ãÿ¶§ì_ªÁ8<#û+\k^,Ó?dˆ~øGñö÷ö¾ñľ"ø›ágíñGÂ~)oø áN©ªÝë>.øcàŸxÑ´ï\øwÁ~ñ>£{®i6À£ÿ¿iÿøGÿáux7öµý²?`|@øoÿ âÆ›©|ÖáMÂ?û,|$þÆøiñcâ_Ư„ßþ>|pÖ¾ÿžý¦¼)ñçá7ÄAñ/[øuáÿøB´m7ĺ—„~ [øËÁ¾ôÁB`_xá×Å/~Ü?²…þ|`ÿ„»þ'Ä_~ÒßôO|Qÿ…¬Aáßÿºñv¥ã[oøÛþŸ\Ûh^.ÿ„gPÔÿáÖ. Ó5Ÿ±^Ë ñÄ€~4ý‡i_ØgÄ?°¯ìâ‹_²çïƒÿðQÿ†?>|ø›ð ឣð3Qý­>7þË_´Æ‹â‡¾ý¢~.|'ð·‰|?â_Šß üq£Ãðãž1ð—…~øW_¸—ÃK£è~ðoÃsÿ²ìûûXü.øõû&x÷ãÀì_3þµ§üg½øuñWá·¼ ð[Xý´à¤ÿ k߀šœú÷ˆõ¯†>!|?ø…ðÿáž·¦øw]ðoÂKé:ƹáþ,|-øQöÏÅáÓÿþÖ?²ÇÂ…ž øéñKö–ýŸþüø•ÿçü+¯Œ^?øÉðëÁß <}ÿ ‡/|aáøB¾!x‹Äzw„¼Uÿ W„´íCÅ>þÂÕïÿ·<9a{­éŸjÓ-gºOŸü9ÿAý‡|mûdøöðÇï‡ÿ>=|HýŸïi[x+ÅÞñƒ®| -‚¼Mà­ÏÅ–ZûYx“âņ^/½øßàx*/k?|âO‹þ#Ã^Ôü¬øÛóàwì¯û@~ÍWß°'ǯÿÁ7á.ðÿÀoˆðY¯Çû,x?ÆŸ²…~;~Èþý¼¿l›?³Ä_ƒº^±ñOLý–%ÿ„_àÇÃø>|BðïÚcšǂüñöëMðŒþ4ðþ‹ã ? |ýš¿iÿÁb?f/Úêëö/ÿ…û9jÿðD‡¿±Ç‹<-ðëâ'ì÷«øöDøíañÊïã¥ÏÀyôMÆþ ñ‰~ü7ðÿ‡ôÿ…^ñŸÁO…ž"ðuö±«øE´­+Hðd^&ÕüöÿìëûnøWß±/ÁŸÚ3ööý®ÿàœ¯ãßx¿ÂzÇoÙ×ãî¥þľ4ñUŸŽ>#ZøcÃ?~'üfñd>#ñ¯‚|=¿‹ôiõËÍFxOâ4:}²é ­¯Ðÿmߨ¿àç…~øëâïíwû0|+ðOÆÏ·‹> øÇâ?Çß…>ð¯ÅÏ ¦áýaüMðÇÄ>'ñf—¤x÷Ãë¤x³Âº£k>¼Õ´åÓ¼Káûãr-µ:[ŸÊ„_ ÿm?€ž'ý•iËoØ£âÄO³ü@ÿ‚Õxwã?ìë¡|fý˜´OÚ;á??à¡ðPM+öËø ñO#øËcû2üFþÊÑ~ øOÁ¼#£~ÓÚ^±á‹¯‹zf¹á™üqÿ‰ôHºØ7öQý¡ÿgoÁ>5oþË~øQák?ƒÿðWË_‰ÿ~ xóá&½ðÏö)Ôn_Ûkö~ý´þüÔ$mGáuÏŒ|?àOøÄ?îµ_Ùóá·<+¤|BÐt¡§iö o­|ylû}á?xWǾðÏŽ¼ âoøÓÁ>4ðþâÏxÇÂzÎâ? ø³Â¾#Ó­µx›Ã>!Ñîo4{Ãúî‘ygªhÚΗyu§jšuÕµõÌöÓÅ+tðüà׈þ~ÈšOïüÿ†lÿŒ€ýµüá/€»þŧøYñ‹öØý¡~0|ðWÙ>ø·Ç t섾;ðTðŽxÅÚï‡<1ÿ"í•ïüJÞ¾ÿ ¾ÿ‚NÿÊ,¿àšö`±¿þ³¯Ãšûþ¾ÿ‚NÿÊ,¿àšö`±¿þ³¯ÃšûþŠ( Š( €?à“¿ò‹/ø&Ÿý˜ìoÿ¬ëðæ¾ÿ¯€?à“¿ò‹/ø&Ÿý˜ìoÿ¬ëðæ¾ÿ Š( ?à ¾)þο°ïígûCüºøoñ7öýŸþ(ütð̼â?øYÿ…?á-Oâ.³ámwþø…ð»Ä ÿ ‡ü7ªø[LÖôÿYÂ'¬kŠî´Ùh—>×yÿø'ÅŸ¿´ì_û<þÑŸ´gˆþëž6ý£>ü%øù¥é>xÓá?…|á_‹ß |㫇º…޾7|tÕüeâ êúÖ¹ouñß\ðvâ-:m*~x~çM¼¼Õúø(OÂßüqýn‚Ÿ t/øJ>&ü`ý?io…¿¼3ý§£èŸð‘xïâÁøOÂ:öψµ #ÃúGö¿ˆ5}?OþÓ×u]3G°ûGÚµ=BÊÊ)îbøöGø‹û{|ý‡c¿Ù3Fÿ‚lþÐøÛð³öý•¿f¯|aø¥ñ#öñìãðçXð—„¾|'øñ»]Ñ>þÞ7¿~&ü?øe¦Yx›â~™ðÛÂ~ð—Žþ0ÚøsOøwm¯|,ÔüZÞ2ð ×ÿkìO~ן¿kÏÚ[öÒ>|ý¯þ+ü'ðÿÄ_‚¿>ÁàOƒÿ 4cÁ>øqðÓö¼ñwÄïÅáÿ‡ÿµþ™âE¡|Wðm–¡¤xrËXñ‚tÍMŠ÷WX.<þÿþ 5ÿÊoŽß³Gìåà¯Û'öøÁñ7ö¨ø¯|:ð·ÁŒ >%øsÃzƉàí[ÄÖsüLñg†ücsáÿÿÂmâ o|*ø_áÛËûüQø§ãß øoÀñ •—uŸxÅÿÙÇâïį‚ðUý;Ųïí®ÿÃDþßÿ~!üðoÂÚ+àŸÀïÚ:ÿÀŸ~Á;~i_µ§Ào‹¿.þøâÂoˆ³‡ŒÿiŸø½ãë1ºøcàÏüTðÏ‚á7Ô4K_€>~··…ÿnÏø%§íAâoÙŸþoü ý¯ÿà©ß>5x¯Gð·ìðö˜Ó< ûküøAð¯áÇÅïÛcÁŸ¾0蟳—ÆÚËÆvþ9ˆŸÿeËïk?g¿†Ÿ|iâ‡úGÇ­YøCõ{EPEPÀ¿å)¿±¿ý˜ü³ÿZ+þ ;E¿å)¿±¿ý˜ü³ÿZ+þ ;E}ÿEPEPEP_|Fÿ”¦þÆÿö`ðRÏýh¯ø$í}ÿ_|Fÿ”¦þÆÿö`ðRÏýh¯ø$í}ÿEP?âÏiÞ ð¯‰¼c¬[x‚óHðŸ‡õŸj–~🊼{â«­;BÓ®uKëo øÀº7ˆümãOOmk,Z7„üáíwÅ^#ÔZÛGðöªj÷–v3þ`xgþ }ÿÇñ£sg}ûCøƒá¦‘¦ü`ñgìõâˆ?¿g?Ú“ökø5࿾ð®³ã_üø…ñËöƒø+ðÏàßïŒ?†ô Rúo†ž:ñׇ¼i<ðÛévz-Ư¨iÖ7_«õþ|> |eø±ÿêÿƒˆu;ÏÚOàÿÂߨóá§ü{öÓø±ûAü×ü¾-þÑšw¿Š¼i¨üðGíâ¿üGðOÁÏ|J¹³ð.™ð7D±ý‘üuâ¯þÐþð'„uOjø‘âBýþx³Ã:w<+âok> ³Ò:ð.³áÏx/Ä[]K.âÏx‡BñW‡5¶Ö<=¬éz½ô~Ê>,ý†ÿfx?i¯Ø»öeñ7Æ RçöðÿÂÝãgÁKYý±¿i¯|ð¯Ä?ƒVþ'ø9à„ñRçâÏŠ|WáýCáOÃè[À_g OÅZv‰¨ÛÉ¡èþÓ9ø˜ÿðP¿Û á½ñ'Ä+øS¡~̰¯Æ¿‡>Öt?‡¶zw¿|Yñ÷íÉðËâVŸá={Âþ ðﵿøºÛöpðŠ®m~&ø«â££xªóÄÃÂú®ƒámBÃÂúH¿øOã¿Ã?üðÏí9à]SÄ4ø7ãOƒú7Çk^ðÄ/ø«ÅŸ üGà»oˆ>Õ<3ð³GðµçÅm{Äï…¯,îôoi~ ºø…ªj7VÞ±ð´þ$ž-%º„ÿ¼ ñÇágÃO ußøJ>|`øàߊ_¼Mý™¬hŸð‘xâ‡4ßxG]þÆñŸ¤xƒHþ×ðþ¯§êٚkhû.§§Ù^Å=´_ÏüÛÅ_þ7xWþ íû"ø3öàñì“àŸ„ÿðBø'íià/‚žýš¼Cñ÷ã·Š¾4éÞ'øeâ/ê÷í9ðÏãþ‘eðö~Ò>xKEºÒ>|Ò5¯~ÑšUçŽ>%CmmàŸ ê?œ??kø)ÿÅßÙïáoì{û|lýŸÿàŸ?ðÅ_ðHø&ÇÏ |mý¥þ)xÁžøÙð³âìÏàO|uý¢µo|[ý†?i_øX_³ÿÀÏì Ÿ×þ&øeñà?‡> üS]{Ä¿¾#ü_ÿ„»Ãÿ >o”WåìSãÏÿ´ïÇŸÚKãGˆjïKð§áOÆ„> ð/ìéð7Qýš¾'þÈþ$ð¯Äø&ßìeûA^kšÇ‹ïÙoÃß´ÅÍñGöŒñ¼ ñ'Gø£ðãNñv‰aà‰åð6“áiuO_þ¯ÐEPÀðIßùE—üOþÌö7ÿÖuøs_×ÀðIßùE—üOþÌö7ÿÖuøs_ÐEP_Á'å_ðM?û0ØßÿY×áÍ}ÿ_Á'å_ðM?û0ØßÿY×áÍ}ÿEPĵOüSöUýŠüUðëÁß´f¿ñƒÂz¿ÅÏxGÁŸ o<'û*~Õßü+㯈^=Ô|E¥ø/á_†|uðWàŸÄ?êŸ9ý¥ÿiÿµø%ð×þŸøM|kÿÏŒ|cý‹ÿ Œ|=àÅ9àx«Åºö‹|U é?ñ)ÐoþÇöÿ·ßý—Lµ½½·ß¹øïðÎÏãîû1Þjž µøÉâ?ƒþ&øïáíçÀ ð®»ðÏÁ~4ðŸÃïêš7Å9|,¿ uOxOÅ>=ð-§‰¼cãiþ!h:w<'â-SÂÖ¾ñ«^~0þÔ_´/íðÏþ¡áŸ…_·'ÄxƒöDý€>þØ_ dÿ„_ö@ñˆÿe_ž%ÿ†óø·¨|þÍÓ?gx¬¯þø‹á—Àï€ßð~ÐZ7Ä‹©ð³Æð–h¿ì|Aã= ǰ{üã_Š¿f¿¶_´gtÿêÞ6øÿaÿ‚Ôükðv—âË]Fûº—оøÓþ “ã¯iþ&±Ñõ]W¼ðý毠ÙÛë6º^¹£j7t—0ØêºuËÅyéõÏdžvtoÙŽóTñ¯ÆOüñ7Çh·>ø……u߆~ ñ§„þxÇTѾ)ËáeøSªxƒÂ~)ñïm?ø…ðÃQñ'…o'·—VðÍÏŒ~x§Á~6µðÿˆí }Åš6â;=;ÅÞ¿Ö|!â{m[ÂÚöµ£ßÿ4_¶GÇÚcþ Ûkð×öàðŸí%ñþ Ññ7ÃÿðHÛ_â?5?xö{ð×Á;ïíoÚ;þ §ø£ãïí ö<ø'àŸ\þÏöÞñµÏÇÿhÞ&ø¡ñ3ÉøEðÞ ãWƒ,§ñ?Å+¾ƒ@øéÿ8øpŸ|+ñþ ?û0xóHý¥üAÿ·ñì‹wð;ãçì·ñïö¸ø9ð öƒý¼ÿgÙãÆ}öðLŸÙ—àßÆßƒÿ|7ñ·Ä£ÀŸõß„Z¿‚þë~ðG…t x÷Wñ³ãk0éwáoÃO|ð&…ðëÂz—Ä _ÃþþÓþÏÔ>)|Xø§ñÇÇwÚúÆ¡®ÝnüRø×ã/ˆ|Qå^êw0iŸð“x»WþÄÑâÓü;£}ƒÃúF•¦YzyÿÂßøáÿ4/ø³âÇÄŽ> Ò?´ÿ´>)|RÓ~i;ñGÛõCSµþÝÓþ |4ø?ðÊßûÊöÛúgü#?¼;æèúFŸ>³ý¯â u]wSô +àˆßò”ߨßþÌþ Yÿ­ÿ¯¿ëàˆßò”ߨßþÌþ Yÿ­ÿ ¿è¢Š(¢Š(¢Š+àø$ïü¢Ëþ §ÿfûÿë:ü9¯¿ëàø$ïü¢Ëþ §ÿfûÿë:ü9 ¿è¢Š+Çü ñûà×ÄωŸ¾ øâ‡üCñ[ökñ‚<3ñËáõ´ÓÛø«áÖ£ñ/áîñOáõγ¤ß[ÚÜÏáÿx'Ävz†|Y¥¦¡á]cQÓ|Yá‹foøƺ.ûU~Òÿ ?cÙÇãGíGñ¯VþÈøeð;áþ¿ãÿyþ°Ö5ßì‹Sýà¯ÿÂY¯x_ÃúŸÄˆ —Jð7ïj!Ò?á+ñ߈¼;á›[دu{lÿ0?²Î¿û@Á7ÿo¯Ù‡ãí û*ü@ý—~ÁS<ÙwöÞ½ñAý5/kðUŒÿ?hÿÚ÷áį…ú÷ìÏñ£ö”øõñ+áþ»ã¯ŒßÿdŸ†z_Å¿|+áÀ›_ƒïÆo‹Ÿ¯|§Åáëö¼ãïíGð'ö_ÿ…+ÿ ÓÇ?ðƒÿÃD~Ð?eσ¿ñLøÇÄßð˜|vø·ý³ÿ ûÀßñGø{ÄðÿÂAÿþ¯ÿ7б<¥}“þ'~!Ó~Ñkç~pEñ7ãÆ_ÛköÉÒÇü{Ãÿ³'ÿؿöŸýƒ> h_³&ƒðçöjÕ4ïøWâïÿgoŒºþ¡ûAx›âþ‘⿌“xƒöÍñ'Ç/~Èÿ³µ×Â/|Ó¼%⯆ÖzŸ…t¯ŒŸ‰tÊø'¯Œÿi‡¿¿à¦_¢ý±ÿhø·Ã¿ðs÷ÂÿÙ£ÄwŸ´¯ÙïÅŸð³>]~Ôÿ±¯ìuã]'Æ¿ÓSÐÿá>ø%ñ_EЮ¬>ê |9ðÏþŸÁÝ3öÐ~øJÃÇþø„ý~Ñ_ÏíÃû`üoð—‡ÿà­~þÞÞýœ¼Sÿ«ñÂûŸ ~ο…?f¯üøËá]Oö_ý¿i¯hß´vñÂ'ö‘´ñíyñGâGÅŸÙ3ᯉ¾ þдë‰ü;á>xO_ø­àßMãþƒö«ø¡ûièþÿ‚ÃþÓŸmˆ ÿáÜŸ ñÂOÙ×Sø3û1x÷öqñß> Á>ÿcßÛ/Ç¿¾) KàÖ•ûM_ÂèÖ¾#|Tð^¡âï~ÓÞ Ö< kâ \ð¤ ÿŒz&°ûýEP_|Fÿ”¦þÆÿö`ðRÏýh¯ø$í}ÿ_|Fÿ”¦þÆÿö`ðRÏýh¯ø$í}ÿEPEPÀ¿å)¿±¿ý˜ü³ÿZ+þ ;_×À¿å)¿±¿ý˜ü³ÿZ+þ ;_ÐEPüwøýðköbøgª|føÿñ Ãÿ ~è> ð†|CñÅ“Ogá_ ê?þ!x[ágƒ®|M«Eo=·‡ýgÿübøÛ¬|¸ý¼Y¬~Ï_µ?í_ðOãWìßû]x·Á´ïí[û?Ùkð‚|2ñoí3ûJëÓ¤ð¿Ä] Çz7ì[ñÁß´/ëZäz¡àoø)×ÇoÁüUñkÅŸþþÌŸ·?ìÿû@|(ÿ‚lþÚ¾)hþñß?fÚa¿j¿‚_²oÇŸÚ]·´O…?|Sÿ·~+[~Õ:f™ásQýžü-¬kuŸx·ÂþñE¥Øô}^ð ö£øûPÂêÿ…ãŸøN?áÿhˆÿ²çÆ/ø¦|cáŸøCþ;|$þÆÿ…ƒàoø¬<=áÿøH?áÿ„ƒHÿŠ›ÂßÛ~Õ~×ÿOê_gºòœø+Ÿ„hhÿðO†qÿÁPh‹ßð‘Á¿a/‚Þ+“JÐ?d øïá®ã|9ø·àÏüiÓ~üоøóâˆÓi¿ƒšW~è^ºÑþ6xóâßÁÏŒ_ðü!ñí‡ßÿ?k‹¿¾ÁLuŸþÑÙ^ø7ÿ?ý’ÿfÿükø—¤üðßü3?ìãû_xsþ —uñXÓuX<á_†SÿÃ:Y~Ø_üYðŸÆÿü3ñ&×Kÿ„oÁ·?døÉáýÄZˆÀ?o¨¯ç‡â'ÇÏÚ«á?Ž?moÙ/Gý¿|Añ‹Wøuñƒþ™ká/ŽÒ|2ý”`ý¨?g=;þ )ûmiß³wÇ‚ž=²ðÂ8?fïø‚×áw‚âøËð“Uñì¹áÿxÃÿ´>›/ˆtÿég9|ð/Ä/ø‡â·ì×âxgã—Ãëi§·ñWíGâ_ÃÝâŸÃëgI¾·µ¹ŸÃþ4ðOˆìõ ø³KMCºƣ¦ø³Ã:ÌÞ)ð7t]Ø+ùý–uÿÚþ ¿û}~Ì?ÿh_ÙWâì»ðËþ ™ä~Ë¿¶õïŠì©x;Xÿ‚«ügøÑûGþ׿~%|/׿f´§Ç¯‰_õß|føßû$ü3Òþ-ø{á\ þÚü×~3|\ø{à­>/ þ¯Åñ7ãÆ_ÛköÉÒÇü{Ãÿ³'ÿؿöŸýƒ> h_³&ƒðçöjÕ4ïøWâïÿgoŒºþ¡ûAx›âþ‘⿌“xƒöÍñ'Ç/~Èÿ³µ×Â/|Ó¼%⯆ÖzŸ…t¯ŒŸ‰t€Ñÿ¿µÀŸÙþ¯ü/OÿÂÿ û@|8ý—>ÿÅ3ãÂañÛâßöÏü+ïÅáïÂ?ÿ ü#ú¿üTÞ)þÄðv•öOøø‡MûE¯ïõüÁ=|gûL|=øuÿ2ø¥íû@xÇžÿƒŸ¾þÍ#¼ø…¥~Ï~,ÿ…™ð²ëö§ýc¯é>0¸½øšž‡ÿ ÷Á/Šú.…uað§Pøkáφð¤þ音þƒðwÂV?ðÇÄ/·ÿnÛㄼ?ÿkøÛðçööðÿìåâŸø%_ˆ>ÜøKöpÕü)û5x·àׯ_ ê²ÿìíûMx3Fý£´ˆ~?´§ˆ?kÏŠ?>,þÉŸ |Mðoöƒø%§\OáßAðÓÂzÿÅoøâo€CÔWàíWñCöÓÑü1ÿ‡ý§>þÚÿ>ÿù> Aâ/„Ÿ³®§ðgöbñïìãã¿|ÿ‚}þÇ¿¶_~|R—Á­+öš¾ÿ…Ñ­|Fø©à½CÅÞ ý§¼¬x׏áHþôMc÷ú€ (¢€>øÿ)MýÿìÀ?य़úÑ_ðIÚ(øÿ)MýÿìÀ?य़úÑ_ðIÚ(ïú(¢€ (¢€ (¢€ øâ7ü¥7ö7ÿ³ÿ‚–ëEÁ'kïúøâ7ü¥7ö7ÿ³ÿ‚–ëEÁ'hïú(¢€ øƒÂðLŸø&߀¼UáŸxþ óûx/ÆÞ ñâÏxDz‡Àox«Â~*ðæ£m¬x{ÄÞñà=_Añ…«ÙÙêš6³¥ÞZê:^£km}csÌJ¿oÑ@0x³ö"ý‹ü{ñ3Äß¼uû"~Ì4øÉãOë>ñů|øSâ?‰ž,ð¯ˆþÜü#ñ†|MãÍcÂwž)×¼?®ü)¼¼øc¬èÚ¦«u§jŸn®|}m?†ç—Mnþëözø}¨ükÖ/¾|¼ÕÿiOé^ý¢õK¯†ž ¸Ô~?xWBð®¡à]Ã?ï¦Ñ^ç⧇ôoêÚ§ƒ´­ÇRëÚvá]KPðõ´:EåÅœžÁE|Áñö"ý‹þ1øWá~.þÈŸ³ÅOüðûxOàσ¾#üøSã ü#ð«éÞÑßÃ? |=â êšG€¼>ÚG„ü+¥¶á[='Nm;Ã^±6ÆÛFÓ¢¶>#þÄ_±Æ? ü1ð/ÅßÙö`ø©àŸ‚~o üðwÄ€_ |qá_„~};Ãú;øgᇼOá=SHð‡ÛHðŸ…t¶Ñ¼+g¤éͧxkÃö&ØÛhÚtVßOÑ@?£~Ï_¼9ñ—ÅŸ´g‡¾|пh?ø~ÛÂ~:øí£|4ð^—ñ—Æž³ƒÃ6¶~ñgÄûxÃö¶Þ ðu½¶¬k—štxOÃ0Ål±è:ZÚûPEPÀðIßùE—üOþÌö7ÿÖuøs_×ÀðIßùE—üOþÌö7ÿÖuøs_ÐEP_Á'å_ðM?û0ØßÿY×áÍ}ÿ_Á'å_ðM?û0ØßÿY×áÍ}ÿEP€|tý“¿eÚƒþoøiÙ§öý¢?áþÛÿ„+þ§Á¿‡_ÿáÿ„›û#þOøE¿á?ðçˆ?áÿ„ƒþýûoû'ìŸÚ¿ØšGÛþÑý›gäóú7ìEûøsà׋?g?~ÈŸ³…û>x÷ÄÞ,ñ×ÀàÂ/à׃Á>#ñ­Ï‚üqm¬ëæ£þðÌÑ\¬š–Ö¿OÑ@0[~Ä_±}ŸÃ=gà­Ÿì‰û0Züñ‡ü3á?ü%¶øð¦†zï…|ñ ÅŸ<ágÀqxM|-ªx¼{㯉Þѯ´©ôíâ#þÄ_±Æ? ü1ð/ÅßÙö`ø©àŸ‚~o üðwÄ€_ |qá_„~};Ãú;øgᇼOá=SHð‡ÛHðŸ…t¶Ñ¼+g¤éͧxkÃö&ØÛhÚtVßOÑ@ãÿÙ;öXø¯ñOÁ_>)~Í?³ÿį¿ áÿ…uñ‹ÇÿþxÇ⟀áñïŒ<#ÿWÄ/xsQño…áñn£¨x§ÃŸØZ½‡öˆïïu½3ìºÔ÷OÏÛ~Ä_±}ŸŠµŸYþÈŸ³¯¼GñƒÃ?´/ˆ|cmð áL*×~>ø/Qñf±àïŽ:ψbðšêú§Æ êþ=ñ֩៉wדøÓAÔ|iâËí/Zµ¹ñ±-çÓôP€xÿöNý–>+üSðWÇOŠ_³Oìÿñ+ãoÃ_øG?á]|bñÿÁ¿‡^1ø§àøC¼G{ãÿÂñ Ä^Ô|[á_øE|[¨ê)ðçö¯aý‡â;ûÝoLû.§u=Óž0ý“¿eˆ_ð¸¿á?ýšgÿÃD¾ÿ†€ÿ„ÃàßïÂóÿ…Iö_øU?𸿶¼9{ÿ 7þ—ج¿á_Âký·ÿgÙ-áþÍû<[=þŠ(¢Š+àˆßò”ߨßþÌþ Yÿ­ÿ¯¿ëàˆßò”ߨßþÌþ Yÿ­ÿ ¿è¢Š(¢Šøâ7ü¥7ö7ÿ³ÿ‚–ëEÁ'kïúøâ7ü¥7ö7ÿ³ÿ‚–ëEÁ'kïú(¢ŠñýözøáÏŒ¾,ý£<=ð;àþ…ûAø÷ÃöÞñ×Çmá§‚ô¿Œ¾4𭜵³ðÏ‹>'Øè°xÛÄ~µ¶ð_ƒ­í´mc\¼Ó ƒÂ~†+eAÒÖ×€øqû~Åÿ<+ñ;À¿¿dOÙƒá_‚~6x}|'ñ›Áß>|)ð?…~.xU4ïhé៉ÞðÇ„ô½#Ǿ]#Åž*Ò×FñUž­§.â_X‹am¬ê1\ý?E|ÿöýáV‹ÿ†ý?áIÂÀÿ…±ÿ wþ§à¿ü*ÏøZðŽÂÿ /þïü!_ð‰ÂÀÿ„Kþ)oøL¿²?á#ÿ„sþ$ŸÚ_ÙŸèµëúoìõð Fð¯Å¿èÿ¾é> øýâø³ã·ƒ´ß†ž ±ð¯Æ¯|TÓ£Ñþ'ø›â߇­tX´ˆþ ø¤E—ãÝgÆ6zΣã :8ì|Cs¨Û"Ä=‚ŠùƒFýˆ¿bÿ|ñgìçáïÙö`пgÏø‚ÛÅž:ø£|øS¥üñ§Š¬çðÍÕŸ‰¼YðÂÇÂpx'Ä~ µ¹ð_ƒ®-µcC¼Ô`ŸÂ~š+•“AÒÚ××ü;ðŸág„ÿ¢Š(¢Š(àø$ïü¢Ëþ §ÿfûÿë:ü9¯¿ëàø$ïü¢Ëþ §ÿfûÿë:ü9¯¿è¯?ø¥ðŸágÇk¿ ~5ü4øñƒá—Š?³?á&øuñKÁ¾øàObkˆ´oíßø³MÕü?«ÿdxƒHÒµÝ3ûCO¸û±¦iú¯•{em<^E|Áñö"ý‹þ1øWá~.þÈŸ³ÅOüðûxOàσ¾#üøSã ü#ð«éÞÑßÃ? |=â êšG€¼>ÚG„ü+¥¶á[='Nm;Ã^±6ÆÛFÓ¢¶è<û'~Ëþ)ø+ã§Å/Ù§öø•ñ·á¯ü#Ÿð®¾1xÿàßïüSðü!Þ#½ñ‡„á ø…â/j>-ð¯ü"¾-Ôuøsû W°þÃñýî·¦}—SºžéýþŠù‚Ûö"ý‹ìüU¬øêÏöDý˜-|mâ?Œý¡|Cão€_ `ñV»ñ÷Áz‹5|qÖ|C„×WÕ>0xOWñO üK¾¼ŸÆš£ãO_izխψõ‰o:þÉß²ÇÅŠ~ øéñKöiýŸþ%|møkÿçü+¯Œ^?ø7ðëÆ?üÿwˆï|aáøB¾!x‹Ãš‹|+ÿ¯‹uCÅ>þÂÕì?°üG{­éŸeÔ¢€<Ʋwì±ñ þü'ÿ³OìÿãøhøWßðÐð˜|øuâoø^ð©>Ëÿ §þöׇ/áfÿ²û—ü+ïøM¶ÿá û%¯ü#ŸÙ¿g‹g¿ÑEQE|ñþR›ûÿÙ€ÁK?õ¢¿à“´QñþR›ûÿÙ€ÁK?õ¢¿à“´PßôQEQEQEü1ÿÁæ_´/ÇßÙ¯Å_ðL¯~Î~0|ñ¶­áÿÛÂz§Œ~ |Kñ§Â¿j^¾Ôc-bûÃ:‡ˆ| ­h:½ç‡ï5}CÕ.´k‹É4ëGFÒ¯¦¶{:ÎXJ(ø¢ÿ‡±ÁSé%Ÿ·ÿþ&GíÿÏø{ü7þ’Yûÿâd~Ñ_üñ¨¢€ø{ü7þ’Yûÿâd~Ñ_üñ¨ÿ‡±ÁSé%Ÿ·ÿþ&GíÿÏŠ(ÿ‡±ÁSé%Ÿ·ÿþ&GíÿÏø{ü7þ’Yûÿâd~Ñ_üñ¨¢€ø{ü7þ’Yûÿâd~Ñ_üñ¨ÿ‡±ÁSé%Ÿ·ÿþ&GíÿÏŠ(ÿ‡±ÁSé%Ÿ·ÿþ&GíÿÏø{ü7þ’Yûÿâd~Ñ_üñ¨¢€ø{ü7þ’Yûÿâd~Ñ_üñ¨ÿ‡±ÁSé%Ÿ·ÿþ&GíÿÏŠ(ý~¿à“¿ò‹/ø&Ÿý˜ìoÿ¬ëðæ¾ÿ¢Š(¢Š+ü9¼'ÿ6ÿ‚’x ¾ð/à ß¶ÿ‚üà¿èÞðwƒ¼'ûWüyðç…|'á_iÖÚ?‡¼3áŸhþ>³Ò4èZEž—£hÚ]®¥éÖ¶Ö66Ð[AJQ@ü=‹þ ›ÿI,ý¿ÿñ2?h¯þxÔÃØ¿à©¿ô’ÏÛÿÿ#öŠÿçEÃØ¿à©¿ô’ÏÛÿÿ#öŠÿçGü=‹þ ›ÿI,ý¿ÿñ2?h¯þxÔQ@ü=‹þ ›ÿI,ý¿ÿñ2?h¯þxÔÃØ¿à©¿ô’ÏÛÿÿ#öŠÿçEÃØ¿à©¿ô’ÏÛÿÿ#öŠÿçGü=‹þ ›ÿI,ý¿ÿñ2?h¯þxÔQ@ü=‹þ ›ÿI,ý¿ÿñ2?h¯þxÔÃØ¿à©¿ô’ÏÛÿÿ#öŠÿçEÃØ¿à©¿ô’ÏÛÿÿ#öŠÿç_Óïü9ûXþÔÿµü7â§ü4¿í-û@~ÑðƒþÀ¿á ÿ…éñ“â/Å¿øCÿá&ý¢¿cïøHÿáÿ„ÿÄ~ ÿ„þøGôí¿ìŸ²jÿbioûGöm—’Q@èùEPEPEP_áÍá?ø)·ü“À^ðÏ| ÿý·üàŸøF🃼á?Ú¿ãχ<+á? øsN¶Ñü=០ø{Gñõž‘ øBÒ,ìô½FÒìítí/Nµ¶±±¶‚Ú¢RŠè?áì_ðTßúIgíÿÿ‰‘ûEóÆ£þÅÿMÿ¤–~ßÿø™´Wÿ6Õ¼?ûqøOTñÁO‰~4øWâ­K·Úìe¬_xgPñu­W¼ðý毠èz¥Öqy&q¨èÚUôÖÏs§YËñEÿbÿ‚¦ÿÒK?oÿüLÚ+ÿž5Pÿbÿ‚¦ÿÒK?oÿüLÚ+ÿž5ðö/ø*oý$³öÿÿÄÈý¢¿ùãQEðö/ø*oý$³öÿÿÄÈý¢¿ùãQÿbÿ‚¦ÿÒK?oÿüLÚ+ÿž5Pÿbÿ‚¦ÿÒK?oÿüLÚ+ÿž5ðö/ø*oý$³öÿÿÄÈý¢¿ùãQEðö/ø*oý$³öÿÿÄÈý¢¿ùãQÿbÿ‚¦ÿÒK?oÿüLÚ+ÿž5Pÿbÿ‚¦ÿÒK?oÿüLÚ+ÿž5ðö/ø*oý$³öÿÿÄÈý¢¿ùãQEðö/ø*oý$³öÿÿÄÈý¢¿ùãWúýÁ'å_ðM?û0ØßÿY×áÍPßôQEQE‡7„ÿà¦ßðROxWÃ>ð/üößð_‚|áýÂ~ðw„ÿjÿ>ð¯„ü+áÍ:ÛGð÷†|3áíÇÖzFƒáý H³³ÒômK³µÓ´½:ÖÚÆÆÚ h"‰zø{ü7þ’Yûÿâd~Ñ_üñ¨¢€ø{ü7þ’Yûÿâd~Ñ_üñ¨ÿ‡±ÁSé%Ÿ·ÿþ&GíÿÏŠ(ÿ‡±ÁSé%Ÿ·ÿþ&GíÿÏø{ü7þ’Yûÿâd~Ñ_üñ¨¢€ø{ü7þ’Yûÿâd~Ñ_üñ¨ÿ‡±ÁSé%Ÿ·ÿþ&GíÿÏŠ(ÿ‡±ÁSé%Ÿ·ÿþ&GíÿÏø{ü7þ’Yûÿâd~Ñ_üñ¨¢€ø{ü7þ’Yûÿâd~Ñ_üñ¨ÿ‡±ÁSé%Ÿ·ÿþ&GíÿÏŠ(ú}ÿƒG?kÚŸö ÿ‚¦üTÿ†—ý¥¿hÚ#þØãwü!_ð½>2|Eø·ÿü$ß´Wì}ÿ ü"ßðŸøÄðÿÂAÿþƒý·ý“öOí_ìM#íÿhþͲòJ( ÿÙscons-doc-2.3.0/doc/python10/scanner.fig0000644000175000017500000000330712114661557020574 0ustar dktrkranzdktrkranz#FIG 3.2 Landscape Center Inches Letter 100.00 Single -2 1200 2 6 2700 5400 4500 6000 2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5 2700 5400 4500 5400 4500 6000 2700 6000 2700 5400 4 0 0 50 0 0 16 0.0000 4 225 1245 3000 5775 ProgScanner\001 -6 6 2700 4200 4500 4800 2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5 2700 4200 4500 4200 4500 4800 2700 4800 2700 4200 4 0 0 50 0 0 16 0.0000 4 165 780 3225 4575 Scanner\001 -6 6 2700 3000 4500 3600 2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5 2700 3000 4500 3000 4500 3600 2700 3600 2700 3000 4 0 0 50 0 0 16 0.0000 4 165 1290 2925 3375 Environment\001 -6 6 5100 5400 6900 6000 2 2 1 1 0 7 50 0 -1 4.000 0 0 7 0 0 5 5100 5400 6900 5400 6900 6000 5100 6000 5100 5400 4 0 0 50 0 0 16 0.0000 4 165 1200 5400 5775 JavaScanner\001 -6 6 300 5400 2100 6000 2 2 0 1 0 7 50 0 -1 4.000 0 0 7 0 0 5 300 5400 2100 5400 2100 6000 300 6000 300 5400 4 0 0 50 0 0 16 0.0000 4 165 945 750 5775 CScanner\001 -6 6 600 3300 1500 3900 2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5 600 3300 1500 3300 1500 3900 600 3900 600 3300 4 0 0 50 0 0 16 0.0000 4 165 525 825 3675 Node\001 -6 2 1 0 1 0 7 50 0 -1 4.000 0 0 -1 0 0 4 1200 5400 1200 5100 6000 5100 6000 5400 2 1 0 1 0 7 50 0 -1 4.000 0 0 -1 0 0 2 3600 4950 3600 5400 2 3 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 4 3600 4800 3525 4950 3675 4950 3600 4800 2 3 0 1 0 7 50 0 -1 4.000 0 0 7 0 0 5 3600 3600 3560 3675 3600 3750 3640 3675 3600 3600 2 3 0 1 0 7 50 0 -1 4.000 0 0 7 0 0 5 1050 3900 1010 3975 1050 4050 1090 3975 1050 3900 2 1 0 1 0 7 50 0 -1 4.000 0 0 -1 1 0 3 1 1 1.00 60.00 120.00 1050 4050 1050 4500 2700 4500 2 1 0 1 0 7 50 0 -1 4.000 0 0 -1 1 0 2 1 1 1.00 60.00 120.00 3600 3750 3600 4200 2 1 1 1 0 7 50 0 -1 4.000 0 0 -1 0 0 2 3600 3000 3600 2700 scons-doc-2.3.0/doc/python10/scanner.eps0000644000175000017500000001112512114661557020613 0ustar dktrkranzdktrkranz%!PS-Adobe-2.0 EPSF-2.0 %%Title: build/doc/python10/scanner.fig %%Creator: /usr/bin/fig2dev Version 3.2 Patchlevel 3d %%CreationDate: Sun Jan 2 01:21:05 2005 %%For: knight@casablanca.home.baldmt.com (Steven Knight) %%BoundingBox: 0 0 398 200 %%Magnification: 1.0000 %%EndComments /$F2psDict 200 dict def $F2psDict begin $F2psDict /mtrx matrix put /col-1 {0 setgray} bind def /col0 {0.000 0.000 0.000 srgb} bind def /col1 {0.000 0.000 1.000 srgb} bind def /col2 {0.000 1.000 0.000 srgb} bind def /col3 {0.000 1.000 1.000 srgb} bind def /col4 {1.000 0.000 0.000 srgb} bind def /col5 {1.000 0.000 1.000 srgb} bind def /col6 {1.000 1.000 0.000 srgb} bind def /col7 {1.000 1.000 1.000 srgb} bind def /col8 {0.000 0.000 0.560 srgb} bind def /col9 {0.000 0.000 0.690 srgb} bind def /col10 {0.000 0.000 0.820 srgb} bind def /col11 {0.530 0.810 1.000 srgb} bind def /col12 {0.000 0.560 0.000 srgb} bind def /col13 {0.000 0.690 0.000 srgb} bind def /col14 {0.000 0.820 0.000 srgb} bind def /col15 {0.000 0.560 0.560 srgb} bind def /col16 {0.000 0.690 0.690 srgb} bind def /col17 {0.000 0.820 0.820 srgb} bind def /col18 {0.560 0.000 0.000 srgb} bind def /col19 {0.690 0.000 0.000 srgb} bind def /col20 {0.820 0.000 0.000 srgb} bind def /col21 {0.560 0.000 0.560 srgb} bind def /col22 {0.690 0.000 0.690 srgb} bind def /col23 {0.820 0.000 0.820 srgb} bind def /col24 {0.500 0.190 0.000 srgb} bind def /col25 {0.630 0.250 0.000 srgb} bind def /col26 {0.750 0.380 0.000 srgb} bind def /col27 {1.000 0.500 0.500 srgb} bind def /col28 {1.000 0.630 0.630 srgb} bind def /col29 {1.000 0.750 0.750 srgb} bind def /col30 {1.000 0.880 0.880 srgb} bind def /col31 {1.000 0.840 0.000 srgb} bind def end save newpath 0 200 moveto 0 0 lineto 398 0 lineto 398 200 lineto closepath clip newpath -17.3 360.7 translate 1 -1 scale /cp {closepath} bind def /ef {eofill} bind def /gr {grestore} bind def /gs {gsave} bind def /sa {save} bind def /rs {restore} bind def /l {lineto} bind def /m {moveto} bind def /rm {rmoveto} bind def /n {newpath} bind def /s {stroke} bind def /sh {show} bind def /slc {setlinecap} bind def /slj {setlinejoin} bind def /slw {setlinewidth} bind def /srgb {setrgbcolor} bind def /rot {rotate} bind def /sc {scale} bind def /sd {setdash} bind def /ff {findfont} bind def /sf {setfont} bind def /scf {scalefont} bind def /sw {stringwidth} bind def /tr {translate} bind def /tnt {dup dup currentrgbcolor 4 -2 roll dup 1 exch sub 3 -1 roll mul add 4 -2 roll dup 1 exch sub 3 -1 roll mul add 4 -2 roll dup 1 exch sub 3 -1 roll mul add srgb} bind def /shd {dup dup currentrgbcolor 4 -2 roll mul 4 -2 roll mul 4 -2 roll mul srgb} bind def /$F2psBegin {$F2psDict begin /$F2psEnteredState save def} def /$F2psEnd {$F2psEnteredState restore end} def $F2psBegin 10 setmiterlimit 0.06000 0.06000 sc % % Fig objects follow % % Polyline 7.500 slw n 2700 5400 m 4500 5400 l 4500 6000 l 2700 6000 l cp gs col0 s gr /Times-Roman ff 240.00 scf sf 3000 5775 m gs 1 -1 sc (ProgScanner) col0 sh gr % Polyline n 2700 4200 m 4500 4200 l 4500 4800 l 2700 4800 l cp gs col0 s gr /Times-Roman ff 240.00 scf sf 3225 4575 m gs 1 -1 sc (Scanner) col0 sh gr % Polyline n 2700 3000 m 4500 3000 l 4500 3600 l 2700 3600 l cp gs col0 s gr /Times-Roman ff 240.00 scf sf 2925 3375 m gs 1 -1 sc (Environment) col0 sh gr % Polyline [60] 0 sd n 5100 5400 m 6900 5400 l 6900 6000 l 5100 6000 l cp gs col0 s gr [] 0 sd /Times-Roman ff 240.00 scf sf 5400 5775 m gs 1 -1 sc (JavaScanner) col0 sh gr % Polyline n 300 5400 m 2100 5400 l 2100 6000 l 300 6000 l cp gs col0 s gr /Times-Roman ff 240.00 scf sf 750 5775 m gs 1 -1 sc (CScanner) col0 sh gr % Polyline n 600 3300 m 1500 3300 l 1500 3900 l 600 3900 l cp gs col0 s gr /Times-Roman ff 240.00 scf sf 825 3675 m gs 1 -1 sc (Node) col0 sh gr % Polyline n 1200 5400 m 1200 5100 l 6000 5100 l 6000 5400 l gs col0 s gr % Polyline n 3600 4950 m 3600 5400 l gs col0 s gr % Polyline n 3600 4800 m 3525 4950 l 3675 4950 l cp gs col0 s gr % Polyline n 3600 3600 m 3560 3675 l 3600 3750 l 3640 3675 l cp gs col0 s gr % Polyline n 1050 3900 m 1010 3975 l 1050 4050 l 1090 3975 l cp gs col0 s gr % Polyline gs clippath 2715 4530 m 2715 4470 l 2564 4470 l 2684 4500 l 2564 4530 l cp eoclip n 1050 4050 m 1050 4500 l 2700 4500 l gs col0 s gr gr % arrowhead n 2564 4530 m 2684 4500 l 2564 4470 l 2564 4530 l cp gs 0.00 setgray ef gr col0 s % Polyline gs clippath 3570 4215 m 3630 4215 l 3630 4064 l 3600 4184 l 3570 4064 l cp eoclip n 3600 3750 m 3600 4200 l gs col0 s gr gr % arrowhead n 3570 4064 m 3600 4184 l 3630 4064 l 3570 4064 l cp gs 0.00 setgray ef gr col0 s % Polyline [60] 0 sd n 3600 3000 m 3600 2700 l gs col0 s gr [] 0 sd $F2psEnd rs scons-doc-2.3.0/doc/python10/future.xml0000644000175000017500000001112012114661557020500 0ustar dktrkranzdktrkranz There are a number of things we would like to do to continue to improve &SCons; in the future.
Distutils Cooperation There is a certain amount of overlap between what &SCons; does to search out and make use of various compilers on a system, and the impressively complete job that the Distutils do of describing much the same thing. Collaborating to provide some sort of common interface between the two tools would benefit both tools.
Additional Builder Support Adding additional builders would broaden the potential user base. In rough order of importance: Java Given the popularity of Java, support for it would greatly increase the appeal of &SCons; in the large community of Java users. Good support for Java is, however, a tricky proposition. Because the Java compiler can make decisions about compiling other files based on what classes it finds in a file, it behaves "unpredictably" from the point of view of an outside build tool like &SCons; or &Make;. Some sort of sophisticated scanning of Java source code to identify what other classes are likely to be compiled would be an obvious first step, but notice that here &SCons; would be scanning the file to find additional targets to be built. This is the inverse of the sort of #include scanning performed for C files, in which &SCons; is looking for additional dependencies. Documentation toolchains A number of early adopters are using &SCons; to build documents from TeX or DocBook source files. Built-in support for various documentation toolchains would be an obvious boon for many people. C# The reality is that anything that Microsoft does will doubtless have a wide audience. Turning &SCons;' back on that would be cutting off its nose to spite its face. Fortran Despite the fact that &SCons; is no longer directly associated with Software Carpentry, it still shares the same goal: to make programming easier for more than just programmers. To that end, good Fortran support would help a great many physical scientists and other computer users out there who still rely on Fortran for a great deal of their work.
Database Interface The Nodes in an &SCons; dependency graph aren't only restricted to files. Creating an interface to mSQL or MySQL databases would allow the possibility of updating external files in response to changes in database fields, or vice versa. This could be handy, for example, for generating a cache of static web pages from a database that only need re-generating when the appropriate database objects change.
Tool Integration &SCons; should work well with as many popular Integrated Development Environments (IDEs) and tool chains as possible: Komodo, Microsoft Visual Studio, ClearCase, etc. Suggestions for additional tools are welcome.
Makefile Interface Because the &SCons; Build Engine can be embedded in any Python interface, there isn't any technical reason why a &Makefile; interpreter couldn't be written in Python and use the &SCons; Build Engine for its dependency analysis. Proof-of-concept for the idea already exists. Gary Holt's make++ (also known as makepp) is a Perl implementation of just such a &Makefile; interpreter. It could possible serve as a model for a Python version, in much the same way the &Cons; design served as the prototype for &SCons;.
scons-doc-2.3.0/doc/python10/builder.eps0000644000175000017500000002461012114661557020613 0ustar dktrkranzdktrkranz%!PS-Adobe-2.0 EPSF-2.0 %%Title: build/doc/python10/builder.fig %%Creator: /usr/bin/fig2dev Version 3.2 Patchlevel 3d %%CreationDate: Sun Jan 2 01:21:05 2005 %%For: knight@casablanca.home.baldmt.com (Steven Knight) %%BoundingBox: 0 0 668 290 %%Magnification: 1.0000 %%EndComments /$F2psDict 200 dict def $F2psDict begin $F2psDict /mtrx matrix put /col-1 {0 setgray} bind def /col0 {0.000 0.000 0.000 srgb} bind def /col1 {0.000 0.000 1.000 srgb} bind def /col2 {0.000 1.000 0.000 srgb} bind def /col3 {0.000 1.000 1.000 srgb} bind def /col4 {1.000 0.000 0.000 srgb} bind def /col5 {1.000 0.000 1.000 srgb} bind def /col6 {1.000 1.000 0.000 srgb} bind def /col7 {1.000 1.000 1.000 srgb} bind def /col8 {0.000 0.000 0.560 srgb} bind def /col9 {0.000 0.000 0.690 srgb} bind def /col10 {0.000 0.000 0.820 srgb} bind def /col11 {0.530 0.810 1.000 srgb} bind def /col12 {0.000 0.560 0.000 srgb} bind def /col13 {0.000 0.690 0.000 srgb} bind def /col14 {0.000 0.820 0.000 srgb} bind def /col15 {0.000 0.560 0.560 srgb} bind def /col16 {0.000 0.690 0.690 srgb} bind def /col17 {0.000 0.820 0.820 srgb} bind def /col18 {0.560 0.000 0.000 srgb} bind def /col19 {0.690 0.000 0.000 srgb} bind def /col20 {0.820 0.000 0.000 srgb} bind def /col21 {0.560 0.000 0.560 srgb} bind def /col22 {0.690 0.000 0.690 srgb} bind def /col23 {0.820 0.000 0.820 srgb} bind def /col24 {0.500 0.190 0.000 srgb} bind def /col25 {0.630 0.250 0.000 srgb} bind def /col26 {0.750 0.380 0.000 srgb} bind def /col27 {1.000 0.500 0.500 srgb} bind def /col28 {1.000 0.630 0.630 srgb} bind def /col29 {1.000 0.750 0.750 srgb} bind def /col30 {1.000 0.880 0.880 srgb} bind def /col31 {1.000 0.840 0.000 srgb} bind def end save newpath 0 290 moveto 0 0 lineto 668 0 lineto 668 290 lineto closepath clip newpath -53.3 342.7 translate 1 -1 scale /cp {closepath} bind def /ef {eofill} bind def /gr {grestore} bind def /gs {gsave} bind def /sa {save} bind def /rs {restore} bind def /l {lineto} bind def /m {moveto} bind def /rm {rmoveto} bind def /n {newpath} bind def /s {stroke} bind def /sh {show} bind def /slc {setlinecap} bind def /slj {setlinejoin} bind def /slw {setlinewidth} bind def /srgb {setrgbcolor} bind def /rot {rotate} bind def /sc {scale} bind def /sd {setdash} bind def /ff {findfont} bind def /sf {setfont} bind def /scf {scalefont} bind def /sw {stringwidth} bind def /tr {translate} bind def /tnt {dup dup currentrgbcolor 4 -2 roll dup 1 exch sub 3 -1 roll mul add 4 -2 roll dup 1 exch sub 3 -1 roll mul add 4 -2 roll dup 1 exch sub 3 -1 roll mul add srgb} bind def /shd {dup dup currentrgbcolor 4 -2 roll mul 4 -2 roll mul 4 -2 roll mul srgb} bind def /reencdict 12 dict def /ReEncode { reencdict begin /newcodesandnames exch def /newfontname exch def /basefontname exch def /basefontdict basefontname findfont def /newfont basefontdict maxlength dict def basefontdict { exch dup /FID ne { dup /Encoding eq { exch dup length array copy newfont 3 1 roll put } { exch newfont 3 1 roll put } ifelse } { pop pop } ifelse } forall newfont /FontName newfontname put newcodesandnames aload pop 128 1 255 { newfont /Encoding get exch /.notdef put } for newcodesandnames length 2 idiv { newfont /Encoding get 3 1 roll put } repeat newfontname newfont definefont pop end } def /isovec [ 8#055 /minus 8#200 /grave 8#201 /acute 8#202 /circumflex 8#203 /tilde 8#204 /macron 8#205 /breve 8#206 /dotaccent 8#207 /dieresis 8#210 /ring 8#211 /cedilla 8#212 /hungarumlaut 8#213 /ogonek 8#214 /caron 8#220 /dotlessi 8#230 /oe 8#231 /OE 8#240 /space 8#241 /exclamdown 8#242 /cent 8#243 /sterling 8#244 /currency 8#245 /yen 8#246 /brokenbar 8#247 /section 8#250 /dieresis 8#251 /copyright 8#252 /ordfeminine 8#253 /guillemotleft 8#254 /logicalnot 8#255 /hyphen 8#256 /registered 8#257 /macron 8#260 /degree 8#261 /plusminus 8#262 /twosuperior 8#263 /threesuperior 8#264 /acute 8#265 /mu 8#266 /paragraph 8#267 /periodcentered 8#270 /cedilla 8#271 /onesuperior 8#272 /ordmasculine 8#273 /guillemotright 8#274 /onequarter 8#275 /onehalf 8#276 /threequarters 8#277 /questiondown 8#300 /Agrave 8#301 /Aacute 8#302 /Acircumflex 8#303 /Atilde 8#304 /Adieresis 8#305 /Aring 8#306 /AE 8#307 /Ccedilla 8#310 /Egrave 8#311 /Eacute 8#312 /Ecircumflex 8#313 /Edieresis 8#314 /Igrave 8#315 /Iacute 8#316 /Icircumflex 8#317 /Idieresis 8#320 /Eth 8#321 /Ntilde 8#322 /Ograve 8#323 /Oacute 8#324 /Ocircumflex 8#325 /Otilde 8#326 /Odieresis 8#327 /multiply 8#330 /Oslash 8#331 /Ugrave 8#332 /Uacute 8#333 /Ucircumflex 8#334 /Udieresis 8#335 /Yacute 8#336 /Thorn 8#337 /germandbls 8#340 /agrave 8#341 /aacute 8#342 /acircumflex 8#343 /atilde 8#344 /adieresis 8#345 /aring 8#346 /ae 8#347 /ccedilla 8#350 /egrave 8#351 /eacute 8#352 /ecircumflex 8#353 /edieresis 8#354 /igrave 8#355 /iacute 8#356 /icircumflex 8#357 /idieresis 8#360 /eth 8#361 /ntilde 8#362 /ograve 8#363 /oacute 8#364 /ocircumflex 8#365 /otilde 8#366 /odieresis 8#367 /divide 8#370 /oslash 8#371 /ugrave 8#372 /uacute 8#373 /ucircumflex 8#374 /udieresis 8#375 /yacute 8#376 /thorn 8#377 /ydieresis] def /Times-Roman /Times-Roman-iso isovec ReEncode /$F2psBegin {$F2psDict begin /$F2psEnteredState save def} def /$F2psEnd {$F2psEnteredState restore end} def $F2psBegin 10 setmiterlimit 0.06000 0.06000 sc % % Fig objects follow % % Polyline 7.500 slw n 2700 1200 m 4500 1200 l 4500 1800 l 2700 1800 l cp gs col0 s gr /Times-Roman-iso ff 240.00 scf sf 2925 1575 m gs 1 -1 sc (Environment) col0 sh gr % Polyline n 2700 2400 m 4500 2400 l 4500 3000 l 2700 3000 l cp gs col0 s gr /Times-Roman-iso ff 240.00 scf sf 3600 2775 m gs 1 -1 sc (BuilderWrapper) dup sw pop 2 div neg 0 rm col0 sh gr % Polyline n 2700 3600 m 4500 3600 l 4500 4200 l 2700 4200 l cp gs col0 s gr /Times-Roman-iso ff 240.00 scf sf 3600 3975 m gs 1 -1 sc (BuilderBase) dup sw pop 2 div neg 0 rm col0 sh gr % Polyline n 8400 3600 m 9900 3600 l 9900 4200 l 8400 4200 l cp gs col0 s gr /Times-Roman-iso ff 240.00 scf sf 9150 3975 m gs 1 -1 sc (ActionBase) dup sw pop 2 div neg 0 rm col0 sh gr /Times-Roman-iso ff 240.00 scf sf 4650 5175 m gs 1 -1 sc (MultiStep-) dup sw pop 2 div neg 0 rm col0 sh gr /Times-Roman-iso ff 240.00 scf sf 4650 5460 m gs 1 -1 sc (Builder) dup sw pop 2 div neg 0 rm col0 sh gr % Polyline n 3900 4800 m 5400 4800 l 5400 5700 l 3900 5700 l cp gs col0 s gr /Times-Roman-iso ff 240.00 scf sf 2550 5175 m gs 1 -1 sc (Composite-) dup sw pop 2 div neg 0 rm col0 sh gr /Times-Roman-iso ff 240.00 scf sf 2550 5460 m gs 1 -1 sc (Builder) dup sw pop 2 div neg 0 rm col0 sh gr % Polyline n 1800 4800 m 3300 4800 l 3300 5700 l 1800 5700 l cp gs col0 s gr /Times-Roman-iso ff 240.00 scf sf 7050 5175 m gs 1 -1 sc (Command) dup sw pop 2 div neg 0 rm col0 sh gr /Times-Roman-iso ff 240.00 scf sf 7050 5460 m gs 1 -1 sc (Action) dup sw pop 2 div neg 0 rm col0 sh gr % Polyline n 6300 4800 m 7800 4800 l 7800 5700 l 6300 5700 l cp gs col0 s gr /Times-Roman-iso ff 240.00 scf sf 9150 5460 m gs 1 -1 sc (Action) dup sw pop 2 div neg 0 rm col0 sh gr /Times-Roman-iso ff 240.00 scf sf 9150 5175 m gs 1 -1 sc (Function) dup sw pop 2 div neg 0 rm col0 sh gr % Polyline n 8400 4800 m 9900 4800 l 9900 5700 l 8400 5700 l cp gs col0 s gr /Times-Roman-iso ff 240.00 scf sf 11250 5175 m gs 1 -1 sc (List) dup sw pop 2 div neg 0 rm col0 sh gr /Times-Roman-iso ff 240.00 scf sf 11250 5460 m gs 1 -1 sc (Action) dup sw pop 2 div neg 0 rm col0 sh gr % Polyline n 10500 4800 m 12000 4800 l 12000 5700 l 10500 5700 l cp gs col0 s gr % Polyline n 900 2400 m 2100 2400 l 2100 3000 l 900 3000 l cp gs col0 s gr /Times-Roman-iso ff 240.00 scf sf 1500 2775 m gs 1 -1 sc (Node) dup sw pop 2 div neg 0 rm col0 sh gr % Polyline n 3600 4200 m 3525 4350 l 3675 4350 l cp gs col0 s gr % Polyline n 3150 4800 m 3150 4500 l 4050 4500 l 4050 4800 l gs col0 s gr % Polyline n 3600 4350 m 3600 4500 l gs col0 s gr % Polyline n 9150 4200 m 9075 4350 l 9225 4350 l cp gs col0 s gr % Polyline n 7050 4800 m 7050 4500 l 10950 4500 l 10950 4800 l gs col0 s gr % Polyline n 9150 4350 m 9150 4800 l gs col0 s gr % Polyline gs clippath 9885 3870 m 9885 3930 l 10036 3930 l 9916 3900 l 10036 3870 l cp eoclip n 11550 4650 m 11550 3900 l 9900 3900 l gs col0 s gr gr % arrowhead n 10036 3870 m 9916 3900 l 10036 3930 l 10036 3870 l cp gs 0.00 setgray ef gr col0 s % Polyline gs clippath 8415 3930 m 8415 3870 l 8264 3870 l 8384 3900 l 8264 3930 l cp eoclip n 4650 3900 m 8400 3900 l gs col0 s gr gr % arrowhead n 8264 3930 m 8384 3900 l 8264 3870 l 8264 3930 l cp gs 0.00 setgray ef gr col0 s % Polyline gs clippath 3930 1785 m 3870 1785 l 3870 1936 l 3900 1816 l 3930 1936 l cp eoclip n 3900 2250 m 3900 1800 l gs col0 s gr gr % arrowhead n 3930 1936 m 3900 1816 l 3870 1936 l 3930 1936 l cp gs 0.00 setgray ef gr col0 s % Polyline gs clippath 3270 2415 m 3330 2415 l 3330 2264 l 3300 2384 l 3270 2264 l cp eoclip n 3300 1950 m 3300 2400 l gs col0 s gr gr % arrowhead n 3270 2264 m 3300 2384 l 3330 2264 l 3270 2264 l cp gs 0.00 setgray ef gr col0 s % Polyline gs clippath 3570 3615 m 3630 3615 l 3630 3464 l 3600 3584 l 3570 3464 l cp eoclip n 3600 3150 m 3600 3600 l gs col0 s gr gr % arrowhead n 3570 3464 m 3600 3584 l 3630 3464 l 3570 3464 l cp gs 0.00 setgray ef gr col0 s % Polyline gs clippath 4380 4185 m 4320 4185 l 4320 4336 l 4350 4216 l 4380 4336 l cp eoclip n 4350 4650 m 4350 4200 l gs col0 s gr gr % arrowhead n 4380 4336 m 4350 4216 l 4320 4336 l 4380 4336 l cp gs 0.00 setgray ef gr col0 s % Polyline gs clippath 2880 4185 m 2820 4185 l 2820 4336 l 2850 4216 l 2880 4336 l cp eoclip n 2850 4650 m 2850 4200 l gs col0 s gr gr % arrowhead n 2880 4336 m 2850 4216 l 2820 4336 l 2880 4336 l cp gs 0.00 setgray ef gr col0 s % Polyline gs clippath 2715 3930 m 2715 3870 l 2564 3870 l 2684 3900 l 2564 3930 l cp eoclip n 1500 3150 m 1500 3900 l 2700 3900 l gs col0 s gr gr % arrowhead n 2564 3930 m 2684 3900 l 2564 3870 l 2564 3930 l cp gs 0.00 setgray ef gr col0 s % Polyline n 4650 3900 m 4575 3860 l 4500 3900 l 4575 3940 l cp gs col0 s gr % Polyline n 1500 3000 m 1460 3075 l 1500 3150 l 1540 3075 l cp gs col0 s gr % Polyline n 3600 3000 m 3560 3075 l 3600 3150 l 3640 3075 l cp gs col0 s gr % Polyline n 3300 1800 m 3260 1875 l 3300 1950 l 3340 1875 l cp gs col0 s gr % Polyline n 3900 2250 m 3860 2325 l 3900 2400 l 3940 2325 l cp gs col0 s gr % Polyline n 4350 4650 m 4310 4725 l 4350 4800 l 4390 4725 l cp gs col0 s gr % Polyline n 2850 4650 m 2810 4725 l 2850 4800 l 2890 4725 l cp gs col0 s gr % Polyline n 11550 4650 m 11510 4725 l 11550 4800 l 11590 4725 l cp gs col0 s gr % Polyline [60] 0 sd n 3600 1200 m 3600 900 l gs col0 s gr [] 0 sd $F2psEnd rs scons-doc-2.3.0/doc/python10/MANIFEST0000644000175000017500000000025612114661557017605 0ustar dktrkranzdktrkranzabstract.xml acks.xml arch.fig builder.fig copyright.xml design.xml future.xml install.xml intro.xml job-task.fig main.xml node.fig process.xml scanner.fig scons.mod sig.fig scons-doc-2.3.0/doc/python10/copyright.xml0000644000175000017500000000226212114661557021205 0ustar dktrkranzdktrkranz
Copyright (c) 2001, 2002 Steven Knight
scons-doc-2.3.0/doc/python10/arch.eps0000644000175000017500000000741312114661557020104 0ustar dktrkranzdktrkranz%!PS-Adobe-2.0 EPSF-2.0 %%Title: build/doc/python10/arch.fig %%Creator: /usr/bin/fig2dev Version 3.2 Patchlevel 3d %%CreationDate: Sun Jan 2 01:21:05 2005 %%For: knight@casablanca.home.baldmt.com (Steven Knight) %%BoundingBox: 0 0 218 182 %%Magnification: 1.0000 %%EndComments /$F2psDict 200 dict def $F2psDict begin $F2psDict /mtrx matrix put /col-1 {0 setgray} bind def /col0 {0.000 0.000 0.000 srgb} bind def /col1 {0.000 0.000 1.000 srgb} bind def /col2 {0.000 1.000 0.000 srgb} bind def /col3 {0.000 1.000 1.000 srgb} bind def /col4 {1.000 0.000 0.000 srgb} bind def /col5 {1.000 0.000 1.000 srgb} bind def /col6 {1.000 1.000 0.000 srgb} bind def /col7 {1.000 1.000 1.000 srgb} bind def /col8 {0.000 0.000 0.560 srgb} bind def /col9 {0.000 0.000 0.690 srgb} bind def /col10 {0.000 0.000 0.820 srgb} bind def /col11 {0.530 0.810 1.000 srgb} bind def /col12 {0.000 0.560 0.000 srgb} bind def /col13 {0.000 0.690 0.000 srgb} bind def /col14 {0.000 0.820 0.000 srgb} bind def /col15 {0.000 0.560 0.560 srgb} bind def /col16 {0.000 0.690 0.690 srgb} bind def /col17 {0.000 0.820 0.820 srgb} bind def /col18 {0.560 0.000 0.000 srgb} bind def /col19 {0.690 0.000 0.000 srgb} bind def /col20 {0.820 0.000 0.000 srgb} bind def /col21 {0.560 0.000 0.560 srgb} bind def /col22 {0.690 0.000 0.690 srgb} bind def /col23 {0.820 0.000 0.820 srgb} bind def /col24 {0.500 0.190 0.000 srgb} bind def /col25 {0.630 0.250 0.000 srgb} bind def /col26 {0.750 0.380 0.000 srgb} bind def /col27 {1.000 0.500 0.500 srgb} bind def /col28 {1.000 0.630 0.630 srgb} bind def /col29 {1.000 0.750 0.750 srgb} bind def /col30 {1.000 0.880 0.880 srgb} bind def /col31 {1.000 0.840 0.000 srgb} bind def end save newpath 0 182 moveto 0 0 lineto 218 0 lineto 218 182 lineto closepath clip newpath -215.3 324.7 translate 1 -1 scale /cp {closepath} bind def /ef {eofill} bind def /gr {grestore} bind def /gs {gsave} bind def /sa {save} bind def /rs {restore} bind def /l {lineto} bind def /m {moveto} bind def /rm {rmoveto} bind def /n {newpath} bind def /s {stroke} bind def /sh {show} bind def /slc {setlinecap} bind def /slj {setlinejoin} bind def /slw {setlinewidth} bind def /srgb {setrgbcolor} bind def /rot {rotate} bind def /sc {scale} bind def /sd {setdash} bind def /ff {findfont} bind def /sf {setfont} bind def /scf {scalefont} bind def /sw {stringwidth} bind def /tr {translate} bind def /tnt {dup dup currentrgbcolor 4 -2 roll dup 1 exch sub 3 -1 roll mul add 4 -2 roll dup 1 exch sub 3 -1 roll mul add 4 -2 roll dup 1 exch sub 3 -1 roll mul add srgb} bind def /shd {dup dup currentrgbcolor 4 -2 roll mul 4 -2 roll mul 4 -2 roll mul srgb} bind def /$F2psBegin {$F2psDict begin /$F2psEnteredState save def} def /$F2psEnd {$F2psEnteredState restore end} def $F2psBegin 10 setmiterlimit 0.06000 0.06000 sc % % Fig objects follow % /Courier-Bold ff 300.00 scf sf 3825 2925 m gs 1 -1 sc (scons) col0 sh gr /Times-Roman ff 300.00 scf sf 3825 3225 m gs 1 -1 sc (Script) col0 sh gr /Times-Roman ff 300.00 scf sf 5100 4875 m gs 1 -1 sc (Build Engine) col0 sh gr /Courier-Bold ff 300.00 scf sf 4200 4875 m gs 1 -1 sc (SCons) col0 sh gr % Polyline 7.500 slw n 3600 4200 m 7200 4200 l 7200 5400 l 3600 5400 l cp gs col0 s gr /Courier-Bold ff 300.00 scf sf 4725 4050 m gs 1 -1 sc (SCons) col0 sh gr /Times-Roman ff 300.00 scf sf 5625 4050 m gs 1 -1 sc (API) col0 sh gr % Polyline n 3600 2400 m 3600 2400 l 3600 2400 l 3600 2400 l cp gs col0 s gr % Polyline n 3600 2400 m 4800 2400 l 4800 3600 l 3600 3600 l cp gs col0 s gr % Polyline n 3600 3600 m 7200 3600 l 7200 4200 l 3600 4200 l cp gs col0 s gr % Polyline [60] 0 sd n 6000 3600 m 7200 3600 l 7200 2400 l 6000 2400 l cp gs col0 s gr [] 0 sd /Times-Italic ff 300.00 scf sf 6300 2925 m gs 1 -1 sc (other) col0 sh gr /Times-Italic ff 300.00 scf sf 6150 3225 m gs 1 -1 sc (interface) col0 sh gr $F2psEnd rs scons-doc-2.3.0/doc/python10/intro.xml0000644000175000017500000001324112114661557020327 0ustar dktrkranzdktrkranz More than twenty years after its creation, the classic UNIX &Make; utility and its descendants are still the dominant way in which software is built. &Make; has maintained this position despite the fact that the intervening years have revealed many shortcomings of the &Make; model for building software: The use of timestamps to decide when a file has been updated is imprecise and prone to error, especially across distributed file systems such as NFS. Builds of typical large software systems still take hours, if not days, despite the tremendous advances in CPU and disk speeds over recent years. &Make; maintains static definitions of dependencies in its &Makefiles;. Much effort has been put into utilities (mkdepend, gcc -M) and schemes (Makefile.d files) to try to keep &Makefile; dependencies up-to-date, but these only confirm that &Make;'s static dependencies are inherently fragile. The standard recursive use of &Make; for build hierarchies leads to incomplete dependency graphs, which must be overcome by manually changing the order in which directories are built, or through the use of multiple build passes. One need only look at the plethora of helper and wrapper utilities (automake, easymake, imake, jmake, makeLib, maketool, mkmed, shake, SMake, TMAKE) and complete alternatives to &Make; (Ant, bake, bau, bras, Cake, Cons, Cook, Jam, jmk, jus, makeme, mash, MK, nmake, Odin, VMake) that have been created over the years to realize that vanilla &Make; is not satisfying everyone's build requirements. So why Yet Another build tool?
Enter Software Carpentry Most of the build tools just mentioned were written by programmers and for programmers. The fact that most programmer-friendly utilities do a poor job of fulfilling the needs of non-programmers prompted Greg Wilson to organize the Software Carpentry competition in January 2000. Software Carpentry was an open design contest with the express goal of producing a set of next-generation utilities, including a build tool, that would be accessible not only to programmers but also to computer users such as physical scientists. The key to this usability would be that all of these utilities, including the build tool, would be written in Python. This provided the catalyst for actually pursuing an idea that had been floating around one of the more intriguing &Make; alternatives, a Perl utility called &Cons;. What if the friendlier syntax of Python could be married to the architectural advantages of &Cons;? The resulting merged design, at that time named &ScCons;, won the Software Carpentry build tool competition. CodeSourcery (by then the administrators of the competition) ultimately decided not to fund development of the build tool, but the seed had been planted and the design had taken root.
Cons It helps to know something about &Cons;. &Cons; was first released in 1996 by Bob Sidebotham, then an employee of Fore Systems, and it has a number of distinctive features that set it apart from most &Make;-alikes: &Cons; "configuration files" are not Yet Another invented mini-language, but are actually Perl scripts, which means the full power and flexibility of a real scripting language can be applied to build problems. &Cons; builds everything from a single process at the top of the source tree, with a global view of the dependencies. &Cons; scans files automatically for dependencies such as files specified on #include lines. &Cons; decides if a file was out-of-date by using MD5 checksums of the contents of files, not timestamps. Despite all of these intriguing architectural features, the great strength of &Cons;—being written in Perl—was also one of its weaknesses, turning away many potential users due to the (real or perceived) steep learning curve of Perl.
&SCons; Through the &ScCons; contest entry, &SCons; is the direct descendant of the &Cons; architecture, and is currently under active, supported development with a growing body of users. Its first release was 13 December 2001, under the simple and non-restrictive MIT license, and from the outset, the goal of the members of the &SCons; project has been to deliver a stable, reliable tool that can be used for industrial-strength software builds. The rest of this paper will give an overview of the &SCons; design (including its architecture and interface), describe the development methodology used, and discuss future directions for &SCons;.
scons-doc-2.3.0/doc/python10/sig.fig0000644000175000017500000000235712114661557017731 0ustar dktrkranzdktrkranz#FIG 3.2 Landscape Center Inches Letter 100.00 Single -2 1200 2 6 1200 3000 2700 3600 2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5 1200 3000 2700 3000 2700 3600 1200 3600 1200 3000 4 1 0 50 0 0 16 0.0000 4 165 1125 1950 3375 Taskmaster\001 -6 6 3300 4200 4500 4800 2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5 3300 4200 4500 4200 4500 4800 3300 4800 3300 4200 4 1 0 50 0 0 16 0.0000 4 165 525 3900 4575 MD5\001 -6 6 5100 4200 6300 4800 2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5 5100 4200 6300 4200 6300 4800 5100 4800 5100 4200 4 1 0 50 0 0 16 0.0000 4 225 780 5700 4575 TStamp\001 -6 6 4200 3000 5400 3600 2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5 4200 3000 5400 3000 5400 3600 4200 3600 4200 3000 4 1 0 50 0 0 16 0.0000 4 225 330 4800 3375 Sig\001 -6 2 3 0 1 0 7 50 0 -1 4.000 0 0 7 0 0 5 2700 3300 2775 3340 2850 3300 2775 3260 2700 3300 2 3 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 4 4800 3600 4725 3750 4875 3750 4800 3600 2 1 0 1 0 7 50 0 -1 0.000 0 0 7 0 0 4 3900 4200 3900 3900 5700 3900 5700 4200 2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 2 4800 3750 4800 3900 2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 0 2 1 1 1.00 60.00 120.00 2850 3300 4200 3300 2 1 1 1 0 7 50 0 -1 4.000 0 0 -1 0 0 2 1950 3000 1950 2700 2 1 1 1 0 7 50 0 -1 4.000 0 0 -1 0 0 2 4800 3000 4800 2700 scons-doc-2.3.0/doc/python10/arch.jpg0000644000175000017500000005276412114661557020106 0ustar dktrkranzdktrkranzÿØÿàJFIFPPÿÛCÿÛCÿÀÊò"ÿÄ ÿĵ}!1AQa"q2‘¡#B±ÁRÑð$3br‚ %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyzƒ„…†‡ˆ‰Š’“”•–—˜™š¢£¤¥¦§¨©ª²³´µ¶·¸¹ºÂÃÄÅÆÇÈÉÊÒÓÔÕÖרÙÚáâãäåæçèéêñòóôõö÷øùúÿÄ ÿĵw!1AQaq"2B‘¡±Á #3RðbrÑ $4á%ñ&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz‚ƒ„…†‡ˆ‰Š’“”•–—˜™š¢£¤¥¦§¨©ª²³´µ¶·¸¹ºÂÃÄÅÆÇÈÉÊÒÓÔÕÖרÙÚâãäåæçèéêòóôõö÷øùúÿÚ ?þŽ¿àžß±/ÃŽ?°/ì=ñ¯â—Æ?ÛÿÅ~0~ȳOÅ/ˆ¾&ÿ‡§ÿÁMtOøH¼wñà¿‚¼YâíwûÿµÆ‘áý#û_ľ¡¨fhZV™£Ø}£ìºfŸeeÑ}ÿÓýè£~ßÿø¶/ø*oÿFEðIßùE—üOþÌö7ÿÖuøs_ÐÀðí?Ù×þŠ7íÿÿ‹bÿ‚¦ÿôdQÿÓýè£~ßÿø¶/ø*oÿFE}ÿE|ÿÓýè£~ßÿø¶/ø*oÿFEðí?Ù×þŠ7íÿÿ‹bÿ‚¦ÿôdWßôPÀðí?Ù×þŠ7íÿÿ‹bÿ‚¦ÿôdQÿÓýè£~ßÿø¶/ø*oÿFE}ÿE|ÿÓýè£~ßÿø¶/ø*oÿFEðí?Ù×þŠ7íÿÿ‹bÿ‚¦ÿôdWßôPÄðLŸx«Ç¿ðM¿ø'ÏŽ¼uâoxÓÆÞ4ýˆ?exÇÆ>,Öuø«Åž*ñÀoë!ñ7‰¼C¬\Þjú÷ˆ5Ý^òóTÖuRòëQÕ5«›ëë™îg–Vû~¾ÿ‚NÿÊ,¿àšö`±¿þ³¯Ãšûþ€ (¢€ (¢€ (¢€ (¢€?8> þÈž ø©ûþÃ~ý²¼YûOé´Â?Ùƒàï†~0^|8ý·jƒž*Õ¾2·ÂŸ‡š_Å량Þ:ý—¾>øÛãgˆ¼5*ø³Å¾!ñì ¨ÜøƒXð¶²±ø³\¾Ö~€øû"|)ø©á_†>ñ?‹?iý/HøGáöðÏ…o>~Û¿¶‡ÁÏjÚsiÞÒÍÏÄï|"øûà|lñ¶ðÖ*ø³ã7ˆ|{â¤Ôn|A¬&²º¿‹Þð­çÃÛwöÐø9â­[Nm;ÃúY¹øã¯„_|ãož ÞÓ¥_|fñ|TšÏˆ5„ÖWWñgНµ“â?ì‰ð§â§…~ø;Äþ,ý§ô½#á‡ÛÃ>¼øqûnþÚx«VÓ›Nðþ–n~'xëáÇßøÛãgˆ·†´éWÅŸ¼Cãß&£sâ a5•ÕüYâ«ídøû"|)ø©á_†>ñ?‹?iý/HøGáöðÏ…o>~Û¿¶‡ÁÏjÚsiÞÒÍÏÄï|"øûà|lñ¶ðÖ*ø³ã7ˆ|{â¤Ôn|A¬&²º¿‹#þÈŸ ~*xWჼOâÏÚKÒ>ø}¼3á[χ¶ïí¡ðsÅZ¶œÚw‡ô³sñ;Ç_¾>øÆß<@-¼5§J¾,øÍâø©5Ÿk ¬®¯âÏ_k?OÑ@0|Gý‘>üTð¯ÃxŸÅŸ´þ—¤|#ðûxg·Ÿ?mßÛCà犵m9´ïéfçâw޾|}ð?¾6x€[xkN•|Yñ›Ä>=ñRj7> ÖY]_Åž*¾ÖOˆÿ²'ÂŸŠžøcàïø³öŸÒô„~o øVóáÇí»ûh|ñV­§6áý,ÜüNñ×Â/¾ñ·ÆÏ o iÒ¯‹>3x‡Ç¾*MFçÄÂk+«ø³ÅWÚÏÓôP€~Éß| ñƒöXýš~-ü-ƒâ·Ã/Š?³ÿÁ¿ˆ¿­¾,ø‹XñÅ;xÛáׇ%ø³Ä^.øâ|@‹ÃúžŸŒ¼E®ø÷ÆÚÆ·â5Ôµ-OÅÞ$½¹ŸY½+ŸýˆµŸƒ^#ý‹ÿdOþÎ~ñ€¿gÍ{ö`ø¬ü ð/‹.g¼ñW‚þ jŸ |'}ðÃÂ~&¼ºñ7.n¼AáÏÏ¡èúÍÍÇŒ|Y<ú̳x›]‘›Tº(Çÿà“¿ò‹/ø&Ÿý˜ìoÿ¬ëðæ¾ÿ¯€?à“¿ò‹/ø&Ÿý˜ìoÿ¬ëðæ¾ÿ ŠþH¿à¥ð]?Ûö`ÿ‚—xëöøw¯Á8?dŸøsÃÿ³êü/ø•ÿ$ð·íË>ûIê?t›‹ýkâ?€>#~ÎÞ —áOÃÿ'][[xËÂÿ ¾ßñ_ü¯öŽý”࿳Çí±û^~Î_þ$~Û_´GÄü-øMû)þʾ5ºñ7>>øïã/Å‚:Á‰¿ ¯l Pÿ„×öeÑbøÍ jv^$ñ®ãbÐx+AÔ4ßø»Ãž€÷úŠþ`í_ÿ5| ø§ûÿÃCþÇ¿°þ ~Ó?´Á?†Ÿ´'ü2o‚¿iï‰ÿÿexóÄ~ÿ„ïRø½»ã&£á/ ÿÂ+á-GÆŸhø±áoø[ß|)â?]ÿÂWã!¦k¾ÿ„ë×Ïüþ ½ûq|eý«<ÿ°øû|:ø7ûþÓÿd?ˆÿ?ࡾ?øÁ«j?þ2ü3ƒÃðxâËá/ÂïÙžÚ]_áLJþjòÝËu¯|Dñ¿ÄÏ ø÷áÆ±áØ¼'âMâG‚|:ýQ_Ëí#ÿÖý® ÿ‚K~ÒŸ··ÀOÙàÿÀ¿Úöý·õoÙöÍýž¿ißkü+áÍGÂþ.ðßÃOÙ|ñ÷À|5¶øâ ?|cø%<úö¹?ƒ|+k§Y|`Óôˆ¼[‹àOxÓßþÁ@?à«ß ¿à¢¿±ìÃÿøûxáOüsÃÿµF³ðgÁÿ³Ïþ0x»ãïìÓ¨þÏŸ ôoŽá?ÚÇ~'¶ƒ‘á_øÒ}O↼M£ø[úw„¼nýQ_Éü¯þ §ûc~ÌðRï~ßõïø'ì“àŸxö}_…ÿ¿à¤žý¹gÓ¿i=GãN“q­|GðÄoÙÛÁ’ü)øqðáÇŠe“á7ŒüAñ[VÒ<+ ø«Á~-ñÇÄë«kox_áWï÷ü{â÷í?ñûö%øñ£öÈðgÁÿ~Ðü?âx«Ãß|Wáÿü“º§Ž(ø³N¾ŸÄ7Û\éñ°Ñôàþ ;ÿ(²ÿ‚iÿÙ€~Æÿúοkïúøþ ;ÿ(²ÿ‚iÿÙ€~Æÿúοkïú(¢Š(¢Š(¢Š(¢Šùƒö"ðÏÆ_þÅÿ²'ƒ¿hËŸ^~Ð~ý˜>xgãµç‹xgãµç‹3ý¨?jÿÂ+ðÿ¾V™£hÚdVºŽþ'xïQµ¾¹ðÏÂß…¾¹¾Ó¿á*øâ¯ìëçÓô÷¾Ó´}GÓµßxÓ]ð¯Ãÿ ø³ÅšäÆø(ßü×ö=ø§ÿÀ¸ý­<3ûi~ÿ‚‰~×úìßâïØãÀ׿áý©ÿgMâïˆáÑþkøë?ÅŸ|2ý¥áFÙx›ÁÐþÔ~7ðÏÀ†¾ƒâ-÷†~ü<’ïÃÿ|;ñO »ß¾?|ý–¾ |Bý ÿh?ˆ^øWðoá_‡æñ7޼uâi§M;HÓ’{{;kk;{Í_]ñ»«ÞiÞðŸ„ü=§jÞ*ñ—еmžѵŸk:^—v|øïðÏöø5ð÷ãÿÁSÄ÷ŸŠÞ‡ÅŸ¼Câo|Bøa¨ø“·“ÜE¤øšÛÁßü-à¿ZøÄvÐ&·á=gQðåžâï ßèÞ/ðÅέám{EÖ/ÿ/Ûö«ÿ‚…øŸö=ÿ‚ôüvñ·ü?¯x[ãí?ñþ·ûþÄúÇÁoZÏñNð·Ç?øfiþ"~Éú–ŸñwM¹øÅñƒâ‚~-þÓñFðïÈ~*ñWÆ_:MÕ§Štï…?çø ¥þüýµ¾)ÿÁ>>6Cû!þÔúÃÿ€ŸðMØËþ ðãìüo¥xÅ¿>`ü]´ý”þüý¤>9|=ñ^©ðâWíñwÀ¿ ¼gãi>| øQ§ÿÂAñvïXø+û?k?´†0üVø´ýQ_„?±üã/ü#âgìû〟ðÌ û>xóįþ4üð߉ ý ÿj€³Å·ÃߊڿÀïÿ´oÆß…´ÿ?²÷ÆŒßmþ x2Ãö×>|cø·iáý_⯋t‹>"“áÇmöóÿ€ßðQßø)'íiûyþÔÿ³ìÇðKö`ñì¹ûþÛú_ÃOŒ·7Ä}3ãÏ ß|$Ðß–_?d_†? æ»ñ^¯ñößøM«ê6´oŽvž6ÓgcNð‡üEâ/„þÐþ/|+¼ñ0ô=EP̱³ðkıì‰âÙÏÂ~ ðìù¯~̵Ÿ>ñeÌ÷ž*ð_Á­SáO„ï¾xOÄ×—^&ñ¥Í׈<9à™ô=Y¹¸ñ‹'ŸQ³¹–ok²3j—E±³ðkıì‰âÙÏÂ~ ðìù¯~̵Ÿ>ñeÌ÷ž*ð_Á­SáO„ï¾xOÄ×—^&ñ¥Í׈<9à™ô=Y¹¸ñ‹'ŸQ³¹–ok²3j—ExÿüwþQeÿÓÿ³ýÿõ~×ßõðüwþQeÿÓÿ³ýÿõ~×ßôüÐþÔ?ðBoÛ ãׯ_ÚÅüÛħìùñ³ãü7û-~Û°×ÀÏø)Ãß¾*X<\é§üµý©|i¬x'ះô Ÿˆ¾=Ð|kðËá—€µ+ឣá¿x£Uñ¼žµñ%÷ÐÁ~ ?ü[àüÏãÏÆ Å?²×ˆ øÙû>~×~»ŸÁ¿>þ×~.ø…ñNøé០xS]Ñ­“ÃúO¾*xêÇFøu}â‹ÍoFø{«Ûiº_ÅOŠÚ‡¾5i>ðþ aûJþÝ¿µü›öqý‰h؃áoÅo؇ãÄ/„þý™ÿjÏÙkãŠ~&|JÓ¾øÃ>×¾0øƒâ×Ã/ÛSÁz¯ÁÿþÔp|BðÖþüñ׊¾ü=á—‰~#x6?üHð¿„õ¯ÑÿÙsö¹ñÜ¿°Ÿ¿iŸø)?„>þÀßtøI¼#ûHhü{£ø7ágÃÿø+ãˆ~Çâ-7Ç~7Õíü?eðÿãˆ4MÆ? çox·GÔü9ñÁºw…þ"üO²¾Ñüoâ€>ÿÁ=à¬6_þk¿´¿üWâÇ?‚^øàßø×àçÃOØ7ömý—üGñ7þïˆôßxsÁZ—ÆŸkž ño‡þøƒÅ¾Ðtÿ‹ÒtÇÿ…›ð²o|-¿½Ò´ÏÞê¶^ãø#§ícðËöŽý¤>5ÿÁ3ÿàªß?`O‡ÿµÇÄ >:ütøâ/Ù‡á·íàKßÚ;]ºÖ&ø‹ñoáÕÇÆÏY²ÿ…›öÝ7Pñw‡ôM*æê÷XÓ`µ¿Ãÿ|.ø{ðËõ~×öÝý‹ï´ï‚šÅíwû0^i´§ˆ5_ þΚ¥¯Çß…7wÇïh^*Óü ®xgà¥ô>,{oŠž Ѽm«i~Õto˯j:wе-?Ã×–Ðê÷–ör|¿cûc~Ï´¯ícû,iŸ²·ü¯ö@Õü?áßø^ð¶ÿc¯…¾:ý™þ8øïö¸þ×øl÷þÂñ>›ñ ÿâoÃøP—ºˆ¾)jð®´-_þM-COñwØxJÛàׇî]#Tñ„ôøTñ6cy>£ éÞ4ðö©mkmâ=[ÎÅßðTØwÀŸ·‡àž>,øýðÿÃÿ´×ˆ>Áã†ðλâï xGÑõx·áׄþüÔ5ŸkúWÛ¿hð±­¼Uð·à·†m|løÁÆ¿ þË_¶Çì5ð3þ Gð÷àoŠ–:iÿmj_k øgáýçâ/tZü2øeà-GJøg¨øoÀ^(Õ|o'„í|I}úÿÿÞýˆ|+ÿáý‰~þÅÞñLj>%é|?âkÏø›NÓ´mGÅ~*ñÏŽçÄÞ:µÖ|GmãOOãk]r]gÅ–þ!×`ñ¢×:Ä:ΩâßOôý|Áûxgã/‚ÿbÿÙÁß´eψ/?h? þ̼3ñÚóÅž,ƒÇ¾*ºøË¡|)ðž—ñ>çÄÞ:µÖ|GmãOOãk]r]gÅ–þ!×`ñ¢×:Ä:ΩâßOÏþÝ?´oü3ìãã?èßÿdƒÿu+Á_…®Ûx­ì´CúÿAðÛöNñV«ñ—Á?´Çíƒã_ƒÿ´/íðgÃþ9ðOìëâ†ß5¿~øWâ”_5Ïø;Æ¿ÿhÏMñƒâÅ·‡ô/ øçâN©ñjçN±øgá}ÀŸ < ðÒ?ü{Ö>9z×í»ûØéßõ‹ïÚïö`³Ò?f¿i^ý¢õK¯¿ môï€>*×|U¨xCðÏÆ»é¼X–ß üA¬øÛIÕ<¥hÞ:—AÔuiº‡‡¬í¦Õìî,ãñÿÚ¯þ ƒûþƾýœþ#ükøýðþÃÂ_µ—Ć>ø¬è^.ð–©£üAÑþ%k¶¸øÇ§øšm~ÃÂV¿³ÿÃ_ xÇJøñKãN¡â _xcÀ“ikªë-ñÃß øÀàÙ_þcÿ öqÿ‚nþÊ¿ÿj/øhoƒÿðOÏÚÿÆŸ¶µ¾™â/‚_ð¯5üS¶µø§¨|Ð>k¿~.iw¿ ~ü"ø›ñgÄ<]¥|N?´‡ˆþ.xî84ñ_€þ¬? mÚþC£þÕ^;ÿ‚¼x›ÅŸµ¯Ä øKþ ¥ðÿöKðë|8ðÂÿ à÷Žÿc½ÀÍðÇâ/ˆ¼Aâ ­sÅ¿ü¿øÚæøgYø£ÏàO|Eðž³>½âÛŸ‡¾þÏ|ký¡~~Í~ÓüuûF|qø?ðÁ:·ˆ-|'¥øÇã_Ä¿ü+ð®¥â«í;UÖ,|3§ø‡ÇZÖƒ¤^x‚óHеÍR×F·¼“Q¸Ó´mVúg¶Ó¯%„ñgí ð À^*ñ7|uñÇàÿ‚üm࿃úÏí ãx³â_‚ü9⯠üðæ£s£ø‡ã‰¼=¬kVz¾ƒðBÕìï4½gâ^©gkà½/Qµ¹±¾Ö ¹‚X”À?cÏÙ÷öŸø á_xã‡í/ðâÇ‚~üð¿Â‡~øûxöKð­ÜÓ¼1£Çã?ˆ^·ø»ñ«H> Ñ´Ùi¼)ðÏösø5àý;Å¿ìu…~-¶›áL_¹ÿø&_ì1ÿñý“¼=û=ë?áüM¹øñwâÇÆ¿ÚCSðOü!;ý¡~)ü\ø“âOê¿þ)Guâïˆ ñGļ?©øgÀz‡Œ¼Yã¿ø]Ñü¡5Î¥mem§èú_Ä·ÏüKàeÄÏÙOöUø ÿý˜?g]_㧈!ø·ñóö¨?¿cßAðöGðÃß üWÓ¤ð½·ÇŠ'Á6Ÿ?kËŸ‰¼!û;éÖ´†¶ZÌüý¦>ø/¿ ¿f¿ÚsöÂý˜<_ûx|0ø?û?èßµ/…ü?ñoន⫯Œ¾6Ó¾x ëÅ‹ðÉÁ$ðχþ0üdø¡à;áµßÃÁâ=Gâ·ÃøwÃ6þ/ðÞƒ8ÛôW€|tý¬eÙþoøiÚ[öýÿá8þÛÿ„+þ§ÆO‡_ ?á0ÿ„gû#þOøE¿á?ñ‡ÿá ÿ„þ ûoû'íÙ_ÛzGÛþÏý¥gçwÿ¾,|,øàMwâ—Æ¿‰þü2ð¿ögü$ß~)xËßü áßí½cOðîý»âïjZG‡ôíjúV…¦hjÿoÖ5=?Lµóoom ”È?b-gà׈ÿbÿÙÄ?³Ÿ„üAà/Ùó^ý˜>k?| âË™ï³sqãO>£gs,Þ&×dfÕ.Š?b-gà׈ÿbÿÙÄ?³Ÿ„üAà/Ùó^ý˜>k?| âË™ï³sqãO>£gs,Þ&×dfÕ.Šñÿø$ïü¢Ëþ §ÿfûÿë:ü9¯¿ëàø$ïü¢Ëþ §ÿfûÿë:ü9¯¿èøâý·|ÿÿ‚‰Ïû]ø«ã×ǃÿðN¯ø)‡ì?ñƒãï…ö’ðŸŠ|Ið×ö ðÄ/Ù·ã/‹>üø½á™´í/áEÏí·â SÁ³€¼U£|+ð!ø½ñ3à߇üamð‹áwŒþüO×ü1ã{þá?íŸûXüý¯ÿàŽßµïü_á—ü)߇þ<ý€>.~Ï~ ø×⯄¿ ´/~ÎßðPOŸ´÷OãϺ­Íωfˆ?b_„l¼rÚÌ^ðÇ‚uüWÐtÿ|!øáßÚƒOøcýžÑ@Æü3Lÿ‚}ÃðOöHñgÃ…¿ðƒþÊ_ÿàçïÙ{ãgí1ñ'ö„ÓüOoû8þÕ_þx‹Ç?¿ißëŸuÝcÂ^;ý2µð?ŽõŸ‡šßÂÛ7ÆÞ+ûöØðŸÀ/ÿÁÃ?ðA ῆ~øOâµç‡ÿઞ,ø›¥ø#Fð^ƒñ ë¾=øãÏx_ÄÞ;±Ðm­|I?‡üiñ“Ä´§Œt gÄ6â?‰š÷Ç/é×7ž)Õ<{y?ô½E_ðKïŠ_ðJÙÃÀŸð\ïÙ›öŠ×gÿ†¿ð½ÿà¯ÿ¶Çìª?f†úd‘þÑßgë ø ðà¿Â€¿³ÆŸuûMxÇáý†µñ+â7ƒ¾h|kà ­â£ááϲø£S´à?à~ø§û ÿÁB?à™Þý¾|kÿ ×Å¿¿à€2| øg®ütñˆüÿ Å?ÿÁLgø¥ð¿ö4ð¶§ñžËž­û@|ø%ñGàÿíoöwðüþ#øsö #Â>Ò.¼%ká}CPýÞÿ‚M~ÈŸ¶‡ìuâ¯Û’ÏöŒðŸìÁ‚kÏÛö˜ý¼4¿üý¡~+|HñW‚ i߇ü5¤xc\¾ºø¹oã­;QÔµ¥ipü%´¶¾¼Ö4¯Ùê(¢Šøþ ;ÿ(²ÿ‚iÿÙ€~Æÿúοkïúøþ ;ÿ(²ÿ‚iÿÙ€~Æÿúοkïú(¢Š(¢Š(¢Š(¢Šùƒö"ðÏÆ_þÅÿ²'ƒ¿hËŸ^~Ð~ý˜>xgãµç‹;þÐþͱý_ýˆ¼3ñ—Á±ì‰àïÚ2çÄŸ´„ÿf€^øíyâÏAãß]|eоøOKøŸsâoZë>#¶ñ§ˆ'ñµ®¹.³âËë°xQkbgTŽño§ú~€?‚?Ú·Ä?²?ücþ £âVø?­|qÿ‚ž~Ûþ1ÿ‚fÁ</xà_Åcâk>øâÏ…p|jð­®áˆ^ñGŒ|'âo†²øÏáÕοáÿ‰ž ÑüGðËY¹Ñ<{àßxsNñ…ÏŠþӺߊ?hsãì ¡ø5âM#Ã~ýt‹Ÿx“MðÙoþÁðÏà¯_ÆW:_†ü]osðÃØ4iÿdícÇðDØÃ>.ÿ†¥ø%ðkö€ñÇí¹¨þÛ×?~$ø»Gýµ?m?‚Z>©§xÆÿös_ø‹ã?ÄÚâ¿¶?íÍ£~ÙŸ´ÇŒm¼qû1|ð'ÃŽÞñgÄÏéŸÿiŸü þ‡¿eoÙ›Nýš|+ñ^xÇÄþ+|xøÁâïÚöƒø›¯ê>*–üeñÎáÝP>ð¿ŠüaãËŸ…ÿüàŸxáÀß„v>*ñ  ¾ |:ð'…uOxçÅ:ˆ|{⟧èù"ÿ‚,ü~ý‰hßÙö´ðwí£ñ Ãú‡üwþ WûOþÑÿ ÿà¡?²?ŠfñƇûPiZˆ|A­ü ðçìÉà†ßgö¶Ó¿f€?³µö™.—x¾!ñÏ…dßê?õsâ—Ãmá§Ä¸~üÿðÃöœø…ñ£þ ÿøƒûd~Úß³ì­ñGö8øÁâ_ÙöýŸ¾'þÏ> ~× ýž |rþ×h ˜?b-gà׈ÿbÿÙÄ?³Ÿ„üAà/Ùó^ý˜>k?| âË™ï³sqãO>£gs,Þ&×dfÕ.Š?b-gà׈ÿbÿÙÄ?³Ÿ„üAà/Ùó^ý˜>k?| âË™ï³sqãO>£gs,Þ&×dfÕ.Šñÿø$ïü¢Ëþ §ÿfûÿë:ü9¯¿ëàø$ïü¢Ëþ §ÿfûÿë:ü9¯¿è¢Š(¢Š(¢Š(¢Š(àø$ïü¢Ëþ §ÿfûÿë:ü9¯¿ëàø$ïü¢Ëþ §ÿfûÿë:ü9¯¿è¢Š(¢Š(¢Š(¢Š(æØ‹Ã?|ûþÈžý£.|AyûAøOö`ø៎מ,ñd=ñUׯ] áO„ô¿‰÷>&ñÕ®³â;ox‚Zë’ë>,·ñ»ˆõ¹Ö!ÖuHïú§ëæØ‹Ã?|ûþÈžý£.|AyûAøOö`ø៎מ,ñd=ñUׯ] áO„ô¿‰÷>&ñÕ®³â;ox‚Zë’ë>,·ñ»ˆõ¹Ö!ÖuHïú§è¢Š(¢Š(¢Š(¢Š(æØ‹Yø5â?Ø¿öDñìçá?x ö|׿f€ZÏÀŸø²æ{Ïx/àÖ©ð§Âwß <'âk˯xÒæëÄðLú¬ÜÜxÇœϨÙÜË7‰µÙµK¢Ø‹Yø5â?Ø¿öDñìçá?x ö|׿f€ZÏÀŸø²æ{Ïx/àÖ©ð§Âwß <'âk˯xÒæëÄðLú¬ÜÜxÇœϨÙÜË7‰µÙµK¢€?0?à™?ðOŸ€Þ4ÿ‚mÿÁ>|c¬xûöß³ÕüYû~Ê&Õ,ü'ÿ6ÿ‚’x ¶ºŽ»ðÀ:¥õ·†| à_ڿÞ ð_‡à¹º–-Â~ðö…á_iËm£ø{FÒô‹;;>ßÿ‡iþοôQ¿oÿü[ü7ÿ£"ø$ïü¢Ëþ §ÿfûÿë:ü9¯¿èàøvŸìëÿEöÿÿűÁSú2(ÿ‡iþοôQ¿oÿü[ü7ÿ£"¾ÿ¢€>ÿ‡iþοôQ¿oÿü[ü7ÿ£"øvŸìëÿEöÿÿűÁSú2+ïú(àøvŸìëÿEöÿÿűÁSú2(ÿ‡iþοôQ¿oÿü[ü7ÿ£"¾ÿ¢€>ÿ‡iþοôQ¿oÿü[ü7ÿ£"øvŸìëÿEöÿÿűÁSú2+ïú(óƒÂðJÙ/À^ðÏ| âÛÁ~ ð_‡ôo ø;ÁÞÿ‚¤ÿÁO<9á_ øWÚu¶áï øgÃÚ?íg¤h>д‹;=/FÑ´»;];KÓ­m¬lm ¶‚(— ÿ‡iþοôQ¿oÿü[ü7ÿ£"¾ÿ¢€>ÿ‡iþοôQ¿oÿü[ü7ÿ£"øvŸìëÿEöÿÿűÁSú2+ïú(àøvŸìëÿEöÿÿűÁSú2(ÿ‡iþοôQ¿oÿü[ü7ÿ£"¾ÿ¢€>ÿ‡iþοôQ¿oÿü[ü7ÿ£"øvŸìëÿEöÿÿűÁSú2+ïú(àøvŸìëÿEöÿÿűÁSú2(ÿ‡iþοôQ¿oÿü[ü7ÿ£"¾ÿ¢€>ÿ‡iþοôQ¿oÿü[ü7ÿ£"øvŸìëÿEöÿÿűÁSú2+ïú(àøvŸìëÿEöÿÿűÁSú2(ÿ‡iþοôQ¿oÿü[ü7ÿ£"¾ÿ¢€>ÿ‡iþοôQ¿oÿü[ü7ÿ£"øvŸìëÿEöÿÿűÁSú2+ïú(àøvŸìëÿEöÿÿűÁSú2(ÿ‡iþοôQ¿oÿü[ü7ÿ£"¾ÿ¢€>ÿ‡iþοôQ¿oÿü[ü7ÿ£"øvŸìëÿEöÿÿűÁSú2+ïú(àø$ïü¢Ëþ §ÿfûÿë:ü9¢ø$ïü¢Ëþ §ÿfûÿë:ü9¢€ø$ïü¢Ëþ §ÿfûÿë:ü9¯¿ëàø$ïü¢Ëþ §ÿfûÿë:ü9¯¿è¢Š(¢Š(¢Š(¯ˆ?à¤?¶÷…àœ?±/ÇÏÛGÆ>ñĽ#ேü?sgà/ ê:v¨ø¯Å^9ñÇ…þxF¹×µEžÛÞºñ·<<¾,ñ4Zgˆu øTk:æá?jú}…õ·ëñþ ûñãgÿaßxgá'ì ÿ(ð—ÆÚÀ_ ?i?Ù~†Ÿ~#kßìã¨xKâ/%øWÆßg7|9áoKñOI÷ÿÿÁGट¶íqñßösÿ‚9|ý˜/>þÆ ñOÂ?Ú‡ö¸ÿ‚€iŸ|=ðkÅ´þ…­ézf¹û<|Ó>Ýé>6Ö|Að®Ù5‹ø¢ÿC×|?¨O5´Äø+Âןü]ûC~ þÅ?²Wм/ÿ.ýœ¼Uÿký‹à³ßðM/…:߯üGý»tÛî×Qø1ûÞþÌ 4›_ ø›á…üãm;ã/¿hŒí¼qã“ðù¼{ñwÄ~ øAãê>*øwáÿ xZ}KâoìõúðÇãí?ÿ[ý¨?oO†¾3ÿ‚lþÛÿ¶oì¹ûfþÛÿ´oíáðƒö¡ýˆ~øã—Š£ñWÇ/üÕ|KðWdzÆâiu‡øq«Ëâß éß>!üFð¦£ñOQð£_øá,ÚÚî§áP¯ÿà’ðTŒ¿´ìûZüsÿ‚¤Ã0~È~6ýÿmÿвÅkÍÄÐ|7ø5à=Gᾉð[K¸²ñg޾"|bøái|A/Åoˆú¿‚­µí'â 𯈧>±ðäWw7qßjÿOþÇ_ðYÿø&íóã»Ï…¿²·í}ðÿâÄÛ³ÿgü:ñ‰ãÿƒþ;ño›£øŸÄW_ð®¼#ñ¯ÂüAñ?ûÃþ ñ»âïøWZŠ?á Ñì Ôü]ý‰e©éSß~@ÿÁE&ý²ટðN?…´¼ðKoˆÿ†;ÿ‚Ÿøö­Ó?`oÚ_T±¶øíûfþÆß³n—ãk¶ú·Â Cá¾»ÿÄ‹ßð›ø‹I¿ýü] øÞûXðw„õëÿ‡Wÿ?á4ø[áÿˆ¼ÿíñgã/ügö¸ÿ‚[ø;áü«ößý˜í¿coÛá·í±ñwö¯ÿ‚†þÏ~ÍZw> |Öô KÅ_~ë"Oˆº¿Ž|AñãW—ÃRÝx+Bñ<Þ*øgðãX×ü7¬x'JñWă@§ÿ?àáoø#§Á|dð/ÄßÛKÃþñ·ÀŒ¿>)ø:çá/í}â­âf—¨øûGÖ´½ÃÚOÂmCWñχü=«ü4ñF—âoø ÏÄÿ|5¨Éá;{Å:uÏÄ?‡ÑxŸíÿøoÏØÛþÛþ ÿ ðÿþãþÿü,¿ø^Ÿl¾ÿ„sþÏ·bfÿd}ƒþßøXð–ÿżÿ…Oÿ÷ü-?øZñjá ÿ…‘ÿµ~þÁß5‹/ø+×üGöÞñüŸöÿÓü%ñöø;ãÙ¿âwŽ?aßü?ñßÄß~Çÿ ôO‡>|oŠ6>ñlß?jé_.|~ø­ûC|)ý§¿fë_~&Þüuÿ…[ñ6o…úV½ðÿÂÿüW¬x_Äz~­âOŒÿÏÀ[hµ?ˆÑ÷ì¹ÿêÿ‚O~Ú?¼ û4~͵gü,¯¿¿á&ÿ„+Á_ð£?i/mÂàïøÿÄñQøÿàï…|%§gxKºö­ÿmzÃíŸ`û‡Úµ;«++ÿø,GÇÚá_ìãþÁ:§ˆ-¿oˆþñ‡‹>[ø?Àø»â '¿³Ö‰ÿ ¿ão‰µ†z¿…¾!Üêžñ?‚<+ìÏàíf‡+ÓŸö ý¦?fo†ú…Ï„oþ)h~/Ñ¿ž/ø$]‡üãþ ð?Mñ€à§ÿ´ì‹áøk¿ÞüTÿ‚Ø~Á¾øñÛö3Ñüsà økÀ û5þÙÞ5øÏñ?âoŒ¾ |D½Ð|ð“â¿€ü'ÃOxƒÁÚŸŽü{eû=Ë­â?ü%ý~Ð>üSý®ÿàªÿµWÅ›¯ÚOöÿý’/go‡ãöMý•ô߇_²/ˆüðOÆDþ%þÒÿ§ø³ûpþË_fˆ>*þÓ÷šÃOÜ|Ó~øÇÄ_¿e?øËJ»ø±ðËP³ñ•ˆêÿìSûPxWöÔý‘ÿg/Ú¿ÁÉáû=#ãçÁÿüG¼ð÷†|i§|CÓ¼ â­wDµ—Ç_ n|c¥Øé6Úψ>øÙÿ‚Äø£þ ¿âüVð‡þ:üiø‡ð§ã_ìáûjŸ³ÆÍ;à_Åÿüû6iöþ ø3ðã'€ |wø…kñsÄtÈ5ÿ¾ë'üû ~ÌOìû9ÙþÑ?ðNø öðýž¼AâÏ|@øƒûLÿÁ=üAð·ã.â­Sö‹øÁã_‚~&ðwÇ/¿ü1sñÄðF—àÛí;Yø_ã¯j? à¶ð~—­\øRý´ úàŸí¥ûXüwÿ‚Ä~Ì_ xÇþƒð÷þ ñ'ö6Ôü%ðÚëXÐhïür»øY®|>ø¥ã¯ü°øÛkÿ ×Lñ¶Ÿ¨h~Õ~]I㿆ºÖ³¢Xi—~6ðwˆÿ«ùáñ6±â ÿÁËßõOƒ_´ý×Á»¯ø%„ÿch¾3øgöHý¨ÿ‚NÿÊ,¿àšö`±¿þ³¯Ãš(ÿ‚NÿÊ,¿àšö`±¿þ³¯Ãš(ÿ‚NÿÊ,¿àšö`±¿þ³¯Ãšûþ¿à™?ðOŸ€Þ4ÿ‚mÿÁ>|c¬xûöß³ÕüYû~Ê&Õ,ü'ÿ6ÿ‚’x ¶ºŽ»ðÀ:¥õ·†| à_ڿÞ ð_‡à¹º–-Â~ðö…á_iËm£ø{FÒô‹;;>ßÿ‡iþοôQ¿oÿü[ü7ÿ£"€>ÿ¢¾ÿ‡iþοôQ¿oÿü[ü7ÿ£"øvŸìëÿEöÿÿűÁSú2(ïú+àøvŸìëÿEöÿÿűÁSú2(ÿ‡iþοôQ¿oÿü[ü7ÿ£"€>ÿ¢¾ÿ‡iþοôQ¿oÿü[ü7ÿ£"øvŸìëÿEöÿÿűÁSú2(ïú+àøvŸìëÿEöÿÿűÁSú2(ÿ‡iþοôQ¿oÿü[ü7ÿ£"€>ÿ¢¾ÿ‡iþοôQ¿oÿü[ü7ÿ£"øvŸìëÿEöÿÿűÁSú2(ïú+àøvŸìëÿEöÿÿűÁSú2(ÿ‡iþοôQ¿oÿü[ü7ÿ£"€>ÿ¢¾ÿ‡iþοôQ¿oÿü[ü7ÿ£"øvŸìëÿEöÿÿűÁSú2(ïú+àøvŸìëÿEöÿÿűÁSú2(ÿ‡iþοôQ¿oÿü[ü7ÿ£"€>ÿ¢¾ÿ‡iþοôQ¿oÿü[ü7ÿ£"øvŸìëÿEöÿÿűÁSú2(ïú+àøvŸìëÿEöÿÿűÁSú2(ÿ‡iþοôQ¿oÿü[ü7ÿ£"€>ÿ¢¾ÿ‡iþοôQ¿oÿü[ü7ÿ£"øvŸìëÿEöÿÿűÁSú2(ïú+àøvŸìëÿEöÿÿűÁSú2(ÿ‡iþοôQ¿oÿü[ü7ÿ£"€>ÿ¢¾ÿ‡iþοôQ¿oÿü[ü7ÿ£"øvŸìëÿEöÿÿűÁSú2(ïú+àøvŸìëÿEöÿÿűÁSú2(ÿ‡iþοôQ¿oÿü[ü7ÿ£"€ø$ïü¢Ëþ §ÿfûÿë:ü9¢ø$ïü¢Ëþ §ÿfûÿë:ü9¢€ø$ïü¢Ëþ §ÿfûÿë:ü9¯¿ëàø$ïü¢Ëþ §ÿfûÿë:ü9¯¿è¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(àø$ïü¢Ëþ §ÿfûÿë:ü9¢ø$ïü¢Ëþ §ÿfûÿë:ü9¢€ø$ïü¢Ëþ §ÿfûÿë:ü9¯¿ëàø$ïü¢Ëþ §ÿfûÿë:ü9¯¿è¢Š(¢Šþhà¬_ðPoÚÃðP¿ðN_ƒ?·ìÁÿ¦ðM×ìÁûeücý¸¿hÛ_„ž4Ô|K§]øûÇ?<û:ü*øsñäè? o:øSð«Á>?û^x/þ§ø;ûÞþÚ>ýº¼?âÚÿÄŠ|uÿÛýa?‚_µ„žø—ã‰t‹?|ø™cðóã_þ4øƒá ·ˆü¾/Òõ3PÓ´ß é?µÈ¾;üOÀÚ_Š~'€c´Wåí{ÿ‹ý˜?eŒ·¿³‡¾þÓÿ¶ía xAñÏŽ¿e¯ØCà?ˆ?h¯Œ¿ ~ø† ^ÏâOÄ»[ïx#Á~‚æëÁÖ—š±ãh~!EÄÿ…þ"‹Á3ø;ƺ_‰Ãþ oûx›ö6ý¥ÿl_‡Z'íñ7þËûÏö´ý–1ðçÅÿõoÿb¤ûG‹¯>&xÖÞü,Ól~|g¸ðïüOÿ ËÄÉhúýE~üÿƒcoŽ>;ý˜´Mözý¿üðö»øð÷à×ÁOÚëâ/ìµ}áßÙ;Ä_¾$h÷k¥|ƒâ¯‹õ{Ý_â‘ñ7Hñ7À¿?„ü-â_X|Ið‹µ{oß|ðþ¡ñV/§ÿmïø+ÇÁ¯ØŸãïÿe¨¿fßÛö¾ý üeðQøý}ð£ö!ýçøéâ¯|¶ñ¢ü;Òþ!xâÞóÅ^ ¶µðÿˆüm±á½:o]x’}'QÑYü.øûð_ödŸÇWÚ†Ÿcà_ˆ/Õüâêþ/Ðíín®5ÍÆžñ¦ªÃ6•¥\éVw—?Í ÷ü§öñgìmûÜÿÁ9?àªß·ýÿüãáÿì»ð¶Ãöxñ‡Ç/ÚÿãÀŸÚ+ö±ø÷}ðOáÆ} ã…ÿo/ |Qý‘Wþ |IñgYø}©øgÅŸ ~h^1½µñ5–¡­®ƒà£¡åòü6·ÂÏøo¯øwGöÄø]¿ðÈðÚßð”ÿexsþgü*Ïø]ð¢ÿ°?¶ÿá+ÿ„·þü%¿ñ1þÊÿ„þÏøG?Òÿá+þÓÿ‰E~0~ÜðUÿø'§íÿÑý¬|[ûd~Äß¶ÿÿeÏ…¶ûþÀŸµ§Á Að |BðÆ_ƒZ·‚þ&x³Åÿj{? h?ôŠÖ~ð%·Äã…¼"ø™«xgÁöÚv³¡ø¶ÂãWþ—¨¯ÀÛö}cHÿ‚ëÿÁí´ÿ|@·ðÿÄøy<þ.ðü,_Ëð³WÖ>þÇž#á׋¿áROâ)~X|@ðí—ÇŠZ7ü,]3Âv^;Õü9âøFußj~Ñ<;¦éœðrí·á_Úwþ¹ñ/Ä_ ?f/Û^ýŸ>+xƒöqñgÀÿÛËCÓ¾~Ï$‚óâ7ƒüEeâoŠ u¾ ý¨í~øCƒÄÞð³ûBþÊV¼]ñnÿàß|s«I¯|ø£~ýŽÑ^ñKᧇ>0x]øuâÍKâ‘áÿfhj ~,|Søã»ìcO×m°¾)|ñ—Ãÿ‰¾óotËh5?øF|]¤mèòêÖ~ßáý_UÓ/ˆ?ØGöÌÿ‚xÿÃ’¼/ûEþßßðTÿÚÿþþÿÇÿøX^øOÿyý­á²á#ÿ…áñOÀßáZ~Ë–¿µ_ü"_ð°?áÿ…s}àßøM¾¬ûöoÄO‹ÿñmÿá0ñ5w”Wå¥ûwü=ý’þ>ü$ÿ‚Xh ?mÿÛ ö–ðŸìAà?Ž^ñ›gðÏǾ*øµð÷Â~4“à=þ¿ñoã—įŠ|¡ü`Ô®|®üDñçŽþ/Ýü,øgâÝE£ðoª|iø…ðçá'‹zÙïþ Íû8þÒ_²w?jÏø#ö€ð¯ü+?Ú¿dþϾZü=ý£¼3ûXê?| ðÁ?³¦»á}sÄñü?Ðþ xûâÅ„þÓ5moânðÛÁw_´ññ‡Ç¿ ÿáø’<úE~@ø þ ñcñâïísð/Mÿ‚}~ßößa¯‡úGÿi A¥~ÆÞ3Ö_ý“¿à¥¿ðMØëþ _ÿöø¿û3þÏÿ´ÿÃØ¿öœý§ïÿdÿÙÏáá [ãGÆ_|Bø•ñ§ãù×5oxV‹Ÿ~"x¯Ãú‡ÄO‡ß5Ûmálj~1üLÔ ×ü3 xáÞ«¨Þ[øWJþ‡¨¯ÊÿÁ_þOñ—ö øûAüý§ÿcŠß²_ìÁ}ûh|Gð÷íðóÁzÆâ?ÙƒÃÐ<¾8ø¿ð«Æ?³?ÄÏÚ/Á?|?à ™4íÄ–^ñÞ ºñUõÿ„<)£ø›Å> ø‘£ø/ ðüOáf³ûdüý‰þ"~Îßµÿì÷ñö—øñ â_ì¹ãÿŽŸü9á¿…Ÿ´'‡>XÝx“Ä:o…¯<3ñÆ?>ü@µøa}ñ[øOûEü;ø)ñOáׇHÑþ.ø7áçÄøÁ^'ý?¢Š(¢Š(àø$ïü¢Ëþ §ÿfûÿë:ü9¢ø$ïü¢Ëþ §ÿfûÿë:ü9¢€ø$ïü¢Ëþ §ÿfûÿë:ü9¯¿ëàø$ïü¢Ëþ §ÿfûÿë:ü9¯¿è¢Š(¯å þ ðŸþ “ñsöìýª<3ñ/þíû?ÿÁ\?à›ÇÃÿ xGöJÒ¾(|\ýŒ¿gÿŠ5üøe§üyñÁ~%žçãׂá7ñÕ·Œ­¼Q>¥£XxîÛÄ~øgâÏ„ÿ| £ø: _ÿW´PñÅû$ÿÁþ>ø—þ £ÿ_ýœþ*xGÃÿ°Þ‘ÿñ…x7þ+/‹ž±á/ü?ñß„µëÿñ ð­ßˆî|Gwý•ªÿ¾Ó>iº—ǘ$ý‘àªÿ³WüÓö¥ø‰ûø_ý´?ೞð·ìåá/Øsá?Ç‚ÖZwì­ðká×ÁŠÿ³gƒ>*üaý£>!üPÒþø÷Ä ðïÅ‘ø“[ðß‹]bx«NðkÛØ|:Ó¾&xãEýœ­Ú(ùý£fÿø+¿ÿãÿ‚ü&ðügþ/¿°íû5|tý >Ãb~;ÿ„sþ祟„ÿ |-ÿ –µ®Ãá-cþËÂZ¥çÄ¿í¿j^3ÿ…ý™ÿoˆôêw±jQùÿüŸö ý»?à¡ß´u¾¯ã/ø#_ÃÿÚ_à(ýŸþ§À?Œ_ÿlƒ¿±×üözñˆn¼ ㈾=|Aøƒñ+ãoìåñWþßè¿4KO hŸþ3|,м1ñbüøÝ¦xëPø…uªg´PóCû;~ÍðU /þWûyþÎßµ†þ0|rý¨þ9xöÉømû.üñÿí#ð[ã×íà/ƒ_¼/Ã?„þ øïûRøVøEð§âgˆ<âŸx×⇊|sÿ •ôëð’ûKÑ|¡éZæ•á¿ÙïÂf¿ð×öÌø™ÿfðGüÓXÿ‚U|`¼ø­ª~ÄcMSâÇ‹ÿðOåý˜>|BÐ~øáe÷íªjÞý©þ9|d¾ðÿÀ_iRüzððwìõ¯|LÖÓ~,|ñƒþ µÿ'ñWüþ 3û3\þÈÿÚ?´×íÿÿø£ûIü1ðï|EàM;áо,~Îâlÿ>#øëàןðþøS^,ðw‡muŸøWãf£¬j~Ô|Mð7ÁZ.£â ß ÿg´PóÃÿsý€hÛûöåýƒ¼=à_xƒÃ?³ç€¿fø*§ÂOŽ_µ%Ωð“Tð¯ÃMGöïýŽ|Oû:|>Fø[}ñwÃ>!øƒÂ¾$Òìõiº_ƒ´¯ ɧx³ÂqXøõ¤5—Á~П³?ü/ãwü‡ÆÿðI/ÿÁ<¼?qñÛá‡þüð?Æï~Óÿ³ö›ð ã·À/Ù{âÿÁsðóâÃküIoŠÚ_Æx[ÀZuî¹ðûã7ƒþ ø.ëÁz¾'§Ä?|IÔ<û%ê?Øíçÿ¼eâ?‡þ×|]á?„ÿ>8øƒHþÌþÏø[ð·RøY¤xïÅoÖ4ý2ëû Pø×ñ/àÿÃ+ìK+ÛŸjð“|Eðé„7ö¿ˆ%Ò´-Oùáÿ‚Iøö´ýŠÿà•ÿ`Ú—þ /ûOü\Õü'áÿ>ø­¥øgâüÇ¿¼uá_Œ~-xêãÃ76??oŸ ÜøŸÃú§‚~!ÚøÅš7‰<m§_NÚÎ’öÚ¦ÑÞ^ÿKÔPóÃû:üÿ‚ŠëðXÿƒ?·§íMû*xÀ^ø—ÿ ñìÉñçàßÅß…þ9ð_ìáñ3Týµ¾#~ÑŸ ~øúoxÿÃ~!øƒÂ¿t¿‡?þ(|Lø?ðßÅŸ|Eñ÷Ä7º‡ìôÿ…-®xGâ ÿgÚÇàïücþ á~Ë¿4?Úöÿÿ‚ßkÿ´?ìsû7韴WÃo‡ÿ´w‹4‹ß¿eïŠ> ñOÂߌ³ÿňÞøQû@| ð—¿ŠôÿÞøÇžø'uð]øÏñ“Á?gxÓOñõû^ûH~ËŸkhÿ¾?xþ?ø_â‚>,x'PÓ&ü.øá»Õ¸ƒOñ—ÃßxkÄcGÔõßÍ©Káÿkšf øÿVý¢4}WǶ~/~Æ¿ðSÿ…_ðSOÚgáþûB|[Ð?mO‡Þð~±ûPxöMÑôo¾ðì¹ã½7à÷ì?û2êðª´Oü+Ó|L¾3øIû>ëøïö‹Ó£ñ_Žüà¯Iãü¿ðãþ £û~ü+ÿ‚:Á¾Þ~Ì ñgíûÁWþþן´Á üVýŠ´Ÿƒ^ý k_‰šï†|_⿌þø7âjžø‡àUÑ´‰ë;ê>&¶µÕ%Ò£ÒüCq£Kß³ßìû'~Ëþ;ñçÅ¿„Ÿ âöüPÝÄ/Ú â—Ž¾$þдwŒ4ìh0øG]ý¢~?xÇâoÆÛ߇öZgÃ/Ǧ|:“Ç¿ð‚iW^Óõ-7öšŸÚ/&úþ€?–/ø)¯üö‡ÿ‚’ÿÁ@l×—áLj>~Ïž=ÿ‚0éÿ²¿ÚÄ>i~ñíqàoÛ3á×í—ð¦É¼7á½sâ§ÆM'àúx“FÑü1ñ;^ñÁŸkw:w„þ#éÞ‰dÔ>xÃÄ>ÿû|ø­¤üLý¼Uâø ìAÿôø­àÿüOÖkßÚKºì^ ѵþø‡áÿ„¼'ûÍû6x»Çÿ-üAñ—Äž-Ó¼U㫟ŒÇÁÞøWðkFø™ð‰)ð¿ßú¢€ (¢€ (¢€>ÿ‚NÿÊ,¿àšö`±¿þ³¯Ãš(ÿ‚NÿÊ,¿àšö`±¿þ³¯Ãš(ÿ‚NÿÊ,¿àšö`±¿þ³¯Ãšûþ¾ÿ‚NÿÊ,¿àšö`±¿þ³¯Ãšûþ€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€>ÿ‚NÿÊ,¿àšö`±¿þ³¯Ãš(ÿ‚NÿÊ,¿àšö`±¿þ³¯Ãš(ÿ‚NÿÊ,¿àšö`±¿þ³¯Ãšûþ¿Ã›ÂðSoø)'€¼+áŸøþ ûoø/Á> ðþá?x;µǟxWÂ~ðæm£ø{Ã>ðöãë=#Aðþ…¤YÙéz6¥ÙÚéÚ^kmccm´D½ü=‹þ ›ÿI,ý¿ÿñ2?h¯þxÔþß4WøƒÃؿ੿ô’ÏÛÿÿ#öŠÿçGü=‹þ ›ÿI,ý¿ÿñ2?h¯þxÔþß4WøƒÃؿ੿ô’ÏÛÿÿ#öŠÿçGü=‹þ ›ÿI,ý¿ÿñ2?h¯þxÔþß4WøƒÃؿ੿ô’ÏÛÿÿ#öŠÿçGü=‹þ ›ÿI,ý¿ÿñ2?h¯þxÔþß4WøƒÃؿ੿ô’ÏÛÿÿ#öŠÿçGü=‹þ ›ÿI,ý¿ÿñ2?h¯þxÔþß4WøƒÃؿ੿ô’ÏÛÿÿ#öŠÿçGü=‹þ ›ÿI,ý¿ÿñ2?h¯þxÔþß4WøƒÃؿ੿ô’ÏÛÿÿ#öŠÿçGü=‹þ ›ÿI,ý¿ÿñ2?h¯þxÔþß4WøƒÃؿ੿ô’ÏÛÿÿ#öŠÿçGü=‹þ ›ÿI,ý¿ÿñ2?h¯þxÔþß4WøƒÃؿ੿ô’ÏÛÿÿ#öŠÿçGü=‹þ ›ÿI,ý¿ÿñ2?h¯þxÔþß4WøƒÃؿ੿ô’ÏÛÿÿ#öŠÿçGü=‹þ ›ÿI,ý¿ÿñ2?h¯þxÔþß4WøƒÃؿ੿ô’ÏÛÿÿ#öŠÿçGü=‹þ ›ÿI,ý¿ÿñ2?h¯þxÔþß4WøƒÃؿ੿ô’ÏÛÿÿ#öŠÿçGü=‹þ ›ÿI,ý¿ÿñ2?h¯þxÔþß4WøƒÃؿ੿ô’ÏÛÿÿ#öŠÿçGü=‹þ ›ÿI,ý¿ÿñ2?h¯þxÔþß4WøƒÃؿ੿ô’ÏÛÿÿ#öŠÿçGü=‹þ ›ÿI,ý¿ÿñ2?h¯þxÔþß4WøƒÃؿ੿ô’ÏÛÿÿ#öŠÿçGü=‹þ ›ÿI,ý¿ÿñ2?h¯þxÔþ¿_ðIßùE—üOþÌö7ÿÖuøsEðIßùE—üOþÌö7ÿÖuøsEÿÙscons-doc-2.3.0/doc/python10/node.jpg0000644000175000017500000016726112114661557020115 0ustar dktrkranzdktrkranzÿØÿàJFIFPPÿÛCÿÛCÿÀ’ö"ÿÄ ÿĵ}!1AQa"q2‘¡#B±ÁRÑð$3br‚ %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyzƒ„…†‡ˆ‰Š’“”•–—˜™š¢£¤¥¦§¨©ª²³´µ¶·¸¹ºÂÃÄÅÆÇÈÉÊÒÓÔÕÖרÙÚáâãäåæçèéêñòóôõö÷øùúÿÄ ÿĵw!1AQaq"2B‘¡±Á #3RðbrÑ $4á%ñ&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz‚ƒ„…†‡ˆ‰Š’“”•–—˜™š¢£¤¥¦§¨©ª²³´µ¶·¸¹ºÂÃÄÅÆÇÈÉÊÒÓÔÕÖרÙÚâãäåæçèéêòóôõö÷øùúÿÚ ?þþ(¢Š(¢Š(¢Š(¢Š(¢Š+æ€^øË¡|Vý·uO‰÷> ŸÁ>4ý§ü'âoÙÎ-gÅxN³ø5kûþÈž×-¼'£Ã¬ê’x ÃíûAøOãµåÏ„î¬|9=犮¼Mã¥Ñ§¶ñ¥¿ˆuß§ëæ€^øË¡|Vý·uO‰÷> ŸÁ>4ý§ü'âoÙÎ-gÅxN³ø5kûþÈž×-¼'£Ã¬ê’x ÃíûAøOãµåÏ„î¬|9=犮¼Mã¥Ñ§¶ñ¥¿ˆuЧ袊(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Šùƒö"ðÏÆ_þÅÿ²'ƒ¿hËŸ^~Ð~ý˜>xgãµç‹xgãµç‹ÿ„ºêö?ø«Å:}‡ëõðü6GíÿHý¿ÿðãÁ,¿úe”Ãd~Ñ_ô‰ßÛÿÿ7üËÿ¦Y_Ñ@Ãd~Ñ_ô‰ßÛÿÿ7üËÿ¦Y_OþÏ_ü+ûJ|øûFxOñ“àŸßþ|kðv—âË]:ÇÅZo…~*x/Eñׇ´ÿXèú®½¤Yø‚ÏH×líõ›]/\Ötë}F;˜lu]FÙ"¼›Ø+àø$ïü¢Ëþ §ÿfûÿë:ü9 ¿è¢Š(¢Š(¢Šùö…ý­®¾|SøIðSÂ?³_íûL|MøÁðÿã7Å-Ã?õÙÇDÿ„wÀŸ¼GðCÂ~7×|S¬þÒ´7ìùáøüÏ~дýLðî«â=bÿí½Ôú}•–—%̾ÿ ‘ûEÒ'oÿü8ßðK/þ™e¿å)¿±¿ý˜ü³ÿZ+þ ;_ÐÀðÙ´Wý"wöÿÿÃÿ²ÿé–Qÿ ‘ûEÒ'oÿü8ßðK/þ™e}ÿE|ÿ ‘ûEÒ'oÿü8ßðK/þ™eðÙ´Wý"wöÿÿÃÿ²ÿé–WßôPÀðÙ´Wý"wöÿÿÃÿ²ÿé–Qÿ ‘ûEÒ'oÿü8ßðK/þ™e}ÿE~@þÉß´Wí»ð£öXýš~ütÿ‚i~ßÿ¾6ü5ýŸþ øãÄ_ø]_ðMOÿÂ}ñOÁß¼9á߈>5ÿ„»ÆðQý;ž*ÿ„«Åºv¯®ÿÂGâ>ÃÄzçÛÿ´õ»+]Nêê÷ÿølÚ+þ‘;ûÿáÆÿ‚YôË+ïú(àølÚ+þ‘;ûÿáÆÿ‚YôË(ÿ†Èý¢¿é¿·ÿþoø%—ÿL²¾ÿ¢€>ÿ†Èý¢¿é¿·ÿþoø%—ÿL²ølÚ+þ‘;ûÿáÆÿ‚YôË+ïú(àølÚ+þ‘;ûÿáÆÿ‚YôË+ øMûgë~=øûáÏÙÏâWìûOþÌ~6ñ§ÁÿŠ¿üª|kÖdøWÅžø/ãO‚>ø§é÷ß³oíWûAjúgˆ4Í_ö‚øsqkkâ@ÓµM:ëUšÃUžçJšÍ¾ß¯€>#ÊScû0ø)gþ´Wüv€>ÿ¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¯€>#ÊScû0ø)gþ´Wüv¾ÿ¯€>#ÊScû0ø)gþ´Wüv€>ÿ¢Š(ð‡á'íUÿ$ý¡ÿࢿðS¿Ø×À¿¿b†> ýƒ¼Aû/Üø;Å^,ý‹þ<üQñWŽü+ûWü/ñ'ÅßhÞ&?ü#áN‘aâ†F“gá-gÄÚ]‚éßu¹ñM„þÛK…`çü?û}þ×þ5ý¯þ~ÁíGÿÀðů‰²‰l_ |n±øGû@|eð'íàOþÐü9ð"ÏöXøM¥þÓ 4_Â9û2ü,µøÏñÂãUý«¾2ëÖ±ãW?<1âï„¿ üñWOóÿÿÁ7l|_ÿœÿ‚™|iý°ÿàšÿÿiÙËö°ÿ†3ÿ†oý¡þ)xOö6øÍàO…ßð¢¿eK¿ |`þÝð_ʼn?¼ÿ ·Ž­¼1à}3þOƒž#ÿ„“XÐtýWÅÙ Òôÿ¯°~Øß³$-Ó¼ÿéñÇü7Ķ×ü‹á÷ìÁû?ÇðwTøIñËà×…>2üøûðcÅ^5ð.Ÿà(ï¿hÚßàÄ™-Ô¾,øwâߊìüú¿û.x‹öŽñGÀŸj_µÏï‡ÿ ¿hÛoøI¼;ñKÃ? ü]uão…šž±áø‡ÂzÄ_†šÎ§¿ˆ,¾ü`ðþ‰¤|[ðo„|_xïáÿ‡ÿ„öƒø³þïü!š÷öß‚uoí/øO¿á"Ò>ÁâïÂ3yÿ O¿øÆ¿µ?ÂïŠ~5ÿ†®ý«ÿ`üøgû?øâÇÅïø@> üEý–þ)üûWˆìÿáX|Kñ¯ü,Ûö—ðýŸüIá/‡Ÿ´ßü$~2ñ?ü+_ìŸü/³ÿ„{RñN™añþ?າGÅßÚïágì£|-ý?᪼?ð;þ û.þÒþ k|°ÿ„ÃöqøgáÏ‹–¿tì¯Ú Ç¿ þxÏþ /éþÿ„#]ñ4¾"ÿ„‹ìÚœqx}5­BÇåÿ‹¿²ïÅmcá'íƒðgþ ýÿ_ðÿì)¤~Ö?±í/ð«ö‰ñWŽü[ûü)ƒâf£á¿Ùãö‚ð¯ì—ð›àoÂ/Ù ö•øµðÛþ‹~;~Ð÷:þ7|dO…ÞÐ> x[_ðlj|Gã^çá`øtû?áßø(Oì âÿ|Eø¥á?Û‡ö@ñGÃ/ƒÿðˆÿÂÛø‹áßÚ[ྷàO…ßð°5‰ü;à/øX¾.Ó|ksáÿÿÂmâ k Â?ð“jgü$šÅ¼úföÛØ¤zŒ?´/…lþxËÇ_þ8þ̾6ñìÁñcö…ýŸ,|KðMäúvƒà¿h—ÐkV·>#ðô·Ÿ/øà=×ÇÛëà߯¿þÇÿØÿ ¼AÿÀý¥¾þÐþ&ø³eû8ø—þ+¯ÚãGìâÏþÇÿôo |Jø‰âˆŸð®ü?ðïö„Ô~Ë_¼Þýš~é°~Â_mn¯µß…>øeãí0ïÿ‡?¶ïÀ-¿²×hÏÚïö Òj?ßþx³Kðwß¾ ±ð¯Æ¯|TÓ¬t{~Ë^ñ׋"ø‰ñàÿĈ‘kš_Á fÞÏYÔ|a§Gcc Σ®%äCØ<ûXþËþ)ø×à_ÂßÚ[öø•ñ·á¯ü$ð±~øã'ïüSðü!Þ#³ð‹¿á5ø{áßê>-ð¯ü"¾-Ôtÿ xûwH°þÃñýž‰©ý—Sº‚Õÿ0?àœ¿iÙwdz„~(~Í¿‡õø$üÛö\ñïÅ/øãö{Õü ðSã·ìu£þÙÚŸÅ¿|EÓäøÙeñ7[ûEïÆøwÂ>&ø7ðëâǃµcWž{ßéÓ5-v×ÇÿdßÙÏö—[ÿ‚9|øÙû xá-Ïü›Ãÿ|'ñöÄÖ>5| øá_ˆþðWìñö&ð?†dûO]jÿ´߇ÿjY?xÛIðý׋5O|øûð§â§Š´ß Øê:V}âmCÃÞñf½«Ùø~ÏW×t=.ëY¸³N·Ôu*Æk”¹Ôlâ›Çÿáì_ðK/úIgìÿ‰‘û:ÿóÆ¯ˆ?gÙ‡ã}Âÿø7³Gñ÷ì‘â|Vý†xëã©þ!AwûCü+³ø£áŸøŸG½øÙôÿìû øïödøÙû$\ü;ÿ‚}ü@ý‘f_ |?ÿ‚Ÿ|(²øc©þÙz?íQ¬|Ñþ3ü]ý†¾#üñÏÅ%ñ÷Æ/ÿ¸ÿ…ñÿ Éñ?YÔ> ~Éž,ý¦< ðãÇ~2мMâ¿C­|Eø¡â }ÿñ7þ ƒûü*øíûþÎZïÇï‡ú¿ÄßÛËÊÔÿg»o x»ÂZׇ5¯jþñ‰¼ ñKWñbëöþ²øñƒÄ&™ð«à^¡§ßêúÇÆßŠ~.Ñ<7ð·Bñ]–‘ãÍgÁ¿×òÅðÛöýª¾~Ò¿ðD‹þýŠüAðûàßì©ûOÿÁp|Mñcá†|eû(èì¹ðköüøßñ þË–×>ðßÇ5øwªxÂü{¢k¾,ðŸÀüKŸÁð¶³ xsF×ÿÁ[5Oþ3üøaÿ×ý¿ü[ñöÿ„_þ••Ÿˆ?àœvžÒ¿á-ñÄÏiÿðŽxÿSÿ‚…Øü?øý•ñà¿Åï†~5ÿ…oâÿ¹ø§ð¿Çÿ þ Â3ñÂZï‡,@?_¨¯€?á²?h¯úDïíÿÿ‡þ eÿÓ,£þ#öŠÿ¤Nþßÿøq¿à–_ý2Êûþ¾ÿ‚NÿÊ,¿àšö`±¿þ³¯Ãš?á²?h¯úDïíÿÿ‡þ eÿÓ,¯@ÿ‚{|-ñßÀïØöø)ñKBÿ„_âoÁÿÙöiø[ñÃ?Úz>·ÿïŽþüðW„ü]¡løwPÕü?«ÿdxƒHÔ4ÿí= UÔô{ÿ³ý«LÔ/l¥‚æP¯è¢Š(¢Š(¢Šøâ7ü¥7ö7ÿ³ÿ‚–ëEÁ'kïúüàý¨ øßà/ÛCöYý£>þËß?iÏø/ö`ýµþ xßKø)âï٫Þ*🊾4|Vý‚üuðÿPÔ,i/Úö}Ò5?êzGìûñÞêëÂú濨éz®• þ•¶« âtðÙ´Wý"wöÿÿÃÿ²ÿé–PßôWç‹?o_Œ𯉼uã¯ø&í¿à¿ø/ÃúÏ‹&ñ7ˆuø)­ž‘ øBÒ,ï5MgYÕ/-tí/Nµ¹¾¾¹‚Ú e_?øÿ@ñ÷í)ðÏKø»ð‹þ cÿÕ¼«xƒÇþŽOOÿôøWâ­7Å_ þ!x§áGÄO ø›áßÅø(‚~"xCÄø‰àŸxWYѼUá]Q·Ôtk’-žÙíî&ý_¢¾ÿ†Èý¢¿é¿·ÿþoø%—ÿL²ølÚ+þ‘;ûÿáÆÿ‚YôË(ïú+àølÚ+þ‘;ûÿáÆÿ‚YôË(ÿ†Èý¢¿é¿·ÿþoø%—ÿL²€>ÿ¢¾ÿ†Èý¢¿é¿·ÿþoø%—ÿL²ølÚ+þ‘;ûÿáÆÿ‚YôË(ïú+àølÚ+þ‘;ûÿáÆÿ‚YôË(ÿ†Èý¢¿é¿·ÿþoø%—ÿL²€>ÿ¢¿8"xgÄßþ+ÿÁ@üñ ð‡ÄOø«ÂºÎ⯠èÚ¾££\‘löÏoq0êý|ñþR›ûÿÙ€ÁK?õ¢¿à“´Ãd~Ñ_ô‰ßÛÿÿ7üËÿ¦Y^à Oö€øãû}|ø×âïØÇö€ý™þ|ý?lo…ºÇ‰¾:xÛö@Öÿá"ñßÇ_°o‹<¡x[Fý›ÿjoÚÄy~ýŸ>"êÞ§â-+Ú=‡Ùô‹X5 ÛÝR;h€?O袊(¢Š(¢Š(¢Š(¢¾@ý¡kk¯ßþ|ðì×û@~Ó~0|?øÍñKGðÏÀ½CöqÑ?áð'À¯üðŸõßë?´‡í û>x~?3Ä´í?DÓ<;ªøX¿ûF¯u>Ÿee¥És(×ôWÀðÙ´Wý"wöÿÿÃÿ²ÿé–Qÿ ‘ûEÒ'oÿü8ßðK/þ™e}ÿE|ÿ ‘ûEÒ'oÿü8ßðK/þ™eðÙ´Wý"wöÿÿÃÿ²ÿé–PßôWÀðÙ´Wý"wöÿÿÃÿ²ÿé–Qÿ ‘ûEÒ'oÿü8ßðK/þ™e}ÿE|ÿ ‘ûEÒ'oÿü8ßðK/þ™eðÙ´Wý"wöÿÿÃÿ²ÿé–PßôWÀðÙ´Wý"wöÿÿÃÿ²ÿé–WAð›öÏÖü{ñ÷ß³ŸÄ¯ØÿöŸý˜ümãOƒÿ~5ø#Tø×¬þÈþ#ð¯‹<+ð_ÆŸ| ñOÓï¿fßÚ¯ö‚ÕôÏiš¿íðæâÖ×ŧjšuÖ«5†«=Ε5›}¿EPEPEPEPEPEPEPEP_„?°Ÿ„üU⌿?g­SÃ> ð·Šà’^ø¿àÚã±ÑµÄµ?ÇßKkàkÛ¯‰ö–Úï†þ&xö¸ø7ucÿRý»þ Þx³^ñ§jŒ_ðOgÇ_>#|Vøyñil¿w«àØßþN+þ Åÿgÿðãÿ]eÿÓ ¿è¢Š(¢Š(¢Š(¢Š(¢Š(¢Šùöõø[㿌_²wÅoü6пá8ñm¿ü ¾=¶ø=>§£èš?í'£ü$ø“àï‹,ý‘¼]­ø›PÓü%¢ü?ý°<%à­sö_ø‹«øÙ5¿i~ø·â+ïøCÇ·Ö|®øÿüÿÄÚwí¨ü}ý½¼mâ |ý¯|AàðÂú„üUðÅüwðkàŸ…n|áÏÚ³âÃ/èÚn¯ÆÚSW¸ñ þø›,∿°gÿØOÖÿ¢Š(¢Š(¢Š(¢Š(äÛ×áoŽþ1~Éß¼ðÛBÿ„ãŶÿð‚øöÛàôúž¢hÿ´žð“âOƒ¾,x³öFñv·âmCOð–‹ðÿöÀð—‚µÏÙâ.¯ãdÖü ¥øâ߈¯¼sáxJßYðn»ãÿðOÿiß´>£ñ÷ööðu·ˆ<5ðoö½ñ€À ê>ñWÃñßÁ¯‚~¹ð‡?jψ? ¼_£iº¼??iM^ãÄ3ø_âl³‰þ"þÁŸÿ`M?Xðÿ„(ý¢>.xŽçÆŸ'ðM®¯âß6“áý:ÖíŸ xsÃþ Ò>ÜüJñ êýùûj~ß?þÂeñà_ˆ?gÿŠ¿µø$íõû|üÔ?áñŠ¿·þ)þÈßðÎ:ïÃßÂÒðÆ O xÿöø³á/Ú?HŸþï xGÃÞ#þ_øH´OŒŽ™âû]3ßÿÃXþËð½¿á—?᥿gÿøi¯ú7Oø\Ÿ¿á{Èÿ þIü$ð°?äŸÿÅsÿ"÷ü‰ßñSÈý:€=þŠùƒáÇí»ûücð¯Äï|"ý®ÿfŠž ø'áõñgÆoü8øûð§ÇøGáWÓ¼A¬'‰¾'x‡Ã,Õ4x}´ ø«T]gÅWšNœÚw†¼A|.M¶¨Ëmßüý¡~~ÒžÔx«öTý‘ÿhßÚsÁß|?ñ[Wýž¾øãã]çÃÏ|AÔ~éÞ)ð¯Ã ëÆ~:Óí¼q¥ü<ø©s¤ø<£x†ïÂv²ø&ÿN×üUm£xwXÕ|)¤j×Þ-Ðÿ0?àÛÏÚïÅ_¶üƒö{Öü3ñÄ8ý¶´?ÚDðÿí­øÇ¿ “XøQÿûý¥ÿe߇ßðLÿÚWàWÀ¥—áÓü ×> xö øQû:Ûx—ľ/Ѿ!ø ü[ñ÷‰|Iám7Ç߿߶S~Ó~Ñß®üû"ü@øëû9ÙÿöˆðÄ/ˆ¿²ÇÅ?Ùïá×ía㉷_¼;ÁÔñwÇ³Gˆ> þÏþ!ðþžŸ/¾!~Ëÿ¡øíâ/Úà¿Àù¯®¾x3ámÄOè æ ØóöÓñßì;ð/à Ïì¡ñÀÿ>Á¸?·Gì®GâƬ~Ö?ü%ûü$ømð§Ãº—‚~>xËÎÿ„¯þ_ÅŸ ñÖ³o¢|6Ñü ã‡Pø›Æ:'Ä ø Àžáø'üŸâì;ÿAÿ‚`x›^ý <#¦~ÑØ¿eø(ïíñGà/‰¾.ü@Ñüká/€:ŸŒÿbÿÛnïö~ø³âß¾-øá-3ÂZÏì›ãoÚ.æOŒ¾øðNÓÅZ„ô½7à—Ã?¾ W´PóáÏØc⟋> ÿÁNo<ÿÇý ?eŸ_µïüÇß¼sã_ÚCþ ¥â?ÚÏÇ´WíOâ„^5ð‚~hþ×?hßÿ ¼Wðÿá•¶—áÏ~Öüð?â.…£ø¾?x[á'†¼ â‰7š7ô{ð·Æ^#øàM ÅÞ,øOñàwˆ5í?í…¿µ/…š¿Žü/ö cPÓ-·u‚Ÿþ0|2¸þÛ²²¶ñ™ÿÏÄ_yZ>¯§Á¬ÿdx‚-WBÓ=Š+àˆßò”ߨßþÌþ Yÿ­ÿ¯¿ëàˆßò”ߨßþÌþ Yÿ­ÿ ¿è¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š+àØßþN+þ Åÿgÿðãÿ]eÿÓ¯¿ëàØßþN+þ Åÿgÿðãÿ]eÿÓ ¿è¢Š(¢Š+äÚö¶ºøñOá'ÁOþÍ´í1ñ7ãÃÿŒß´ ü Ô?gþß| ñÁ øß]ñN³ûH~Ðß³ç‡ãóñg‹à”#ð¯‹<+â=:çGñ†|Máícþ Gy¤kÞ×t‹ËÍ/YѵK;­;TÓ®®lo­§¶žX›âø%—íÿíÿ‚}þËß±ÕÏüóöÿñGˆ>ü?ŸþÍv‹ðK-oG¾ø§ñÄúÿÅ/‹_ðˆêrÁ@|{sðþÛâo¼YgðëûgÃgˆáð%¿‡müMößE©j¿ÔPÀðÙ´Wý"wöÿÿÃÿ²ÿé–WAð›öÏÖü{ñ÷ß³ŸÄ¯ØÿöŸý˜ümãOƒÿ~5ø#Tø×¬þÈþ#ð¯‹<+ð_ÆŸ| ñOÓï¿fßÚ¯ö‚ÕôÏiš¿íðæâÖ×ŧjšuÖ«5†«=Ε5›}¿_|Fÿ”¦þÆÿö`ðRÏýh¯ø$í}ÿEPEPÈ´/ímuð;âŸÂO‚žýšÿhÚcâoƇÿ¾)hþø¨~Î:'ü#¾øâ?‚ñ¾»âgöý¡¿gÏÇæxƒöƒøu§èšg‡u_ëÿhÕî§Ó쬴¹.eóÿølÚ+þ‘;ûÿáÆÿ‚YôË(øÿ)MýÿìÀ?य़úÑ_ðIÚûþ€?8&ðö±ÿ#¼Ò5ïëºEåæ—¬èÚ¥Öªi×W67ÖÓÛO,MñüÆËöŠÿ‚vÿÁ>ÿeïØêçþ yûø£ÄþÏÿ æ»Å¿ø%–·£ß|Søâ}â—ůøDu9?à > ½¹ømñ7ÆÞ,³øuý³á3Äpøßöþ&ûoˆ"Ôµ ¿ßê(àølÚ+þ‘;ûÿáÆÿ‚YôË(ÿ†Èý¢¿é¿·ÿþoø%—ÿL²¾ÿ¢€>ÿ†Èý¢¿é¿·ÿþoø%—ÿL²ølÚ+þ‘;ûÿáÆÿ‚YôË+ïú(àølÚ+þ‘;ûÿáÆÿ‚YôË(ÿ†Èý¢¿é¿·ÿþoø%—ÿL²¾ÿ¢€> øMûgë~=øûáÏÙÏâWìûOþÌ~6ñ§ÁÿŠ¿üª|kÖdøWÅžø/ãO‚>ø§é÷ß³oíWûAjúgˆ4Í_ö‚øsqkkâ@ÓµM:ëUšÃUžçJšÍ¾ß¯€>#ÊScû0ø)gþ´Wüv¾ÿ Š( ¿8?j>7ø öÐý–hφ¿²÷ÆÚsÁ> ý˜?m‚ž7Òþ x»öjð犼'⯿`¿|?Ô5 ÚKöýŸtOÃúž‘û>üF·ººð¾¹¯ê:^£k¥C¥AmªÃxŸ£ôPÀðÙ´Wý"wöÿÿÃÿ²ÿé–Qÿ ‘ûEÒ'oÿü8ßðK/þ™e}ÿE|ÿ ‘ûEÒ'oÿü8ßðK/þ™eðÙ´Wý"wöÿÿÃÿ²ÿé–WßôP䌿୚§€~;|'ýš­ðÓþ†½àïøZÿð§~,X|3ÿ„³Søkã[-ßÿá²?h¯úDïíÿÿ‡þ eÿÓ,¯ˆ?j øªãö øíûËáŸx“ÆßðSÿ~ÏÞ>ø+ûDÚèÚŽ¥«~Í¿f/ø3Eý§ì¼?ã-ÛÅz¿À~ÄÚ¿„4ÚgöøÉâ°ð\?ðRïø)W–ðwÂù.|#ñ÷↥û½@Ãd~Ñ_ô‰ßÛÿÿ7üËÿ¦YGü6GíÿHý¿ÿðãÁ,¿úe•÷ýðü6GíÿHý¿ÿðãÁ,¿úe•çþÔÿhŽ?·×Á~.ýŒhÙŸá—ÁÿÙöÆø[¬x›ã§¿d oþ/üuøÑûø³Á…´oÙ¿ö¦ý üA'—áÿÙóâ.¡­ê~"Ò¼9£Ø}ŸHµƒP½½Õ#¶‹ôþŠ(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š+àØßþN+þ Åÿgÿðãÿ]eÿÓ¯¿ëàØßþN+þ Åÿgÿðãÿ]eÿÓ ¿è¢Š(¢Š+àˆßò”ߨßþÌþ Yÿ­ÿ¯¿ëàˆßò”ߨßþÌþ Yÿ­ÿ ¿ëòþ uûb~ÔÿðOØOâWí­û4j³ýïü(ÏøCá5øiñÓàÿÄ_ˆ_ð±?áfübøOð›Ãð‹xËÀ´Á¯øVÿðˆÿÂk¯kúßö·…>&ÿÂa³HÒ¬?á û楪~¿Wãüû:þÐÿµ÷ü¿ö€ý–¿e¯ƒ> øÙñ“ãgˆ> èÚ…£x¿á'ôï éÞøÓà?‹úÿ‹øûBüøÍñã·ÂOŒ—¾'ÏâÝ]Ÿö™Õ4ïü3ñ—ÀûÝ?Áf¯ã׎ßôãgícð³DÑÿiß…¿ ?ioÙHý®¾~Ïÿ¾,\ü:øÙñ“Ö?ÁÿìZx‹ÂÿißøgÄq|Møû?é—¾(ð»ñ ÆRiúGؼ â+MOMÔ¢›Wѧ¸üøÍÿäý >(xö¨×ÿaoØþkðÅÿðLÚ÷ötøµðûgö@ðßü<ƒãgÆ…Ÿ|1û3x þÇìËñ«âWìåðgþ¯Æzí÷ÿá­ügã¿ |]ñ7ü-/øQ²é-ð}/„~'EðÊÿáÿˆ¬¾x«Ãÿ~/xçâoÃ˯øBñ€øÛðßökðýŸÃ¯|VõóûG|}Òßþ 1y}âÿÙƒÇ:Gíƒâ ¾~ÑzÏÁÍ#Æž5ø{â¯j°gÇ_Ú£Cøû$üW›âVmcð~O|Õ,t%ñ×~%ê?þøÿÃÚ¥Ÿˆ<«øzâçÄüÿÄø(ŸÂÉm?Øûö]øûP~ÈüAñö€øëð/ö ø ¡xÓß?hï‡ÿð®ÿf/ÚãŠtûOüe³½øSÿ óâoÀ{†¿´Oˆ_üeý§uñLÒáÕþøƒAH|Qäg_ÚÀÿ¿àßÏ„ÿÁŸE«þÈ>ð³ûRø’ÛÅÿ .|+ðOQøqÿ¯ý ?e«¯ ë.Ÿ‡‰>|4ý©õˆþ3ü|ðíúGˆþü'ñÞ‘ãx¶Oø¾ÇÄ5çŽ5pÝí7öÝý‹õŸ|[ð.û]þÌ·¾øǾ,øíàí7ãï›ï|ð¯Â½F=≾-øz×Å’êÿþËÿ5¿.¶ñÁÿÚ?ÆÚݿƃ?´gÁØëà×ì;&¯¡¦©ãÏxB;¯ëwáoŒ¼GñÀš‹¼YðŸâÀïjÿÚÚ ~)j_ 5ø_ìÆ¡¦Zÿnê>%ü`øeqý·eemâ-3þŸˆ¾"ò´}_OƒYþÈñZ®…¦z|ñþR›ûÿÙ€ÁK?õ¢¿à“µ÷ý|ñþR›ûÿÙ€ÁK?õ¢¿à“´÷ýQ@Q@|Fÿ”¦þÆÿö`ðRÏýh¯ø$í}ÿ_|Fÿ”¦þÆÿö`ðRÏýh¯ø$í}ÿ@˜ðU?Û§Xý„þ~ÎÚæ…ã?Ùÿá5ïíû_ü#ý—gøïûQÅã½OàOÀ]ÆÞø‡ãÿ|JñÏ…¾_xoÄ/Ýáÿ…ÚŸ¼3¥ê~xGñ|=ãoŠ_< ðËÂ~.Õ+Ø> x÷ö¸øi¨þÒiû{j_³þxÁÿ¾~×_ ãÖþü=ñÃÛ¯ øžóã'‡þ0üø³ñoã¯ðoÄuOâ}oâ<ÿ¼GðÏÇ? þ$ø7QÓÛÂÞ$ð_ÄmO?nÿ~Ðþø{ðÓKøûxöæðOÄ?Œ k€úωþxcQÔ?dü3ø¡¤üIñ„ÇÇOx áO‹:ý°¾ø3áÿí™ñÀ:ç‡>xWÆßüqàM;ÇzçŒ|OãŸ|(ð×3k`«ÿÿmߨ¿àç…~øëâïíwû0|+ðOÆÏ·‹> øÇâ?Çß…>ð¯ÅÏ ¦áýaüMðÇÄ>'ñf—¤x÷Ãë¤x³Âº£k>¼Õ´åÓ¼Káûãr-µ:[žƒÇÿµì±ð£ág‚¾:|Rý¥¿gÿ†¿¾%Â9ÿ ëãþ2|:ðwÂÏÂcáËßxGþ¯ˆ^"ñá/ÂUá-;PñO‡?°µ{ÿíÏX^ëzgÚ´ËYî“òƒöý˜~7üø¡ûk~É ø/ð§AðÿüÂzÏ€´möj³Ó¿e? þ×ðQ_„µoì¡áŸx[áÇÆ=gÃoáý[àß»[hß³œ¿´ï‡þ*¼ðχµËmÂÉ©xEüà²ÿ‚OüvÓ¼OðGâ¯d_ÚÇŸ².½ðÿþ _û1øûþ sðö·ðwìŸã¿Ù»ágíQÿý¡jï…ÿ4X~ þ×ß ¿e‹¿þ%|Õ>þÏ¿´ìá¦üð÷‡4MHøU­GÆh~Yx_áèô½©~Û¿±~⯄žÖ?k¿ÙƒIñ·Çïø ÅŸ|©|}øScâ¯^ø©¨É£ü0ñ7ÂO]x²-_â?‡þ#êñK¥ø Yðuž³§xÃQŽK\ê7(ѧëñþ áðOã·ìÕñ³Ášn·û |@øðâ7ìû üÓôßþ×ý­ü û#øö}ø»ÿø—uðGâ/ż%ûFøïûÁŸ´ÁïøEþü&ø«ð³ÁzÆ¡?ÃOø´|øq¥xÉÿo¨àˆßò”ߨßþÌþ Yÿ­ÿ¯¿ëàˆßò”ߨßþÌþ Yÿ­ÿ¯¿è¢Š(¢Š(¢Š(¢Š(àˆßò”ߨßþÌþ Yÿ­ÿ¯¿ëàˆßò”ߨßþÌþ Yÿ­ÿ¯¿è¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¯€?cù8¯ø+ýŸÿÃýu—üN¾ÿ¯€?cù8¯ø+ýŸÿÃýu—üN€>ÿ¢Š(¢Š(¯€>#ÊScû0ø)gþ´Wüv¾ÿ¯ÎÚ‚þý´?eŸÚ3á¯ì½ñƒöœðO‚ÿfÛ_à§ô¿‚ž.ýš¼9⯠ø«ãGÅoØ/Ç_õ BÇö’ý gÝ#Sðþ§¤~Ï¿­î®¼/®kúŽ—¨ÚéPßéP[j°Þ èýðü6GíÿHý¿ÿðãÁ,¿úe”Ãd~Ñ_ô‰ßÛÿÿ7üËÿ¦Y@Ñ_Ãd~Ñ_ô‰ßÛÿÿ7üËÿ¦YGü6GíÿHý¿ÿðãÁ,¿úe”÷ýðü6GíÿHý¿ÿðãÁ,¿úe”Ãd~Ñ_ô‰ßÛÿÿ7üËÿ¦Y@Ñ_Ãd~Ñ_ô‰ßÛÿÿ7üËÿ¦Y^?ðþ âoÚ—à×ÃßÚö|ÿ‚oþÛÿ> üTðü>&ð/޼3ñ3þ rúv¯§<÷7–×6wßðRK=_Bñ…«Ùê>ñg„üC§i>*ðoŠ´gž+Ñ´ohÚ¦—hú¿_|Fÿ”¦þÆÿö`ðRÏýh¯ø$íðÙ´Wý"wöÿÿÃÿ²ÿé–WŸøSý >8þß_~5ø»ö1ý ?f†_ÿdÛán±âoŽž6ý5¿øH¼wñ×ãGìâÏh^Ñ¿fÿÚ›öƒñž_‡ÿgψº†·©ø‹Jðæaö}"Ö Bö÷TŽÚ Óú(¢€ (¢€>øÿ)MýÿìÀ?य़úÑ_ðIÚûþ¿8?j>7ø öÐý–hφ¿²÷ÆÚsÁ> ý˜?m‚ž7Òþ x»öjð犼'⯿`¿|?Ô5 ÚKöýŸtOÃúž‘û>üF·ººð¾¹¯ê:^£k¥C¥AmªÃxü6GíÿHý¿ÿðãÁ,¿úe”÷ýðü6GíÿHý¿ÿðãÁ,¿úe•ãÿà¨Þ&ý©~ |=ý ÿgÏø&ÿí¿ñSàßÅOÃâoøëÃ??à—/§júsÏqcymsg}ÿ$³Õô/hZ½ž£áßxOÄ:v“â¯ø«IÖ|)â½Fñ&ªiv€«ôWÀðÙ´Wý"wöÿÿÃÿ²ÿé–Qÿ ‘ûEÒ'oÿü8ßðK/þ™e}ÿE|ÿ ‘ûEÒ'oÿü8ßðK/þ™eðÙ´Wý"wöÿÿÃÿ²ÿé–PßôWÀðÙ´Wý"wöÿÿÃÿ²ÿé–Qÿ ‘ûEÒ'oÿü8ßðK/þ™e¿å)¿±¿ý˜ü³ÿZ+þ ;_׿€5?ÚãíõðGã_‹¿cÚögøeðö@ý±¾ë&øéãoÙ[ÿ„‹Ç~4~Á¾,ðF…ámöoý©¿h?Iåøö|ø‹¨kzŸˆ´¯högÒ-`Ô/ouHí¢ý? Š( Š+äÚö¶ºøñOá'ÁOþÍ´í1ñ7ãÃÿŒß´ ü Ô?gþß| ñÁ øß]ñN³ûH~Ðß³ç‡ãó~Ù¿?g9þ"ügñ¯Ã¨>ßÂ9ÿ Ïÿ 3ñKÄÞ#ð¾™ãøsÄß /lþ!ë¿>cø‹Ãºoôû_ÏÆï|`øËÿ.ý…¿oû¯ø$¿í¿¢jÿ±×ÁÿÚÃÂw¦ñ þ A¦üBñÿо3é?ü ð§Ã>,¾“öùÕ.|Uðáç‚|OûRø‚ÛFøcQð—ÄÏøgVðݶ«¤x—â%Çèÿü6GíÿHý¿ÿðãÁ,¿úe”÷ýðü6GíÿHý¿ÿðãÁ,¿úe”Ãd~Ñ_ô‰ßÛÿÿ7üËÿ¦Y@Ñ_Ãd~Ñ_ô‰ßÛÿÿ7üËÿ¦Y]ÂoÛ?[ñïÇß~οcÿÚöcñ·>üUø×àSã^³û#øÂ¾,ð¯Á|ð/Ä ?O¾ý›j¿Ú WÓøÛ¨þÏþ>ðÁÝ ö\ðçˆüUñÚÃâŸÅ}-þü>ñ¯´ÏÞØx¶×þ¯‹|c¤|Eñ7ˆü-;øÁÞð¯ˆ|]¢Yj:ž‡k§Ý~PÁ¦z—ÇÔÿ‚?x À¿þxƒáN‘ðÓãÄ­7àlj¼ãOj?>|O²ð—íáÏ‹vÓx¾Amãø—Æß> é~ñì<¬øWÃÚ5Œz†¯¤ëzæ­ý/WÀðIßùE—üOþÌö7ÿÖuøs@ÑEQEQEQEðüSÇÿþÿÁ8ÿlÿ| ðWíãïºìÿãï|пeÏxÅ_¬>)üWÒßáoÃïøLð}퇋máZø·Æ:GÄ_xÂÓ¿ˆüàï ø‡ÅÚ%–£©èvº}×åüg©|}Oø#÷€¼ ñÏá'ˆ>é >0|JÓ~\x›À^4ð6£ñSàÄû/ ~Ñžø·m7‹äÞ8ðÿ‰|mñ³â—á?ø*ÎÃÁzÏ…|=£XØÇ¨júN·®jßÒõ|ÿÿ”YÁ4ÿìÀ?cýg_‡4÷ýQ@Q@Q@Q@Q@|ñþR›ûÿÙ€ÁK?õ¢¿à“µ÷ý|ñþR›ûÿÙ€ÁK?õ¢¿à“´÷ýÏø²ÛÅWžñ5ŸuŸøsÆ×^Öm¼âxgQñ§…t/O§\ÅáígÄÞÑüYà-_ÅžÒõv³¾Ö|3¥øëÁzŽ»§As¥Øø³Ã—7QkËÄø+ÇüKáŸücâ÷í±¬øçöÕ¾ ~Ä·ÿÄÿØëþ ð÷Á²çíEâ áîá_þÚ^~ÍóxÇö´ø]ÿ §â“ÿ †~3GoâÏ|.ð'Ä/|_Ñüsâƒÿ> xªßâ`õ{E~px¯öŽøû£ÁBþ"|2¾ñìÁðÏöýšÿb†µíã‰ÚG-þ2¿Š¾.øûö³ð–†žø‡7į |ømðÁýšuOüGñ§Ž¼/â-GIƒMmÎÆk\xÏá_°ÃÂ`_øUŸð½?á¸døR_ð°?áSÿÂâÿ†–ø/ÿ ³þŸü#Ÿð˜´ÿ……ÿ ¯ü"_ð°?áÿЧþßíøHÿáÿ‰ßöoögúU}E|ãÿø(Oì ð£þ¯øZ_·ìð×þWÃÿ|Xøuÿ ÿí-ð_ÁßðŸ|,ñÛáø—à¯øH¼k§ÂUðÿÅ_ÙÚ‡ü#ž2оßáÍsì¿Ùš•×ÙgÙÐ|ký·bÿÙ¯ÅZhÏÚïö`øãm[Ãö¾,Òüñ¯ã~*Ô¼+}¨êº=‰´ÿxëÅš¯yáûÍ_B×4»]fÞÎM:ãQѵ[n^çN¼Š§è¯øéûXþ˲ÿü"ßðÒÿ´·ìÿû;ÿÂqý·ÿWü/OŒŸ¾ÂaÿÏöGü$Ÿð‹Ââ?ÿÂAÿÿü$ößöOÚÿ²¿¶ô·ýŸûJÏÎ?á¬eø^ßðËŸðÒß³ÿü4×ý§ü.O‡_ð½¿äNÿ…‰ÿ$‹þ?øXòOÿâ¹ÿ‘{þDïø©¿ä þ@ÿ_|Fÿ”¦þÆÿö`ðRÏýh¯ø$í}ÿ_|Fÿ”¦þÆÿö`ðRÏýh¯ø$í}ÿEPEPEPEPEPEPEPEP_~Æÿòq_ðV/û?ÿ‡úë/ø&}ÿ_~Æÿòq_ðV/û?ÿ‡úë/ø&}ÿEPEPEPEPEPEP_Á'å_ðM?û0ØßÿY×áÍ}?ñûã_…gOƒ_¾5xÇOñ½¤x ÃójV~ðe®ª|Bø“â«Éíôü$øWáíSUÐí¼cñƒâ÷µ/|1øCà(µ[=GÇÿ!Ó´)tï xª?‰?|O©Çñ§à?Æ øTô~Š( Š( Š( Š( ¾ÿ‚NÿÊ,¿àšö`±¿þ³¯Ãšúã÷Æ¿ þΟ¾!|jñŽŸâ {Hð‡æÔ¬üàË];Tø…ñ'ÅW“ÛèþøIð¯ÃÚ¦«¡ÛxÇãÅïj^øcð‡ÀQj¶zþ&x³Âž Ñäm_]±ŠO?à–·>*øyû#üýŽ>3hÞð7í-ûüøYû<üYð‡âmGÅ:v©á_‡z%ïÃ/ƒ´µí_Â~¹ñOÁÿÚ[Á? Ÿâ‚|C§hRéÞñT~øŸSãOÀŒ>ð¨èýQ@Q@Q@Q@Q@|ñþR›ûÿÙ€ÁK?õ¢¿à“µ÷ý|ñþR›ûÿÙ€ÁK?õ¢¿à“´÷ý<>>ÿ‚6ê?¿à±´·í)ñRñ©ûþÑ¿³€uŸŒß .|eá]2‹µÂþÏß´·üÕ<'£Â1àû‹žø?àØsã/Ä­GÄ×6<­ê_~6øOÄñ7Œ¬<'¨øsá‡ô=E8?²Wücö±øÿ$ý³d?ŠMÿ +öÚøïû?þÖ eÑtÏ|6Àšþ±àïÙ®?Ø7öBоø†ËEøu¢hßüOû2þϳmüV–ÃÇqëþ»¯|`Ô<9â[­{@ðçØ|iû]øóâŸÀÛÿÁ;?h > jß³ÿí7àÿŠV_ ~2þÅý¾¼ãxÁÞ Óþë^?ÔÿjxKáìÿãÏ hV´_¿c¯Û\øí®ühø]û?Ý|Ó>k¾ø½úýE(_²ÿ†þ4|Ö!ø[ã?ø%ÄÚ›öøÿÿ‚n~Ⱦ6øC©øÏö‹Gðïü"Þ;ÿ‚Š|7ñϯŠ_þ$þÑÓø}?gÿÚ«Ä ü)®ê ð¯Oøáöÿx_BÔþ/|,Ójžðœ~=ñ׈¼3£_|.ŸNÐ~!xÓÅŸt»k_‰>#ÖÿÁ>~ x{À–?¾!xKÇ_´/ìåáoŠ_³ÆßŠ×ºçíðû㿇|iãO¿›â£ì¹‰3þÏ~ÿ…áÏ ÿÂyâZx·Å¶z ÷éýWÀ¿å)¿±¿ý˜ü³ÿZ+þ ;_×À¿å)¿±¿ý˜ü³ÿZ+þ ;@ÑEQEQEQEQEQEQEQEWÀ±¿üœWü‹þÏÿáÇþºËþ §_×À±¿üœWü‹þÏÿáÇþºËþ §@ÑEQEQEQEQEòí û[]|ø§ð“à§„f¿Úö˜ø›ñƒáÿÆoŠZ?†~곎‰ÿï>xà‡„üo®ø§Yý¤?hoÙóÃñùž ý þiú&™áÝWÄzÅÿÚ5{©ôû+-.K™|ÿþ#öŠÿ¤Nþßÿøq¿à–_ý2Ê>#ÊScû0ø)gþ´Wüv¾ÿ ÈÚâ¯íûGü ñÏÂ{/ø&7íÿàê?ðŒøÃáOÄ¿øJÿà–^*ÿ…MñÛáGŒ|=ñgöøÅÿl¿ðT= Lñçü*¾ðÄ¿øW¾#Ô“ÁÞ?ÿ„WþßÚê>×5­6ïÏÿdߌ߶áo|gý¤?à’Ÿµü´×í ñã[ñÝŸÃŽßðMO‰?¾ü,ðÚüû=ü øYãÿ~Ý_ ~ _ü?ðwÃûY>&x³HÖ-Ôáñ‡ÂŸ‰ð•ÿÁ,¼Uÿ ›ã·Âø{âÏìÿñ‹þÙà¨z™ãÏøT?|à‰ð¯|G©'ƒ¼ÿ¯ü!¾5µÔ|%®kZmߟþÉ¿¿mßÂÞøÏûHÁ%?køÿi¯ÚâÆ·ã»?†?¿àšŸ~|5øYàµø+ö{øð³Çþ<ýº¾ü@¿øàï‡ö²|Lñf‘¬x_Cðå×íOñ¯ö¡ø¡à x;Eø®Þ°ý¾¢€>ÿ†Èý¢¿é¿·ÿþoø%—ÿL²º„ß¶~·ãß¾ýœþ%~Çÿ´ÿìÇão|ø«ñ¯Á§Æ½göGñ…|Yá_‚þ4ø#à_ˆ~Ÿ}û6þÕ´¯¦xƒLÕÿh/‡7¶¾(Ðô ;TÓ®µY¬5Yît©¬Ûíúøâ7ü¥7ö7ÿ³ÿ‚–ëEÁ'hïú(¢€ (¢€ (¢€ (¢€ üàý¨ øßà/ÛCöYý£>þËß?iÏø/ö`ýµþ xßKø)âï٫Þ*🊾4|Vý‚üuðÿPÔ,i/Úö}Ò5?êzGìûñÞêëÂú濨éz®• þ•¶« â~Ñ@Ãd~Ñ_ô‰ßÛÿÿ7üËÿ¦YGü6GíÿHý¿ÿðãÁ,¿úe•÷ýðü6GíÿHý¿ÿðãÁ,¿úe”Ãd~Ñ_ô‰ßÛÿÿ7üËÿ¦Y_Ñ@>2ÿ‚¶jžøíðŸöhñGü_öÿµøÛñ³ûKþi¾ ÿ‚qø‹Ø<ñ/Çöð™xßðP½[Â_ ¿á/ð—ÁoŒú·ÃOøZ÷ƒ¿ákÿÂø±aðÏþÍOá¯l´?ÿ†Èý¢¿é¿·ÿþoø%—ÿL²¾ ý©ü'â«Úƒã·ì{/†|AâOÁOüAû?xûà¯ík£j:–­û5üý˜¼?àÍöŸ²ðÿŒ´koêÿ8þß_~5ø»ö1ý ?f†_ÿdÛán±âoŽž6ý5¿øH¼wñ×ãGìâÏh^Ñ¿fÿÚ›öƒñž_‡ÿgψº†·©ø‹Jðæaö}"Ö Bö÷TŽÚ/Óú(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¯€?cù8¯ø+ýŸÿÃýu—üN¾ÿ¯€?cù8¯ø+ýŸÿÃýu—üN€>ÿ¢Š(¢ŠüAÿ‚{~Ä¿ >8þÀ¿°÷Æ¿Š_ÿoÿ|MøÁû ~Í?¾"ø›þŸÿ5Ñ?á"ñßÄ‚þ ñg‹µßìoþ×G‡ôíjú†¡ý™¡iZfaö²éš}•”P[Dû}E|ÿÓýè£~ßÿø¶/ø*oÿFEðí?Ù×þŠ7íÿÿ‹bÿ‚¦ÿôdPßôWÀðí?Ù×þŠ7íÿÿ‹bÿ‚¦ÿôdQÿÓýè£~ßÿø¶/ø*oÿFE}ÿ^ãÿÚÇöXøQñOÁ_þ)~Òß³ÿÃ_¿¿áÿ…uðwÇÿ>x;⟿á1ñïƒü#ÿWÃßxNñoŠ¿á*ñn¨x[ߨZEÿöçˆì/tM3íZ¬ö©óÿü;Oöuÿ¢ûÿâØ¿à©¿ýùûkÁ¬ÿ²wíÑûSè¾)~Ó_µþ‘ðËÿ³þ•ð¢Ïá¯ü.ï‰?~)¿Žôˆ¾+ñ}¿Ž᣿l_þÓ~ Ó¾·‡ü]¨hð¤´/i=§ˆâÿ„ïLñU…î¯â}#_ý~øÿ)MýÿìÀ?य़úÑ_ðIÚûþ¿c¿ø&_ìÿÀý¾¿dŸ„Ÿ²o‡¾ xÃþ(ý€?à ZŸî|kñwâOÄøNüwá¯ðIo ÞüR½ðŸ‰øo£øŸû3ÚmÆ…‡üà­ÿ¿ÔQEQEøÃûpÁBþ>þÀµÇÂñÀžøÙûüGø?ñãâçÅÝSàßÂEûCþÅŸ þk²Ÿ|UûCøúø|cñµ·í-ðIñ¿í­ñCÂÿ >ü=ø…ð›á$·¿mÇÄ{…ž*³ñWÓúí ñO÷ímð÷â×ÄÙÿ²/ìùû ~Ï?´½¶¯©øÄ~ñß„?áqø·ö”ð·‹5oŠ_ øÃ¦|?ñ­·ÃÀßï~Ö?²ÇÅ…ž5øéð·ö–ýŸþ%|økÿ ü,_Œ^øÉðëÆ? <ÿw‡,üaâïøM~!xwÄz„¼+ÿ¯„µ?Å>#þÝÕì?°ü9g­êeÓ. ºÿg¿ø(ŸÂÏ¿´wí½¦xgö ý>,þÈ¿³wìÿû/|tÒ¾4üñ§‡5áWü,«¯Ú¯Jø×áo_ôÿŒ¾:øe©ÿÂeû9hß­5»m áü!¾ø…ŸâÍ#[‡KµñƵðÇÙö˜ý©íà¥_#ÿ‚x|?ðïÆøuWü!¿²‡íñGö{>;ý¸¿áÜŸ´vµûOüXÿ…ÿ¬üo¿|ÿ çÀ¾*Ócÿ…_ðžüJø©ö½áf‹{ñoþ×Ã+MÒøÛ³öCý»?oüføéð÷öý ?føVÿð눞ýž¾%þÒ~ xÇöäÿ†øíÿøÍñ7ötÓ~8~Æ´÷ÆŸøP?ÛŸðÐ|SðŸâïŒ5{øF¾;x+ÁºÝ­„á‹â߀?¡ß‚Ÿ´/À/ÚSº‡Ž¿g?Ž?þ?x'IñׄõOüø—࿊žÓ|Uc§iZÅ÷†uøZ×´‹?Yé]hבê6úv³¥_Ml–Úœ³{~PÁ0?fÝ;ö~Ôk-G¿±ÆØŸÁ?¾0h~?·³øýûrx«ö´øËñÿâ­áXõOŠßþ!x?´ÇŸƒ~ ÖüS¬ M⟠þÐ~=ø™ñÖ [ø‹âî—á+o ü;±½ý_ Š( ¾øÿ)MýÿìÀ?य़úÑ_ðIÚûþ¿ mßÙ‹áíwû}~É¿>:Z|@Õþx‹öÿ‚Á⟠øãgÆÏßð˜hú¿Æø%ß„õ¿ø×Yøñ Ὲ韼Oá-ÓÀŸ>|Oðe¯Ã¯…¿¾ Iðköšøoÿ޵ã_ _iþ.Õ>7ü@Ö+xÂßð”øƒáOíC[Ôü;¥išÅÿü":E¬úƒYE%´¿_ÐEPEPEPEPEPEPEPEPEPEP_~Æÿòq_ðV/û?ÿ‡úë/ø&}ÿ_~Æÿòq_ðV/û?ÿ‡úë/ø&}ÿEP_Á'å_ðM?û0ØßÿY×áÍ}ÿ_Á'å_ðM?û0ØßÿY×áÍ}ÿEPEPEPÀ¿å)¿±¿ý˜ü³ÿZ+þ ;_×çíAÆÿ~Ú²Ïíð×ö^øÁûNx'Á³í¯ðSÆú_ÁO~Í^ñW„üUñ£â·ì㯇ú†¡cûI~Ð?³î‘©øSÒ?g߈Ö÷W^×5ýGKÔmt¨oô¨-µXo ÿ†Èý¢¿é¿·ÿþoø%—ÿL²€>ÿ¢¿8"xgÄßþ+ÿÁ@üñ ð‡ÄOø«ÂºÎ⯠èÚ¾££\‘löÏoq0êýðü6GíÿHý¿ÿðãÁ,¿úe”Ãd~Ñ_ô‰ßÛÿÿ7üËÿ¦Y@Ñ_Ãd~Ñ_ô‰ßÛÿÿ7üËÿ¦Y\ÿ‹?o_Œ𯉼uã¯ø&í¿à¿ø/ÃúÏ‹&ñ7ˆuø)­ž‘ øBÒ,ï5MgYÕ/-tí/Nµ¹¾¾¹‚Ú ePÑú+òƒàGüÇß´§Ã=/âïÂ/ø%üVðN­âøN9ϤZÁ¨^Þê‘ÛDúEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEP_~Æÿòq_ðV/û?ÿ‡úë/ø&}ÿ_~Æÿòq_ðV/û?ÿ‡úë/ø&}ÿEP_Á'å_ðM?û0ØßÿY×áÍ}ÿ_Á'å_ðM?û0ØßÿY×áÍ}ÿEPEPEPEPÈ·¯Âßübý“¾+x/á¶…ÿ Ç‹mÿáñí·Áéõ=DÑÿi=á'ÄŸ|XñgìâíoÄÚ†Ÿá-áÿíá/kŸ²ÿÄ]_ÆÉ­øKð'Å¿_xçÂ8𕾳àÝwÇÿàŸþ&Ó¿h}Gãïííàëoxkàßí{â€Ô|'⯆/㿃_ü+sà~ÕŸ~x¿FÓux~0~Òš½Çˆgð¿ÄÙgüEýƒ>þÀš~±áÿx“ÀúÞƒèý|ÿÿ”YÁ4ÿìÀ?cýg_‡4÷ýQ@~@ÿÁI¿kÙc@ø§û<þô'í-û?üð—ÆŸí?¿´‡ü.ߌŸ¾XøöXø!â?ùOü&þ#‡LÖ?ᬾ6êžø]âï‡<ðŽ«ðëã¿ìmáÛÓÁ+¬é>-Ð43õúŠüaø)ûnþÅçþ +¨xötý®ÿfŽþÿ‚x~ëÅš§ƒ¾ |}øSñgÅ^ý°¿gO…úV}âmCÃÞñgŽüy?‡ÿhØóáÖ‡¥Ýk7~øðWQý…4«®uoŒŸµõœZÏìõ|ÿÿ”YÁ4ÿìÀ?cýg_‡5÷ýQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEðìoÿ'ÿbÿ³ÿøqÿ®²ÿ‚i×ßõðìoÿ'ÿbÿ³ÿøqÿ®²ÿ‚iÐßôQEùÁá?ø%?ì—à/ øgÀ¾ñí¿à¿ø/Ãú7„üàï ÿÁRà§žð¯„ü+áÍ:ÛGð÷†|3áíöÀ³Ò4èZEž—£hÚ]®¥éÖ¶Ö66Ð[AKú?E|ÿÓýè£~ßÿø¶/ø*oÿFEðí?Ù×þŠ7íÿÿ‹bÿ‚¦ÿôdWßôPÀðí?Ù×þŠ7íÿÿ‹bÿ‚¦ÿôdQÿÓýè£~ßÿø¶/ø*oÿFE}ÿE|ÿÓýè£~ßÿø¶/ø*oÿFEðí?Ù×þŠ7íÿÿ‹bÿ‚¦ÿôdWßôPÀðí?Ù×þŠ7íÿÿ‹bÿ‚¦ÿôdQÿÓýè£~ßÿø¶/ø*oÿFE}ÿE|ÿÓýè£~ßÿø¶/ø*oÿFE}ðŸáo>|,øiðSán…ÿ¿Ã/ƒÿüð·á׆´õoþß|?ðæ›á?è_Û>"Ô5jÿÙÒ4ý?ûO]Õu=bÿìÿjÔõ ÛÙg¹—Ð( Š( Š( €?à“¿ò‹/ø&Ÿý˜ìoÿ¬ëðæ¾ÿ¯€?à“¿ò‹/ø&Ÿý˜ìoÿ¬ëðæ¾ÿ Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( ¾ýÿäâ¿à¬_öÿ?õÖ_ðM:ûþ¾ýÿäâ¿à¬_öÿ?õÖ_ðM:ûþŠ( ¿0>ÿÁF¾'üqøYðÓã_Âßø%×íÿâ†_>ø7â—ïÂgÿÊÑ?á"ð'Äi¾,ðŽ»ýâ/ø(æ‘â #û_Ãú¾Ÿ¨fkºV™¬X}£ìºžŸe{öÑ~Ÿ×ÀðIßùE—üOþÌö7ÿÖuøs@ü6GíÿHý¿ÿðãÁ,¿úe”Ãd~Ñ_ô‰ßÛÿÿ7üËÿ¦Y_Ñ@Ãd~Ñ_ô‰ßÛÿÿ7üËÿ¦YGü6GíÿHý¿ÿðãÁ,¿úe•÷ýðü6GíÿHý¿ÿðãÁ,¿úe”Ãd~Ñ_ô‰ßÛÿÿ7üËÿ¦Y_Ñ@Ãd~Ñ_ô‰ßÛÿÿ7üËÿ¦YGü6GíÿHý¿ÿðãÁ,¿úe•÷ýðü6GíÿHý¿ÿðãÁ,¿úe”Ãd~Ñ_ô‰ßÛÿÿ7üËÿ¦Y_Ñ@Ãd~Ñ_ô‰ßÛÿÿ7üËÿ¦YGü6GíÿHý¿ÿðãÁ,¿úe•÷ýðü6GíÿHý¿ÿðãÁ,¿úe”Ãd~Ñ_ô‰ßÛÿÿ7üËÿ¦Y_Ñ@Œ?±ÅïÚÓöký‹ÿdOÙÏÇ_ðJßÛVñ·ÀÙƒàÁOêžø£ÿþð®¥â¯… |'à_ê¾Ö?à¢ú¯yáûÍ_B¼¸Ñ®µMFÔn4é-¦¾Ò´ë—–Χÿá²?h¯úDïíÿÿ‡þ eÿÓ,¯¿è €?á²?h¯úDïíÿÿ‡þ eÿÓ,£þ#öŠÿ¤Nþßÿøq¿à–_ý2ÊûþŠøþ#öŠÿ¤Nþßÿøq¿à–_ý2Ê?á²?h¯úDïíÿÿ‡þ eÿÓ,¯¿è €?á²?h¯úDïíÿÿ‡þ eÿÓ,£þ#öŠÿ¤Nþßÿøq¿à–_ý2ÊûþŠøþ#öŠÿ¤Nþßÿøq¿à–_ý2Ê?á²?h¯úDïíÿÿ‡þ eÿÓ,¯¿è €?á²?h¯úDïíÿÿ‡þ eÿÓ,£þ#öŠÿ¤Nþßÿøq¿à–_ý2ÊûþŠøþ#öŠÿ¤Nþßÿøq¿à–_ý2Ê?á²?h¯úDïíÿÿ‡þ eÿÓ,¯¿è €?á²?h¯úDïíÿÿ‡þ eÿÓ,£þ#öŠÿ¤Nþßÿøq¿à–_ý2ÊûþŠùƒöbý§`ý¥ øÑgyð_ãÀ|øÁÁO‰? ¾5ÏðjûÅZoН¾ |øñ¤ê~­ðãLJz·‡õo‡| }kucã©5}FMWKÕ4­:çN"o§ëàØßþN+þ Åÿgÿðãÿ]eÿÓ¯¿è¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢¾ÿ‚™Ï¬Ã.èú6âïˆÿ„ãö¿ÿ‚q|-ñˆ>üEñßÂOÿ ñoþ %û,ü1ø¡h_þø‹Â_<%ÿ oÃÿø›Âzž§á?hšÇö>·¨[[jÿhf£þ§û:ÿÑFý¿ÿñl_ðTßþŒŠûþ¿/ø!güGþWþ WÿÌø¥}á?øCü?ðßâðŸà ŸÂÝ þÿÙÆ…ŸµÞ½í‹?ÃM ÞoøvÊ/ˆ¾X~Ëú§àßê>øgkã=CL}7ľñÃß§ï÷ü;Oöuÿ¢ûÿâØ¿à©¿ýÏèßðJÙ/Ú‹5x‡ößеø‚ÛÅž:Õ4oø*OüóKÔ|iâ«? øgÀ¶~&ñeõíψüAkàŸø;ÁÖÚα-棅|'áŸErºFƒ¥ÙÚ€~Ñ_ôÿg_ú(ß·ÿþ-‹þ ›ÿÑ‘Gü;Oöuÿ¢ûÿâØ¿à©¿ý÷ý|ÿÿ”YÁ4ÿìÀ?cýg_‡4ôÿg_ú(ß·ÿþ-‹þ ›ÿÑ‘\ÿ„ÿà”ÿ²_€¼+áŸøÄ?¶ÿ‚üà¿èÞðwƒ¼'ÿIÿ‚žxs¾ð¯‡4ëmÃÞðχ´ÛÏHÐ|?¡ivz^£ivvºv—§ZÛXØÛAmQ(èýðü;Oöuÿ¢ûÿâØ¿à©¿ýôÿg_ú(ß·ÿþ-‹þ ›ÿÑ‘@Ñ_Œ?µ¯ì•àÙ¯À? ~.ü"øµûoé>6Òmÿø&ï„ã“ÅŸðR/ø(_ÅO ê^ø©ÿ ý˜>|Dðω¾üWý§ümðïÅþñÿx«ÂºÎ⯠ë:uƬܑl—)oqìõQEQEQEQEQEQEQEQEQEQEQ_xgáwìÿû@~Ó<{g®~×úGÄßÙ‡ö€øqðëÆºeŸí§û_øàN±ã½#ö{ý¿h? ÏáÏÙçÀ´—À]oáýÇ~0xMñ¯‡|Gð“HÑüwãOÇã_ø¦ËW¼ñ‹@>ÿ¢¾`ñ7ì‰ð§ÅŸm¾;jž,ý§í|mkâ øš-Ã?¶ïí¡à¿ƒM¨ø. ßG¶¹ý<ñ÷BýŸ/ ƒF·Ñí®gO|}пgËÏÞG¡X·‹<'yð øöyõ›¯hÞ#¹ñˆfÕ>Ÿ¢¾`ñ7ì‰ð§ÅŸm¾;jž,ý§í|mkâ øš-Ã?¶ïí¡à¿ƒM¨ø. ßG¶¹ý<ñ÷BýŸ/ ƒF·Ñí®gO|}пgËÏÞG¡X·‹<'yð øöyõ›¯hÞ#¹ñˆfÕ<ö7ÿ“Šÿ‚±Ùÿü8ÿ×YÁ4ëïúù÷öýŸî~;x“ö޳Ÿö€ð¿Äßü@ðWÅ/ÛøöÄý¯þü,ñ·Žþø;À|7®ø×à€>:xoà/‹÷xáw€2Û|vÕ^x~ò= żYá;φøWdzϬÝxëFñψüC6¨x›öDøSâÏŒ¶ßµO~Óö¾6µñ„üM‡áŸÛwöÐð_Á¦Ô|o£Û\þΞøû¡~Ï—ž¼B±oxNóá„þñìóë7^:ѼGsâ?ͪ}?E|ÁâoÙáO‹>2Û|vÕ^x~ò= żYá;φøWdzϬÝxëFñψüC6¨|ðÏÆ] â·í»ª|O¹ñþ ñ§í?á?~Îqk>,ƒÄzuŸÁ«_Ø¿öDðv¹má=gT“À^oÚ¯.|'ucáÉï%ø/Ç*øGâ§Ô|A£§†~'x{ÃÖ©«ø Ä «øOÅZZèÞ*³ÒuÔ|5â lntmF+cáÇí ð ãо'xáǃÿ£â <3ñ;ÃÞÖµM_À^ m_Â~*Ò×FñUž“¨¶£á¯X‹cs£j1[~ þØß³ÇÆ_ÁO>7~Õ²8ñ×í¡ûþÄðMß|'ð-—ÅÈ>x/ö‘ø5ãOÚÓþ 7}ûQþÇŸ/ ðþ³âÏøÇÅšÎáÏ øO¾Ó®uø›ÄÞ!Ö.lôÃú‘gyªk:Ωyk§izu­ÍõõÌÐK*üAÿbÿ‚YÒK?`üLÙ×ÿž5tðO‰¶×ÅOÙ¾!ÿ‚‡þÏ^ýšÿk?xÇFø‡à_k^×¾êÚu¿ˆ.ï¼âφמ øãñúæ?ßx#QÐ4}rÛÆ~1Ó|TŸ´/K†lü7„µMgùáÿ‚j·ˆ?àˆ±/Ã/þÉ?²ÆÙ—Ç<]ðëân©ñKö†ñn±ã¿~Î?¿oïˆý«'×e~Í~øKâøGþøƒâÐÓ<;©~Õš­¯ˆ4}OñžñÞµ|>ø€ú^ñ7í»ûø/ã-·ìçãÚïö`ðŸíyâ øNÏàO‰¾>ü)Ð~2Ýx«Ç°h×^ðÍ·Ã SÅ–¾6ŸÄ4¶ñ‡®<'£E¡¶£â85ÝmÚò=Rŧú~¿”/ŽZfû|:ý²ôoÚëáoìÿû~ÿÁ¿m¿Úÿöˆø¯ûS||ø_§øáïÅ?ØcâOÇ?ÚŸWø}ãO|gøyá-w\ñ×í}ÿ ßãÖ«ð³Â~øÛð«ÄÚwícû=xáÄËfð®šßÿggõ{@ÁK?äÝ~ÿÙÿÿÁ'õ鿱½}ÿ_œðUgQðçì—áïèþñu}ößÿ‚[k:_|'sá[?xÓQÒÿà§Ÿ±ýõ„ü3yã¯x/Á6¾ ñÌhú5ÏŒ|cá? Á¨Þ[Kâh:BÞj–¿„?ðRïø.Ÿü¯öUÿ„²ÇáoüÏâÃø?ûwâ•çÇï‹:ƽû]ü,ƒöqðÿü'Ük¿íÿc©ì>|ø—‡tÿxËL×jÚü3ð厤šžŸâ/ø—Ãúý¢¾ÿ†Èý¢¿é¿·ÿþoø%—ÿL²ølÚ+þ‘;ûÿáÆÿ‚YôË(ïú+àølÚ+þ‘;ûÿáÆÿ‚YôË(ÿ†Èý¢¿é¿·ÿþoø%—ÿL²€>ÿ¢¾ÿ†Èý¢¿é¿·ÿþoø%—ÿL²¾Ÿýž¾5øWö”øð?öŒð.Ÿâ 'Á?¾ü4ø×àí/Å–ºuŠ´ß üTð^‹ã¯iþ&±Ñõ]{H³ñž‘®ÙÛë6º^¹¬éÖúŒw0Øêº²Ey0°QEQEQEQEQEQEQEW̵ŸƒZ§ÅoÛvÇᇄüAáÏxsöŸðžûFkÍÌóéÞ=øË?ì_û"x‡Cñg„â›ÄÚìv~³ýŸ5ï>¹¶µÒüx«Á~&¼o Ý\Ý\xÇÅŸO×̵ŸƒZ§ÅoÛvÇᇄüAáÏxsöŸðžûFkÍÌóéÞ=øË?ì_û"x‡Cñg„â›ÄÚìv~³ýŸ5ï>¹¶µÒüx«Á~&¼o Ý\Ý\xÇÅ€ÿÆ¿Úàì×á]?Ç_´gǃÿ|«x‚×Âz_Œ~5üKð_¿ ê^*¾Óµ]bÇÃ:ˆ|u­h:Eçˆ/4 \Õ-tk{É5;FÕo¡¶{m:òXO´/À/ٯºŽ¿hÏŽ?þø'Vñ¯„ô¿ükø—à¿…~Ô¼U}§jºÅ†tÿøëZÐt‹Ï^i¹ªZèÖ÷’j7vªßClöÚuä°üÁÿbÿ”YÁK?ìÀ?lýg_ˆÕøÃñÂçÅ_ðN„øWÿÖñæñƒ^ý“¾.ÁGÿà—z¯ü»ã‡ˆÏÊgöÍý´ O þÓ>‡ö`Õ?dûÏø(ýÏì㯀šÏ~+h_´?‡<*¿·Ÿ‰¿à™ö¼'ûJXüWñÃox‚ŽÖ¾øásðXý–ü§Zü$×|MðÂ/‹Wž1ðž—ñÆŸ?ü}ø³ûh~Ñÿ î~-x‡Ä³ÃÏÙ?ÂðYïÙà~ hß ¾+x÷ö‡ñw…fø,Ïìëû3^|Bñgí }ñ»À? ¼âüvø_ãÛ|>Ñÿfˆšv‰ð‘¼3á9~!êž1ÕõOxtú¯˜>5þÛ¿±ì×â­?À¿´gíwû0|ñ¶­áû_i~ø×ñ÷áO¿j^¾Ôu]ÇÄÚ‡¼uâÍW¼ðý毡kš]®³og&q¨èÚ­Œ7/s§^EãìYÿJÿ‚“þÞ¿ÿfOÚ/à/ì9öŸø%_Lj<;âïø¦Çà/‡¾;|5ð'‚|G⯄?¾"é+Ÿþ %-ïÄí?‰¾»ñ·ÇOÍû#x#XøOàë=oáßÁùÿj]VÛÿñ ¿‹5™uÅÓ¼9>…¬Ã¬\ÙÉ¥ß,Að·þ ûüqñÞ…ð·à§íÃû |`ø›âí?øF~|-ý¥¾ ü@ñ߈¿±4}CÄZÏö„|'ã]_Ä¿öG‡ôW]Ôÿ³ôû°húf¡©ÝyVVW3ÅùñgáÖû6ÿÁİdŸ±äÿð€ÿÃhþÏÿ¶ˆà¦¾øwÀ–~ñ7>iž*ñßìÿûW|zðG…<#µ¡ü@ñ÷í5ñ_Äþ´ý¦üw©G¬xÓXÑaøIáÛÿn|IÐÿ྾ðŸÂOÙÿ]ý™~ÿÁ¿mÏÚã߯}Ä^1ý¬~xá‰þøã?Še_€ž'ð×Ã_„·ÿ<ð–Öç]>+ñ7íwà=cÇÿ ¯þ4|Ѽk­xßÞ'»ÿCÊùƒà†~2è_¿mÝSâ}ψ'ðO?iÿ ø›ös‹Yñd#Ó¬þ ZþÅÿ²'ƒµËo èðë:¤žðû~Ð~øíysá;«Oy⫯xétií¼ioâw×þüRð'Ç…Ÿ >5ü-×á(øeñƒáÿƒ~)|:ñ7öf±¢ÂEàOˆÓ|YáwûÄZ~‘â #û_Ãú¾Ÿ¨fkºV™¬X}£ìºžŸe{öÑyÀ/ üeо+~Ûº§ÄûŸOàŸ~Óþñ7ìç³âÈžóÅW^&ñÒèÓÛxÒßÄ:èÓôQEQEQEQEQEQEQEQEQEQE|ÿ,ÿ“uøsÿgÿÿÿצþÆõ÷ý|ÿ,ÿ“uøsÿgÿÿÿצþÆõ÷ýx‡f/„^ý£¾"þÖ5§ÄøÛñ_áÿ„~øçUÔ>6|l×< ¨øÀ7Sê дςšçÄ-Gà—…áÔõjÚ§á?‡z±§k>ø¥¬[j ©üWø“uâ¯ñÏü{ö%ø‡á_‰>×þøOÒ>+~Óúí­â‹Ïx‡Ç ÖüO¨ßøÛXñ-Ö­«üKø}⯷è €?á×°Ÿü(ŸøgOøQŸñoÿá ?á¬á!ÿ…›ñ‹þ·ü5?ü&?ðœÿÃKÃQÿÂÁÿ†šÿ†€þÚÿAÿ…Éÿ wþ/ü!ßñnÿá#ÿ…ÿÍ¿ðKÿسWøYâO„>#øuñÆ~ñGíà¯Úª÷Æ?ý¤¿i߈´Ú;ῇ<àï|hðWí3ã_Œz÷íðïâƒ|ðÃÁ^ ðæ¿àŠžºÒ<¦^øRÌÅáÿxNÕþÿ¢€> ø3ÿäýgÏ|9ñ×Áÿ†^ ðo¾øƒöŠñfã~3|sÖÿ¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¯˜>k?µOŠß¶íà øƒÃž6ðçí?á=öŒÖ5›™çÓ¼{ñ–Ø¿öDñ‡âÏ Å7‰µØìü?gû>kß| smk¥ø.ñW‚üMxÞº¹º¸ñ‹>Ÿ¯˜>k?µOŠß¶íà øƒÃž6ðçí?á=öŒÖ5›™çÓ¼{ñ–Ø¿öDñ‡âÏ Å7‰µØìü?gû>kß| smk¥ø.ñW‚üMxÞº¹º¸ñ‹;ÿß>þÓ¿¾!|øÍ¥øƒ^øSñ[ÃóxO⇼3ãÿˆ_ 5xVò{yuo ÜøÇágŠ|ãk_øŽÚѱâïüGÔ¼[áÿøÏL×½þŠøþqû ÿÂöÿ†‹ÿ…ÿþþÇþïøY¿¿áDÿÃSÿÂÿ7ü4·ü2çü,øf_øhì_ôïø\Ÿð¨¿ábÿÂcÿþ?øXñSQ{ÿ¿ý‹.~)ø“âõŸÃ¯ˆñŒÿh~Õ^5ð€?i/Úwá¿ÀŸ~ÑßüGàøoãGfo|cðßìåâÿˆ ã?…ÞñŸˆõÿü+Ôî¼mã Yø¯Æ§Ä –óQ¹ûþŠøþqû ÿÂöÿ†‹ÿ…ÿþþÇþïøY¿¿áDÿÃSÿÂÿ7ü4·ü2çü,øf_øhì_ôïø\Ÿð¨¿ábÿÂcÿþ?øXñSW?¬Á&a½g¿²„¥ðÆ &ÛöðÿÄO þÉÞ%ðŸí}ûcx#âgÁ_ üTÓ´ ƾðÏÅÿü|Ð~+^x~óÂÚà}FñŒõ;Áÿtûo‡ž¶Ð<ŸØô~Šùƒörý¿gÙKQø«âƒ>ñ¯¾9xƒÃ~&øÇñGâOÅ?‹¾2üMÔ|á[ø"ÛÆß><øëâ_ÅoøÀÞÓÓGð7„õÏá_A}®KáJ¹ñ¿>§ñþÿHÿ‚phÚw‹<<~ü`ñ‚~"ü`¶øýñ_áwÿmÛâgÁ¯_`ñW†|mqñ ã¿ÁˆŸ´gŠ>ütñµâŸøWXñLß|ãH¥Åâ«}bÚÖ8êýW̼3ñ—Bø­ûnêŸî|A?‚|iûOøOÄß³œZÏ‹ ñgðj×ö/ý‘<®[xOG‡YÕ$ð‡ÛöƒðŸÇkËŸ ÝXør{Ï]x›ÇK£OmãKë¿O×̼3ñ—Bø­ûnêŸî|A?‚|iûOøOÄß³œZÏ‹ ñgðj×ö/ý‘<®[xOG‡YÕ$ð‡ÛöƒðŸÇkËŸ ÝXør{Ï]x›ÇK£OmãKë OÑEQEQEQEQEQEQEQEQEQEãÿþ|3ý¥>ꟾ.é~ Õ¼«xƒÀ,’? øÿâ¿i¾*øWñ Âßþø›Ã?>x§Á?ñV¨Ûê:5°7/l÷ó|ÁÿÓýè£~ßÿø¶/ø*oÿFE}ÿE|ÿÓýè£~ßÿø¶/ø*oÿFEðí?Ù×þŠ7íÿÿ‹bÿ‚¦ÿôdWßôPÀðí?Ù×þŠ7íÿÿ‹bÿ‚¦ÿôdWÀµÏì¥üGñß„?c¯Ù3ö¨ý¿þ|`ñÃÿübø©ûCØÿÁH?ࣟî¿g?…š«øSá Ô^ ñŸíâ/KñãíáK?x?â†,<9ñöqø#ûtÛü;ø…àŒ |)â÷ú¿h~ß_²7Œ~:~Ôß¿ioÙÅŸðÔŸµÿìaðŸOðïÅoØ{ãGŠ|cð«ágÆ/Žßc?…¿ 4_‰ºü'ÂÚßþ ÂÑñwí/ƒ`ð_‡|9âÿŽß?høwMø_ÿ ³S‹ÃÀû.~Èÿ³¯íð'Àß/uïÛÿÀ>-Ôá&ðÅo†Ÿð÷ßø*oŠ¿áS|vøQãü&ý >ÿÂeíc¡iž<ÿ…Cñ·Á?øiÿ Újx;ÇÿðŠÿÂeà«­GÂZæ‹©]ûÿü;Oöuÿ¢ûÿâØ¿à©¿ýÐ~Ê?³Ç߃?i¯‰¿¾:|ø¡sûHxƒáoŽoü+ðSösñ§ÀO èŸ<ðößá‰þ$êâ7í9ûQø§\ñÄ…>ø à+­HñGƒ¾økNø¥kÚ‚WÆÞ>ø•â¯ý¿@ôÿg_ú(ß·ÿþ-‹þ ›ÿÑ‘Gü;Oöuÿ¢ûÿâØ¿à©¿ý÷ýððLÏÙwûcÂ:γ¬~×þ8ÿ„âïŠ^ðÿÅ/ø(çüKâß?á;øIã¿üNøq®ë¿>'~Ôþ-øâßøD¾ xKÃ>,Ó4Ïxg[Ñÿ¶4M>æçO¸û:­}ÿEQEQEWÀðIßùE—üOþÌö7ÿÖuøs_×ÀðIßùE—üOþÌö7ÿÖuøs@ÑEQEQEQEQEQEQEW€xƒöbøEâ 㮌¶Ÿ<%ÿ 'ñÃ_¾0øƒáoÆÏŸ|wâøKÀŸ >èšî…ñá?Ä/|@ø{³áÿÀφ>Ôôχ&ð–â BÔ-¼A§êŸð–øÉ¼AïôPÌ7ì‰ð§Bø5âÏ6>,ý§çðOð/ÄÿxVò ¯ZÞhÓÛxÓÆ0ë¦û"|)о x³àM‹?iùüãO[x›X×5ŸÛwöÐñÆ[=FÖ ÜEmá?Ú/Ä?uOÚÀ^i<'¥­Ï„ü ñ?Þ¼‚ëÄÖ·š4öÞ4ñŒ:ïÓôPÌ7ì‰ð§Bø5âÏ6>,ý§çðOð/ÄÿxVò ¯ZÞhÓÛxÓÆ0ë¼þ§û8~Ïÿ ?gŠ^ñgÅÚÂ_¿âañKâOÄ߈¿··íÿ ßô Zè^ ×5Ø?jÏþÐ×~|?Ñ4ÏÛj^"Ó<3ñoÂ^·Ñÿá.“YÓÿ³<[ãa­ý_ ~Ü_³×Å?Ú“àLŸ>ü[øð‡ûâÃ]oâ-çÄσ>#øïàïˆÿ <ã/ƾ.øâxwãÀoí_‡ÿÿ°4ÿ†ôwÅï‡ê~“þÈꟴ€¼>ÒxOK[Ÿ øâ‡<+y׉­o4ií¼iãuÓFý‘>è_¼Yð&ÇÅŸ´üþ ñ§ˆ-¼M¬kšÏí»ûhxã-ž£k?†n"¶ðŸí⺧íà/´žÒÖçÂ~øŸáÏ ÞAuâk[Í{oxÆwéú(æ öDøS¡|ñgÀ›~Óóø'Æž ¶ñ6±®k?¶ïí¡â?Œ¶z¬þ¸ŠÛ´_ˆ~>ꟴ€¼>ÒxOK[Ÿ øâ‡<+y׉­o4ií¼iãuÞÿà§Àïüð®¡àïë_5Ý#Rñ׉®/>5þпiOG¨ÞiÚV—5¶Ÿã¯Ú3â_ÅOi>KmÎ[_ é~!³ð­Ž£6«¬XèÖú¾»®_j>ÁEQEQEQEQEQEQEQEà´ïí £þËÿ®þ-ë>øñCþ.Á?…¾ø{ðµ| ÿ ߌðÍæ—ká[?xF¼ñ׉¼à˜~Ùúß~>øsösø•ûþÓÿ³¼iðâ¯Æ¿jŸõŸÙÄ~ñg…~ øÓà~ iú}÷ìÛûUþÐZ¾™â 3Wý ¾ÜZÚø£CÐ4íSNºÕf°Õg¹Ò¦³o·ëàˆßò”ߨßþÌþ Yÿ­ÿ¯¿è¯€?à“¿ò‹/ø&Ÿý˜ìoÿ¬ëðæ¾ÿ¯€?à“¿ò‹/ø&Ÿý˜ìoÿ¬ëðæ€>ÿ¢Š(¢Š(¢Š(¢Š(¢Š(æ ÿiØ5H?j?†_þ0|eñ·ì©ñƒÂ?|;ŸÃúÿÚ+Â÷Úå×|uà Eµ xÛKÐt­ræß£ņñ÷â¶©ðkÅŸï¿b/ÚÞ6ðçˆ-´mötÖ|YûÏñ—Ǻtóøf|Yá=sÃßµÞ»û>Yø~Î={Tº¹¶ñ×Çoø© ð_‰–ÏÃ7W7^·ñað Yø5ª|Vý·l~xOÄñ·‡?iÿ èß´f±¬ÜÏ>ãߌ³þÅÿ²'ˆt?xN)¼M®Çgáû?Ùó^øà[›k]/Áp7мâkÆðÍÕÍÕÇŒ|Yôý|Á£|}ø­ª|ñgÄûïØ‹öŸðç¼9â mGý5Ÿ~Åóüeñî<þ†_xO\ð÷íw®þÏ–~³^Õ.®m¼uñÛÁ~*h<âe³ðÍÕÍ׃­üXhß~+jŸ¼Yñ>ûö"ý§ü9ãox‚ÛFÑÿgMgÅŸ±|ÿ|{§O?†a—Åž×<=û]뿳埇ìã×µK«›o|vð_Ššø™lü3usuàë}?E|Á£|}ø­ª|ñgÄûïØ‹öŸðç¼9â mGý5Ÿ~Åóüeñî<þ†_xO\ð÷íw®þÏ–~³^Õ.®m¼uñÛÁ~*h<âe³ðÍÕÍ׃­üXhß~+jŸ¼Yñ>ûö"ý§ü9ãox‚ÛFÑÿgMgÅŸ±|ÿ|{§O?†a—Åž×<=û]뿳埇ìã×µK«›o|vð_Ššø™lü3usuàë}?E|Á£|}ø­ª|ñgÄûïØ‹öŸðç¼9â mGý5Ÿ~Åóüeñî<þ†_xO\ð÷íw®þÏ–~³^Õ.®m¼uñÛÁ~*h<âe³ðÍÕÍ׃­üXhß~+jŸ¼Yñ>ûö"ý§ü9ãox‚ÛFÑÿgMgÅŸ±|ÿ|{§O?†a—Åž×<=û]뿳埇ìã×µK«›o|vð_Ššø™lü3usuàë}?E|Á£|}ø­ª|ñgÄûïØ‹öŸðç¼9â mGý5Ÿ~Åóüeñî<þ†_xO\ð÷íw®þÏ–~³^Õ.®m¼uñÛÁ~*h<âe³ðÍÕÍ׃­üXhß~+jŸ¼Yñ>ûö"ý§ü9ãox‚ÛFÑÿgMgÅŸ±|ÿ|{§O?†a—Åž×<=û]뿳埇ìã×µK«›o|vð_Ššø™lü3usuàë}?E~p|ý³þ7üTðÿí»â?þÇÿ,üSû5þÓþø)à_Ù“FÖf«ÚVð®½û/þÈŸo5xšûö«ÔeËïG®|~ñ‹í®´úœŸ 4ß è’éS|V³Õ4gè ãïÅmSà׋>'ß~Ä_´ÿ‡ûö"ý§ü9ãox‚ÛFÑÿgMgÅŸ±|ÿ|{§O?†a—Åž×<=û]뿳埇ìã×µK«›o|vð_Ššø™lü3usuàë7ÇߊڧÁ¯|O¾ýˆ¿iÿxÛÞ ¶Ñ´ÙÓYñgì_?Æ_éÓÏá˜eñg„õÏ~×zïìùgáû8õíRêæÛÇ_¼⦃Á~&[? Ý\Ýx:ßÅŸOÑ@0hß~+jŸ¼Yñ>ûö"ý§ü9ãox‚ÛFÑÿgMgÅŸ±|ÿ|{§O?†a—Åž×<=û]뿳埇ìã×µK«›o|vð_Ššø™lü3usuàë7ÇߊڧÁ¯|O¾ýˆ¿iÿxÛÞ ¶Ñ´ÙÓYñgì_?Æ_éÓÏá˜eñg„õÏ~×zïìùgáû8õíRêæÛÇ_¼⦃Á~&[? Ý\Ýx:ßÅŸOÑ@0hß~+jŸ¼Yñ>ûö"ý§ü9ãox‚ÛFÑÿgMgÅŸ±|ÿ|{§O?†a—Åž×<=û]뿳埇ìã×µK«›o|vð_Ššø™lü3usuàë7ÇߊڧÁ¯|O¾ýˆ¿iÿxÛÞ ¶Ñ´ÙÓYñgì_?Æ_éÓÏá˜eñg„õÏ~×zïìùgáû8õíRêæÛÇ_¼⦃Á~&[? Ý\Ýx:ßÅŸOÑ@0hß~+jŸ¼Yñ>ûö"ý§ü9ãox‚ÛFÑÿgMgÅŸ±|ÿ|{§O?†a—Åž×<=û]뿳埇ìã×µK«›o|vð_Ššø™lü3usuàë7ÇߊڧÁ¯|O¾ýˆ¿iÿxÛÞ ¶Ñ´ÙÓYñgì_?Æ_éÓÏá˜eñg„õÏ~×zïìùgáû8õíRêæÛÇ_¼⦃Á~&[? Ý\Ýx:ßÅŸOÑ@0hß~+jŸ¼Yñ>ûö"ý§ü9ãox‚ÛFÑÿgMgÅŸ±|ÿ|{§O?†a—Åž×<=û]뿳埇ìã×µK«›o|vð_Ššø™lü3usuàë7ÇߊڧÁ¯|O¾ýˆ¿iÿxÛÞ ¶Ñ´ÙÓYñgì_?Æ_éÓÏá˜eñg„õÏ~×zïìùgáû8õíRêæÛÇ_¼⦃Á~&[? Ý\Ýx:ßÅŸOÑ@ Mû[]xWá§Å¿³_íû>ÿhþÐg½/áïÄ]CöqñWŽõcöŒøÙðà€xëŖͪ]Ayá_k6w^ñ¥´óx;]ú~€ (¢€ (¢€ +ñþ íûü0øãûþÃßþ)|cý¿üQñ7ãìû4üRø‹âoøzü×Dÿ„‹Ç> ø+Åž.×±¼;û\iÒ?µüA«ê‡öf…¥iš=‡Ú>˦iöVQAm×ÿðí?Ù×þŠ7íÿÿ‹bÿ‚¦ÿôdPßõðü³þM×áÏýŸÿüwÿ^›ûÑÿÓýè£~ßÿø¶/ø*oÿFEsþ&ÿ‚SþÉ~4Ó­´ø‡ößñf‘gâ ø²ÏKñ7ü'þ y¯iÖ¾*ðŠ´oxÄÖÖ:§íumˆ<ãox{Æ>Öb‰ux«BѼC£ÜÙêú]ä£ôWÀðí?Ù×þŠ7íÿÿ‹bÿ‚¦ÿôdQÿÓýè£~ßÿø¶/ø*oÿFE}ÿE|ÿÓýè£~ßÿø¶/ø*oÿFExÿ‚~ 蟳_ü“öxð/Ã_‰?´þ­àŸŠ_±í×âÏø;ã_í•û\~Ó>Ô¼Uð¯ãÏüƒGøâm?Ãß´—Æß‹G†¼Aá­#âÇÄm.×Y𽞨ÜiÞ.Õloîníž¡ý_¢Š(¢¿àžß±/ÃŽ?°/ì=ñ¯â—Æ?ÛÿÅ~0~ȳOÅ/ˆ¾&ÿ‡§ÿÁMtOøH¼wñà¿‚¼YâíwûÿµÆ‘áý#û_ľ¡¨fhZV™£Ø}£ìºfŸeeÑ}ÿÓýè£~ßÿø¶/ø*oÿFE}ÿ_ÁK?äÝ~ÿÙÿÿÁ'õ鿱½ðí?Ù×þŠ7íÿÿ‹bÿ‚¦ÿôdW?âoø%?ì—ãM:ÛGñˆmÿi~ ðŸ‹,ô¿ÁRà§šökâ¯x«Fñ×|Mmcª~ØVÐxƒÁ~6ð燼cá=f(—Qð犴-Ä:=Íž¯¥ØÞ@ú?E|ÿÓýè£~ßÿø¶/ø*oÿFEðí?Ù×þŠ7íÿÿ‹bÿ‚¦ÿôdPßôWÀðí?Ù×þŠ7íÿÿ‹bÿ‚¦ÿôdWø'உû5ÿÁI?gü5ø“ûOêÞ ø¥û~Ý~,ñ¿ƒ¾5þÙ_µÇí3á]KÅ_ þ<ÿÁ84‡þ&Óü=ûI|mø±¤xkÄÒ>,|FÒíuŸ ÙèÚÆâíVÆþæîÙáŠÕú(¢€ +ñþ íûü0øãûþÃßþ)|cý¿üQñ7ãìû4üRø‹âoøzü×Dÿ„‹Ç> ø+Åž.×±¼;û\iÒ?µüA«ê‡öf…¥iš=‡Ú>˦iöVQAm×ÿðí?Ù×þŠ7íÿÿ‹bÿ‚¦ÿôdPñþR›ûÿÙ€ÁK?õ¢¿à“µ÷ý~p\ÿÁ)ÿd»ÏhÞ:¼ñí¿uãoxÄÞð÷Œnà©?ðSÉüU¡xWÆš„õøgFñ ¿¶jú_‡üY«ø Àº§‰´kÈ4í{Qð_„ïµKk«ŸhòÙôðí?Ù×þŠ7íÿÿ‹bÿ‚¦ÿôdPAÿ!ñgмûü|ñ?†¼Mâi‡ü=ÆŠ~ Öu|Bø'û0jž8ð¾“û]|~øW­h·1ëšwÆ€?²å÷ÅÿŒßn4 ;Åž < áDоüKÕÚÏá÷‰|ÿöðŸ…~ø«ö¬ø;û9øgÃþý‡>ü`›Â~×í³û?Mmàï„~ |9ð_AÿÓýè£~ßÿø¶/ø*oÿFEyÿÂßø#ßìEð;ÀšÂß‚‘~×ÿþx_ûOþŸ‡_ à¦ðR¿‡þðïöÞ±¨x‹YþÂð„ÿk}#ÃúGö¿ˆ5}W]Ôÿ³ôû·ëž¡©Ýy···3ÊúE|ÿÓýè£~ßÿø¶/ø*oÿFExÿ‚~ 蟳_ü“öxð/Ã_‰?´þ­àŸŠ_±í×âÏø;ã_í•û\~Ó>Ô¼Uð¯ãÏüƒGøâm?Ãß´—Æß‹G†¼Aá­#âÇÄm.×Y𽞨ÜiÞ.Õloîníž¡ý_¢Š(¢Š(¢¾ ý£¼)ðCã_Çß³ŸÄ­SöŸÐümâOƒÿ´gÆ¿jŸÿjÚWöbð¨ð¯Â~Ì~ø§üB¾ý›~<üÕüaâ ½_ãÏÛ‡Ö¾(Ðüc§hÚu‡Ä9¬5_ÜêSYøÃÐ>#þÈŸ ~*xWჼOâÏÚKÒ>ø}¼3á[χ¶ïí¡ðsÅZ¶œÚw‡ô³sñ;Ç_¾>øÆß<@-¼5§J¾,øÍâø©5Ÿk ¬®¯âÏ_k OÑ_0|Gý‘>üTð¯ÃxŸÅŸ´þ—¤|#ðûxg·Ÿ?mßÛCà犵m9´ïéfçâw޾|}ð?¾6x€[xkN•|Yñ›Ä>=ñRj7> ÖY]_Åž*¾ÖOˆÿ²'ÂŸŠžøcàïø³öŸÒô„~o øVóáÇí»ûh|ñV­§6áý,ÜüNñ×Â/¾ñ·ÆÏ o iÒ¯‹>3x‡Ç¾*MFçÄÂk+«ø³ÅWÚÈð Yø5ª|Vý·l~xOÄñ·‡?iÿ èß´f±¬ÜÏ>ãߌ³þÅÿ²'ˆt?xN)¼M®Çgáû?Ùó^øà[›k]/Áp7мâkÆðÍÕÍÕÇŒ|Yôý|AªÿÁ<¿fíR ¨­ïÿiÿ ÜêÞ ÓüYâoáÇíáûtü+ñW|U¥üø)ðÃÄß¼_ð×öŽð§Š~)x‚ÛáOìõð§ÃˬüDÖ¼øqûnþÚx«VÓ›Nðþ–n~'xëáÇßøÛãgˆ·†´éWÅŸ¼Cãß&£sâ a5•ÕüYâ«ídøû"|)ø©á_†>ñ?‹?iý/HøGáöðÏ…o>~Û¿¶‡ÁÏjÚsiÞÒÍÏÄï|"øûà|lñ¶ðÖ*ø³ã7ˆ|{â¤Ôn|A¬&²º¿‹ [|2ø£û?üø‹ðêÛâψµüS·ð'¾xsÄÞƒâ_‹þÑŸüª|ý©ÿi_ً£¿|iû1øâŸñ ûömøóð{Wñ‡ˆ.õ?n>Zø£CñŽ£iÖæ°Õ|#s©Mgã@øû"|)ø©á_†>ñ?‹?iý/HøGáöðÏ…o>~Û¿¶‡ÁÏjÚsiÞÒÍÏÄï|"øûà|lñ¶ðÖ*ø³ã7ˆ|{â¤Ôn|A¬&²º¿‹#þÈŸ ~*xWჼOâÏÚKÒ>ø}¼3á[χ¶ïí¡ðsÅZ¶œÚw‡ô³sñ;Ç_¾>øÆß<@-¼5§J¾,øÍâø©5Ÿk ¬®¯âÏ_k À/~*ø©ñ[öÝð/ˆtÿÙé³_í?á?‚ž¸Ñ­u}GVð®½ûþÈŸ´eæ¡âɯµ]FÚûÄøÛã÷Œt»k­ÏAӓºo†leÒ¦ÕìõMsYú~¾`øû"|)ø©á_†>ñ?‹?iý/HøGáöðÏ…o>~Û¿¶‡ÁÏjÚsiÞÒÍÏÄï|"øûà|lñ¶ðÖ*ø³ã7ˆ|{â¤Ôn|A¬&²º¿‹Þð­çÃÛwöÐø9â­[Nm;ÃúY¹øã¯„_|ãož ÞÓ¥_|fñ|TšÏˆ5„ÖWWñgНµ§è¯˜>#þÈŸ ~*xWჼOâÏÚKÒ>ø}¼3á[χ¶ïí¡ðsÅZ¶œÚw‡ô³sñ;Ç_¾>øÆß<@-¼5§J¾,øÍâø©5Ÿk ¬®¯âÏ_k'ÄÙáOÅO ü1ðw‰üYûOézGÂ?·†|+yðãöÝý´>x«VÓ›Nðþ–n~'xëáÇßøÛãgˆ·†´éWÅŸ¼Cãß&£sâ a5•ÕüYâ«ídéú+æˆÿ²'ÂŸŠžøcàïø³öŸÒô„~o øVóáÇí»ûh|ñV­§6áý,ÜüNñ×Â/¾ñ·ÆÏ o iÒ¯‹>3x‡Ç¾*MFçÄÂk+«ø³ÅWÚÉñöDøSñS¿ |â~Óú^‘ðÃí០Þ|8ý·mƒž*Õ´æÓ¼?¥›Ÿ‰Þ:øEñ÷Àþ6øÙâmá­:UñgÆoø÷ÅI¨ÜøƒXMeuxªûYú~Šùþ§Å? þÔÿ²çü!z—Ä ¯Ù—á¯ìûQ|4ñßü$>üTðN“â¹Ñ¬âµñf—áë?XéÓjº=޳o¤kºåŽ¢|ý‘>üñV¡ãø³öŸ×u}KÃ÷^¸³ø×ûnþÚ´§…cÓ¯5+TšçOð/íñ÷â§‚tŸ%Îg¯‹4¿YøªÇN›UÑìu›}#]×,uö»Ö~ h? |'}ñÛÂ~ ñ§‚gý§ÿb-CÑü3s=®£gñ—͇À/~Î~,¹–ßÄÞ‘¼?à/ÚTøaã¯[6©uç…|9¬ÙÝxgÆ–ÓÍàíwéúøƒÃÿðO/Ù»Ae¼¿ý§ümˆ>ø²ßDø×ûx~Ý?¼+g⯃_~|øgâm?Âÿhïˆ>ÓüAáOŠß ¼ â]fÇG·Ôo4í;Uðv©s}àŸø¿Ãš÷ |ý‘>üñV¡ãø³öŸ×u}KÃ÷^¸³ø×ûnþÚ´§…cÓ¯5+TšçOð/íñ÷â§‚tŸ%Îg¯‹4¿YøªÇN›UÑìu›}#]×,u§è¯˜> ~ÈŸ ~ø«Pñ|YûO뺾¥áû¯ ÜYüký·mÚS±é×šŽ•ªMs§øöŒøûñSÁ:OˆçF³Š×Åš_‡¬üUc§Mªèö:;‘®ë–:‰ðSöDøSðÅZ‡Œ| âÏÚ]Õõ/ÝxfâÏã_í»ûh~ÒžN¼Ôt­Rk?À¿´gÇߊž Ò|@—:5œV¾,Òü=gâ«:mWG±Ömôw\±Ô@>Ÿ¢¾ø_ð»öý™?jü$ð޹û_ø‹âoÆoÙÿãßÄ]çãí§û_þÒÿ 4ï|ø‹û2øgÆð]xOöý¤þ'xAøâ¤ðˆ¼;à)u…ðæŸñ MŸÅÞ²¿“Fñq@üwþQeÿÓÿ³ýÿõ~×°|ký·bÿÙ¯ÅZhÏÚïö`øãm[Ãö¾,Òüñ¯ã~*Ô¼+}¨êº=‰´ÿxëÅš¯yáûÍ_B×4»]fÞÎM:ãQѵ[n^çN¼Šÿ‚NÿÊ,¿àšö`±¿þ³¯ÃšüÀÿ‚°ê_ôø,çüå¨|ðoÃÿˆmÿáëÿðŒøGâ—Ä¿üð&¯æþÊŸ`Ö·~"øOá?Çxwì—UÔôÏìÿ…¾(þ×Ö,´ý ëûËS¹ñûð/ö±ý–?jøJáš?ioÙÿöˆÿ„ûþ_øQ>|[ÿ„?þoíøFÿá)ÿ„Ä~ ÿ„þøGõïìOío²jÿbjÿ`ûGömç“ïõøÃñ/öý®>ø/ãŒ~'ü*ýˆ?gÛCâ—í?û~Î_³›hÞ$Öÿi;âìã_¿±WÀs⯋.¡¿ý”>8üfðÿÀŽ?·WÇh­¼7uáï€z'Ãÿx»Ã::Ø]éÞ?·øŸñƒóƒþ ûkÿÁMg/ØOþ ‹à+ߌ¿³ÿ‚þ0~ÎÿaŸŠþ ý¥¿f…ÿþxÆ¿³í½ñ‹âìÓâ?i>ñÿÆo‰÷¿³íàO‰¿|©X|mðçÅ¿ºŸÂÍkA“Á^øAñ‚K?xú½¢¿(<ñ#þ Iñ?Çß¶·…| ñWö {ŸÙSÄ¿üáŸ~Ëÿt>+|}ÖàžŸ³—í áïˆ^&ø—£þØÞ+“áoÁûŸÚö…³ÏÃí/áÅOi?¼-sá[ˆzçµh¼}aäðK?ø(·ÆÏÛ«Æ:â~Ïúµï¯ÙÁž+ý¸¾xKàgÅß‚_ÿgoÛ‹â_Ço‹~Òÿg‡Ðþ%|sø—â #þ‡þügðGÆïüPð'¼Z|G¢üøåðÏÅ>2øeûAÉàŸƒ€·ÕðÄoùJoìoÿfÿ,ÿÖŠÿ‚N×ßõðÄoùJoìoÿfÿ,ÿÖŠÿ‚NÐßôQE|ÿÿ”YÁ4ÿìÀ?cýg_‡5÷ý|ÿÿ”YÁ4ÿìÀ?cýg_‡5÷ý|ÿbÿ‚YÒK?`üLÙ×ÿž5{Ÿ¶ïì_…|{ã©k¿Ù‚?ü+ðÿÂO|OñŒŸ~§…~xWãö¦ë|Mãß·‹‘àÿüjÒ5TøI¬ø†óNÓ¾#éÚ¶›}àëfÚúÖYœø&w?à¡~ ð¯üŽëö"ý›ÿfú½‡ü{þ ‡¬ø:ãã_í?ãï…>*ºøËk§x OxOOøQ£þΞ#ðOx{áÆ_èþ'ÔôоøA¤~ËÖvÿ[Kð‡Ã­;á‡ÃßËðÎÇÃú•¶ˆõ`èzŠþxl¿ø)üWÄ_µíiûÁ'¿dünøÉûøör¹ø¿ñ¯Hø_?Ã?x«öðû|FðÖ§ø£_ý»e?ü2ðÿƒ>é—ú­¯‰´~КÄψWz¯µ¯ ü ð߃4/‰_¼'¬ê>#ð¯†|C¬xOÄÕõïèÚΩà_\øVóÅ^ ÔuM:ÚúûÂ~&¼ð/‰¼ià›¯xræyt}fçÁÞ1ñg…gÔlîeð÷‰µÝ!¬õK ‚¾øÿ)MýÿìÀ?य़úÑ_ðIÚûþ¾øÿ)MýÿìÀ?य़úÑ_ðIÚûþŠ( €?à“¿ò‹/ø&Ÿý˜ìoÿ¬ëðæ¾ÿ¯€?à“¿ò‹/ø&Ÿý˜ìoÿ¬ëðæ¾ÿ ø×ûBüýšü+§øëöŒøãðà‚uoZøOKñÆ¿‰~ øWá]KÅWÚv«¬XøgOñ޵­H¼ñæ‘¡kš¥®oy&£q§hÚ­ô6Ïm§^KÀ¿ÚÇöXý¨?á)ÿ†hý¥¿gÿÚ#þìOøMáEüdøuñoþÿøI¿µÿáÿ„§þøƒþÿøH?á׿±?µ¾Éý«ý‰«ýƒíÙ·žOäüÿ((ý¹¿îÙ¿õ°ÿgÚóÿø9#áÖàÙ;ÁŸ¶÷ÀIÿáZÿÁM>~гW‡¿b‰ß |;àI?hï‹Þ;ñÄOáÃ~Ê:j>ñÄŸüYðÿãÆj³'‡ÿGñåÖ¨j>'ðLjükãë€¿Þø±ð³â¿ü&¿ð«~%ü?ø•ÿ ×âˆþüEÿ„Æ^ñü ?üö?øK¾x×þÝKQÿ„Wâ…´tÿøHü®ýƒÄzÛìÿ´ôÛ_µA¿Ð+ñoŸþxþ )§üð?ìðÛâo‡ÿà¯ÿ²ÿÀ/ ø»ÂŸ³oˆü5àOÿÃ{kðMÉõŸ_´?ï üqðçˆ>6~Ððÿ핪éž4ø§üRømÿ sXøoámvëFðe•Õχm¿_¾é¿ôhZÆ¿|?øñ6ßûOþo|-øiâ?ƒþÕüÝcPŸFþÂøuâÏ‹|Aáß°x~]+LÔÿ´>)x£û_X²Ô5Û_ìK-NÛú@ WÀ¿å)¿±¿ý˜ü³ÿZ+þ ;_×À¿å)¿±¿ý˜ü³ÿZ+þ ;@ÑEQEó‹5ŸƒP~Ú¼=®xOÄŸ´©û0~×zÏÃ[\ξðçÁ­â·ìEcñÛÂzÍšøšÖÚxÓÆÞ#ýœõ ÜÍàív{];À^,ŠßÄÞŽòëKñ§¿ø³Åžð…|Mã¯x›Ãþ ðO‚ü?¬ø³Æ>1ñf³§xs¾ð¯‡4ëcÄ>&ñ7ˆu‹›=#Aðþ…¤YÞjšÎ³ª^ZéÚ^ks}}s´ʾâÍgàÔ¶‡À/kžñçíª~̵޳ðÃÇV×3¯…|9ðkAø­ûXüvðž³f¾&µ¶ŸÄ4ñ·ˆÿg=cÃ73x;]ž×Nð‹"·ñ7„㼺Òüiëÿ~øãÂω>)h_ð”|2øÁðÿÆ_ ~"øgûOXÑ?á"ð'Äj^ñv…ý³áÝCHñ‘ý¯áý_PÓÿ´ô-WLÖ,>Ñö­3P²½Š ˜€>@ÿ‡±Á,¿é%Ÿ°þ&GìëÿϾßðŸ‹<+ãß øgÇ^ñ7‡üiàŸxFñgƒ¼cá=gNñ…|Yá_éÖÚLJ¼MáŸè÷7šF½áýwH¼³Õ4mgK¼ºÓµM:êÚúÆæ{iâ•¿žøTÿ ?â$øQð­>ÿÂ’ÿˆpáSÿÂÿ„7ßð«?áVÃyÂÿ Óþïöoü"_ð¯ÿáÿŠ[þßìøG?áÿ‰'öoögú-}ñkã—ÆÏØßXøCð;öIµýŸÏì¥û6~ÐðL¯ØGÄßãøeñwãŽþ|"ý¢¼wðsà¾â?þÓ¾,ý¤þ xKá¿ÄøKƺ‹£üð÷ÃOÛ?ãf‹£ë¿³í ñŽóKø[ûS[Oð´õûáoÅ…Ÿ| ¡|Rø)ñ/áÿƆ^(þÓÿ„gâ/Âßxsâ¸ý¢>iÖZv¡âÏÍâÍ\ð†þ übþ—¨¯ˆ?àž?ÿm¯ŠŸ²ÿ…|Cÿýž¼?û5þÖ6~ ñŽñÀ¾Ö¼¯|3Õ´ë]ßxÅŸ ¯<ñÇãõÌ~¾ðF£ húå·Œüc¦ø©>!h^6– Ùø:o jšÏÛôóìE¬üñì_û"x‡ösðŸˆ<û>kß³À-gàO|Ys=犼ðkTøSá;ï†ñ5å׉¼isuâx&}GÖnn h?¿b+ŽÞÖl×ÄÖ¶ÓøƒÆž6ñìç¬xfæok³ÚéÞñdVþ&ðœw—Z_>Ÿ¯Î†ÿ|?ñSöÐøUáï‹_±í?û5þÐv³íA¬üñׯ¿~Ëú÷…uoƒVÿ¿c›ÚÂz}ŸìÍû]ü~¶ÄÞ6Ôf}bÖçÇ~ÓgM;BÕb🉬ã›Å^³ú?@ñÓö±ý–?eÿøE¿á¥ÿioÙÿöwÿ„ãûoþ¯ø^Ÿ>|$ÿ„ÃþŸìøI?áÿ„ÿÄ~ÿ„ƒþÿøH4í¿ìŸµÿeméoû?ö•Ÿ¿ðSö…øûJxWPñ×ìçñÇàÿÇïé> ºðž©ã‚ŸüñSºoЬtí+X¾ðΡâëZö‘gâ =#]ÐõK­âò=FßNÖt«é­’ÛQ³–oÄø8†çÅV~ÿ‚>^xFðÿˆümkÿÞÿ‚~Üø;ÃÞ,ñ6£à¿ ë¾*ƒNøÙ/‡´oxÇGðŸu øTÕÖÎÇYñ6—à_j:=Ωcá?ÜÚÅ£Þ|àŸÛ{Å_~!ÁV¿à¹_´ü?ðâ·ìëðá×ì]ûDÁ¾j:Œÿ£ø™ÄÏ…Ú/ì§ûJþÖÿuåð‚~(xƒÇ×>1ñw‡¾ |zøuð7â/ÃÛØ¿_Ñâø?ñâŠ| ãÿ‡Zˆö;E~ü0ý·ÿàª>ÿÁDþ3~ß¿±_‡ô?ƒ³ìÁâ_Ú7ögñŸ…#ø-ð_Qø¹¨ü!øgâÏ|ZøUñ{Â^ý¿à¡ú¿ÃŸkú½¦‰§|+ñ'†Æ>_ è3ñ?oô¯j^øysïú×ÅŸø)'ìåðC_ñŸíEâ?؃ÇÞ6ñψ?`ß‚Ÿ nþü*øó௠ü-øûûXþÒ¾ý˜þ*j¼;ñãw5Ž_þ êÿ¾üCø}uá¿þÏ~4øÛ§h_üâ=+à]Σá¿Y~¯×?âÏxWÀ^ñ7޼uâoø/Á> ðþ³âÏøÇÅšÎáÏ øO¾Ó®uø›ÄÞ!Ö.lôÃú‘gyªk:Ωyk§izu­ÍõõÌÐK*þ0üAý±?o¯‚÷ß´×Àoˆ:Ÿìâ_¾øÿ§ÿ…ñóÁ¿þ4hÿ 5_…ŸðRÛ%¿c}_þÏìÁ­þÐz¯‹l~ |ñoïŒ^%Óáý­o<9ñÚÇÃIî¿áÔôÿè×Þ¿qñ#þ IâÚVoÙ3Àÿb Wø1û0|=øùñïö„ñ_ì¿ñçĺwÄ?~Ñ_þ?x à—Ãß„?³.‘ûcxZçá·‡üàŸÙ§â¡ñSâŒÿj¯‰úмU­ø2 |<ðÆ‘>¸úXéÿ„üYá_øWÃ>:ð/‰¼?ãOøÓÃú7‹<ã ë:wˆü+âÏ øN¶Ö<=âo ø‡G¹¼Ò5ïëºEåž©£k:]åÖªi×V×Ö73ÛO­ÐWÀðIßùE—üOþÌö7ÿÖuøs_ÐÌ,ðÏÆ[¯ÛCàŒt;Ÿ/ìù¡~̵߆~'ÙÛx² ? Ïñ—ÅŸ¿b-SàMγàVÖ`¹ñˆ-|à¿Ú2/ ø²jx/N¼ñfq¬èRxöÖÇÄO×çí‡âÿÛ áOÇ߃gÙ«ãíiàö`ý²¼âoƒ?>6| øCá[ÿ¾-ñ§ìâÙ»\øœÿ¾.ü8Ò"ðüZGÃŽ¾_‰>ð¯Å¯|0Ó¼câ¦xR¶ñmî«~ ü:ÿ‚åÁ`|Oÿø'û>|Wÿ‚&|`øû>x¿Ä~x«FšÊ÷Æß>!üeøyð¿ã§ÄqÀÚ¿âïŒÿdØ¿]ðýÖðÎßÄPi²j>)ƒÅ_|ñ Åü{âù5ï éºXõ»_0~ÄZÏÁ¯þÅÿ²'ˆg? øƒÀ_³æ½û0|Ö~øÅ—3Þx«ÁµO…>¾øaá?^]x›Æ—7^ ðç‚gÐô}fæãÆ>,ž}FÎæY¼M®Èͪ]xÿü6GíÿHý¿ÿðãÁ,¿úe•ì±³ðkıì‰âÙÏÂ~ ðìù¯~̵Ÿ>ñeÌ÷ž*ð_Á­SáO„ï¾xOÄ×—^&ñ¥Í׈<9à™ô=Y¹¸ñ‹'ŸQ³¹–ok²3j—@Æ¿Ûwö/ýšüU§øöŒý®ÿf€>6Õ¼?kâÍ/Áßþ>ü)øWâ­K·ڎ«£Øø›Oð÷޼Y ê÷ž¼Õô-sKµÖmìäÓ®5U±†åîtëÈ¡çüÿ ý~%}þ×íÃû xûûGâ€>éÿð…~ÒßüUöÿŠÿá#ÿ…[ðÒÏû Æ·ÿjøñ+þïÂàØ7øÆ?ðŠøþÝ7QþÃÔþËùÿaÔ¾)éðYÏø7ËPø)à߇ÿ>&ÛÿÃ×ÿáðÅ/‰~#ø?àM_Íý•>Á¬ÿnüEðŸÂŽ> ðïØ2~ÕðYïø*7À=Æÿí|i¯ü3øSá]öéÿ‚šþÒ_þ!jŸþj¾ñOÅ/[|)ýž|má|>´øð¯NÕ¾!x§Â×þ"ø‡¡øoIÕ¡¿ýžðÅ…Ÿÿá5ÿ…[ñ/áÿįøV¿ ðïØ`ñf³ðjÛCà‡µÏ ø‚óöƒÕ?fÚïYøaã«k™×¾ø5 üVýˆ¬~;xOY³_ZÛOâxÛijž±á›™¼®Ïk§x Å‘[ø›ÂqÞ]i~4(ñf³ðjÛCà‡µÏ ø‚óöƒÕ?fÚïYøaã«k™×¾ø5 üVýˆ¬~;xOY³_ZÛOâxÛijž±á›™¼®Ïk§x Å‘[ø›ÂqÞ]i~4(Çÿà“¿ò‹/ø&Ÿý˜ìoÿ¬ëðæ½ö•ý„fÚßÇ~)|kð¿Ä ‰¿³ü,ŸøQÿ~üý¡?gŸü9ÿ…Á£è>ø—ý…âïÙëâŸÂï7ü%þðÞ•¡jÚ…î4uÔ4Ë_³YkzÔ‡ŸÿÁ'å_ðM?û0ØßÿY×áÍ}ÿ@œÁ&a»Éü{«kþøÁã_|EðÿÂO ë_¾#þ×ß¶7ÄϺ…~üeÓho…žøcûC|Aøùâ_Ž?ðwµÍÆ7:7Áȼ§x»Å^$ñ7ˆ|omâM_ÄzååÿÛôPÄ_²v‰û:j?>7~ÉÞ ñ¿h?øÀ:±á_ß·íq¥üø“?„<+ðoá^7ø„|Bÿµ†íþ0iÿþ ø+H›ã—ü(OüeñüþƒCñ—ŽÖ?xÇÅR°_쇧~È þ+Ayeáû_Šß´çí?ûC~Ùß´¾×¼Uâ? Œ¿´Ä-CÆ:‡†¼3¬x®-&çYðÿ¿§~ èÞ-±ðOÂè>$iß ­¾(êŸ üâŸx‡ÃÖŸoÑ@|ñþR›ûÿÙ€ÁK?õ¢¿à“µ÷ý|ñþR›ûÿÙ€ÁK?õ¢¿à“´÷ýQ@Á'å_ðM?û0ØßÿY×áÍ}ÿ_Á'å_ðM?û0ØßÿY×áÍ}ÿ@ |ý„fÙâŸÆ¯Œ_ü/ñÀþ-ý¢> |Gø±ñ‹Hÿ…ÿûBx›ágŒ>)ü[ñ⟈?ÿáEøÃ⟈> xâˆ5?é¿ð™x[áö‰â=+ÖŸð‡èš–›á+‹­câŸì#û3üiÿ†¡ÿ…‘áˆïü6WÃÿ†? ÿh±üý¡<1ÿ ÂÏ„?ð–ÂðÓßðŠ|SÐÿáT|?øO¾ Âkàß„?ðøsâü,ÿÂÏÓ|aÿ ¿Šµ¾¿¢€> ý¨?àœŸ±ïí‹â¤ñ×dž^ Õ'ñº¯×þðŸ…|á_ øÀ¾ðÿ‚üà¿èÞðwƒ¼'£iÞð¯„ü+áÍ:ÛGð÷†|3áíÚÏHÐ|?¡ivz^£ivvºv—§ZÛXØÛAmQ/AEðÄoùJoìoÿfÿ,ÿÖŠÿ‚N×ßõðÄoùJoìoÿfÿ,ÿÖŠÿ‚NÐßôQE|ÿÿ”YÁ4ÿìÀ?cýg_‡5÷ý|ÿÿ”YÁ4ÿìÀ?cýg_‡5÷ýxíGû.| ý´~øçöhý¥ü ÿ +à—įøFá5ðWü$Þ1ðwö×ü!Þ1ð÷ü9ÿ€Áö­2êöÊãÇþÁ:ÿe_†|ûAØè>"üdøcáÿøgáoŽ¿hßÚ¯ö®ý«õ…:wÄÈ4;ˆW? ¬ÿiïŸt†~ ñ¦‘áÝ/þ$ñgtíÅZ·…Vÿ—šÌ¾Ö5.ÿíú(ä~Â?³?Š< ñ×áÖ·áˆ>ý¤h ~Ôõ>?þКwŽõ?ŽÞ Ö>k¾ñÏ„~)i¿ìþ&ü)ÿ…y{ðàô¼3ðŸÅÞ ðwÃýáï‡|;àßhž¶“L—éÿ øgNð_…|3àíçÄšG„ü?£xgK¼ñg‹`ñf³ðjÛCà‡µÏ ø‚óöƒÕ?fÚïYøaã«k™×¾ø5 üVýˆ¬~;xOY³_ZÛOâxÛijž±á›™¼®Ïk§x Å‘[ø›ÂqÞ]i~4ú~¾`ñf³ðjÛCà‡µÏ ø‚óöƒÕ?fÚïYøaã«k™×¾ø5 üVýˆ¬~;xOY³_ZÛOâxÛijž±á›™¼®Ïk§x Å‘[ø›ÂqÞ]i~4ú~€>@ÿ†ý™ÿá²á¿¿áøÿ [ÿ ÿþ?ü,/ø_ÿ´'ü#Ÿð«>Ãö_øVŸð§áiÿÂ’ÿ…ý§ÿ·ü!¿ð®áÿ…§ÿû7þŸüVãþ9ÿ‚LþÃ~=ñWÄŸÞxã‚î~/ü`Ðÿh_‰>ø)û_~Øß³×Ã?|}ðæ£àwIøã¨| øñóáǘ¾0E⟇ñÅ×Ä»ZøÓTø…á}+â©­^øÚ¯·èýñƒà²¯€¾2蟼5 |`ÆÞøÁñ‡ã÷…t=gö«ý«¼UðkÂ~?AñbßâïÄ/ þΞ*øÙ­~ÏžñŒ£øéñ}®fÐ>iÐisüCñ5Ö…o¥Üß”ñÏü“ö=ø—á_‰>ñßÃ/xžÛâ§í?¡þÚ!ñ.¥ñ›ã›üLð÷í?á];Á:ƒ¾/ü$ø¿Ĩ¾+|ñ¼-ðëÁþð—Àÿü<ðÿ¾é2|<ð–£x'TÖ4 Cíú(À?fÿÙsàOì‘àMcá×ÀÂáÿ|@ñ¿ÅjŸ‰¼cñǾ)üHÖ$×|sñ/â—Å/‰>!ñÄߊ?x{\ðŸˆ/?h=Sö`ý®õŸ†:¶¹|+áσZÅoØŠÇã·„õ›5ñ5­´þ ñ§¼Gû9ë¹›ÁÚìöºw€¼Y¿‰¼'åÖ—ãO§ëæk? ý´>x{\ðŸˆ/?h=Sö`ý®õŸ†:¶¹|+áσZÅoØŠÇã·„õ›5ñ5­´þ ñ§¼Gû9ë¹›ÁÚìöºw€¼Y¿‰¼'åÖ—ãO§èæÚ§ö6ýž?mO ü:ðwíàïx³HøGñƒÂ?¾ÞxOâŸÅ¿ƒž*ð/Æ_iÞ"Òüñ Ã>:ø+㯇ž6ÒüAá‹ok’èÓ[ø…`²Ônmµˆm×WÒô«ë.â/ìû'|[ý£ ý¬>#ü)ÿ„·ãoü3ÿˆ¿e]gUÔüuñ'þO~Î>-ºñv¡âo‚ÿ¾ [xÆßà—Å¿‡úÞ§ãjÚ†ñ;áß‹mn5ì-a‡öŸ„¼'u¢}E|ð/þ qû þÎÿð”ÿÂð3þøL?gýöNÕ¿áz|MøÅûP}“öXðÿö¿Ø?fŸ ÃKüAø·ÿ?ìÿqý·yý·ðoÁ_ðü:ñ7“¤ÂIáÍWþýû7Ÿð§üOöðŸÁ¯‹ß³àø+âüøåðá_ÀˆøÉñçö‹øõ§/Á¯søÎûàŸÃßÞ|møµñWø?áÿƒú¿Äx‹á|?µOà/_Yx¯Â·W‰4-TÒÿGè €4¯ø%ÿìY£øãO€í¾|@»ÿ†ˆøðsâ—Æ‰ºïí%ûNø›öŽñߎÿg}cÀþ ø ®êµgˆþ1ê¿´Õü)}káÏ„õ/…ºfñoMÑü ui©Éá?Mÿ„›ÄãXè>#ÿÁ:ÿe_Šž*øcñÄúÆ /â·Â?ƒíð¿~~ÕµwÁϺ·Á¦Ô|?®‡¿¾=ü"øÙàŒŸ<>xÇC¹ñþÏšìÁû]øgâ}·‹ ³ð¬ÿ|Yñ[ö"Õ>Üë>mf Ÿø‚×Á> ý£"ðÏ‹!ðö©‚ôëÏh÷Î…'ml|Gôý|ÁâÏ üeºý´>xÇC¹ñþÏšìÁû]øgâ}·‹ ³ð¬ÿ|Yñ[ö"Õ>Üë>mf Ÿø‚×Á> ý£"ðÏ‹!ðö©‚ôëÏh÷Î…'ml|GôýóìE¬üñì_û"x‡ösðŸˆ<û>kß³À-gàO|Ys=犼ðkTøSá;ï†ñ5å׉¼isuâx&}GÖnnkß³À-gàO|Ys=犼ðkTøSá;ï†ñ5å׉¼isuâx&}GÖnnøÁsûBøóYøÍñÏTøËà¿·ŸüMñÎóã„ÿikï‰SþÒ>øÁuñGÆž1ñ%ÏĽâÅŸ'ƒÅž&ðäºÓx[^Õ4K¯·è ýžÿf/„_²þãÍá%§Äøºâ—Ä/|RøÙñ³ö€ñߌîë¿þ?|Bø›ñ÷ì_þxÂzf™'‰¿±ô­Ã:}¶›§Ú¤4ÞÿEó‹5ŸƒP~Ú¼=®xOÄŸ´©û0~×zÏÃ[\ξðçÁ­â·ìEcñÛÂzÍšøšÖÚxÓÆÞ#ýœõ ÜÍàív{];À^,ŠßÄÞŽòëKñ¡G‹5ŸƒP~Ú¼=®xOÄŸ´©û0~×zÏÃ[\ξðçÁ­â·ìEcñÛÂzÍšøšÖÚxÓÆÞ#ýœõ ÜÍàív{];À^,ŠßÄÞŽòëKñ¡@ã á?ø)·ü“À^ðÏ| ÿý·üàŸøF🃼á?Ú¿ãχ<+á? øsN¶Ñü=០ø{Gñõž‘ øBÒ,ìô½FÒìítí/Nµ¶±±¶‚Ú¢^ƒþÅÿMÿ¤–~ßÿø™´Wÿ ðþá?x;µǟxWÂ~ðæm£ø{Ã>ðöãë=#Aðþ…¤YÙéz6¥ÙÚéÚ^kmccm´D½ü=‹þ ›ÿI,ý¿ÿñ2?h¯þxÔQ@ü=‹þ ›ÿI,ý¿ÿñ2?h¯þxÔÃØ¿à©¿ô’ÏÛÿÿ#öŠÿçEÃØ¿à©¿ô’ÏÛÿÿ#öŠÿçGü=‹þ ›ÿI,ý¿ÿñ2?h¯þxÔQ@ü=‹þ ›ÿI,ý¿ÿñ2?h¯þxÕý>ÿÁ£ŸµíOûPÁS~*ÃKþÒß´íÿ?ìñ»þ¯ø^Ÿ>"ü[ÿ„?þoÚ+ö>ÿ„þoøOüGâøGÿá ÿ„AþÛþÉû'ö¯ö&‘öÿ´fÙy%þ”QE‡7„ÿà¦ßðROxWÃ>ð/üößð_‚|áýÂ~ðw„ÿjÿ>ð¯„ü+áÍ:ÛGð÷†|3áíÇÖzFƒáý H³³ÒômK³µÓ´½:ÖÚÆÆÚ h"‰zø{ü7þ’Yûÿâd~Ñ_üñ¨¢€ø{ü7þ’Yûÿâd~Ñ_üñ¨ÿ‡±ÁSé%Ÿ·ÿþ&GíÿÏŠ(ÿ‡±ÁSé%Ÿ·ÿþ&GíÿÏø{ü7þ’Yûÿâd~Ñ_üñ¨¢€ø{ü7þ’Yûÿâd~Ñ_üñ«ú}ÿƒG?kÚŸö ÿ‚¦üTÿ†—ý¥¿hÚ#þØãwü!_ð½>2|Eø·ÿü$ß´Wì}ÿ ü"ßðŸøÄðÿÂAÿþƒý·ý“öOí_ìM#íÿhþͲòJ(ý(¢Š(¢Šøâ7ü¥7ö7ÿ³ÿ‚–ëEÁ'kø¢ÿƒE?mßÛCö”ÿ‚’|mð/íû]þÓÿ¼¤þÄ|Y¥ø;ã_Çߊß<+¦øªÇãÏìÕ£Øø›Oð÷޼Y¯i~ ³Ò5ÝsKµÖmìãÔmôígU±†å-µȦ( þ ý·mÚSþ Iñ·À¿´gíwûOü~ðN“û|Iñf—àï~+|Tð®›â«?³Vcâm?ÃÞ:ñf½¤Yø‚ÏH×uÍ.×Y·³Q·ÓµVÆ”¶Ôo"˜ÿƒE?mßÛCö”ÿ‚’|mð/íû]þÓÿ¼¤þÄ|Y¥ø;ã_Çߊß<+¦øªÇãÏìÕ£Øø›Oð÷޼Y¯i~ ³Ò5ÝsKµÖmìãÔmôígU±†å-µȦ( þ ý·mÚSþ Iñ·À¿´gíwûOü~ðN“û|Iñf—àï~+|Tð®›â«?³Vcâm?ÃÞ:ñf½¤Yø‚ÏH×uÍ.×Y·³Q·ÓµVÆ”¶Ôo"˜ÿƒE?mßÛCö”ÿ‚’|mð/íû]þÓÿ¼¤þÄ|Y¥ø;ã_Çߊß<+¦øªÇãÏìÕ£Øø›Oð÷޼Y¯i~ ³Ò5ÝsKµÖmìãÔmôígU±†å-µȦ( þ ý·mÚSþ Iñ·À¿´gíwûOü~ðN“û|Iñf—àï~+|Tð®›â«?³Vcâm?ÃÞ:ñf½¤Yø‚ÏH×uÍ.×Y·³Q·ÓµVÆ”¶Ôo"˜ÿƒE?mßÛCö”ÿ‚’|mð/íû]þÓÿ¼¤þÄ|Y¥ø;ã_Çߊß<+¦øªÇãÏìÕ£Øø›Oð÷޼Y¯i~ ³Ò5ÝsKµÖmìãÔmôígU±†å-µȦ( íwþ ;ÿ(²ÿ‚iÿÙ€~Æÿúοkïú( €>#ÊScû0ø)gþ´Wüv¿Š/ø4SöÝý´?iOø)'ÆßþÑŸµßí?ñûÁ:OìAñ'Åš_ƒ¾5ü}ø­ñSºoЬ~<þÍZ=‰´ÿxëÅšö‘gâ =#]×4»]fÞÎ=FßNÖu[nRÛQ¼ŠbŠ?àÑOÛwöÐý¥?ट| ûF~×´ÿÇïé?±ÄŸi~ø×ñ÷â·ÅO é¾*±øóû5hö>&Óü=ã¯kÚEŸˆ,ôw\Òíu›{8õ};YÕla¹KmFò)ø4SöÝý´?iOø)'ÆßþÑŸµßí?ñûÁ:OìAñ'Åš_ƒ¾5ü}ø­ñSºoЬ~<þÍZ=‰´ÿxëÅšö‘gâ =#]×4»]fÞÎ=FßNÖu[nRÛQ¼ŠbŠ?àÑOÛwöÐý¥?ट| ûF~×´ÿÇïé?±ÄŸi~ø×ñ÷â·ÅO é¾*±øóû5hö>&Óü=ã¯kÚEŸˆ,ôw\Òíu›{8õ};YÕla¹KmFò)ø4SöÝý´?iOø)'ÆßþÑŸµßí?ñûÁ:OìAñ'Åš_ƒ¾5ü}ø­ñSºoЬ~<þÍZ=‰´ÿxëÅšö‘gâ =#]×4»]fÞÎ=FßNÖu[nRÛQ¼ŠbŠ?àÑOÛwöÐý¥?ट| ûF~×´ÿÇïé?±ÄŸi~ø×ñ÷â·ÅO é¾*±øóû5hö>&Óü=ã¯kÚEŸˆ,ôw\Òíu›{8õ};YÕla¹KmFò)ø4SöÝý´?iOø)'ÆßþÑŸµßí?ñûÁ:OìAñ'Åš_ƒ¾5ü}ø­ñSºoЬ~<þÍZ=‰´ÿxëÅšö‘gâ =#]×4»]fÞÎ=FßNÖu[nRÛQ¼ŠbŠ?àÑOÛwöÐý¥?ट| ûF~×´ÿÇïé?±ÄŸi~ø×ñ÷â·ÅO é¾*±øóû5hö>&Óü=ã¯kÚEŸˆ,ôw\Òíu›{8õ};YÕla¹KmFò)ø4SöÝý´?iOø)'ÆßþÑŸµßí?ñûÁ:OìAñ'Åš_ƒ¾5ü}ø­ñSºoЬ~<þÍZ=‰´ÿxëÅšö‘gâ =#]×4»]fÞÎ=FßNÖu[nRÛQ¼ŠbŠ?àÑOÛwöÐý¥?ट| ûF~×´ÿÇïé?±ÄŸi~ø×ñ÷â·ÅO é¾*±øóû5hö>&Óü=ã¯kÚEŸˆ,ôw\Òíu›{8õ};YÕla¹KmFò)¿ÑrŠ(¯óƒÿ‚ãþÖ?µ?ì¿ÿ²ÿƒmÿáš?ioÚöwÿ„ãö´ÿ„×þ_ÆOˆ¿ ?á0ÿ„göuý†?áÿ„§þøþøGÿá ׿±?µ¾×ý•ý·«ýƒìÿÚW¾qEtð_?ÛwöÐø9ÿÛÿƒw|uð‹ö»ý§þøÛãgìAâÏŒÞ1øqñ÷â·üUñsÅIðö ÖÄß¼Cáiz¿|@º¿‹Á6ÿàÝß|"ý®ÿiÿ…~6øÙû_ø³ã7Œ~|}ø­à|\ñR|ýˆ5„ñ7ÄïøcÅš^¯ãß.¯âÏj‹¬øªóVÔWQñ/ˆ/…ȹÖunOø/Ÿí»ûh|ÿ‚mÿÁ»¾:øEû]þÓÿ ümñ³ö ¿ñgÆoü8øûñ[Àþ*ø¹â¤ø ûk âo‰Þ!ðÇ‹4½_Ǿ ]_Åž*ÕYñUæ­¨®£â__ ‘s¬ê2Ü”Pÿóý·mƒŸðM¿ø7wÇ_¿k¿Úá_¾6~Äþ,øÍã‡~+xÅ_ñf—«ø÷Ä «ø³ÅZ¢ë>*¼ÕµÔ|Kâ ár.uF[“þ çûnþÚ?à›ðn~×´ÿ¿|lýˆ/üYñ›Æ?>>üVð?о.x©>~ÄÂx›âwˆ|1âÍ/WñïˆWñgеEÖ|Uy«j+¨ø—ÄÂä\ë:Œ·%ý?~Â,ñW|Uÿ0ñ׎¼MâxÛÆŸðB¾,ñŒ|Y¬ê>#ñW‹ Initial installation of a new utility provides the first, lasting impression of how well the software is likely to perform. From the start, &SCons; has made clean installation a priority.
Version Control Distributing an application like &SCons; that depends on a package normally found in a library poses a problem. If the &scons; script and the &SCons; Build Engine are installed separately, it could be easy to introduce a version mismatch between the Build Engine installed in /usr/lib/python*/site-packages and the &scons; script installed in /usr/bin. Such a mismatch could possible mean exceptions that prevent builds, or even worse, silently unreliable builds. To reduce the possibility of a version mismatch, the &scons; script looks first for its imported modules in /usr/lib/scons-{version}/, then in /usr/lib/scons/, and then in the normal &PYTHONPATH; locations, including /usr/lib/python*/site-packages). Searching in a version-specific library directory first makes it convenient to install and use multiple side-by-side versions of &SCons;, which is sometimes important when verifying that a new version does not introduce any errors into the local build process. Searching next in an &SCons;-specific library directory makes it convenient for other software to find the &SCons; Build Engine without having to worry about installing separate copies for multiple versions of Python.
Packages &SCons; is currently distributed in the following packages: scons-version.tar.gz The traditional .tar.gz file, installable by running setup.py. scons-version.noarch.rpm An RPM file for typical installation. scons-version_all.deb A Debian package. scons-version.win32.exe A Windows installer. scons-version.src.rpm A source RPM file. scons-src-version.tar.gz A tarball of the &SCons; source tree, including the full set of regression tests.
Like other software written in Python, &SCons; benefits greatly from the tremendous effort put into the distutils by Greg Ward and others. These take care of 90% of the work by making it almost trivial to generate the appropriate RPM files, Debian packages, and Windows installer.
Default Builder Objects As part of the installation process, &SCons; runs a set of scripts that look for popular compilers and other tools and set up appropriate default &Builder; objects for the tools found. These &Builder; objects are then used to initialize the default &consenv; values.
Default Scanner Objects Additionally, &SCons; comes with a stock set of &Scanner; objects for the various file types that it supports out of the box. Any unusal &Scanner; objects required for a specific tool will be detected at installation time and associated with the appropriate &Builder; object for the tool.
scons-doc-2.3.0/doc/python10/acks.xml0000644000175000017500000000160012114661557020111 0ustar dktrkranzdktrkranz First, many thanks to the great group of developers who dove in right from the beginning and have contributed the code and ideas to make &SCons; a success: Chad Austin, Charles Crain, Steve Leblanc, and Anthony Roach. Thanks also to those on the scons-devel mailing list who have contributed greatly to the discussion, notably including David Abrahams, Trent Mick, and Steven Shaw. &SCons; would not exist today without the pioneering work of Bob Sidebotham on the original &Cons; tool, and without Greg Wilson's having started the Software Carpentry contest. Thanks also to Peter Miller for: Aegis; the testing discipline that it enforces, without which creating a stable but flexible tool would be impossible; the "Recursive Make Considered Harmful" paper which led me to experiment with &Cons; in the first place. scons-doc-2.3.0/doc/python10/arch.fig0000644000175000017500000000204312114661557020054 0ustar dktrkranzdktrkranz#FIG 3.2 Landscape Center Inches Letter 100.00 Single -2 1200 2 6 3825 2775 4650 3300 4 0 0 50 0 14 20 0.0000 4 135 825 3825 2925 scons\001 4 0 0 50 0 0 20 0.0000 4 255 690 3825 3225 Script\001 -6 6 3600 4200 7200 5400 6 4200 4650 6675 4950 4 0 0 50 0 0 20 0.0000 4 255 1515 5100 4875 Build Engine\001 4 0 0 50 0 14 20 0.0000 4 165 825 4200 4875 SCons\001 -6 2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5 3600 4200 7200 4200 7200 5400 3600 5400 3600 4200 -6 6 4725 3825 6150 4050 4 0 0 50 0 14 20 0.0000 4 165 825 4725 4050 SCons\001 4 0 0 50 0 0 20 0.0000 4 195 465 5625 4050 API\001 -6 2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5 3600 2400 3600 2400 3600 2400 3600 2400 3600 2400 2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5 3600 2400 4800 2400 4800 3600 3600 3600 3600 2400 2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5 3600 3600 7200 3600 7200 4200 3600 4200 3600 3600 2 2 1 1 0 7 50 0 -1 4.000 0 0 -1 0 0 5 6000 3600 7200 3600 7200 2400 6000 2400 6000 3600 4 0 0 50 0 1 20 0.0000 4 210 570 6300 2925 other\001 4 0 0 50 0 1 20 0.0000 4 270 975 6150 3225 interface\001 scons-doc-2.3.0/doc/python10/node.eps0000644000175000017500000002211212114661557020105 0ustar dktrkranzdktrkranz%!PS-Adobe-2.0 EPSF-2.0 %%Title: build/doc/python10/node.fig %%Creator: /usr/bin/fig2dev Version 3.2 Patchlevel 3d %%CreationDate: Sun Jan 2 01:21:05 2005 %%For: knight@casablanca.home.baldmt.com (Steven Knight) %%BoundingBox: 0 0 452 362 %%Magnification: 1.0000 %%EndComments /$F2psDict 200 dict def $F2psDict begin $F2psDict /mtrx matrix put /col-1 {0 setgray} bind def /col0 {0.000 0.000 0.000 srgb} bind def /col1 {0.000 0.000 1.000 srgb} bind def /col2 {0.000 1.000 0.000 srgb} bind def /col3 {0.000 1.000 1.000 srgb} bind def /col4 {1.000 0.000 0.000 srgb} bind def /col5 {1.000 0.000 1.000 srgb} bind def /col6 {1.000 1.000 0.000 srgb} bind def /col7 {1.000 1.000 1.000 srgb} bind def /col8 {0.000 0.000 0.560 srgb} bind def /col9 {0.000 0.000 0.690 srgb} bind def /col10 {0.000 0.000 0.820 srgb} bind def /col11 {0.530 0.810 1.000 srgb} bind def /col12 {0.000 0.560 0.000 srgb} bind def /col13 {0.000 0.690 0.000 srgb} bind def /col14 {0.000 0.820 0.000 srgb} bind def /col15 {0.000 0.560 0.560 srgb} bind def /col16 {0.000 0.690 0.690 srgb} bind def /col17 {0.000 0.820 0.820 srgb} bind def /col18 {0.560 0.000 0.000 srgb} bind def /col19 {0.690 0.000 0.000 srgb} bind def /col20 {0.820 0.000 0.000 srgb} bind def /col21 {0.560 0.000 0.560 srgb} bind def /col22 {0.690 0.000 0.690 srgb} bind def /col23 {0.820 0.000 0.820 srgb} bind def /col24 {0.500 0.190 0.000 srgb} bind def /col25 {0.630 0.250 0.000 srgb} bind def /col26 {0.750 0.380 0.000 srgb} bind def /col27 {1.000 0.500 0.500 srgb} bind def /col28 {1.000 0.630 0.630 srgb} bind def /col29 {1.000 0.750 0.750 srgb} bind def /col30 {1.000 0.880 0.880 srgb} bind def /col31 {1.000 0.840 0.000 srgb} bind def end save newpath 0 362 moveto 0 0 lineto 452 0 lineto 452 362 lineto closepath clip newpath 0.7 414.7 translate 1 -1 scale /cp {closepath} bind def /ef {eofill} bind def /gr {grestore} bind def /gs {gsave} bind def /sa {save} bind def /rs {restore} bind def /l {lineto} bind def /m {moveto} bind def /rm {rmoveto} bind def /n {newpath} bind def /s {stroke} bind def /sh {show} bind def /slc {setlinecap} bind def /slj {setlinejoin} bind def /slw {setlinewidth} bind def /srgb {setrgbcolor} bind def /rot {rotate} bind def /sc {scale} bind def /sd {setdash} bind def /ff {findfont} bind def /sf {setfont} bind def /scf {scalefont} bind def /sw {stringwidth} bind def /tr {translate} bind def /tnt {dup dup currentrgbcolor 4 -2 roll dup 1 exch sub 3 -1 roll mul add 4 -2 roll dup 1 exch sub 3 -1 roll mul add 4 -2 roll dup 1 exch sub 3 -1 roll mul add srgb} bind def /shd {dup dup currentrgbcolor 4 -2 roll mul 4 -2 roll mul 4 -2 roll mul srgb} bind def /$F2psBegin {$F2psDict begin /$F2psEnteredState save def} def /$F2psEnd {$F2psEnteredState restore end} def $F2psBegin 10 setmiterlimit 0.06000 0.06000 sc % % Fig objects follow % % Polyline 7.500 slw n 2700 1200 m 4500 1200 l 4500 1800 l 2700 1800 l cp gs col0 s gr /Times-Roman ff 240.00 scf sf 2925 1575 m gs 1 -1 sc (Environment) col0 sh gr % Polyline n 2700 3600 m 4500 3600 l 4500 4200 l 2700 4200 l cp gs col0 s gr /Times-Roman ff 240.00 scf sf 3375 3975 m gs 1 -1 sc (Node) col0 sh gr % Polyline n 5700 1800 m 6900 1800 l 6900 2400 l 5700 2400 l cp gs col0 s gr /Times-Roman ff 240.00 scf sf 5925 2175 m gs 1 -1 sc (Walker) col0 sh gr % Polyline n 2100 2400 m 3300 2400 l 3300 3000 l 2100 3000 l cp gs col0 s gr /Times-Roman ff 240.00 scf sf 2325 2775 m gs 1 -1 sc (Builder) col0 sh gr % Polyline n 3900 2400 m 5100 2400 l 5100 3000 l 3900 3000 l cp gs col0 s gr /Times-Roman ff 240.00 scf sf 4125 2775 m gs 1 -1 sc (Scanner) col0 sh gr % Polyline n 2400 6300 m 3300 6300 l 3300 6900 l 2400 6900 l cp gs col0 s gr /Times-Roman ff 240.00 scf sf 2700 6675 m gs 1 -1 sc (Dir) col0 sh gr % Polyline n 0 6300 m 900 6300 l 900 6900 l 0 6900 l cp gs col0 s gr /Times-Roman ff 240.00 scf sf 150 6675 m gs 1 -1 sc (Entry) col0 sh gr % Polyline n 1200 6300 m 2100 6300 l 2100 6900 l 1200 6900 l cp gs col0 s gr /Times-Roman ff 240.00 scf sf 1425 6675 m gs 1 -1 sc (File) col0 sh gr % Polyline n 1050 5100 m 2250 5100 l 2250 5700 l 1050 5700 l cp gs col0 s gr /Times-Roman ff 240.00 scf sf 1200 5475 m gs 1 -1 sc (Node.FS) col0 sh gr % Polyline n 1650 5700 m 1575 5850 l 1725 5850 l cp gs col0 s gr % Polyline n 450 6300 m 450 6000 l 2700 6000 l 2700 6300 l gs col0 s gr % Polyline n 1650 6300 m 1650 5850 l gs col0 s gr % Polyline [60] 0 sd n 5100 6300 m 6300 6300 l 6300 6900 l 5100 6900 l cp gs col0 s gr [] 0 sd /Times-Roman ff 240.00 scf sf 5325 6675 m gs 1 -1 sc (Record) col0 sh gr % Polyline [60] 0 sd n 6600 6300 m 7500 6300 l 7500 6900 l 6600 6900 l cp gs col0 s gr [] 0 sd /Times-Roman ff 240.00 scf sf 6750 6675 m gs 1 -1 sc (Field) col0 sh gr % Polyline [60] 0 sd n 4950 5100 m 6150 5100 l 6150 5700 l 4950 5700 l cp gs col0 s gr [] 0 sd /Times-Roman ff 240.00 scf sf 5100 5475 m gs 1 -1 sc (Node.DB) col0 sh gr % Polyline n 5550 5700 m 5475 5850 l 5625 5850 l cp gs col0 s gr % Polyline [60] 0 sd n 4350 6300 m 4350 6000 l 7050 6000 l 7050 6300 l gs col0 s gr [] 0 sd % Polyline [60] 0 sd n 5550 5850 m 5550 6300 l gs col0 s gr [] 0 sd % Polyline [60] 0 sd n 3900 6300 m 4800 6300 l 4800 6900 l 3900 6900 l cp gs col0 s gr [] 0 sd /Times-Roman ff 240.00 scf sf 4050 6675 m gs 1 -1 sc (Table) col0 sh gr % Polyline n 5700 3000 m 6900 3000 l 6900 3600 l 5700 3600 l cp gs col0 s gr /Times-Roman ff 240.00 scf sf 5850 3375 m gs 1 -1 sc (Wrapper) col0 sh gr % Polyline n 900 1200 m 1800 1200 l 1800 1800 l 900 1800 l cp gs col0 s gr /Times-Roman ff 240.00 scf sf 1200 1575 m gs 1 -1 sc (FS) col0 sh gr % Polyline n 3600 4200 m 3525 4350 l 3675 4350 l cp gs col0 s gr % Polyline n 1800 5100 m 1800 4800 l 5550 4800 l 5550 5100 l gs col0 s gr % Polyline n 3600 4800 m 3600 4350 l gs col0 s gr % Polyline n 4200 1800 m 4160 1875 l 4200 1950 l 4240 1875 l cp gs col0 s gr % Polyline n 3000 6150 m 2960 6225 l 3000 6300 l 3040 6225 l cp gs col0 s gr % Polyline n 6300 3600 m 6260 3675 l 6300 3750 l 6340 3675 l cp gs col0 s gr % Polyline n 6300 2400 m 6260 2475 l 6300 2550 l 6340 2475 l cp gs col0 s gr % Polyline n 3000 4200 m 2960 4275 l 3000 4350 l 3040 4275 l cp gs col0 s gr % Polyline n 4200 3450 m 4160 3525 l 4200 3600 l 4240 3525 l cp gs col0 s gr % Polyline n 3000 3450 m 2960 3525 l 3000 3600 l 3040 3525 l cp gs col0 s gr % Polyline gs clippath 2235 5370 m 2235 5430 l 2386 5430 l 2266 5400 l 2386 5370 l cp eoclip n 3000 6150 m 3000 5400 l 2250 5400 l gs col0 s gr gr % arrowhead n 2386 5370 m 2266 5400 l 2386 5430 l 2386 5370 l cp gs 0.00 setgray ef gr col0 s % Polyline gs clippath 2715 3930 m 2715 3870 l 2564 3870 l 2684 3900 l 2564 3930 l cp eoclip n 3000 4350 m 3000 4500 l 1800 4500 l 1800 3900 l 2700 3900 l gs col0 s gr gr % arrowhead n 2564 3930 m 2684 3900 l 2564 3870 l 2564 3930 l cp gs 0.00 setgray ef gr col0 s % Polyline gs clippath 4485 3870 m 4485 3930 l 4636 3930 l 4516 3900 l 4636 3870 l cp eoclip n 6300 3750 m 6300 3900 l 4500 3900 l gs col0 s gr gr % arrowhead n 4636 3870 m 4516 3900 l 4636 3930 l 4636 3870 l cp gs 0.00 setgray ef gr col0 s % Polyline gs clippath 4230 2985 m 4170 2985 l 4170 3136 l 4200 3016 l 4230 3136 l cp eoclip n 4200 3450 m 4200 3000 l gs col0 s gr gr % arrowhead n 4230 3136 m 4200 3016 l 4170 3136 l 4230 3136 l cp gs 0.00 setgray ef gr col0 s % Polyline gs clippath 3030 2985 m 2970 2985 l 2970 3136 l 3000 3016 l 3030 3136 l cp eoclip n 3000 3450 m 3000 3000 l gs col0 s gr gr % arrowhead n 3030 3136 m 3000 3016 l 2970 3136 l 3030 3136 l cp gs 0.00 setgray ef gr col0 s % Polyline n 3000 1800 m 2960 1875 l 3000 1950 l 3040 1875 l cp gs col0 s gr % Polyline gs clippath 2970 2415 m 3030 2415 l 3030 2264 l 3000 2384 l 2970 2264 l cp eoclip n 3000 1950 m 3000 2400 l gs col0 s gr gr % arrowhead n 2970 2264 m 3000 2384 l 3030 2264 l 2970 2264 l cp gs 0.00 setgray ef gr col0 s % Polyline gs clippath 4170 2415 m 4230 2415 l 4230 2264 l 4200 2384 l 4170 2264 l cp eoclip n 4200 1950 m 4200 2400 l gs col0 s gr gr % arrowhead n 4170 2264 m 4200 2384 l 4230 2264 l 4170 2264 l cp gs 0.00 setgray ef gr col0 s % Polyline gs clippath 6270 3015 m 6330 3015 l 6330 2864 l 6300 2984 l 6270 2864 l cp eoclip n 6300 2550 m 6300 3000 l gs col0 s gr gr % arrowhead n 6270 2864 m 6300 2984 l 6330 2864 l 6270 2864 l cp gs 0.00 setgray ef gr col0 s % Polyline gs clippath 4785 6570 m 4785 6630 l 4936 6630 l 4816 6600 l 4936 6570 l cp eoclip n 5100 6600 m 4800 6600 l gs col0 s gr gr % arrowhead n 4936 6570 m 4816 6600 l 4936 6630 l 4936 6570 l cp gs 0.00 setgray ef gr col0 s % Polyline gs clippath 6285 6570 m 6285 6630 l 6436 6630 l 6316 6600 l 6436 6570 l cp eoclip n 6600 6600 m 6300 6600 l gs col0 s gr gr % arrowhead n 6436 6570 m 6316 6600 l 6436 6630 l 6436 6570 l cp gs 0.00 setgray ef gr col0 s % Polyline n 1350 1800 m 1310 1875 l 1350 1950 l 1390 1875 l cp gs col0 s gr % Polyline gs clippath 1320 5115 m 1380 5115 l 1380 4964 l 1350 5084 l 1320 4964 l cp eoclip n 1350 1950 m 1350 5100 l gs col0 s gr gr % arrowhead n 1320 4964 m 1350 5084 l 1380 4964 l 1320 4964 l cp gs 0.00 setgray ef gr col0 s % Polyline [60] 0 sd n 1350 1200 m 1350 900 l gs col0 s gr [] 0 sd % Polyline [60] 0 sd n 3600 1200 m 3600 900 l gs col0 s gr [] 0 sd $F2psEnd rs scons-doc-2.3.0/doc/python10/sig.eps0000644000175000017500000001016012114661557017742 0ustar dktrkranzdktrkranz%!PS-Adobe-2.0 EPSF-2.0 %%Title: build/doc/python10/sig.fig %%Creator: /usr/bin/fig2dev Version 3.2 Patchlevel 3d %%CreationDate: Sun Jan 2 01:21:05 2005 %%For: knight@casablanca.home.baldmt.com (Steven Knight) %%BoundingBox: 0 0 308 128 %%Magnification: 1.0000 %%EndComments /$F2psDict 200 dict def $F2psDict begin $F2psDict /mtrx matrix put /col-1 {0 setgray} bind def /col0 {0.000 0.000 0.000 srgb} bind def /col1 {0.000 0.000 1.000 srgb} bind def /col2 {0.000 1.000 0.000 srgb} bind def /col3 {0.000 1.000 1.000 srgb} bind def /col4 {1.000 0.000 0.000 srgb} bind def /col5 {1.000 0.000 1.000 srgb} bind def /col6 {1.000 1.000 0.000 srgb} bind def /col7 {1.000 1.000 1.000 srgb} bind def /col8 {0.000 0.000 0.560 srgb} bind def /col9 {0.000 0.000 0.690 srgb} bind def /col10 {0.000 0.000 0.820 srgb} bind def /col11 {0.530 0.810 1.000 srgb} bind def /col12 {0.000 0.560 0.000 srgb} bind def /col13 {0.000 0.690 0.000 srgb} bind def /col14 {0.000 0.820 0.000 srgb} bind def /col15 {0.000 0.560 0.560 srgb} bind def /col16 {0.000 0.690 0.690 srgb} bind def /col17 {0.000 0.820 0.820 srgb} bind def /col18 {0.560 0.000 0.000 srgb} bind def /col19 {0.690 0.000 0.000 srgb} bind def /col20 {0.820 0.000 0.000 srgb} bind def /col21 {0.560 0.000 0.560 srgb} bind def /col22 {0.690 0.000 0.690 srgb} bind def /col23 {0.820 0.000 0.820 srgb} bind def /col24 {0.500 0.190 0.000 srgb} bind def /col25 {0.630 0.250 0.000 srgb} bind def /col26 {0.750 0.380 0.000 srgb} bind def /col27 {1.000 0.500 0.500 srgb} bind def /col28 {1.000 0.630 0.630 srgb} bind def /col29 {1.000 0.750 0.750 srgb} bind def /col30 {1.000 0.880 0.880 srgb} bind def /col31 {1.000 0.840 0.000 srgb} bind def end save newpath 0 128 moveto 0 0 lineto 308 0 lineto 308 128 lineto closepath clip newpath -71.3 288.7 translate 1 -1 scale /cp {closepath} bind def /ef {eofill} bind def /gr {grestore} bind def /gs {gsave} bind def /sa {save} bind def /rs {restore} bind def /l {lineto} bind def /m {moveto} bind def /rm {rmoveto} bind def /n {newpath} bind def /s {stroke} bind def /sh {show} bind def /slc {setlinecap} bind def /slj {setlinejoin} bind def /slw {setlinewidth} bind def /srgb {setrgbcolor} bind def /rot {rotate} bind def /sc {scale} bind def /sd {setdash} bind def /ff {findfont} bind def /sf {setfont} bind def /scf {scalefont} bind def /sw {stringwidth} bind def /tr {translate} bind def /tnt {dup dup currentrgbcolor 4 -2 roll dup 1 exch sub 3 -1 roll mul add 4 -2 roll dup 1 exch sub 3 -1 roll mul add 4 -2 roll dup 1 exch sub 3 -1 roll mul add srgb} bind def /shd {dup dup currentrgbcolor 4 -2 roll mul 4 -2 roll mul 4 -2 roll mul srgb} bind def /$F2psBegin {$F2psDict begin /$F2psEnteredState save def} def /$F2psEnd {$F2psEnteredState restore end} def $F2psBegin 10 setmiterlimit 0.06000 0.06000 sc % % Fig objects follow % % Polyline 7.500 slw n 1200 3000 m 2700 3000 l 2700 3600 l 1200 3600 l cp gs col0 s gr /Times-Roman ff 240.00 scf sf 1950 3375 m gs 1 -1 sc (Taskmaster) dup sw pop 2 div neg 0 rm col0 sh gr % Polyline n 3300 4200 m 4500 4200 l 4500 4800 l 3300 4800 l cp gs col0 s gr /Times-Roman ff 240.00 scf sf 3900 4575 m gs 1 -1 sc (MD5) dup sw pop 2 div neg 0 rm col0 sh gr % Polyline n 5100 4200 m 6300 4200 l 6300 4800 l 5100 4800 l cp gs col0 s gr /Times-Roman ff 240.00 scf sf 5700 4575 m gs 1 -1 sc (TStamp) dup sw pop 2 div neg 0 rm col0 sh gr % Polyline n 4200 3000 m 5400 3000 l 5400 3600 l 4200 3600 l cp gs col0 s gr /Times-Roman ff 240.00 scf sf 4800 3375 m gs 1 -1 sc (Sig) dup sw pop 2 div neg 0 rm col0 sh gr % Polyline n 2700 3300 m 2775 3340 l 2850 3300 l 2775 3260 l cp gs col0 s gr % Polyline n 4800 3600 m 4725 3750 l 4875 3750 l cp gs col0 s gr % Polyline n 3900 4200 m 3900 3900 l 5700 3900 l 5700 4200 l gs col0 s gr % Polyline n 4800 3750 m 4800 3900 l gs col0 s gr % Polyline gs clippath 4215 3330 m 4215 3270 l 4064 3270 l 4184 3300 l 4064 3330 l cp eoclip n 2850 3300 m 4200 3300 l gs col0 s gr gr % arrowhead n 4064 3330 m 4184 3300 l 4064 3270 l 4064 3330 l cp gs 0.00 setgray ef gr col0 s % Polyline [60] 0 sd n 1950 3000 m 1950 2700 l gs col0 s gr [] 0 sd % Polyline [60] 0 sd n 4800 3000 m 4800 2700 l gs col0 s gr [] 0 sd $F2psEnd rs scons-doc-2.3.0/doc/python10/scanner.jpg0000644000175000017500000006424412114661557020616 0ustar dktrkranzdktrkranzÿØÿàJFIFPPÿÛCÿÛCÿÀÞº"ÿÄ ÿĵ}!1AQa"q2‘¡#B±ÁRÑð$3br‚ %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyzƒ„…†‡ˆ‰Š’“”•–—˜™š¢£¤¥¦§¨©ª²³´µ¶·¸¹ºÂÃÄÅÆÇÈÉÊÒÓÔÕÖרÙÚáâãäåæçèéêñòóôõö÷øùúÿÄ ÿĵw!1AQaq"2B‘¡±Á #3RðbrÑ $4á%ñ&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz‚ƒ„…†‡ˆ‰Š’“”•–—˜™š¢£¤¥¦§¨©ª²³´µ¶·¸¹ºÂÃÄÅÆÇÈÉÊÒÓÔÕÖרÙÚâãäåæçèéêòóôõö÷øùúÿÚ ?þþ(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š+æxgã-×í¡ð Æ:ψö|пfÚïÃ?ìí¼YŸ…gøËâÏŠß±©ð&çYð+k0\øÄ¾ ð_í†|Y‡µH<§^x³G¸Öt)<{kcâ?§ëæxgã-×í¡ð Æ:ψö|пfÚïÃ?ìí¼YŸ…gøËâÏŠß±©ð&çYð+k0\øÄ¾ ð_í†|Y‡µH<§^x³G¸Öt)<{kcâ0§è¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢ŠüÀøOÿøŸñÇágÃO à—_·ÿŠ>|`øàߊ_¼Mÿ Ÿü+Dÿ„‹ÀŸ<9¦ø³Â:ïö7ˆ¿à£šGˆ4íêú~¡ý™®éZf±aö²êz}•ìSÛEèðÙ´Wý"wöÿÿÃÿ²ÿé–Qÿÿ”YÁ4ÿìÀ?cýg_‡5÷ý|ÿ ‘ûEÒ'oÿü8ßðK/þ™eðÙ´Wý"wöÿÿÃÿ²ÿé–WßôPÀðÙ´Wý"wöÿÿÃÿ²ÿé–Qÿ ‘ûEÒ'oÿü8ßðK/þ™e}ÿE|ÿ ‘ûEÒ'oÿü8ßðK/þ™exˆÿh¯ÛwQý©þ üRÒ?àš_·ý‡Á/~Ïÿ´·€> ü:ÿ…ÕÿÔµÿ„«âŸÄ¯ˆ¿²wˆ¾ø×þ_ø(ûøK\ÿ„Â_ >:h_ð‘ëz…¯ˆü+ÿ +û3ÂÖWúgŒ|a>úýE|ÿ ‘ûEÒ'oÿü8ßðK/þ™eðÙ´Wý"wöÿÿÃÿ²ÿé–WßôPÀðÙ´Wý"wöÿÿÃÿ²ÿé–Qÿ ‘ûEÒ'oÿü8ßðK/þ™e}ÿE|ÿ ‘ûEÒ'oÿü8ßðK/þ™eðÙ´Wý"wöÿÿÃÿ²ÿé–WßôPÀðÙ´Wý"wöÿÿÃÿ²ÿé–WÓÿ³×Æ¿ þÒŸ¾þÑžÓüA¤ø'ã÷Áÿ†Ÿü¥ø²×N±ñV›á_Šž Ñ|uáí?ÄÖ:>«¯i~ ³Ò5Û;}f×K×5:ßQŽæWQ¶H¯&ö øþ ;ÿ(²ÿ‚iÿÙ€~Æÿúοhïú(¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ +óö¨øwkñÇöúý> x»âíá†Zìû~|RÖ<3ð/öžý£¿fOøH¼wðÿãGüÂ~×|S¬þÍÿ>øƒÅ?ð‹xâ·Ä]?DÓ,Öuø«Åž*ñÀoë!ñ7‰¼C¬\Þjú÷ˆ5Ý^òóTÖuRòëQÕ5«›ëë™îg–Vû~€ (¢€ (¢€ (¢€ (¢€>ÿ‚NÿÊ,¿àšö`±¿þ³¯Ãšûþ¾ÿ‚NÿÊ,¿àšö`±¿þ³¯Ãšûþ€?žØÇöÍÿ‚’~Ô¾ý¾¼uã¯Úwþ Áû=ø'ö ý·ÿjÙÆ>1ñgìEñçSð®¯á_ÙsNð¶±âŽþ&ñ±ÿ=ð6‘ðÓÃú†‘âÍSYеKÍsNðn¢Üß_xßS¶’Ym?Oõ/Ûwàì×á_„žý¿ÿk¿ØƒàíG«|ð‹>'x;Røûà¿…~Ô¼U}§I£øÓÄß <=ñ«ÅšÄKσ÷Ÿ4/é~Ö|Ag&£q§hÒXê×/®iÚ¬Pþü ÿ‚ki>ñWüGý·à†Þ ý³nj¿ø(ÿí³ñ¯á/Ço Ÿø&Ž¿â¥ý˜>9j:E¯€ôÿ üOø¹ûdüý þød¶ñgŒtk_ EáOx QñM·ˆtmWCñ´Ú’é§ÿ<+ûax³âßíñwöáý‡ü?«[~Ò_ðNƒžñWÁŸ…~:øñká›ë ÿhø(‰Çì?â·ø»ñ3ÁüAøÁ®~ÏŸµÁ¿ øÏÇZ—…toÙÇ¿´ß‹Ä|LðW‚_ºv¨ú}£~п¼Gñ—ÅŸ³Ÿ‡¾8ü×h?x~ÛÅž:ø£|Kð^©ñ—Á~¼ƒÃ7V~&ñgà jxsÃ÷VÞ4ðuŶ³¬hvztðx³Ã3ErÑëÚ[]|Áÿ cÿOÿ'-ûÃÿÃÃXÿÉdÿŒ§ÿ’‹ÿ'-ÿ#ü*_ø`øT¿óY?è¢ÿÌÇÿÕ~@übÿ‚TþÙ:¿ìÿ‘ñoì¿âOøfÛëörý>ÿÁ=?jOèþ/±Ó5#öXøëû?ø_à_í©É­ü2ñ_‚ï~4|@ýˆ~&ÉcûS~ÍþOÚß´ø+Æþ4ð\:ø‹ªxHÕþ¿ÿ‚„þÉ~(ÿÂ}ð“öhý?³¾x_þƒÿ9ý’>ÜøsVø'ðÿác|]ý¦ÿᓼ3û?þΞðœ¾=мAá?ìoþκô—º¶­à/üð‡‡5?i¶=–õµ­Ã`ÿÃÂ`_øUŸð½?á¸døR_ð°?áSÿÂâÿ†–ø/ÿ ³þŸü#Ÿð˜´ÿ……ÿ ¯ü"_ð°?áÿЧþßíøHÿáÿ‰ßöoögúUtÿmߨ¿àç…~øëâïíwû0|+ðOÆÏ·‹> øÇâ?Çß…>ð¯ÅÏ ¦áýaüMðÇÄ>'ñf—¤x÷Ãë¤x³Âº£k>¼Õ´åÓ¼Káûãr-µ:[Ÿþ/kµSü}ýŠmoþÃý¨>éß1ðÿ<àüºÕgφÞ4ð®‘ñ AÒ†§Ùü)¾µñå°éÿÄÛwö/ø9á_†>:ø»û]þÌ üñ³Ãíâσ>1øñ÷áOü+ñs©§xX|1ñ‰üY¥é=ðúé,ð®¨ÚÏ…o5m9tïø~øÜ‹mgN–çŸø[ÿ ý~8øïBø[ðSöáý>0|MñGöŸü#?¾þÒßþ xïÄ_Øš>¡â-gû Â>ñ®¯â _û#ÃúF«®êÙú}ÇØ4}3PÔ++™âüÀýŽg¯Úöjñÿì;âkØoâ‚þiðù_‡Z÷¯x£ö@Ò?á‘ü ûoÁOþþÑ¿³Dþ5ðÞ“ûDiž—áÿ…þü?Õ®|IáßÙ£YøÑ¬x/û"ÏÂz„u{Ù,,ø;àÚîÇþ?þ?ØWö€ð§ü+¯ø+ÿü[ö£ø¡ý¯ñ+ö(¾ÿ…sð'ö¨ÿ‡‚³ñηÿ¿ímâíß³Ã}ø+þŸ øþ/é_ð¥þ:}ÃÚ¯“ðcþ@ëÿü5ì±ÿ Ûþsþ[öÿ†šÿ£tÿ…Éðëþ·ü‰ßð±?ä‘ÂGÿ þIÿüW?ò/Èÿ7üÓ«Ïüÿ ý~+ÿÂkÿ ·öáý>%µøâ?‹áý¥¾ øÇþ…žûü%ßükÿïuøE~øWûGOÿ„ÆZïØ<9¡ý¾ÏûORµûTÿbÏø%ÇÆÏÙ¿âŸìÉàÿ_²íû\üzý˜kÿ/ðü'Å?ðT‹¾øßþ:xÅ_~"þÐ:Gìáíã?‰¾ý >$^ø‚Óà·ÇOÙZÙ³SøEûCxÆÓ[ñ?ÆÚ‡Å>ñ—ˆ¼Ysöÿìñû0üo±ø_ÿöhþ>ý’ѾEãm;Q𯂴oüs¶øeâŸhžŒôöýºgø(—À˜ÿhßÙsÆð™ü2—âį‡_i¼Š×Lñޱð߯:§†|ÿxOí×> ðOü&Þ¶ð÷Å_xwÇ6ñÜß > xÄž&ð…ï|Aýiõý~0ÿÁ¿Ÿ³¯íû ÿÁ+ÿgÿÙkö¥ø3â‚> øƒãV®èZÏ‹þxãNñ&ã><ø¿ x³Âzÿˆßô‰|?.‘ñ×Ãw6Þ$ºð׊­¼Uá¯#ødøløkľ$ýž ¾ÿ‚NÿÊ,¿àšö`±¿þ³¯Ãšûþ¾ÿ‚NÿÊ,¿àšö`±¿þ³¯ÃšûþŠ( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( €>#ÊScû0ø)gþ´Wüv¾ÿ¯€>#ÊScû0ø)gþ´Wüv¾ÿ øûBüø9⯆>ø»ñÇàÿ¿|lñxOàσ¾#üKð_üUñsÅI¨øG ü1ð÷‰õ­/WñïˆWñg…t¶Ñ¼+g«j+¨ø—Ãö&Ø\ë:tW'ÄÚàÁÏ|1ð/Åߎ?þøÛãgˆÂ|ñâ_‚ü⯋ž*MGÃú;øgᇼO­iz¿|@º¿‹<+¥¶á[=[Q]Gľ±6ÂçYÓ¢¹øƒþ ð·ÀŸb)~ |Rпá(øeñƒö¿ÿ‚gü-ø‹áŸí=cDÿ„‹ÀŸ?ॲG„ü]¡løwPÒ0xÇã'ÁÏÛö‡ø“û1þÔ_u(>#éß¶—ìK¡ÿÁ?¿à£^ðO¼Eñcþð&‘í?ûø­ðïãïìµâ¯~Èþ:ý¶¾hÚçÄmwâ¿Å_‡µ‡ôχ>%|&ø“âk†Ÿ²Æ£wãKÏ|FðÇ„í´;Ýáî‘ñŽ?lOÚž_„ÿðKoø)÷íy©þÏö³–¯ñãïí­áÿÙŸöoø?ñWøÙðÿád¿ðH_ø)‡Ço‡±ûF|Ný ôOü`øeðÞ-ÆúU—ìãð;ÚŸÅÝ^C ø¯Hðg‡VçÅÓíøCÿêý·ÿવoÆ_„ÿ>3~Å~ð'ü¿öý˜4¯Ÿ ~+èÑüÑ~!|2ø…ñ #â'ßø²âÃöýøÇ⟋ŸÛáMËø~Ûâîû5üø™ã?‰ž!ðΣâÙ«àG„,|O™û½@Á'å_ðM?û0ØßÿY×áÍ}ÿ_Á'å_ðM?û0ØßÿY×áÍ}ÿ@Q@Q@Q@Q@Á'å_ðM?û0ØßÿY×áÍ}ÿ_Œ?±ÅïÚÓöký‹ÿdOÙÏÇ_ðJßÛVñ·ÀÙƒàÁOêžø£ÿþð®¥â¯… |'à_ê¾Ö?à¢ú¯yáûÍ_B¼¸Ñ®µMFÔn4é-¦¾Ò´ë—–Χÿá²?h¯úDïíÿÿ‡þ eÿÓ, ¿è¯€?á²?h¯úDïíÿÿ‡þ eÿÓ,£þ#öŠÿ¤Nþßÿøq¿à–_ý2ÊûþŠøþ#öŠÿ¤Nþßÿøq¿à–_ý2Ê?á²?h¯úDïíÿÿ‡þ eÿÓ, ¿è¯€?á²?h¯úDïíÿÿ‡þ eÿÓ,¯ñ—ü³TðÇo„ÿ³GŠ?àšÿ·ý¯ÆßŸÚ_ð€x+MñüÄ[þÁàï‰~?³ÿ„ËÄ~ÿ‚…êÞø]ÿ „¾ |gÕ¾ÂÐ×¼ÿ _þïÅ‹†ð–j |ke¡€~¿Q_Ãd~Ñ_ô‰ßÛÿÿ7üËÿ¦YGü6GíÿHý¿ÿðãÁ,¿úe”÷ýðü6GíÿHý¿ÿðãÁ,¿úe”Ãd~Ñ_ô‰ßÛÿÿ7üËÿ¦Y@Ñ_Ãd~Ñ_ô‰ßÛÿÿ7üËÿ¦YGü6GíÿHý¿ÿðãÁ,¿úe”÷ý|ÿÿ”YÁ4ÿìÀ?cýg_‡4Ãd~Ñ_ô‰ßÛÿÿ7üËÿ¦Y^ÿöø[ã¿ß°/ì=ðSâ–…ÿ¿Ä߃ÿ²ìÓð·â/†´ô}oþßü?ø/௠ø»BþÙðî¡«øWþÈñ‘¨iÿÚz«©è÷ÿgûV™¨^ÙKÌ _ÑEQEQEQEQEQEQEQEQEQEQEðÄoùJoìoÿfÿ,ÿÖŠÿ‚N×ßõðÄoùJoìoÿfÿ,ÿÖŠÿ‚N×ßôà´‡ìÅð‹ö²ð&ðÛã]§Ä ÿ h<ñKK¶øuñ³ãgÀbü5Ö#ñ7€5ÙüYð#âÃ_ßÿÂâÛ]+ÆÞÓ5 rëGÒüwáÏøæ×Ožð¶³£ïøïàÁ¯‰Ÿ>üfñ×Ãßø‡â·ì×âø›àoĘg·ñWíGâ_ÃÝágÄmV±¸µ¹ŸÃþ4ðOˆï4ïxOT}Cºƣ¦øOÄ÷Ú4Þ)ð7‚µ­Ø( €>ÿÁ.?a?ÙÏ⟄¾1|øÿÇ‹~ÿÂåÿ…9¤_üMøÅã…Ÿ³ïü4'ˆÓÅ?áš>øãâ‰> ~Íð°õ15®½ÿ á÷íÞ¿Õ¼iö_ k:®‰x|!ÿ‚_þÅŸ)|$øe¦~Ò_´íÇìãðŸÇô‰þñî»ð·öSÔ¾1ß~Ì¿µt_4Ý?LðgÂMGðůŽ5Øü)§èŸhŒÅ÷ýðÁø%Çì'û9üSð—Æ/ƒÿ?áñoÃ_ø\¿ð§4‹ÿ‰¿¼cð³ö}ÿ†„ñx§ãOü3GÀ¿|Añ'Á/Ù£þ¦&µ×¿áBü>øu»Ã—ú·ƒí>Ëá-gUÑ/>ÿ¢Šøþ ;ÿ(²ÿ‚iÿÙ€~Æÿúοkïúøþ ;ÿ(²ÿ‚iÿÙ€~Æÿúοkïú(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š+ð‡ö§ðŸŠ®?jŽß±ì¾ñ‰Ò¯¬-­.RifñÿøvŸìëÿEöÿÿűÁSú2+ïú(àøvŸìëÿEöÿÿűÁSú2(ÿ‡iþοôQ¿oÿü[ü7ÿ£"¾ÿ¢€?˜/Ù#övÒüoã¿ÙÓâ÷ÄoÚöÿøðþ wðÿVø±û8üðÏüïþ 9£øŸö9øYá]Ç¿>üKñ¦‘ûT7ŒüIðÿâ—ìåâÿÙ×áÿíiã/üOñï‡?gïø(&¿ðóá¿ÁKÆ¿ÿk?CðCõûþ§û:ÿÑFý¿ÿñl_ðTßþŒŠñÿØWà§…|5ûaÁM¿>ø3ZºÓµ üð¯Ç/Ÿ³çü?ö“Ô>O}¥KãÄ´×ícû]xëâÇ«ïêºw‰ôï‡_³×ô½+@ðGÀo†ú‰ú¿@ôÿg_ú(ß·ÿþ-‹þ ›ÿÑ‘Gü;Oöuÿ¢ûÿâØ¿à©¿ý÷ýçÿ þøàwÂφŸ>è_ð‹ü2ø?ðÿÁ¿ ~xgûOXÖÿáð'Ãÿi¾ðŽ…ý³â-CWñ¯ý‘áý#OÓÿ´õÝWSÖ/þÏö­OP½½–{™}Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¯€>#ÊScû0ø)gþ´Wüv¾ÿ¯æ ößÿ‚›üSýÿàáoø'çì—§þÈÿð·áfþÏþ,ø_áxâ÷ˆï|Gið³öÍøùû9Ïñã?~AðûþÏøføaŸŠ^&ñ…ôÏøŸÃž&øY{gñ]ø™ðËûÄ^Ó@?§Ú(¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€>ýÿäâ¿à¬_öÿ?õÖ_ðM:ûþ¾ýÿäâ¿à¬_öÿ?õÖ_ðM:ûþ€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€?0>ÿÁF¾'üqøYðÓã_Âßø%×íÿâ†_>ø7â—ïÂgÿÊÑ?á"ð'Äi¾,ðŽ»ýâ/ø(æ‘â #û_Ãú¾Ÿ¨fkºV™¬X}£ìºžŸe{öÑzü6GíÿHý¿ÿðãÁ,¿úe”Á'å_ðM?û0ØßÿY×áÍ}ÿ@Ãd~Ñ_ô‰ßÛÿÿ7üËÿ¦YGü6GíÿHý¿ÿðãÁ,¿úe•÷ýðü6GíÿHý¿ÿðãÁ,¿úe”Ãd~Ñ_ô‰ßÛÿÿ7üËÿ¦Y_Ñ@Ãd~Ñ_ô‰ßÛÿÿ7üËÿ¦Y_œ¼ ñƒã/ü»öý¿î¿à’ÿ¶þ‰«þÇ_ÿk Üjš7Ä/ø%›ñ Çþ*øÏ¤ü?ð/Ÿ ø²úOÛçT¹ñWÁÿ‡ž ñ?íKâ mügáGÂ_$ü]¼¿ý§üão‹þ ÐüYñ&O‚Ÿ·‡íÓû=xWÅž*ðçÃßü(Ò|M¨|;øûG|8øwˆ"øwðãÀ¾ºÖl|+k¨êšw…ô£ª\ÞÜÀnŸÿ‡iþοôQ¿oÿü[ü7ÿ£"€>ÿ¢¾ÿ‡iþοôQ¿oÿü[ü7ÿ£"øvŸìëÿEöÿÿűÁSú2(ïú+àøvŸìëÿEöÿÿűÁSú2(ÿ‡iþοôQ¿oÿü[ü7ÿ£"€>ÿ¢¾ÿ‡iþοôQ¿oÿü[ü7ÿ£"øvŸìëÿEöÿÿűÁSú2(ïú+àøvŸìëÿEöÿÿűÁSú2(ÿ‡iþοôQ¿oÿü[ü7ÿ£"€>ÿ¢¾ÿ‡iþοôQ¿oÿü[ü7ÿ£"øvŸìëÿEöÿÿűÁSú2(íÿx³Â¾𯉼uã¯xÁ~ ð_‡õŸxÇÆ>,ÖtïxWÂ~ðæs¬x‡ÄÞ&ñ±sg¤h>д‹;ÍSYÖuKË];KÓ­no¯®`¶‚YWÏþ ~п¿iO ê:ýœþ8üøýà'Ä^Õ ñ7Â/~ØÏàŸŠžÑ®uhµgáÇŒQ¼+ã:ÚçÃ!VÒ5KÀ0cÏø4cþ £û1x«ÀŸüuãoÚöƒø­àßø^êßXÔ¾0êßü+ῌ¾Ô|1â~5ü$‹ösáŸÆO‡^ ÑüI ^]xJÕ>;xòéÚÔ‹}¨x£Å:F‡ãÙïø$ïü¢Ëþ §ÿfûÿë:ü9¯¿ëàø$ïü¢Ëþ §ÿfûÿë:ü9¯¿è¢Š(óþ §ûtë°ŸÂÏÙÛ\мgû?ü&½ý£kÿ„²ìÿÿj8¼w©ü ø £øÛßüâ/‰^9ð·Ã«ï øƒÅû¼?ð»Sð7†t½CâOÁÏhþ1ñ¯‡¼mñKâç~xOÅÚ¥{Áoþ× 5ÚM?omKö`ƒàßÂø?âwÃOÚëá ð˜øéãÏ|)ñgˆ4¿·Ãë;Ÿ‡5¾Ô4O‰~Õý“¿à¢ÿ>~ÈÿðŒþįû@~Á_àžßðKÚÓãßÃokŽû6üIø-ûG~Úÿ x\ðçÃÏ øÛã?Ž< §xï\ñ‰üsào…ñ§ˆõÿCÚ7í»ûøà׋?hÏ~׳»û>x ÄÞñ×ÇmãïÂSàׂüUy?†mlü3âωö>,ŸÁ>ñÕÏ<om£k垣<þ,ðÌ1[4šö–·\ÿˆ¿à¡?°/„< ðëâ—‹?nÙÂÿ ¾0Â]ÿ “â/ˆ¿io‚ú'>(ÿÂ¿Ö ðïá]x»Rñ­·‡ümÿOˆ.m´/Â3¨jðëišÏدeŽøƒâ?¿lÏk ~*|>ÿ‚Røƒáö‘gûo·þ/ÅÄ_ø'ö½ûrë~ ð§ìáÿxkã¿€´}⎭û.hÞ ø©®>­û|[ñNµûTê|#ûè:–Ÿð³K³Õþ,è^!ýœ<þ õðö˜øIñ‹öRÔükû%~Ð >øþqà {Xø³ñ·ö{ø·ãøsöÈý»?g¯ÛKöhñ¯Ä¿é?µWÆ_ˆ?á.øà¯ü-ñ—ˆìµßŠ_íþ;hš–¡ãû+‡úÕ‡Æmlõâ?í»ûüð¯Ã|]ý®ÿf…~ øÙáöñgÁŸüGøûð§Àþø¹áTÓ¼?¬?‰¾ø‡Äþ,Òôø}txWTmgÂ·š¶œºw‰|?|nE¶³§KsÐxÿö±ý–>|,ðWÇOŠ_´·ìÿð×à—įøG?á]|bñÿÆO‡^øYãïøL|9{ãÿÂñ Ä^#Ó¼%â¯øJ¼%§j)ðçö¯ý¹áË ÝoLûV™k=Ò~P~³Æÿ€ÿ?bmboÙ#Äþè>ÿ‚àøOYð¯þÍVzwì§á_Úïþ +ðƒö­ý”<3âÏ |8øÇ¬ømü?«|øWqàëmös—âöðÿÅWžðö¹m øY5/远_ðIÿŽÚw‰þüUñïì‹û@xóöE×¾ÿÁKÿf?Á.~~Öþý“üwû7|,ýª?à Ÿ´/í]ð¿â‹Á¿Úûá7ì±ñwáÿįƒ§Â_Ù÷ö‚ýœ4ßþðæ‰£é µ¨ãøÍÃK/ ü=þ—µ/Ûwö/ѼUð“ÀºÇíwû0i>6øýáÿx³àOƒµ/¿ l|Uñ«Â¿54†&øIáë¯E«üGðÿÄ}^)t¿k>³Ötïj1ÉcáëFå!ôý~ ÿÁ<> üvýš¾6x3MÖÿaˆþüFý€?dƒZ~›áßÚãÁßµ¿?dþÏ¿à¢_î¾üEø±ñ¯â—„¿hßÿcø3ö€ø=à߿„ß~x/XÔ'øià߃ÿ4¯?íõQEðüwþQeÿÓÿ³ýÿõ~×ßõðüwþQeÿÓÿ³ýÿõ~ÐßôQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE|ÿÿ”YÁ4ÿìÀ?cýg_‡5÷ý~0þÄ_¿kOÙ¯ö/ý‘?g?Á+mý[Æßf€_ÿ¢¾ÿ†Èý¢¿é¿·ÿþoø%—ÿL²ølÚ+þ‘;ûÿáÆÿ‚YôË(ïú+àølÚ+þ‘;ûÿáÆÿ‚YôË(ÿ†Èý¢¿é¿·ÿþoø%—ÿL²€>ÿ¢¾ÿ†Èý¢¿é¿·ÿþoø%—ÿL²¼Æ_ðVÍSÀ?¾þÍ(ÿ‚kþßö¿~6iÂà­7ÄðN?oûƒ¾%øþÏþ/øsþ «xKáwü%þø-ñŸVøiÿ C^ðwü-øS¿,>ÿÂY©ü5ñ­–†úýE|ÿ ‘ûEÒ'oÿü8ßðK/þ™eðÙ´Wý"wöÿÿÃÿ²ÿé–PßôWÀðÙ´Wý"wöÿÿÃÿ²ÿé–Qÿ ‘ûEÒ'oÿü8ßðK/þ™e}ÿE|ÿ ‘ûEÒ'oÿü8ßðK/þ™eðÙ´Wý"wöÿÿÃÿ²ÿé–PßõðüwþQeÿÓÿ³ýÿõ~Ñÿ ‘ûEÒ'oÿü8ßðK/þ™ezüÛáoŽþ~À¿°÷ÁOŠZü"ÿ~þȳOÂ߈¾þÓÑõ¿øG|wðÿà¿‚¼'âí ûgú†¯áý_û#ÄF¡§ÿièZ®§£ßýŸíZf¡{e,2€}EPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEP?âÏxWÀ^ñ7޼uâoø/Á> ðþ³âÏøÇÅšÎáÏ øO¾Ó®uø›ÄÞ!Ö.lôÃú‘gyªk:Ωyk§izu­ÍõõÌÐK*ÿ8>ý°¿à›µGì¿ñËâ¯Ä¯ø)ìAð[ö‘ýµ xƒ@øƒÿÚðŽ»ð³Å?t^ÓIJ֯à߇?´GÇÙ{Å4ñÁ«ÿÛkÇ_µÝµ„÷¿ ¾/êúMçô½_|Fÿ”¦þÆÿö`ðRÏýh¯ø$í{ì_ûPxWöÌý—þ~Ò^OÚÛ|Eðýì~%Ñ|'ãM;âg…|%ñ3Á~ Ö>|_ð†~)èV:…¾,øá÷Åo xÏÁ7Å¿Ã?Ãߊzvmñáæ¥¬x'Äz­{ôýPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEP_˜µGû_Ž?·×ìðSÅß?h ü2ÔdÛó▱៴÷íû2ÂE㿇ÿ?à›þðF»âgöoø©ð§Ä)ÿ„[Ãÿ¾"éú&™â-WSÑì?á.Õî ÓÖöXîbý?¯€>#ÊScû0ø)gþ´Wüv€øvŸìëÿEöÿÿűÁSú2(ÿ‡iþοôQ¿oÿü[ü7ÿ£"¾ÿ¢€>ÿ‡iþοôQ¿oÿü[ü7ÿ£"øvŸìëÿEöÿÿűÁSú2+ïú(àøvŸìëÿEöÿÿűÁSú2(ÿ‡iþοôQ¿oÿü[ü7ÿ£"¾ÿ¢€>ÿ‡iþοôQ¿oÿü[ü7ÿ£"øvŸìëÿEöÿÿűÁSú2+ïú(àøvŸìëÿEöÿÿűÁSú2(ÿ‡iþοôQ¿oÿü[ü7ÿ£"¾ÿ¢€>ÿ‡iþοôQ¿oÿü[ü7ÿ£"øvŸìëÿEöÿÿűÁSú2+ïú(àøvŸìëÿEöÿÿűÁSú2(ÿ‡iþοôQ¿oÿü[ü7ÿ£"¾ÿ¢€>ÿ‡iþοôQ¿oÿü[ü7ÿ£"øvŸìëÿEöÿÿűÁSú2+ïú(àøvŸìëÿEöÿÿűÁSú2(ÿ‡iþοôQ¿oÿü[ü7ÿ£"¾ÿ¢€>ÿ‡iþοôQ¿oÿü[ü7ÿ£"¾ ñ÷üçà5¯ü“öPðt^>ý·ÛH×bø(7‰¯¯.à¦ßðRKÏA¨øOãÏü'KÒí´o^~ÕóøÛÞº¶ñ¦±/‰¼'áïé~ñ¦£gá=cÆ:6»«ø À·ÞýÞ¯€>#ÊScû0ø)gþ´Wüv€øvŸìëÿEöÿÿűÁSú2(ÿ‡iþοôQ¿oÿü[ü7ÿ£"¾ÿ¢€>ÿ‡iþοôQ¿oÿü[ü7ÿ£"øvŸìëÿEöÿÿűÁSú2+ïú(àøvŸìëÿEöÿÿűÁSú2(ÿ‡iþοôQ¿oÿü[ü7ÿ£"¾ÿ¢€>ÿ‡iþοôQ¿oÿü[ü7ÿ£"øvŸìëÿEöÿÿűÁSú2+ïú(àøvŸìëÿEöÿÿűÁSú2(ÿ‡iþοôQ¿oÿü[ü7ÿ£"¾ÿ¢€>ÿ‡iþοôQ¿oÿü[ü7ÿ£"øvŸìëÿEöÿÿűÁSú2+ïú(àøvŸìëÿEöÿÿűÁSú2(ÿ‡iþοôQ¿oÿü[ü7ÿ£"¾ÿ¢€>ÿ‡iþοôQ¿oÿü[ü7ÿ£"øvŸìëÿEöÿÿűÁSú2+ïú(àøvŸìëÿEöÿÿűÁSú2(ÿ‡iþοôQ¿oÿü[ü7ÿ£"¾ÿ¢€>ÿ‡iþοôQ¿oÿü[ü7ÿ£"¼Á?tOÙ¯þ Iû xsMñg„ußìoÁG4iÚþÕôýCû3]Ò´ÍbÃíeÔôû+ا¶‹Ð?á²?h¯úDïíÿÿ‡þ eÿÓ,£þ ;ÿ(²ÿ‚iÿÙ€~Æÿúοkïúøþ#öŠÿ¤Nþßÿøq¿à–_ý2Ê?á²?h¯úDïíÿÿ‡þ eÿÓ,¯¿è €?á²?h¯úDïíÿÿ‡þ eÿÓ,£þ#öŠÿ¤Nþßÿøq¿à–_ý2ÊûþŠøþ#öŠÿ¤Nþßÿøq¿à–_ý2ÊùƒÅŸ¿kMwöÐøûFYÿÁ+møüð¯ö`ý®þ x‡K¹ø£ÿÃO^x«ã÷ÅoØ‹Ç^Ô4k¿à¢óiÓtÙ¯ÇVþ&º¾×4íFÏQÕ¼'—¥k6×ÚÅæƒû=E|ÿ ‘ûEÒ'oÿü8ßðK/þ™eðÙ´Wý"wöÿÿÃÿ²ÿé–WßôPÀðÙ´Wý"wöÿÿÃÿ²ÿé–Qÿ ‘ûEÒ'oÿü8ßðK/þ™e}ÿE|ÿ ‘ûEÒ'oÿü8ßðK/þ™eðÙ´Wý"wöÿÿÃÿ²ÿé–WßôPÀðÙ´Wý"wöÿÿÃÿ²ÿé–WÓÿ³×Æ¿ þÒŸ¾þÑžÓüA¤ø'ã÷Áÿ†Ÿü¥ø²×N±ñV›á_Šž Ñ|uáí?ÄÖ:>«¯i~ ³Ò5Û;}f×K×5:ßQŽæWQ¶H¯&ö øþ ;ÿ(²ÿ‚iÿÙ€~Æÿúοhïú(¢€ (¢€?0>ÿÁF¾'üqøYðÓã_Âßø%×íÿâ†_>ø7â—ïÂgÿÊÑ?á"ð'Äi¾,ðŽ»ýâ/ø(æ‘â #û_Ãú¾Ÿ¨fkºV™¬X}£ìºžŸe{öÑzü6GíÿHý¿ÿðãÁ,¿úe”Á'å_ðM?û0ØßÿY×áÍ}ÿ@Ãd~Ñ_ô‰ßÛÿÿ7üËÿ¦YGü6GíÿHý¿ÿðãÁ,¿úe•÷ýðü6GíÿHý¿ÿðãÁ,¿úe”Ãd~Ñ_ô‰ßÛÿÿ7üËÿ¦Y_Ñ@Ãd~Ñ_ô‰ßÛÿÿ7üËÿ¦Y_0x³â÷íi®þÚ¿hË?ø%oí¿‚~þ̵ßÁOéw?à˜iâ«Ï|~ø­ûxëÁÚ†cü^m"ãÃún‘û5øêßÄ×WÚæ¨Ùê:·„áÒô­fÚûX¼Ðg¨ €?á²?h¯úDïíÿÿ‡þ eÿÓ,£þ#öŠÿ¤Nþßÿøq¿à–_ý2ÊûþŠøþ#öŠÿ¤Nþßÿøq¿à–_ý2Ê?á²?h¯úDïíÿÿ‡þ eÿÓ,¯¿è €?á²?h¯úDïíÿÿ‡þ eÿÓ,£þ#öŠÿ¤Nþßÿøq¿à–_ý2ÊûþŠøþ#öŠÿ¤Nþßÿøq¿à–_ý2Êúözø×á_ÚSàÀÿÚ3Àºˆ4Ÿü~ø?ðÓã_ƒ´¿ZéÖ>*Ó|+ñSÁz/޼=§øšÇGÕuí"ÏÄzF»go¬Úézæ³§[ê1ÜÃcªê6ÉäÞÁ_Á'å_ðM?û0ØßÿY×áÍ}ÿEPEPÌ>>üVø©â­CÃÞ:ýˆ¿iÿÙ¯H³ðýÖ³oã¯~,ý‹õï êÚ¾£¥XÃá=>Ïösý®þ?xÚ?_[j7šÅ­Î©àí7©§hZ¬WÞ&³ÕæÐô½dø)ñ÷â·ÅOjñ×ìEûOþÍzEŸ‡îµ›|kñgì_¯xWVÔmõ*Æ éö³ŸíwñûÆÑø‚úÛQ¼Ö-nuOi¾M;BÕb¾ñ5ž¯6‡¥ë'ìE៌¾ ý‹ÿdO~Ñ—> ¼ý ü'û0|ðÏÇkÏx²øªëã.…ð§Âz_ÄûŸxê×Yñ· |}ø­ñSÅZ‡‡¼uû~Óÿ³^‘gáû­fßÇ_üYûëÞÕµ}GJ±‡Âz}Ÿìçû]ü~ñ´~ ¾¶Ôo5‹[SÁÚo…SNеX¯¼Mg«Í¡ézÏÓôPÌ>>üVø©â­CÃÞ:ýˆ¿iÿÙ¯H³ðýÖ³oã¯~,ý‹õï êÚ¾£¥XÃá=>Ïösý®þ?xÚ?_[j7šÅ­Î©àí7©§hZ¬WÞ&³ÕæÐô½dø)ñ÷â·ÅOjñ×ìEûOþÍzEŸ‡îµ›|kñgì_¯xWVÔmõ*Æ éö³ŸíwñûÆÑø‚úÛQ¼Ö-nuOi¾M;BÕb¾ñ5ž¯6‡¥ë?OÑ@0|øûñ[â§Šµxëö"ý§ÿf½"ÏÃ÷ZÍ¿Ž¾5ø³ö/×¼+«j6úŽ•c„ôû?ÙÏö»øýãhüA}m¨Þk·:§ƒ´ß ¦¡j±_xšÏW›CÒõŸ—ü©þÐo¯‚?ü]ûþг?Ã/ƒÿ²íð·Xñ7ÇO~Èßü$^;øëñ£ö ñg‚4/ hß³íMûAø‚O/Ãÿ³çÄ]C[ÔüE¥xsG°û>‘k¡{{ªGméýóÁO¿¾*x«Pð÷Ž¿b/ÚökÒ,ü?u¬Ûøëã_‹?bý{º¶£o¨éV0øOO³ýœÿk¿Þ6ÄÖÚæ±ksªx;MðªiÚ«÷‰¬õy´=/Y> |}ø­ñSÅZ‡‡¼uû~Óÿ³^‘gáû­fßÇ_üYûëÞÕµ}GJ±‡Âz}Ÿìçû]ü~ñ´~ ¾¶Ôo5‹[SÁÚo…SNеX¯¼Mg«Í¡ézÏÓôPÌ>>üVø©â­CÃÞ:ýˆ¿iÿÙ¯H³ðýÖ³oã¯~,ý‹õï êÚ¾£¥XÃá=>Ïösý®þ?xÚ?_[j7šÅ­Î©àí7©§hZ¬WÞ&³ÕæÐô½dø)ñ÷â·ÅOjñ×ìEûOþÍzEŸ‡îµ›|kñgì_¯xWVÔmõ*Æ éö³ŸíwñûÆÑø‚úÛQ¼Ö-nuOi¾M;BÕb¾ñ5ž¯6‡¥ë?OÑ@0|øûñ[â§Šµxëö"ý§ÿf½"ÏÃ÷ZÍ¿Ž¾5ø³ö/×¼+«j6úŽ•c„ôû?ÙÏö»øýãhüA}m¨Þk·:§ƒ´ß ¦¡j±_xšÏW›CÒõ“à§ÇߊßÓìÿg?Úïã÷£ñõ¶£y¬ZÜêžÓ|*šv…ªÅ}âk=^mKÖ{ÿÙëã_…iO€_ÿhÏéþ Ò|ñûàÿÃO~ÒüYk§Xø«Mð¯ÅOè¾:ðöŸâkU×´‹?Yéí¾³k¥ëšÎo¨Çs Ž«¨Û$W“{|Áûk?¼GûþÈž!ýœü'â~Ïš÷ìÁð Yøà_\Ïyâ¯üÕ>øNûᇄüMyuâo\ÝxƒÃž ŸCÑõ››ø²yõ;™fñ6»#6©tãÿðIßùE—üOþÌö7ÿÖuøs_×ÀðIßùE—üOþÌö7ÿÖuøs_Ð\ÿ‹|yø·ñ3⇅tŸx¦ÇàïÅ‹ÿ~üzýŸ´€øë¤YxwÄV^ ðÂïÚ§Nð¯ÃÝKGø£áoŒ?­¼mc௠ü¿û7ÿÁE¿à¢_¶‡Ä_Œ³ÏÃO~À²ßíuû:þȱßÅ/~Ë´¿ÀÏÚ‹ÅŸü}ñ³ã÷ì±ðóã׼wVÒôߎ/gÙÿá÷Äß‹ž ýŸ/ôÍ7Lý«þ.ü9ñ¯x‡âÆŸ¡Mâï„þñ¿Ð³G?à¡ß°Ï챤þÈïü³âísâßÙ+áý‡À¿Ù»ã§ìëñ—öJøYð'ö˜øYð¿áÖƒ¢|ñOÄ}#ö‰ý¨<3ñ·öwø©i–šg~>hšÃo~ðçŒ|;âoü-Õüá-ÃÞ±ùþ CðgöÂý´¼?ñóáÏÆoø$׈> üVøáÿ|Jÿ‚T~Ù²ícð3ÂúÁ/ÚŲÿ…î|C«êÞ?ø‘ûJ~É?´üý¯­žÿGñ~ð×OðÿÆo ø7áߊ:~Ò´OøG|wiáÏ j_¾x[Ǻî»áoüVÿ…SâØøg[ñwƒ-ŸG¿ûF‘¬Å•–¿¦G/ xÿö±ý–>|SðWÀ¿Š_´·ìÿð×ãoįøG?á]|ñÿÆO‡^ø§ãïøL|G{àÿÿÂð÷Ä^#Ó¼[â¯øJ¼[§jðçö‘ý¹â; ÝLûV§k=ª~@x·àÇí§ðâš|kø±ûü?ÿ‚¥|MøÉÿ€ø#û|kñ7€<}û1xí´wÁïüeñgLJÞ5Ñ¿i+‚¾³ý?m_|xÓuë 4¯ÝhŸð¤,­|eû-jVOà{k.ö­ðOíÉãψ_ô™ÿàŸŸ~Éß?fÏx¿â—Ç»¿‰Ÿ´Wì¿ñÛâÇí?ñá·ƒ#ƒ]ѦÑí¯#Õ,ZsÄß¶ïì_࿌¶ß³ŸŒk¿ÙƒÂ´çˆ<'á;?>&øûð§AøËuâ¯Á£]xÃ6ß 5OZøÚxÒÛÄ~¸ðž†Úˆà×ti´{kÈõKŸóöµý˜~7üOý?à¾_WöHñÆ[ŸÚãÄ‹?eK{öjºð¯Åox·öý“g? ø›F‡âOÆ?Ià/~Í´ÀK¯‰Þ&Ö~'é^Ôt;BðŸ>ÜüLñµ½¦‡`~Ö¿³Æÿ‰ÿ³§üËàªþÉ øËsû\xƒCñgì©os¯þÍW^ø­â¯þÀ_²oìçá?hÐüIøÇáé<âÙ£öƒø uñ;ÄÚÏÄý+ÀŽ“§h^ñ§À›Ÿ‰ž6·´Ðì?WüûXþË >)ø+à_Å/Ú[öøkñ·âWü#Ÿð®¾øÿã'ï|Sñ÷ü&>#½ð„á ø{â/éÞ-ñWü%^-Óµ xsû H¿þÜñ…}«SµžÕ~Ö?²ÇÅŠ~5øð·ö–ýŸþ%|møkÿ ü,_ƒ¾øÉðëÆ?üÿwˆìüâïøM~øwÄz‹|+ÿ¯‹u?ÂÞ#þÝÒ,?°üGg¢jeÔî µçâüããfûG~Ö—?d/Úþ Iÿ ŸöÿðwíÝû!ü|Ò¿à¨ÿc¿‚³Þ±ãK¯ ü>‹ÃŸ~h?´F›ñágÄØOáÿ„Œ?>>~Ïi¿üEøE/þø^Ïá7ü"¶_|1û}ÿüðÿÅ߇>øõð“âÇÀ¯ˆ ¿áý¯ÿm¿Š^ ø…â/üñ>4xö–ý¸¿ißÞ×~CðÛâߎþ hÿØÿüwàÙ<]¦|aðÂbÇXñn‰§øƒû;\¸Ò€>ÿ¢Š(¯€?à“¿ò‹/ø&Ÿý˜ìoÿ¬ëðæ¾ÿ¯€?à“¿ò‹/ø&Ÿý˜ìoÿ¬ëðæ€>ÿ¢Š(¢Š(àø$ïü¢Ëþ §ÿfûÿë:ü9¯¿ëàø$ïü¢Ëþ §ÿfûÿë:ü9¯¿èòƒþ eûfü}ÿ‚zÁ:¾1þÙ_³œ?õO|ñ‹S¿ü ãOxWÆ>ø‡ñC¯´m<øâ¿Âm_ž Óõˆ:‹m|Mqâ­:M;ÃZ¯…¦ð™¹ñ=ŸŠ¼-Àkß·‡ÇߨÃöÚýŸeÏÛ÷Ç?³þþ×ÿÿhŸ| ý£þ |8ñ§ì¿§|ñWìuà{ÏŒ´ —í'á?´gÇï Çð~ûàÞ£¢x“Áÿ<1ñcJÔ|5â¯øŸÃž5ø_ÿ¶¥iñAè?à½_²çÇoÛGþ =ûV~ͳG¿áe|mø•ÿ 3þ¯ÂMàïmÂûIüñÿˆÿâ£ñÿˆ|+á-;û;Â^×µoø›kÖlûØ,>Õ©ÝYY\yÿÄOø'|,ðWÇOŠ_´·ìÿð×à—įøG?á]|bñÿÆO‡^øYãïøL|9{ãÿÂñ Ä^#Ó¼%â¯øJ¼%§j)ðçö¯ý¹áË ÝoLûV™k=Ò|?ñ{[ýªŸãïìSûkxöøÁã-#Ã_ÿlßÙËã—ìÛsñsöQðÇíAð¯NøéãOÙ·âÃ47ß®ÿeω:çì‡g x›Ãz_íU¢ø«FðÿÆ ø¦ÆÃ[Õü/ã_i??|!ñ÷ö"ýž?à™÷ý|AàOÚ{àgÆø(×íááÿØëöBøsãOÛ‹Nø7ðÏö‡ø·ãßê?l¿e/†ÿ¿a¯ƒ>|;¼ðä¿hŸ…º>‘áÍ"÷AÑôˆŸ~&ü)ðv¯öÿÂŽ_~|øÃãOø)®¿û0| ¶ø=ñƒ[ð¦›ûFxgÇp|/ýž>.|×gðlßþ/\è¼oâ-_ö|ñ¯«øò‚¾,øWãωÞ5~-ø#YÕ|ã?x'Ç>¸¹ü€ðÅ8?iØ¿öiýŒþÿÁ-iÿÚ³öðGÁÿ~Ÿ·gìíûVY|ýšo?ÙÛÅ_þþÍ:×ìªøƒÁ¿¾2üøwâŸ|Cøw¯i¿lj>ø³EÔ|&þ)øeñ“À>xûáùø_âLß±gíÙð»öXý›dñÿìÉÿ ßÂ_±¯üÿÇ´§ì£ûüKøÑðwÄÿ¾ Á8ãøuñ³àìÛðÃMø§ãxëà—ŠÿhÙCSø¿gûGü'Ð|kûNkú^“àï‡Þ øáÏÚ3Áþ-ð烼/áÝígöÝý‹ü9ðk´gˆk¿ÙƒBýŸ<{â Ÿ øã¶³ñ÷áN—ðkÆž*³ŸÄÖ·žðŸÄûïAàŸø‚ÖçÁ~1·¹Ñ´}róQ‚ øšm–MT[^ÇÿðPŸØáGü!_ð´¿nÙá¯ü,¯‡þø±ðëþÿÚ[࿃¿á>øYã¶ÿÂ#ñ/Á_ð‘x×Nÿ„«áÿŠ¿³µøGk¾"ý¢<ÿþ ûû=~Ð < §øOö“ý†þ k^ø"ìû$x÷á¶»âÙÇÚ?¾.þÅÚÇí§ào‹³¦¡¡ÉûDj¾×ÿá|øKãƒüMð·Vñ=Í¿Á=sÀšæ§£|iñïÂÙê^ @Õÿ¶ïì_û5ø«Oð/íû]þÌ|m«x~×Åš_ƒ¾5ü}øSð¯ÅZ—…oµWG±ñ6Ÿáïx³AÕï3ø+ãwü/~Ð 5_øD¿`_Ø7öjñ'Ã?øY>?ø‘cûcüFø¥|Hø/ñ1¿áh|gýžü%ÿ Þÿ„óÅ(´ño‹lôïÓú+àø$ïü¢Ëþ §ÿfûÿë:ü9¯¿ëàø$ïü¢Ëþ §ÿfûÿë:ü9 ¿è¢Š(¢Šùƒö"ðÏÆ_þÅÿ²'ƒ¿hËŸ^~Ð~ý˜>xgãµç‹xgãµç‹8øƒöÍ;[ðÏ™áËÏì;O7ü ú¿‹¼GúûB|Tø»ð“Gð¹ð“öaøûTl|@_üBðŸÂ߈ü ã¿øoxïY‡â.…mñûâ'ÂO‡þ8û?Ä À~Ôü#'ÄŸ kú?Žõi³ëð†Üxg[þx`_Ùöýýƒ?cßø'OìËñ?ö7ñÅÿ |<ý˜?lý#ãçŠ?c/Ž?³¦ƒûWx+âíCñÎéß³Œß~8|Pý™®~|´ðO‹´¿øëâWì}ûBjø'ãg‡ÛÅŸ|cñãïŸøWâç…SNðþ°þ&øcâø³KÒ<{áõÒüQ·¶øÍâÙ{ÆÚ~‡ûoþÈ~ ¸ý¥|%¢xsQÓuïÍá)¾>^èß à÷ÿø(¿À?Ú«Æ>ý·?e¯¿°ˆ5oÙóâwüƒÃ?²Çì©âØÏâoì£ðV KÆ–wÅ'ðŸ…?jGâOÅÏÙkã'Ãÿƒÿ³g‰.<=á¿‚ÿ¾Ý|Oø®|øÕûG§Å½/âtŸ­> ü!öþÚÿµÿÇŸÛ³öùý‰ÿdŒ¿°Â_‰¿±gÃÿƒúŸƒþ~Òÿ ÿh‹¿þ>ë~øâÅ×Å-[[ø}ñ›à‡þ~ÏúGˆ>%ü6ø3¨xõŽü+â;M{ƾ=м1eヾ ñ§ßúßí§ðOàwÂÏ~,ýº~(þÏÿ°¿Äߌôj >:~Ò´OøG|wiáÏ j_¾x[Ǻî»áoüVÿ…SâØøg[ñwƒ-ŸG¿ûF‘¬Å•–¿¦G/ãü‡áOíUûtxã烾"Á!þ0x›Æÿ¼?áï‹ðH¿Ú¿á?í)û(ü/øËû=þÐþ7ý—ü/©_Iñ‡Yµý¹~øÇÀÞ øûXźޥð§Ä?>|OƒÁ Ö-üâkŸƒÞøñ3Ø<[ðcöÓø?ñM>5üXýƒ~ÿÁR¾&üdÿ‚@|ý>5ø›À>ý˜¼öÚ;à÷ˆþ2ø³ãÃïèß´•‡Á_Ùþȶ¯ˆ>ÿ„ÇÄw¾ðü!_|Eâ=;ž*ÿ„«Åºv¡áoaiÿÛž#°½Ñ4ϵjv³Ú§?áŸÛwö/ñ§Æ[ŸÙÏÁßµßìÁâÏÚÏÄ,ðçÀŸ ü}øS¯|eµñW€ Ön¼uᛟ†_‹.¼mˆ<máÏÜx³F—C]GÃhZÌÚŵœz]óAùûVø'öäñçÄ/zLÿðOÏŒ*Ò4oø(ÿì ûMüñì¥ñ¿ö9ðÁ¯Š ?d³g¼_ñKãÝßÄÏÚ+ö_øíñcöŸø‹ðÛÁž6øY«|3øÑðûÆ?³Ç€gø+û&è¿ ¯¯ð¦ãöœñ k?³ÆÿþÌžøGâÙ#ÄïÄOÁw®iïø—Y×ÿf­SNð_Á Ïø+¿‰¿mÛÏÚßÂzÍ÷Æ9õÏx~ëö\ñ§Œ~Üè>›gûJÏâ¯x›Àrüo‡zö©âË Óÿ þÛ¿±>2ÜþÎ~ý®ÿf~Ðv~ ñg„ï>øgãïÂ{ã-¯Š¼³u㯠Üü0ÒüYuãhƒBÖfÖ-¬ãÒþÇöXÿ…íÿ ¹ÿ -û?ÿÃMѺÂäøuÿ ÛþDïøXŸòH¿á#ÿ…ÿ$ÿþ+Ÿù¿äNÿŠ›þ@¿éÕùA¬þÌ?üGû2xOáˆdk¿<ÿÞ¹ý§¼ â]g_ýšµM;Á/?à®þ&ý·o?k ë7ßç×<9áû¯ÙsÆž1øKs húmŸí+?мYâoËðq¾ëÚ§‹.¾ ý‹?à—?fÿг'ƒþ=~È_´ísñëöaý¯þ4x¿Â?ðTŸÿÁQþ.øàK|øéâ?|mø‹û@鳄´GŒþ&økö€ø‘{â O‚ß?ehfÍOáí ãMoÄÿ?jøÆ^"ñeÈô}ð/ö±ý–?jøJáš?ioÙÿöˆÿ„ûþ_øQ>|[ÿ„?þoíøFÿá)ÿ„Ä~ ÿ„þøGõïìOío²jÿbjÿ`ûGömç“ïõðüßÃÿ~þÀ¿²ŸÁOŽŸ¾ |ø›û:þÏÿ?g¿øgÇþ%ø'âÏøJ5„þxO[ñ炵Ÿþ0x~ãáþ·â -_Oðãx§Uð§ŽåþƽºÖü ¡Y\é:ŸßôW̱³ðkıì‰âÙÏÂ~ ðìù¯~̵Ÿ>ñeÌ÷ž*ð_Á­SáO„ï¾xOÄ×—^&ñ¥Í׈<9à™ô=Y¹¸ñ‹'ŸQ³¹–ok²3j—_O×̱³ðkıì‰âÙÏÂ~ ðìù¯~̵Ÿ>ñeÌ÷ž*ð_Á­SáO„ï¾xOÄ×—^&ñ¥Í׈<9à™ô=Y¹¸ñ‹'ŸQ³¹–ok²3j—@?ÿÿ”YÁ4ÿìÀ?cýg_‡5÷ý ±ü×ÿÛýšÿbÿÙösñ×Á/ÛVñ·ÀÙƒàÁOêžømðûº—оü)ðŸ|C¨xfûXý¥t^óÃ÷š¾…yq£]jš¨ÜiÒ[M}¥i×/-œ?OÿÄjßðK/ú ·ÿþÏÙ×ÿ¢ª€?¯Ú+ùÿˆÕ¿à–_ô@ÿoÿü5Ÿ³¯ÿEUñ·üËþˆíÿÿ†³öuÿ誠ëöŠþ@¿â5oø%—ý?Ûÿÿ gìëÿÑUGüF­ÿ²ÿ¢ûÿá¬ýú*¨úý¢¿/ø[þ eÿDöÿÿÃYû:ÿôUQÿ«Á,¿èþßÿøk?g_þŠªþ¿h¯ä þ#Vÿ‚YÑý¿ÿðÖ~οýTÄjßðK/ú ·ÿþÏÙ×ÿ¢ª€?¯Ú+ùÿˆÕ¿à–_ô@ÿoÿü5Ÿ³¯ÿEUñ·üËþˆíÿÿ†³öuÿ誠ëöŠþ@¿â5oø%—ý?Ûÿÿ gìëÿÑUGüF­ÿ²ÿ¢ûÿá¬ýú*¨úý¯€?à“¿ò‹/ø&Ÿý˜ìoÿ¬ëðæ¿â5oø%—ý?Ûÿÿ gìëÿÑU_¿ßðIßùE—üOþÌö7ÿÖuøs@ÑEQEðüwþQeÿÓÿ³ýÿõ~×ßõü1þÄ_ðw_üoöký‹ÿdOÙÏÇ_¿mý[Æßf€_xëá§ÅoxÇ>ÔGñÏ„ôïÁá_Ác¡Ëâ}U¹ðæ>™üÐÿÄjßðK/ú ·ÿþÏÙ×ÿ¢ªø[þ eÿDöÿÿÃYû:ÿôUPõ{ðŸáo>|,øiðSán…ÿ¿Ã/ƒÿüð·á׆´õoþß|?ðæ›á?è_Û>"Ô5jÿÙÒ4ý?ûO]Õu=bÿìÿjÔõ ÛÙg¹—Ð+ùÿˆÕ¿à–_ô@ÿoÿü5Ÿ³¯ÿEUñ·üËþˆíÿÿ†³öuÿ誠ëöŠþ@¿â5oø%—ý?Ûÿÿ gìëÿÑUGüF­ÿ²ÿ¢ûÿá¬ýú*¨úý¢¿/ø[þ eÿDöÿÿÃYû:ÿôUQÿ«Á,¿èþßÿøk?g_þŠªþ¿h¯ä þ#Vÿ‚YÑý¿ÿðÖ~οýTÄjßðK/ú ·ÿþÏÙ×ÿ¢ª€?¯Úøþ ;ÿ(²ÿ‚iÿÙ€~Æÿúοkðþ#Vÿ‚YÑý¿ÿðÖ~οýUûýÿÿ”YÁ4ÿìÀ?cýg_‡4÷ýQ@Q@0~Ä^øËà¿Ø¿öDðwísâ ÏÚ³À/ üv¼ñg‹ ñ¾2è_ |'¥üO¹ñ7Ž­uŸÛxÓÄøÚ×\—Yñe¿ˆuØ'øÕàý#FñÒ>5xVÝm|+®xÓN:ŽŸâNª-­tëÍT÷zŠü!ýµ¿àáoØ¿öøû þÑŸ~þÓþ#ðOüàüßþ é<ð§WñW†|+‚þxéôÿ‰Ö>'øÕàý#FñÒ>5xVÝm|+®xÓN:ŽŸâNª-­tëÍTýµ¿àáoØ¿öøû þÑŸ~þÓþ#ðOüàüßþ é<ð§WñW†|+‚þxéôÿ‰Ö>'øÕàý#FñÒ>5xVÝm|+®xÓN:ŽŸâNª-­tëÍT÷zŠü!ýµ¿àáoØ¿öøû þÑŸ~þÓþ#ðOüàüßþ é<ð§WñW†|+‚þxéôÿ‰Ö>'øÕàý#FñÒ>5xVÝm|+®xÓN:ŽŸâNª-­tëÍTýµ¿àáoØ¿öøû þÑŸ~þÓþ#ðOüàüßþ é<ð§WñW†|+‚þxéôÿ‰Ö>'øÕàý#FñÒ>5xVÝm|+®xÓN:ŽŸâNª-­tëÍT÷zŠü!ýµ¿àáoØ¿öøû þÑŸ~þÓþ#ðOüàüßþ é<ð§WñW†|+‚þxéôÿ‰Ö>'øÕàý#FñÒ>5xVÝm|+®xÓN:ŽŸâNª-­tëÍTýµ¿àáoØ¿öøû þÑŸ~þÓþ#ðOüàüßþ é<ð§WñW†|+‚þxéôÿ‰Ö>'øÕàý#FñÒ>5xVÝm|+®xÓN:ŽŸâNª-­tëÍT÷zŠü!ýµ¿àáoØ¿öøû þÑŸ~þÓþ#ðOüàüßþ é<ð§WñW†|+‚þxéôÿ‰Ö>'øÕàý#FñÒ>5xVÝm|+®xÓN:ŽŸâNª-­tëÍTýµ¿àáoØ¿öøû þÑŸ~þÓþ#ðOüàüßþ é<ð§WñW†|+‚þxéôÿ‰Ö>'øÕàý#FñÒ>5xVÝm|+®xÓN:ŽŸâNª-­tëÍT÷zŠü!ýµ¿àáoØ¿öøû þÑŸ~þÓþ#ðOüàüßþ é<ð§WñW†|+‚þxéôÿ‰Ö>'øÕàý#FñÒ>5xVÝm|+®xÓN:ŽŸâNª-­tëÍTýµ¿àáoØ¿öøû þÑŸ~þÓþ#ðOüàüßþ é<ð§WñW†|+‚þxéôÿ‰Ö>'øÕàý#FñÒ>5xVÝm|+®xÓN:ŽŸâNª-­tëÍT÷zŠü!ýµ¿àáoØ¿öøû þÑŸ~þÓþ#ðOüàüßþ é<ð§WñW†|+‚þxéôÿ‰Ö>'øÕàý#FñÒ>5xVÝm|+®xÓN:ŽŸâNª-­tëÍTýµ¿àáoØ¿öøû þÑŸ~þÓþ#ðOüàüßþ é<ð§WñW†|+‚þxéôÿ‰Ö>'øÕàý#FñÒ>5xVÝm|+®xÓN:ŽŸâNª-­tëÍT÷z¾`ýˆµŸƒ^#ý‹ÿdOþÎ~ñ€¿gÍ{ö`ø¬ü ð/‹.g¼ñW‚þ jŸ |'}ðÃÂ~&¼ºñ7.n¼AáÏÏ¡èúÍÍÇŒ|Y<ú̳x›]‘›TºüÀýµ¿àáoØ¿öøû þÑŸ~þÓþ#ðOüàüßþ é<ð§WñW†|+‚þxéôÿ‰Ö>'øÕàý#FñÒ>5xVÝm|+®xÓN:ŽŸâNª-­tëÍWôÿö"Ö~ xö/ý‘‡£ë771ñdóê6w2ÍâmvFmRèÿÙscons-doc-2.3.0/doc/python10/main.xml0000644000175000017500000001433212114661557020122 0ustar dktrkranzdktrkranz %scons; ]>
SCons Design and Implementation Steven Knight 2001 2002 Steven Knight 2002 4-7 February 2002 The Tenth International Python Conference
Alexandria, Virginia
0.2 16 December 2001 Internal re-review. 0.1 8 October 2001 Submitted for Python10 conference.
&abstract;
Introduction &intro;
Architecture &design;
Installation &install;
Development Process &process;
Future Directions &future;
Summary This paper has introduced &SCons;, a next-generation build tool with a modular, embeddable architecture and a direct Python interface. &SCons; has a global view of the dependencies in a source tree, uses MD5 signatures to decide if derived files are out of date, and automatically scans files for dependencies, all of which make &SCons; builds exceptionally reliable. The &SCons; development methodology has been described, notable for its emphasis on automated regression testing to ensure a robust and reliable tool from day one. Several future directions for &SCons; have also been discussed.
Acknowledgements &acks;
References 1 Stuart I.Feldman Aug 1978 Bell Laboratories Make - A Program for Maintaining Computer Programs 2 PeterMiller 1997 Peter Miller Recursive Make Considered Harmful 3 AndrewOram SteveTalbott 1986 1991 O'Reilly & Associates, Inc. O'Reilly & Associates, Inc. Managing Projects with Make, 2nd Ed. 4 Richard M.Stallman RolandMcGrath 1988 '89 '90 '91 '92 '93 '94 '95 '96 '97 '98 '99 2000 Free Software Foundation, Inc. Free Software Foundation, Inc. GNU Make A Program for Directing Recompilation
scons-doc-2.3.0/doc/SConscript0000644000175000017500000005412412114661557017007 0ustar dktrkranzdktrkranz# # SConscript file for building SCons documentation. # # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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. import os.path import re Import('build_dir', 'env', 'whereis') env = env.Clone() build = os.path.join(build_dir, 'doc') # # # dist_doc_tar_gz = '$DISTDIR/scons-doc-${VERSION}.tar.gz' # # We'll only try to build text files (for some documents) # if lynx is available to do the dump. # fig2dev = whereis('fig2dev') epydoc_cli = whereis('epydoc') groff = whereis('groff') lynx = whereis('lynx') man2html = whereis('man2html') jade_original = whereis('jade') jade = whereis('openjade') or jade_original jadetex = whereis('jadetex') pdfjadetex = whereis('pdfjadetex') jw = whereis('jw') tidy = whereis('tidy') tar_deps = [] tar_list = [] entity_re = re.compile(r'', re.I) format_re = re.compile(r'<(?:graphic|imagedata)\s+fileref="([^"]*)"(?:\s+format="([^"]*)")?') # # Find internal dependencies in .xml files: # # # # # # This only finds one per line, and assumes that anything # defined as a SYSTEM entity is, in fact, a file included # somewhere in the document. # def scanxml(node, env, target): includes = [] contents = node.get_text_contents() includes.extend(entity_re.findall(contents)) matches = format_re.findall(contents) for m in matches: file, format = m if format and file[-len(format):] != format: file = file + '.' + format if not os.path.isabs(file): a = [] f = file while f: f, tail = os.path.split(f) if tail == 'doc': break a = [tail] + a file = os.path.join(*a) includes.append(file) return includes s = Scanner(name = 'xml', function = scanxml, skeys = ['.xml', '.mod']) orig_env = env env = orig_env.Clone(SCANNERS = [s], SCONS_DOC_PY = File('#bin/scons-doc.py').rfile(), SCONS_PROC_PY = File('#bin/scons-proc.py').rfile()) # Fetch the list of files in the build engine that contain # SCons documentation XML for processing. def chop(s): return s[:-1] # If we ever read doc from __scons_doc__ strings in *.py files again, # here's how it's done: #manifest_in = File('#src/engine/MANIFEST.in').rstr() #manifest_xml_in = File('#src/engine/MANIFEST-xml.in').rstr() #scons_doc_files = map(chop, open(manifest_in).readlines() +\ # open(manifest_xml_in).readlines()) #scons_doc_files = map(lambda x: '#src/engine/'+x, scons_doc_files) #manifest_in = File('#src/engine/MANIFEST.in').rstr() manifest_xml_in = File('#src/engine/MANIFEST-xml.in').rstr() scons_doc_files = list(map(chop, open(manifest_xml_in).readlines())) scons_doc_files = [File('#src/engine/'+x).rstr() for x in scons_doc_files] if not jw: print "doc: jw not found, skipping building User Guide." else: # # Always create a version.xml file containing the version information # for this run. Ignore it for dependency purposes so we don't # rebuild all the docs every time just because the date changes. # date, ver, rev = env.Dictionary('DATE', 'VERSION', 'REVISION') version_xml = File(os.path.join(build, "version.xml")) #version_xml = File("version.xml") verfile = str(version_xml) try: os.unlink(verfile) except OSError: pass # okay if the file didn't exist dir, f = os.path.split(verfile) try: os.makedirs(dir) except OSError: pass # okay if the directory already exists open(verfile, "w").write(""" """ % (date, ver, rev)) builders_gen = os.path.join(build, 'user', 'builders.gen') builders_mod = os.path.join(build, 'user', 'builders.mod') functions_gen = os.path.join(build, 'user', 'functions.gen') functions_mod = os.path.join(build, 'user', 'functions.mod') tools_gen = os.path.join(build, 'user', 'tools.gen') tools_mod = os.path.join(build, 'user', 'tools.mod') variables_gen = os.path.join(build, 'user', 'variables.gen') variables_mod = os.path.join(build, 'user', 'variables.mod') # We put $( - $) around $SOURCES in the command line below because # the path names will change when a given input file is found in # a repository one run and locally the next, and we don't want # to rebuild documentation just because it's found in one location # vs. the other. The *.gen and *.mod targets will still be dependent # on the list of the files themselves. doc_output_files = [builders_gen, builders_mod, functions_gen, functions_mod, tools_gen, tools_mod, variables_gen, variables_mod] b = env.Command(doc_output_files, scons_doc_files, "$PYTHON $SCONS_PROC_PY --xml -b ${TARGETS[0]},${TARGETS[1]} -f ${TARGETS[2]},${TARGETS[3]} -t ${TARGETS[4]},${TARGETS[5]} -v ${TARGETS[6]},${TARGETS[7]} $( $SOURCES $)") env.Depends(b, "$SCONS_PROC_PY") env.Local(b) # # Each document will live in its own subdirectory. List them here # as hash keys, with a hash of the info to control its build. # docs = { 'design' : { 'htmlindex' : 'book1.html', 'ps' : 1, 'pdf' : 1, 'text' : 0, }, # This doesn't build on all systems, and the document is old # enough that there's reallyno need to build it every time any # more, so just comment it out for now. #'python10' : { # 'htmlindex' : 't1.html', # 'html' : 1, # 'ps' : 1, # 'pdf' : 0, # 'text' : 0, # 'graphics' : [ # 'arch.fig', # 'builder.fig', # 'job-task.fig', # 'node.fig', # 'scanner.fig', # 'sig.fig' # ], #}, 'reference' : { 'htmlindex' : 'book1.html', 'html' : 1, 'ps' : 1, 'pdf' : 1, 'text' : 0, }, # For whenever (if ever?) we start putting developer guide # information in a printable document instead of the wiki. #'developer' : { # 'htmlindex' : 'book1.html', # 'html' : 1, # 'ps' : 1, # 'pdf' : 1, # 'text' : 0, #}, 'user' : { 'htmlindex' : 'book1.html', 'html' : 1, 'ps' : 1, 'pdf' : 1, 'text' : 1, 'graphics' : [ 'SCons-win32-install-1.jpg', 'SCons-win32-install-2.jpg', 'SCons-win32-install-3.jpg', 'SCons-win32-install-4.jpg', ], 'scons-doc' : 1, }, } # # We have to tell SCons to scan the top-level XML files which # get included by the document XML files in the subdirectories. # manifest = File('MANIFEST').rstr() src_files = [x[:-1] for x in open(manifest).readlines()] for s in src_files: base, ext = os.path.splitext(s) if ext in ['.fig', '.jpg']: orig_env.Install(build, s) else: orig_env.SCons_revision(os.path.join(build, s), s) Local(os.path.join(build, s)) # # For each document, build the document itself in HTML, Postscript, # and PDF formats. # for doc in docs.keys(): manifest = File(os.path.join(doc, 'MANIFEST')).rstr() src_files = [x[:-1] for x in open(manifest).readlines()] build_doc = docs[doc].get('scons-doc') and int(ARGUMENTS.get('BUILDDOC', 0)) for s in src_files: doc_s = os.path.join(doc, s) build_s = os.path.join(build, doc, s) base, ext = os.path.splitext(doc_s) if ext in ['.fig', '.jpg']: orig_env.InstallAs(build_s, doc_s) else: if build_doc and ext == '.xml': env.Command(doc_s, base + '.in', "$PYTHON $SCONS_DOC_PY $SOURCE > $TARGET") orig_env.SCons_revision(build_s, doc_s) Local(build_s) main = os.path.join(build, doc, 'main.xml') out = 'main.out' # Hard-coding the scons-src path is a bit of a hack. This can # be reworked when a better solution presents itself. scons_src_main = os.path.join(build_dir, 'scons-src', 'doc', main) env.Ignore(scons_src_main, version_xml) htmldir = os.path.join(build, 'HTML', 'scons-%s' % doc) htmlindex = os.path.join(htmldir, docs[doc]['htmlindex']) html = os.path.join(build, 'HTML', 'scons-%s.html' % doc) ps = os.path.join(build, 'PS', 'scons-%s.ps' % doc) pdf = os.path.join(build, 'PDF', 'scons-%s.pdf' % doc) text = os.path.join(build, 'TEXT', 'scons-%s.txt' % doc) if docs[doc].get('html') and jade: def copy_index_html(target, source, env): # Older versions of DocBook|jw|jade|whatever would # create a book1.html file, while newer versions create # an index.html file (logically enough). The scons.org # web site links expect book1.html, so we're going to # leave the target as is, and run this post-processing # action function to check that the target really did # get created, and if it didn't, copy it from index.html. t = str(target[0]) if not os.path.exists(t): i = os.path.join(os.path.split(t)[0], 'index.html') open(t, 'w').write(open(i, 'r').read()) return None cmds = [ Delete("${TARGET.dir}/*.html"), "jw -b html -o ${TARGET.dir} $SOURCES", ] if tidy: cmds.append("tidy -m -q $TARGET || true") cmds.append(Action(copy_index_html)) env.Command(htmlindex, File(main), cmds) Local(htmlindex) cmds = [ Delete("${TARGET.dir}/main.html"), "jw -u -b html -o ${TARGET.dir} $SOURCES", Move("$TARGET", "${TARGET.dir}/main.html"), ] if tidy: cmds.append("tidy -m -q $TARGET || true") env.Command(html, File(main), cmds) Local(html) env.Ignore([html, htmlindex], version_xml) tar_deps.extend([html, htmlindex]) tar_list.extend([html, htmldir]) for g in docs[doc].get('graphics', []): base, ext = os.path.splitext(g) if ext == '.fig': jpg = base + '.jpg' htmldir_jpg = os.path.join(htmldir, jpg) if fig2dev: fig = os.path.join(build, doc, g) env.Command(htmldir_jpg, fig, "%s -L jpeg -q 100 $SOURCES $TARGET" % fig2dev) else: env.InstallAs(htmldir_jpg, jpg) env.Depends(html, htmldir_jpg) Local(htmldir_jpg) else: src = os.path.join(build, doc, g) Local(env.Install(htmldir, src)) if docs[doc].get('ps') and jadetex and jade_original: env.Command(ps, main, [ Delete("${TARGET.dir}/%s" % out), "jw -b ps -o ${TARGET.dir} -p %s $SOURCES" % jade_original, "mv ${TARGET.dir}/main.ps $TARGET", Delete("${TARGET.dir}/%s" % out), ]) Local(ps) env.Ignore(ps, version_xml) tar_deps.append(ps) tar_list.append(ps) for g in docs[doc].get('graphics', []): base, ext = os.path.splitext(g) if ext == '.fig': eps = base + '.eps' build_eps = os.path.join(build, 'PS', eps) if fig2dev: fig = os.path.join(build, doc, g) env.Command(build_eps, fig, "%s -L eps $SOURCES $TARGET" % fig2dev) else: env.InstallAs(build_eps, eps) env.Depends(ps, build_eps) Local(build_eps) else: src = os.path.join(build, doc, g) Local(env.Install(htmldir, src)) if docs[doc].get('pdf') and pdfjadetex and jade_original: env.Command(pdf, main, [ Delete("${TARGET.dir}/%s" % out), "jw -b pdf -o ${TARGET.dir} -p %s $SOURCES" % jade_original, "mv ${TARGET.dir}/main.pdf $TARGET", Delete("${TARGET.dir}/out"), ]) Local(pdf) env.Ignore(pdf, version_xml) tar_deps.append(pdf) tar_list.append(pdf) if docs[doc].get('text') and jade and lynx: env.Command(text, html, "lynx -dump ${SOURCE.abspath} > $TARGET") Local(text) env.Ignore(text, version_xml) tar_deps.append(text) tar_list.append(text) # # Man page(s), in good ol' troff format. # man_page_list = ['scons.1', 'sconsign.1', 'scons-time.1'] for m in man_page_list: x = orig_env.SCons_revision(os.path.join(build, 'man', m), os.path.join('man', m)) man_i_files = ['builders.man', 'functions.man', 'tools.man', 'variables.man'] man_intermediate_files = [os.path.join(build, 'man', x) for x in man_i_files] cmd = "$PYTHON $SCONS_PROC_PY --man -b ${TARGETS[0]} -f ${TARGETS[1]} -t ${TARGETS[2]} -v ${TARGETS[3]} $( $SOURCES $)" man_intermediate_files = env.Command(man_intermediate_files, scons_doc_files, cmd) env.Depends(man_intermediate_files, "$SCONS_PROC_PY") Local(man_intermediate_files) for man_1 in man_page_list: man, _1 = os.path.splitext(man_1) man_1 = os.path.join(build, 'man', man_1) if groff: ps = os.path.join(build, 'PS', '%s-man.ps' % man) text = os.path.join(build, 'TEXT', '%s-man.txt' % man) b = env.Command(ps, man_1, "( cd ${SOURCES.dir} && groff -man -Tps ${SOURCES.file} ) > $TARGET") Local(ps) env.Depends(b, man_intermediate_files) b = env.Command(text, man_1, "( cd ${SOURCES.dir} && groff -man -Tascii ${SOURCES.file} ) > $TARGET") Local(text) env.Depends(b, man_intermediate_files) tar_deps.extend([ps, text]) tar_list.extend([ps, text]) else: print "doc: WARNING: no groff, skipping text and PostScript versions of man pages" if man2html: html = os.path.join(build, 'HTML' , '%s-man.html' % man) def strip_to_first_html_tag(target, source, env): t = str(target[0]) contents = open(t).read() contents = contents[contents.find(''):] open(t, 'w').write(contents) return 0 cmds = [ "( cd %s/man && cp %s .. )" % (build, ' '.join(man_i_files)), "( cd ${SOURCE.dir} && man2html ${SOURCE.file} ) > $TARGET", Action(strip_to_first_html_tag), ] if tidy: cmds.append("tidy -m -q $TARGET || true") b = env.Command(html, man_1, cmds) Local(html) env.Depends(b, man_intermediate_files) tar_deps.append(html) tar_list.append(html) else: print "doc: WARNING: no man2html, skipping HTML versions of man pages" if not epydoc_cli: try: import epydoc except ImportError: epydoc = None else: # adding Epydoc builder using imported module def epydoc_builder_action(target, source, env): """ Take a list of `source` files and build docs for them in `target` dir. `target` and `source` are lists. Uses OUTDIR and EPYDOCFLAGS environment variables. http://www.scons.org/doc/2.0.1/HTML/scons-user/x3594.html """ # the epydoc build process is the following: # 1. build documentation index # 2. feed doc index to writer for docs from epydoc.docbuilder import build_doc_index from epydoc.docwriter.html import HTMLWriter from epydoc.docwriter.latex import LatexWriter # first arg is a list where can be names of python package dirs, # python files, object names or objects itself docindex = build_doc_index([str(src) for src in source]) if docindex == None: return -1 if env['EPYDOCFLAGS'] == '--html': html_writer = HTMLWriter(docindex, docformat='restructuredText', prj_name='SCons', prj_url='http://www.scons.org/') try: html_writer.write(env['OUTDIR']) except OSError: # If directory cannot be created or any file cannot # be created or written to. return -2 """ # PDF support requires external Linux utilites, so it's not crossplatform. # Leaving for now. # http://epydoc.svn.sourceforge.net/viewvc/epydoc/trunk/epydoc/src/epydoc/cli.py elif env['EPYDOCFLAGS'] == '--pdf': pdf_writer = LatexWriter(docindex, docformat='restructuredText', prj_name='SCons', prj_url='http://www.scons.org/') """ return 0 epydoc_commands = [ Delete('$OUTDIR'), epydoc_builder_action, Touch('$TARGET'), ] else: # epydoc_cli is found epydoc_commands = [ Delete('$OUTDIR'), '$EPYDOC $EPYDOCFLAGS --debug --output $OUTDIR --docformat=restructuredText --name SCons --url http://www.scons.org/ $SOURCES', Touch('$TARGET'), ] if not epydoc_cli and not epydoc: print "doc: epydoc not found, skipping building API documentation." else: # XXX Should be in common with reading the same thing in # the SConstruct file. e = os.path.join('#src', 'engine') manifest_in = File(os.path.join(e, 'MANIFEST.in')).rstr() sources = [x[:-1] for x in open(manifest_in).readlines()] sources = [x for x in sources if x.find('Platform') == -1] sources = [x for x in sources if x.find('Tool') == -1] # XXX sources = [x for x in sources if x.find('Options') == -1] e = os.path.join(build, '..', 'scons', 'engine') sources = [os.path.join(e, x) for x in sources] htmldir = os.path.join(build, 'HTML', 'scons-api') env.Command('${OUTDIR}/index.html', sources, epydoc_commands, EPYDOC=epydoc_cli, EPYDOCFLAGS='--html', OUTDIR=htmldir) tar_deps.append(htmldir) tar_list.append(htmldir) if not epydoc_cli: print "doc: command line epydoc is not found, skipping PDF/PS/Tex output" else: # PDF and PostScript and TeX are built from the # same invocation. api_dir = os.path.join(build, 'scons-api') api_pdf = os.path.join(api_dir, 'api.pdf') api_ps = os.path.join(api_dir, 'api.ps') api_tex = os.path.join(api_dir, 'api.tex') api_targets = [api_pdf, api_ps, api_tex] env.Command(api_targets, sources, epydoc_commands, EPYDOC=epydoc_cli, EPYDOCFLAGS='--pdf', OUTDIR=api_dir) Local(api_targets) pdf_install = os.path.join(build, 'PDF', 'scons-api.pdf') env.InstallAs(pdf_install, api_pdf) tar_deps.append(pdf_install) tar_list.append(pdf_install) Local(pdf_install) ps_install = os.path.join(build, 'PS', 'scons-api.ps') env.InstallAs(ps_install, api_ps) tar_deps.append(ps_install) tar_list.append(ps_install) Local(ps_install) # # Now actually create the tar file of the documentation, # for easy distribution to the web site. # if tar_deps: tar_list = ' '.join([x.replace(build+'/', '') for x in tar_list]) t = env.Command(dist_doc_tar_gz, tar_deps, "tar cf${TAR_HFLAG} - -C %s %s | gzip > $TARGET" % (build, tar_list)) AddPostAction(dist_doc_tar_gz, Chmod(dist_doc_tar_gz, 0644)) Local(t) Alias('doc', t) else: Alias('doc', os.path.join(build_dir, 'doc')) scons-doc-2.3.0/doc/man/0000755000175000017500000000000012114661557015542 5ustar dktrkranzdktrkranzscons-doc-2.3.0/doc/man/scons.10000644000175000017500000046225212114661557016764 0ustar dktrkranzdktrkranz.\" Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation .\" .\" 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. .\" .\" doc/man/scons.1 2013/03/03 09:48:35 garyo .\" .TH SCONS 1 "March 2013" .\" ES - Example Start - indents and turns off line fill .rm ES .de ES .RS .nf .. .\" EE - Example End - ends indent and turns line fill back on .rm EE .de EE .fi .RE .. .SH NAME scons \- a software construction tool .SH SYNOPSIS .B scons [ .IR options ... ] [ .IR name = val ... ] [ .IR targets ... ] .SH DESCRIPTION The .B scons utility builds software (or other files) by determining which component pieces must be rebuilt and executing the necessary commands to rebuild them. By default, .B scons searches for a file named .IR SConstruct , .IR Sconstruct , or .I sconstruct (in that order) in the current directory and reads its configuration from the first file found. An alternate file name may be specified via the .B -f option. The .I SConstruct file can specify subsidiary configuration files using the .BR SConscript () function. By convention, these subsidiary files are named .IR SConscript , although any name may be used. (Because of this naming convention, the term "SConscript files" is sometimes used to refer generically to all .B scons configuration files, regardless of actual file name.) The configuration files specify the target files to be built, and (optionally) the rules to build those targets. Reasonable default rules exist for building common software components (executable programs, object files, libraries), so that for most software projects, only the target and input files need be specified. Before reading the .I SConstruct file, .B scons looks for a directory named .I site_scons in various system directories (see below) and the directory containing the .I SConstruct file; for each of those dirs which exists, .I site_scons is prepended to sys.path, the file .IR site_scons/site_init.py , is evaluated if it exists, and the directory .I site_scons/site_tools is prepended to the default toolpath if it exists. See the .I --no-site-dir and .I --site-dir options for more details. .B scons reads and executes the SConscript files as Python scripts, so you may use normal Python scripting capabilities (such as flow control, data manipulation, and imported Python libraries) to handle complicated build situations. .BR scons , however, reads and executes all of the SConscript files .I before it begins building any targets. To make this obvious, .B scons prints the following messages about what it is doing: .ES $ scons foo.out scons: Reading SConscript files ... scons: done reading SConscript files. scons: Building targets ... cp foo.in foo.out scons: done building targets. $ .EE The status messages (everything except the line that reads "cp foo.in foo.out") may be suppressed using the .B -Q option. .B scons does not automatically propagate the external environment used to execute .B scons to the commands used to build target files. This is so that builds will be guaranteed repeatable regardless of the environment variables set at the time .B scons is invoked. This also means that if the compiler or other commands that you want to use to build your target files are not in standard system locations, .B scons will not find them unless you explicitly set the PATH to include those locations. Whenever you create an .B scons construction environment, you can propagate the value of PATH from your external environment as follows: .ES import os env = Environment(ENV = {'PATH' : os.environ['PATH']}) .EE Similarly, if the commands use external environment variables like $PATH, $HOME, $JAVA_HOME, $LANG, $SHELL, $TERM, etc., these variables can also be explicitly propagated: .ES import os env = Environment(ENV = {'PATH' : os.environ['PATH'], 'HOME' : os.environ['HOME']}) .EE Or you may explicitly propagate the invoking user's complete external environment: .ES import os env = Environment(ENV = os.environ) .EE This comes at the expense of making your build dependent on the user's environment being set correctly, but it may be more convenient for many configurations. .B scons can scan known input files automatically for dependency information (for example, #include statements in C or C++ files) and will rebuild dependent files appropriately whenever any "included" input file changes. .B scons supports the ability to define new scanners for unknown input file types. .B scons knows how to fetch files automatically from SCCS or RCS subdirectories using SCCS, RCS or BitKeeper. .B scons is normally executed in a top-level directory containing a .I SConstruct file, optionally specifying as command-line arguments the target file or files to be built. By default, the command .ES scons .EE will build all target files in or below the current directory. Explicit default targets (to be built when no targets are specified on the command line) may be defined the SConscript file(s) using the .B Default() function, described below. Even when .B Default() targets are specified in the SConscript file(s), all target files in or below the current directory may be built by explicitly specifying the current directory (.) as a command-line target: .ES scons . .EE Building all target files, including any files outside of the current directory, may be specified by supplying a command-line target of the root directory (on POSIX systems): .ES scons / .EE or the path name(s) of the volume(s) in which all the targets should be built (on Windows systems): .ES scons C:\\ D:\\ .EE To build only specific targets, supply them as command-line arguments: .ES scons foo bar .EE in which case only the specified targets will be built (along with any derived files on which they depend). Specifying "cleanup" targets in SConscript files is not usually necessary. The .B -c flag removes all files necessary to build the specified target: .ES scons -c . .EE to remove all target files, or: .ES scons -c build export .EE to remove target files under build and export. Additional files or directories to remove can be specified using the .BR Clean() function. Conversely, targets that would normally be removed by the .B -c invocation can be prevented from being removed by using the .BR NoClean () function. A subset of a hierarchical tree may be built by remaining at the top-level directory (where the .I SConstruct file lives) and specifying the subdirectory as the target to be built: .ES scons src/subdir .EE or by changing directory and invoking scons with the .B -u option, which traverses up the directory hierarchy until it finds the .I SConstruct file, and then builds targets relatively to the current subdirectory: .ES cd src/subdir scons -u . .EE .B scons supports building multiple targets in parallel via a .B -j option that takes, as its argument, the number of simultaneous tasks that may be spawned: .ES scons -j 4 .EE builds four targets in parallel, for example. .B scons can maintain a cache of target (derived) files that can be shared between multiple builds. When caching is enabled in a SConscript file, any target files built by .B scons will be copied to the cache. If an up-to-date target file is found in the cache, it will be retrieved from the cache instead of being rebuilt locally. Caching behavior may be disabled and controlled in other ways by the .BR --cache-force , .BR --cache-disable , and .B --cache-show command-line options. The .B --random option is useful to prevent multiple builds from trying to update the cache simultaneously. Values of variables to be passed to the SConscript file(s) may be specified on the command line: .ES scons debug=1 . .EE These variables are available in SConscript files through the ARGUMENTS dictionary, and can be used in the SConscript file(s) to modify the build in any way: .ES if ARGUMENTS.get('debug', 0): env = Environment(CCFLAGS = '-g') else: env = Environment() .EE The command-line variable arguments are also available in the ARGLIST list, indexed by their order on the command line. This allows you to process them in order rather than by name, if necessary. ARGLIST[0] returns a tuple containing (argname, argvalue). A Python exception is thrown if you try to access a list member that does not exist. .B scons requires Python version 2.4 or later. There should be no other dependencies or requirements to run .B scons. .\" The following paragraph reflects the default tool search orders .\" currently in SCons/Tool/__init__.py. If any of those search orders .\" change, this documentation should change, too. By default, .B scons knows how to search for available programming tools on various systems. On Windows systems, .B scons searches in order for the Microsoft Visual C++ tools, the MinGW tool chain, the Intel compiler tools, and the PharLap ETS compiler. On OS/2 systems, .B scons searches in order for the OS/2 compiler, the GCC tool chain, and the Microsoft Visual C++ tools, On SGI IRIX, IBM AIX, Hewlett Packard HP-UX, and Sun Solaris systems, .B scons searches for the native compiler tools (MIPSpro, Visual Age, aCC, and Forte tools respectively) and the GCC tool chain. On all other platforms, including POSIX (Linux and UNIX) platforms, .B scons searches in order for the GCC tool chain, the Microsoft Visual C++ tools, and the Intel compiler tools. You may, of course, override these default values by appropriate configuration of Environment construction variables. .SH OPTIONS In general, .B scons supports the same command-line options as GNU .BR make , and many of those supported by .BR cons . .TP -b Ignored for compatibility with non-GNU versions of .BR make. .TP -c, --clean, --remove Clean up by removing all target files for which a construction command is specified. Also remove any files or directories associated to the construction command using the .BR Clean () function. Will not remove any targets specified by the .BR NoClean () function. .TP .RI --cache-debug= file Print debug information about the .BR CacheDir () derived-file caching to the specified .IR file . If .I file is .B \- (a hyphen), the debug information are printed to the standard output. The printed messages describe what signature file names are being looked for in, retrieved from, or written to the .BR CacheDir () directory tree. .TP --cache-disable, --no-cache Disable the derived-file caching specified by .BR CacheDir (). .B scons will neither retrieve files from the cache nor copy files to the cache. .TP --cache-force, --cache-populate When using .BR CacheDir (), populate a cache by copying any already-existing, up-to-date derived files to the cache, in addition to files built by this invocation. This is useful to populate a new cache with all the current derived files, or to add to the cache any derived files recently built with caching disabled via the .B --cache-disable option. .TP --cache-show When using .BR CacheDir () and retrieving a derived file from the cache, show the command that would have been executed to build the file, instead of the usual report, "Retrieved `file' from cache." This will produce consistent output for build logs, regardless of whether a target file was rebuilt or retrieved from the cache. .TP .RI --config= mode This specifies how the .B Configure call should use or generate the results of configuration tests. The option should be specified from among the following choices: .TP --config=auto scons will use its normal dependency mechanisms to decide if a test must be rebuilt or not. This saves time by not running the same configuration tests every time you invoke scons, but will overlook changes in system header files or external commands (such as compilers) if you don't specify those dependecies explicitly. This is the default behavior. .TP --config=force If this option is specified, all configuration tests will be re-run regardless of whether the cached results are out of date. This can be used to explicitly force the configuration tests to be updated in response to an otherwise unconfigured change in a system header file or compiler. .TP --config=cache If this option is specified, no configuration tests will be rerun and all results will be taken from cache. Note that scons will still consider it an error if --config=cache is specified and a necessary test does not yet have any results in the cache. .TP .RI "-C" " directory" ", --directory=" directory Change to the specified .I directory before searching for the .IR SConstruct , .IR Sconstruct , or .I sconstruct file, or doing anything else. Multiple .B -C options are interpreted relative to the previous one, and the right-most .B -C option wins. (This option is nearly equivalent to .BR "-f directory/SConstruct" , except that it will search for .IR SConstruct , .IR Sconstruct , or .I sconstruct in the specified directory.) .\" .TP .\" -d .\" Display dependencies while building target files. Useful for .\" figuring out why a specific file is being rebuilt, as well as .\" general debugging of the build process. .TP -D Works exactly the same way as the .B -u option except for the way default targets are handled. When this option is used and no targets are specified on the command line, all default targets are built, whether or not they are below the current directory. .TP .RI --debug= type Debug the build process. .I type specifies what type of debugging: .TP --debug=count Print how many objects are created of the various classes used internally by SCons before and after reading the SConscript files and before and after building targets. This is not supported when SCons is executed with the Python .B -O (optimized) option or when the SCons modules have been compiled with optimization (that is, when executing from .B *.pyo files). .TP --debug=duplicate Print a line for each unlink/relink (or copy) of a variant file from its source file. Includes debugging info for unlinking stale variant files, as well as unlinking old targets before building them. .TP --debug=dtree A synonym for the newer .B --tree=derived option. This will be deprecated in some future release and ultimately removed. .TP --debug=explain Print an explanation of precisely why .B scons is deciding to (re-)build any targets. (Note: this does not print anything for targets that are .I not rebuilt.) .TP --debug=findlibs Instruct the scanner that searches for libraries to print a message about each potential library name it is searching for, and about the actual libraries it finds. .TP --debug=includes Print the include tree after each top-level target is built. This is generally used to find out what files are included by the sources of a given derived file: .ES $ scons --debug=includes foo.o .EE .TP --debug=memoizer Prints a summary of hits and misses using the Memoizer, an internal subsystem that counts how often SCons uses cached values in memory instead of recomputing them each time they're needed. .TP --debug=memory Prints how much memory SCons uses before and after reading the SConscript files and before and after building targets. .TP --debug=nomemoizer A deprecated option preserved for backwards compatibility. .TP --debug=objects Prints a list of the various objects of the various classes used internally by SCons. .TP --debug=pdb Re-run SCons under the control of the .RI pdb Python debugger. .TP --debug=prepare Print a line each time any target (internal or external) is prepared for building. .B scons prints this for each target it considers, even if that target is up to date (see also --debug=explain). This can help debug problems with targets that aren't being built; it shows whether .B scons is at least considering them or not. .TP --debug=presub Print the raw command line used to build each target before the construction environment variables are substituted. Also shows which targets are being built by this command. Output looks something like this: .ES $ scons --debug=presub Building myprog.o with action(s): $SHCC $SHCFLAGS $SHCCFLAGS $CPPFLAGS $_CPPINCFLAGS -c -o $TARGET $SOURCES \&... .EE .TP --debug=stacktrace Prints an internal Python stack trace when encountering an otherwise unexplained error. .TP --debug=stree A synonym for the newer .B --tree=all,status option. This will be deprecated in some future release and ultimately removed. .TP --debug=time Prints various time profiling information: the time spent executing each individual build command; the total build time (time SCons ran from beginning to end); the total time spent reading and executing SConscript files; the total time spent SCons itself spend running (that is, not counting reading and executing SConscript files); and both the total time spent executing all build commands and the elapsed wall-clock time spent executing those build commands. (When .B scons is executed without the .B -j option, the elapsed wall-clock time will typically be slightly longer than the total time spent executing all the build commands, due to the SCons processing that takes place in between executing each command. When .B scons is executed .I with the .B -j option, and your build configuration allows good parallelization, the elapsed wall-clock time should be significantly smaller than the total time spent executing all the build commands, since multiple build commands and intervening SCons processing should take place in parallel.) .TP --debug=tree A synonym for the newer .B --tree=all option. This will be deprecated in some future release and ultimately removed. .TP .RI --diskcheck= types Enable specific checks for whether or not there is a file on disk where the SCons configuration expects a directory (or vice versa), and whether or not RCS or SCCS sources exist when searching for source and include files. The .I types argument can be set to: .BR all , to enable all checks explicitly (the default behavior); .BR none , to disable all such checks; .BR match , to check that files and directories on disk match SCons' expected configuration; .BR rcs , to check for the existence of an RCS source for any missing source or include files; .BR sccs , to check for the existence of an SCCS source for any missing source or include files. Multiple checks can be specified separated by commas; for example, .B --diskcheck=sccs,rcs would still check for SCCS and RCS sources, but disable the check for on-disk matches of files and directories. Disabling some or all of these checks can provide a performance boost for large configurations, or when the configuration will check for files and/or directories across networked or shared file systems, at the slight increased risk of an incorrect build or of not handling errors gracefully (if include files really should be found in SCCS or RCS, for example, or if a file really does exist where the SCons configuration expects a directory). .TP .RI --duplicate= ORDER There are three ways to duplicate files in a build tree: hard links, soft (symbolic) links and copies. The default behaviour of SCons is to prefer hard links to soft links to copies. You can specify different behaviours with this option. .IR ORDER must be one of .IR hard-soft-copy (the default), .IR soft-hard-copy , .IR hard-copy , .IR soft-copy or .IR copy . SCons will attempt to duplicate files using the mechanisms in the specified order. .\" .TP .\" -e, --environment-overrides .\" Variables from the execution environment override construction .\" variables from the SConscript files. .TP .RI -f " file" ", --file=" file ", --makefile=" file ", --sconstruct=" file Use .I file as the initial SConscript file. Multiple .B -f options may be specified, in which case .B scons will read all of the specified files. .TP -h, --help Print a local help message for this build, if one is defined in the SConscript file(s), plus a line that describes the .B -H option for command-line option help. If no local help message is defined, prints the standard help message about command-line options. Exits after displaying the appropriate message. .TP -H, --help-options Print the standard help message about command-line options and exit. .TP -i, --ignore-errors Ignore all errors from commands executed to rebuild files. .TP .RI -I " directory" ", --include-dir=" directory Specifies a .I directory to search for imported Python modules. If several .B -I options are used, the directories are searched in the order specified. .TP --implicit-cache Cache implicit dependencies. This causes .B scons to use the implicit (scanned) dependencies from the last time it was run instead of scanning the files for implicit dependencies. This can significantly speed up SCons, but with the following limitations: .IP .B scons will not detect changes to implicit dependency search paths (e.g. .BR CPPPATH ", " LIBPATH ) that would ordinarily cause different versions of same-named files to be used. .IP .B scons will miss changes in the implicit dependencies in cases where a new implicit dependency is added earlier in the implicit dependency search path (e.g. .BR CPPPATH ", " LIBPATH ) than a current implicit dependency with the same name. .TP --implicit-deps-changed Forces SCons to ignore the cached implicit dependencies. This causes the implicit dependencies to be rescanned and recached. This implies .BR --implicit-cache . .TP --implicit-deps-unchanged Force SCons to ignore changes in the implicit dependencies. This causes cached implicit dependencies to always be used. This implies .BR --implicit-cache . .TP --interactive Starts SCons in interactive mode. The SConscript files are read once and a .B "scons>>>" prompt is printed. Targets may now be rebuilt by typing commands at interactive prompt without having to re-read the SConscript files and re-initialize the dependency graph from scratch. SCons interactive mode supports the following commands: .RS 10 .TP 6 .BI build "[OPTIONS] [TARGETS] ..." Builds the specified .I TARGETS (and their dependencies) with the specified SCons command-line .IR OPTIONS . .B b and .B scons are synonyms. The following SCons command-line options affect the .B build command: .ES --cache-debug=FILE --cache-disable, --no-cache --cache-force, --cache-populate --cache-show --debug=TYPE -i, --ignore-errors -j N, --jobs=N -k, --keep-going -n, --no-exec, --just-print, --dry-run, --recon -Q -s, --silent, --quiet --taskmastertrace=FILE --tree=OPTIONS .EE .IP "" 6 Any other SCons command-line options that are specified do not cause errors but have no effect on the .B build command (mainly because they affect how the SConscript files are read, which only happens once at the beginning of interactive mode). .TP 6 .BI clean "[OPTIONS] [TARGETS] ..." Cleans the specified .I TARGETS (and their dependencies) with the specified options. .B c is a synonym. This command is itself a synonym for .B "build --clean" .TP 6 .BI exit Exits SCons interactive mode. You can also exit by terminating input (CTRL+D on UNIX or Linux systems, CTRL+Z on Windows systems). .TP 6 .BI help "[COMMAND]" Provides a help message about the commands available in SCons interactive mode. If .I COMMAND is specified, .B h and .B ? are synonyms. .TP 6 .BI shell "[COMMANDLINE]" Executes the specified .I COMMANDLINE in a subshell. If no .I COMMANDLINE is specified, executes the interactive command interpreter specified in the .B SHELL environment variable (on UNIX and Linux systems) or the .B COMSPEC environment variable (on Windows systems). .B sh and .B ! are synonyms. .TP 6 .B version Prints SCons version information. .RE .IP An empty line repeats the last typed command. Command-line editing can be used if the .B readline module is available. .ES $ scons --interactive scons: Reading SConscript files ... scons: done reading SConscript files. scons>>> build -n prog scons>>> exit .EE .TP .RI -j " N" ", --jobs=" N Specifies the number of jobs (commands) to run simultaneously. If there is more than one .B -j option, the last one is effective. .\" ??? If the .\" .B -j .\" option .\" is specified without an argument, .\" .B scons .\" will not limit the number of .\" simultaneous jobs. .TP -k, --keep-going Continue as much as possible after an error. The target that failed and those that depend on it will not be remade, but other targets specified on the command line will still be processed. .\" .TP .\" .RI -l " N" ", --load-average=" N ", --max-load=" N .\" No new jobs (commands) will be started if .\" there are other jobs running and the system load .\" average is at least .\" .I N .\" (a floating-point number). .\" .\" .TP .\" --list-derived .\" List derived files (targets, dependencies) that would be built, .\" but do not build them. .\" [XXX This can probably go away with the right .\" combination of other options. Revisit this issue.] .\" .\" .TP .\" --list-actions .\" List derived files that would be built, with the actions .\" (commands) that build them. Does not build the files. .\" [XXX This can probably go away with the right .\" combination of other options. Revisit this issue.] .\" .\" .TP .\" --list-where .\" List derived files that would be built, plus where the file is .\" defined (file name and line number). Does not build the files. .\" [XXX This can probably go away with the right .\" combination of other options. Revisit this issue.] .TP -m Ignored for compatibility with non-GNU versions of .BR make . .TP .RI --max-drift= SECONDS Set the maximum expected drift in the modification time of files to .IR SECONDS . This value determines how long a file must be unmodified before its cached content signature will be used instead of calculating a new content signature (MD5 checksum) of the file's contents. The default value is 2 days, which means a file must have a modification time of at least two days ago in order to have its cached content signature used. A negative value means to never cache the content signature and to ignore the cached value if there already is one. A value of 0 means to always use the cached signature, no matter how old the file is. .TP .RI --md5-chunksize= KILOBYTES Set the block size used to compute MD5 signatures to .IR KILOBYTES . This value determines the size of the chunks which are read in at once when computing MD5 signatures. Files below that size are fully stored in memory before performing the signature computation while bigger files are read in block-by-block. A huge block-size leads to high memory consumption while a very small block-size slows down the build considerably. The default value is to use a chunk size of 64 kilobytes, which should be appropriate for most uses. .TP -n, --just-print, --dry-run, --recon No execute. Print the commands that would be executed to build any out-of-date target files, but do not execute the commands. .TP .RI --no-site-dir Prevents the automatic addition of the standard .I site_scons dirs to .IR sys.path . Also prevents loading the .I site_scons/site_init.py modules if they exist, and prevents adding their .I site_scons/site_tools dirs to the toolpath. .\" .TP .\" .RI -o " file" ", --old-file=" file ", --assume-old=" file .\" Do not rebuild .\" .IR file , .\" and do .\" not rebuild anything due to changes in the contents of .\" .IR file . .\" .TP .\" .RI --override " file" .\" Read values to override specific build environment variables .\" from the specified .\" .IR file . .\" .TP .\" -p .\" Print the data base (construction environments, .\" Builder and Scanner objects) that are defined .\" after reading the SConscript files. .\" After printing, a normal build is performed .\" as usual, as specified by other command-line options. .\" This also prints version information .\" printed by the .\" .B -v .\" option. .\" .\" To print the database without performing a build do: .\" .\" .ES .\" scons -p -q .\" .EE .TP .RI --profile= file Run SCons under the Python profiler and save the results in the specified .IR file . The results may be analyzed using the Python pstats module. .TP -q, --question Do not run any commands, or print anything. Just return an exit status that is zero if the specified targets are already up to date, non-zero otherwise. .TP -Q Quiets SCons status messages about reading SConscript files, building targets and entering directories. Commands that are executed to rebuild target files are still printed. .\" .TP .\" -r, -R, --no-builtin-rules, --no-builtin-variables .\" Clear the default construction variables. Construction .\" environments that are created will be completely empty. .TP --random Build dependencies in a random order. This is useful when building multiple trees simultaneously with caching enabled, to prevent multiple builds from simultaneously trying to build or retrieve the same target files. .TP -s, --silent, --quiet Silent. Do not print commands that are executed to rebuild target files. Also suppresses SCons status messages. .TP -S, --no-keep-going, --stop Ignored for compatibility with GNU .BR make . .TP .RI --site-dir= dir Uses the named dir as the site dir rather than the default .I site_scons dirs. This dir will get prepended to .IR sys.path , the module .IR dir /site_init.py will get loaded if it exists, and .IR dir /site_tools will get added to the default toolpath. The default set of .I site_scons dirs used when .I --site-dir is not specified depends on the system platform, as follows. Note that the directories are examined in the order given, from most generic to most specific, so the last-executed site_init.py file is the most specific one (which gives it the chance to override everything else), and the dirs are prepended to the paths, again so the last dir examined comes first in the resulting path. .IP "Windows:" .nf %ALLUSERSPROFILE/Application Data/scons/site_scons %USERPROFILE%/Local Settings/Application Data/scons/site_scons %APPDATA%/scons/site_scons %HOME%/.scons/site_scons ./site_scons .fi .IP "Mac OS X:" .nf /Library/Application Support/SCons/site_scons /opt/local/share/scons/site_scons (for MacPorts) /sw/share/scons/site_scons (for Fink) $HOME/Library/Application Support/SCons/site_scons $HOME/.scons/site_scons ./site_scons .fi .IP "Solaris:" .nf /opt/sfw/scons/site_scons /usr/share/scons/site_scons $HOME/.scons/site_scons ./site_scons .fi .IP "Linux, HPUX, and other Posix-like systems:" .nf /usr/share/scons/site_scons $HOME/.scons/site_scons ./site_scons .fi .TP .RI --stack-size= KILOBYTES Set the size stack used to run threads to .IR KILOBYTES . This value determines the stack size of the threads used to run jobs. These are the threads that execute the actions of the builders for the nodes that are out-of-date. Note that this option has no effect unless the .B num_jobs option, which corresponds to -j and --jobs, is larger than one. Using a stack size that is too small may cause stack overflow errors. This usually shows up as segmentation faults that cause scons to abort before building anything. Using a stack size that is too large will cause scons to use more memory than required and may slow down the entire build process. The default value is to use a stack size of 256 kilobytes, which should be appropriate for most uses. You should not need to increase this value unless you encounter stack overflow errors. .TP -t, --touch Ignored for compatibility with GNU .BR make . (Touching a file to make it appear up-to-date is unnecessary when using .BR scons .) .TP .RI --taskmastertrace= file Prints trace information to the specified .I file about how the internal Taskmaster object evaluates and controls the order in which Nodes are built. A file name of .B - may be used to specify the standard output. .TP .RI -tree= options Prints a tree of the dependencies after each top-level target is built. This prints out some or all of the tree, in various formats, depending on the .I options specified: .TP --tree=all Print the entire dependency tree after each top-level target is built. This prints out the complete dependency tree, including implicit dependencies and ignored dependencies. .TP --tree=derived Restricts the tree output to only derived (target) files, not source files. .TP --tree=status Prints status information for each displayed node. .TP --tree=prune Prunes the tree to avoid repeating dependency information for nodes that have already been displayed. Any node that has already been displayed will have its name printed in .BR "[square brackets]" , as an indication that the dependencies for that node can be found by searching for the relevant output higher up in the tree. .IP Multiple options may be specified, separated by commas: .ES # Prints only derived files, with status information: scons --tree=derived,status # Prints all dependencies of target, with status information # and pruning dependencies of already-visited Nodes: scons --tree=all,prune,status target .EE .TP -u, --up, --search-up Walks up the directory structure until an .I SConstruct , .I Sconstruct or .I sconstruct file is found, and uses that as the top of the directory tree. If no targets are specified on the command line, only targets at or below the current directory will be built. .TP -U Works exactly the same way as the .B -u option except for the way default targets are handled. When this option is used and no targets are specified on the command line, all default targets that are defined in the SConscript(s) in the current directory are built, regardless of what directory the resultant targets end up in. .TP -v, --version Print the .B scons version, copyright information, list of authors, and any other relevant information. Then exit. .TP -w, --print-directory Print a message containing the working directory before and after other processing. .TP --no-print-directory Turn off -w, even if it was turned on implicitly. .TP .RI --warn= type ", --warn=no-" type Enable or disable warnings. .I type specifies the type of warnings to be enabled or disabled: .TP --warn=all, --warn=no-all Enables or disables all warnings. .TP --warn=cache-write-error, --warn=no-cache-write-error Enables or disables warnings about errors trying to write a copy of a built file to a specified .BR CacheDir (). These warnings are disabled by default. .TP --warn=corrupt-sconsign, --warn=no-corrupt-sconsign Enables or disables warnings about unfamiliar signature data in .B .sconsign files. These warnings are enabled by default. .TP --warn=dependency, --warn=no-dependency Enables or disables warnings about dependencies. These warnings are disabled by default. .TP --warn=deprecated, --warn=no-deprecated Enables or disables all warnings about use of currently deprecated features. These warnings are enabled by default. Note that the .B --warn=no-deprecated option does not disable warnings about absolutely all deprecated features. Warnings for some deprecated features that have already been through several releases with deprecation warnings may be mandatory for a release or two before they are officially no longer supported by SCons. Warnings for some specific deprecated features may be enabled or disabled individually; see below. .RS .TP --warn=deprecated-copy, --warn=no-deprecated-copy Enables or disables warnings about use of the deprecated .B env.Copy() method. .TP --warn=deprecated-source-signatures, --warn=no-deprecated-source-signatures Enables or disables warnings about use of the deprecated .B SourceSignatures() function or .B env.SourceSignatures() method. .TP --warn=deprecated-target-signatures, --warn=no-deprecated-target-signatures Enables or disables warnings about use of the deprecated .B TargetSignatures() function or .B env.TargetSignatures() method. .RE .TP --warn=duplicate-environment, --warn=no-duplicate-environment Enables or disables warnings about attempts to specify a build of a target with two different construction environments that use the same action. These warnings are enabled by default. .TP --warn=fortran-cxx-mix, --warn=no-fortran-cxx-mix Enables or disables the specific warning about linking Fortran and C++ object files in a single executable, which can yield unpredictable behavior with some compilers. .TP --warn=future-deprecated, --warn=no-future-deprecated Enables or disables warnings about features that will be deprecated in the future. These warnings are disabled by default. Enabling this warning is especially recommended for projects that redistribute SCons configurations for other users to build, so that the project can be warned as soon as possible about to-be-deprecated features that may require changes to the configuration. .TP --warn=link, --warn=no-link Enables or disables warnings about link steps. .TP --warn=misleading-keywords, --warn=no-misleading-keywords Enables or disables warnings about use of the misspelled keywords .B targets and .B sources when calling Builders. (Note the last .B s characters, the correct spellings are .B target and .B source.) These warnings are enabled by default. .TP --warn=missing-sconscript, --warn=no-missing-sconscript Enables or disables warnings about missing SConscript files. These warnings are enabled by default. .TP --warn=no-md5-module, --warn=no-no-md5-module Enables or disables warnings about the version of Python not having an MD5 checksum module available. These warnings are enabled by default. .TP --warn=no-metaclass-support, --warn=no-no-metaclass-support Enables or disables warnings about the version of Python not supporting metaclasses when the .B --debug=memoizer option is used. These warnings are enabled by default. .TP --warn=no-object-count, --warn=no-no-object-count Enables or disables warnings about the .B --debug=object feature not working when .B scons is run with the python .B \-O option or from optimized Python (.pyo) modules. .TP --warn=no-parallel-support, --warn=no-no-parallel-support Enables or disables warnings about the version of Python not being able to support parallel builds when the .B -j option is used. These warnings are enabled by default. .TP --warn=python-version, --warn=no-python-version Enables or disables the warning about running SCons with a deprecated version of Python. These warnings are enabled by default. .TP --warn=reserved-variable, --warn=no-reserved-variable Enables or disables warnings about attempts to set the reserved construction variable names .BR CHANGED_SOURCES , .BR CHANGED_TARGETS , .BR TARGET , .BR TARGETS , .BR SOURCE , .BR SOURCES , .BR UNCHANGED_SOURCES or .BR UNCHANGED_TARGETS . These warnings are disabled by default. .TP --warn=stack-size, --warn=no-stack-size Enables or disables warnings about requests to set the stack size that could not be honored. These warnings are enabled by default. .\" .TP .\" .RI --write-filenames= file .\" Write all filenames considered into .\" .IR file . .\" .\" .TP .\" .RI -W " file" ", --what-if=" file ", --new-file=" file ", --assume-new=" file .\" Pretend that the target .\" .I file .\" has been .\" modified. When used with the .\" .B -n .\" option, this .\" show you what would be rebuilt if you were to modify that file. .\" Without .\" .B -n .\" ... what? XXX .\" .\" .TP .\" --warn-undefined-variables .\" Warn when an undefined variable is referenced. .TP .RI -Y " repository" ", --repository=" repository ", --srcdir=" repository Search the specified repository for any input and target files not found in the local directory hierarchy. Multiple .B -Y options may be specified, in which case the repositories are searched in the order specified. .SH CONFIGURATION FILE REFERENCE .\" .SS Python Basics .\" XXX Adding this in the future would be a help. .SS Construction Environments A construction environment is the basic means by which the SConscript files communicate build information to .BR scons . A new construction environment is created using the .B Environment function: .ES env = Environment() .EE Variables, called .I construction .IR variables , may be set in a construction environment either by specifying them as keywords when the object is created or by assigning them a value after the object is created: .ES env = Environment(FOO = 'foo') env['BAR'] = 'bar' .EE As a convenience, construction variables may also be set or modified by the .I parse_flags keyword argument, which applies the .B ParseFlags method (described below) to the argument value after all other processing is completed. This is useful either if the exact content of the flags is unknown (for example, read from a control file) or if the flags are distributed to a number of construction variables. .ES env = Environment(parse_flags = '-Iinclude -DEBUG -lm') .EE This example adds 'include' to .BR CPPPATH , \&'EBUG' to .BR CPPDEFINES , and 'm' to .BR LIBS . By default, a new construction environment is initialized with a set of builder methods and construction variables that are appropriate for the current platform. An optional platform keyword argument may be used to specify that an environment should be initialized for a different platform: .ES env = Environment(platform = 'cygwin') env = Environment(platform = 'os2') env = Environment(platform = 'posix') env = Environment(platform = 'win32') .EE Specifying a platform initializes the appropriate construction variables in the environment to use and generate file names with prefixes and suffixes appropriate for the platform. Note that the .B win32 platform adds the .B SystemDrive and .B SystemRoot variables from the user's external environment to the construction environment's .B ENV dictionary. This is so that any executed commands that use sockets to connect with other systems (such as fetching source files from external CVS repository specifications like .BR :pserver:anonymous@cvs.sourceforge.net:/cvsroot/scons ) will work on Windows systems. The platform argument may be function or callable object, in which case the Environment() method will call the specified argument to update the new construction environment: .ES def my_platform(env): env['VAR'] = 'xyzzy' env = Environment(platform = my_platform) .EE Additionally, a specific set of tools with which to initialize the environment may be specified as an optional keyword argument: .ES env = Environment(tools = ['msvc', 'lex']) .EE Non-built-in tools may be specified using the toolpath argument: .ES env = Environment(tools = ['default', 'foo'], toolpath = ['tools']) .EE This looks for a tool specification in tools/foo.py (as well as using the ordinary default tools for the platform). foo.py should have two functions: generate(env, **kw) and exists(env). The .B generate() function modifies the passed-in environment to set up variables so that the tool can be executed; it may use any keyword arguments that the user supplies (see below) to vary its initialization. The .B exists() function should return a true value if the tool is available. Tools in the toolpath are used before any of the built-in ones. For example, adding gcc.py to the toolpath would override the built-in gcc tool. Also note that the toolpath is stored in the environment for use by later calls to .BR Clone () and .BR Tool () methods: .ES base = Environment(toolpath=['custom_path']) derived = base.Clone(tools=['custom_tool']) derived.CustomBuilder() .EE The elements of the tools list may also be functions or callable objects, in which case the Environment() method will call the specified elements to update the new construction environment: .ES def my_tool(env): env['XYZZY'] = 'xyzzy' env = Environment(tools = [my_tool]) .EE The individual elements of the tools list may also themselves be two-element lists of the form .RI ( toolname ", " kw_dict ). SCons searches for the .I toolname specification file as described above, and passes .IR kw_dict , which must be a dictionary, as keyword arguments to the tool's .B generate function. The .B generate function can use the arguments to modify the tool's behavior by setting up the environment in different ways or otherwise changing its initialization. .ES # in tools/my_tool.py: def generate(env, **kw): # Sets MY_TOOL to the value of keyword argument 'arg1' or 1. env['MY_TOOL'] = kw.get('arg1', '1') def exists(env): return 1 # in SConstruct: env = Environment(tools = ['default', ('my_tool', {'arg1': 'abc'})], toolpath=['tools']) .EE The tool definition (i.e. my_tool()) can use the PLATFORM variable from the environment it receives to customize the tool for different platforms. If no tool list is specified, then SCons will auto-detect the installed tools using the PATH variable in the ENV construction variable and the platform name when the Environment is constructed. Changing the PATH variable after the Environment is constructed will not cause the tools to be redetected. SCons supports the following tool specifications out of the box: .ES 386asm aixc++ aixcc aixf77 aixlink ar as bcc32 c++ cc cvf dmd dvipdf dvips f77 f90 f95 fortran g++ g77 gas gcc gfortran gnulink gs hpc++ hpcc hplink icc icl ifl ifort ilink ilink32 intelc jar javac javah latex lex link linkloc m4 masm midl mingw mslib mslink mssdk msvc msvs mwcc mwld nasm pdflatex pdftex qt rmic rpcgen sgiar sgic++ sgicc sgilink sunar sunc++ suncc sunf77 sunf90 sunf95 sunlink swig tar tex textfile tlib yacc zip .EE Additionally, there is a "tool" named .B default which configures the environment with a default set of tools for the current platform. On posix and cygwin platforms the GNU tools (e.g. gcc) are preferred by SCons, on Windows the Microsoft tools (e.g. msvc) followed by MinGW are preferred by SCons, and in OS/2 the IBM tools (e.g. icc) are preferred by SCons. .SS Builder Methods Build rules are specified by calling a construction environment's builder methods. The arguments to the builder methods are .B target (a list of targets to be built, usually file names) and .B source (a list of sources to be built, usually file names). Because long lists of file names can lead to a lot of quoting, .B scons supplies a .B Split() global function and a same-named environment method that split a single string into a list, separated on strings of white-space characters. (These are similar to the split() member function of Python strings but work even if the input isn't a string.) Like all Python arguments, the target and source arguments to a builder method can be specified either with or without the "target" and "source" keywords. When the keywords are omitted, the target is first, followed by the source. The following are equivalent examples of calling the Program builder method: .ES env.Program('bar', ['bar.c', 'foo.c']) env.Program('bar', Split('bar.c foo.c')) env.Program('bar', env.Split('bar.c foo.c')) env.Program(source = ['bar.c', 'foo.c'], target = 'bar') env.Program(target = 'bar', Split('bar.c foo.c')) env.Program(target = 'bar', env.Split('bar.c foo.c')) env.Program('bar', source = 'bar.c foo.c'.split()) .EE Target and source file names that are not absolute path names (that is, do not begin with .B / on POSIX systems or .B \\ on Windows systems, with or without an optional drive letter) are interpreted relative to the directory containing the .B SConscript file being read. An initial .B # (hash mark) on a path name means that the rest of the file name is interpreted relative to the directory containing the top-level .B SConstruct file, even if the .B # is followed by a directory separator character (slash or backslash). Examples: .ES # The comments describing the targets that will be built # assume these calls are in a SConscript file in the # a subdirectory named "subdir". # Builds the program "subdir/foo" from "subdir/foo.c": env.Program('foo', 'foo.c') # Builds the program "/tmp/bar" from "subdir/bar.c": env.Program('/tmp/bar', 'bar.c') # An initial '#' or '#/' are equivalent; the following # calls build the programs "foo" and "bar" (in the # top-level SConstruct directory) from "subdir/foo.c" and # "subdir/bar.c", respectively: env.Program('#foo', 'foo.c') env.Program('#/bar', 'bar.c') # Builds the program "other/foo" (relative to the top-level # SConstruct directory) from "subdir/foo.c": env.Program('#other/foo', 'foo.c') .EE When the target shares the same base name as the source and only the suffix varies, and if the builder method has a suffix defined for the target file type, then the target argument may be omitted completely, and .B scons will deduce the target file name from the source file name. The following examples all build the executable program .B bar (on POSIX systems) or .B bar.exe (on Windows systems) from the bar.c source file: .ES env.Program(target = 'bar', source = 'bar.c') env.Program('bar', source = 'bar.c') env.Program(source = 'bar.c') env.Program('bar.c') .EE As a convenience, a .B srcdir keyword argument may be specified when calling a Builder. When specified, all source file strings that are not absolute paths will be interpreted relative to the specified .BR srcdir . The following example will build the .B build/prog (or .B build/prog.exe on Windows) program from the files .B src/f1.c and .BR src/f2.c : .ES env.Program('build/prog', ['f1.c', 'f2.c'], srcdir='src') .EE It is possible to override or add construction variables when calling a builder method by passing additional keyword arguments. These overridden or added variables will only be in effect when building the target, so they will not affect other parts of the build. For example, if you want to add additional libraries for just one program: .ES env.Program('hello', 'hello.c', LIBS=['gl', 'glut']) .EE or generate a shared library with a non-standard suffix: .ES env.SharedLibrary('word', 'word.cpp', SHLIBSUFFIX='.ocx', LIBSUFFIXES=['.ocx']) .EE (Note that both the $SHLIBSUFFIX and $LIBSUFFIXES variables must be set if you want SCons to search automatically for dependencies on the non-standard library names; see the descriptions of these variables, below, for more information.) It is also possible to use the .I parse_flags keyword argument in an override: .ES env = Program('hello', 'hello.c', parse_flags = '-Iinclude -DEBUG -lm') .EE This example adds 'include' to .BR CPPPATH , \&'EBUG' to .BR CPPDEFINES , and 'm' to .BR LIBS . Although the builder methods defined by .B scons are, in fact, methods of a construction environment object, they may also be called without an explicit environment: .ES Program('hello', 'hello.c') SharedLibrary('word', 'word.cpp') .EE In this case, the methods are called internally using a default construction environment that consists of the tools and values that .B scons has determined are appropriate for the local system. Builder methods that can be called without an explicit environment may be called from custom Python modules that you import into an SConscript file by adding the following to the Python module: .ES from SCons.Script import * .EE All builder methods return a list-like object containing Nodes that represent the target or targets that will be built. A .I Node is an internal SCons object which represents build targets or sources. The returned Node-list object can be passed to other builder methods as source(s) or passed to any SCons function or method where a filename would normally be accepted. For example, if it were necessary to add a specific .B -D flag when compiling one specific object file: .ES bar_obj_list = env.StaticObject('bar.c', CPPDEFINES='-DBAR') env.Program(source = ['foo.c', bar_obj_list, 'main.c']) .EE Using a Node in this way makes for a more portable build by avoiding having to specify a platform-specific object suffix when calling the Program() builder method. Note that Builder calls will automatically "flatten" the source and target file lists, so it's all right to have the bar_obj list return by the StaticObject() call in the middle of the source file list. If you need to manipulate a list of lists returned by Builders directly using Python, you can either build the list by hand: .ES foo = Object('foo.c') bar = Object('bar.c') objects = ['begin.o'] + foo + ['middle.o'] + bar + ['end.o'] for object in objects: print str(object) .EE Or you can use the .BR Flatten () function supplied by scons to create a list containing just the Nodes, which may be more convenient: .ES foo = Object('foo.c') bar = Object('bar.c') objects = Flatten(['begin.o', foo, 'middle.o', bar, 'end.o']) for object in objects: print str(object) .EE Note also that because Builder calls return a list-like object, not an actual Python list, you should .I not use the Python .B += operator to append Builder results to a Python list. Because the list and the object are different types, Python will not update the original list in place, but will instead create a new Node-list object containing the concatenation of the list elements and the Builder results. This will cause problems for any other Python variables in your SCons configuration that still hold on to a reference to the original list. Instead, use the Python .B .extend() method to make sure the list is updated in-place. Example: .ES object_files = [] # Do NOT use += as follows: # # object_files += Object('bar.c') # # It will not update the object_files list in place. # # Instead, use the .extend() method: object_files.extend(Object('bar.c')) .EE The path name for a Node's file may be used by passing the Node to the Python-builtin .B str() function: .ES bar_obj_list = env.StaticObject('bar.c', CPPDEFINES='-DBAR') print "The path to bar_obj is:", str(bar_obj_list[0]) .EE Note again that because the Builder call returns a list, we have to access the first element in the list .B (bar_obj_list[0]) to get at the Node that actually represents the object file. Builder calls support a .B chdir keyword argument that specifies that the Builder's action(s) should be executed after changing directory. If the .B chdir argument is a string or a directory Node, scons will change to the specified directory. If the .B chdir is not a string or Node and is non-zero, then scons will change to the target file's directory. .ES # scons will change to the "sub" subdirectory # before executing the "cp" command. env.Command('sub/dir/foo.out', 'sub/dir/foo.in', "cp dir/foo.in dir/foo.out", chdir='sub') # Because chdir is not a string, scons will change to the # target's directory ("sub/dir") before executing the # "cp" command. env.Command('sub/dir/foo.out', 'sub/dir/foo.in', "cp foo.in foo.out", chdir=1) .EE Note that scons will .I not automatically modify its expansion of construction variables like .B $TARGET and .B $SOURCE when using the chdir keyword argument--that is, the expanded file names will still be relative to the top-level SConstruct directory, and consequently incorrect relative to the chdir directory. If you use the chdir keyword argument, you will typically need to supply a different command line using expansions like .B ${TARGET.file} and .B ${SOURCE.file} to use just the filename portion of the targets and source. .B scons provides the following builder methods: '\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" '\" BEGIN GENERATED BUILDER DESCRIPTIONS '\" '\" The descriptions below of the various SCons Builders are generated '\" from the .xml files that live next to the various Python modules in '\" the build enginer library. If you're reading this [gnt]roff file '\" with an eye towards patching this man page, you can still submit '\" a diff against this text, but it will have to be translated to a '\" diff against the underlying .xml file before the patch is actually '\" accepted. If you do that yourself, it will make it easier to '\" integrate the patch. '\" '\" BEGIN GENERATED BUILDER DESCRIPTIONS '\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" .so builders.man '\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" '\" END GENERATED BUILDER DESCRIPTIONS '\" '\" The descriptions above of the various SCons Builders are generated '\" from the .xml files that live next to the various Python modules in '\" the build enginer library. If you're reading this [gnt]roff file '\" with an eye towards patching this man page, you can still submit '\" a diff against this text, but it will have to be translated to a '\" diff against the underlying .xml file before the patch is actually '\" accepted. If you do that yourself, it will make it easier to '\" integrate the patch. '\" '\" END GENERATED BUILDER DESCRIPTIONS '\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" .P All targets of builder methods automatically depend on their sources. An explicit dependency can be specified using the .B Depends method of a construction environment (see below). In addition, .B scons automatically scans source files for various programming languages, so the dependencies do not need to be specified explicitly. By default, SCons can C source files, C++ source files, Fortran source files with .B .F (POSIX systems only), .B .fpp, or .B .FPP file extensions, and assembly language files with .B .S (POSIX systems only), .B .spp, or .B .SPP files extensions for C preprocessor dependencies. SCons also has default support for scanning D source files, You can also write your own Scanners to add support for additional source file types. These can be added to the default Scanner object used by the .BR Object (), .BR StaticObject (), and .BR SharedObject () Builders by adding them to the .B SourceFileScanner object. See the section "Scanner Objects" below, for more information about defining your own Scanner objects and using the .B SourceFileScanner object. .SS Methods and Functions to Do Things In addition to Builder methods, .B scons provides a number of other construction environment methods and global functions to manipulate the build configuration. Usually, a construction environment method and global function with the same name both exist so that you don't have to remember whether to a specific bit of functionality must be called with or without a construction environment. In the following list, if you call something as a global function it looks like: .ES .RI Function( arguments ) .EE and if you call something through a construction environment it looks like: .ES .RI env.Function( arguments ) .EE If you can call the functionality in both ways, then both forms are listed. Global functions may be called from custom Python modules that you import into an SConscript file by adding the following to the Python module: .ES from SCons.Script import * .EE Except where otherwise noted, the same-named construction environment method and global function provide the exact same functionality. The only difference is that, where appropriate, calling the functionality through a construction environment will substitute construction variables into any supplied strings. For example: .ES env = Environment(FOO = 'foo') Default('$FOO') env.Default('$FOO') .EE In the above example, the first call to the global .B Default() function will actually add a target named .B $FOO to the list of default targets, while the second call to the .B env.Default() construction environment method will expand the value and add a target named .B foo to the list of default targets. For more on construction variable expansion, see the next section on construction variables. Construction environment methods and global functions supported by .B scons include: '\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" '\" BEGIN GENERATED FUNCTION DESCRIPTIONS '\" '\" The descriptions below of the various SCons functions are generated '\" from the .xml files that live next to the various Python modules in '\" the build enginer library. If you're reading this [gnt]roff file '\" with an eye towards patching this man page, you can still submit '\" a diff against this text, but it will have to be translated to a '\" diff against the underlying .xml file before the patch is actually '\" accepted. If you do that yourself, it will make it easier to '\" integrate the patch. '\" '\" BEGIN GENERATED FUNCTION DESCRIPTIONS '\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" .so functions.man '\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" '\" END GENERATED FUNCTION DESCRIPTIONS '\" '\" The descriptions above of the various SCons functions are generated '\" from the .xml files that live next to the various Python modules in '\" the build enginer library. If you're reading this [gnt]roff file '\" with an eye towards patching this man page, you can still submit '\" a diff against this text, but it will have to be translated to a '\" diff against the underlying .xml file before the patch is actually '\" accepted. If you do that yourself, it will make it easier to '\" integrate the patch. '\" '\" END GENERATED FUNCTION DESCRIPTIONS '\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" .SS SConscript Variables In addition to the global functions and methods, .B scons supports a number of Python variables that can be used in SConscript files to affect how you want the build to be performed. These variables may be accessed from custom Python modules that you import into an SConscript file by adding the following to the Python module: .ES from SCons.Script import * .EE '\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" .TP ARGLIST A list .IR keyword = value arguments specified on the command line. Each element in the list is a tuple containing the .RI ( keyword , value ) of the argument. The separate .I keyword and .I value elements of the tuple can be accessed by subscripting for element .B [0] and .B [1] of the tuple, respectively. Example: .ES print "first keyword, value =", ARGLIST[0][0], ARGLIST[0][1] print "second keyword, value =", ARGLIST[1][0], ARGLIST[1][1] third_tuple = ARGLIST[2] print "third keyword, value =", third_tuple[0], third_tuple[1] for key, value in ARGLIST: # process key and value .EE '\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" .TP ARGUMENTS A dictionary of all the .IR keyword = value arguments specified on the command line. The dictionary is not in order, and if a given keyword has more than one value assigned to it on the command line, the last (right-most) value is the one in the .B ARGUMENTS dictionary. Example: .ES if ARGUMENTS.get('debug', 0): env = Environment(CCFLAGS = '-g') else: env = Environment() .EE '\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" .TP BUILD_TARGETS A list of the targets which .B scons will actually try to build, regardless of whether they were specified on the command line or via the .BR Default () function or method. The elements of this list may be strings .I or nodes, so you should run the list through the Python .B str function to make sure any Node path names are converted to strings. Because this list may be taken from the list of targets specified using the .BR Default () function or method, the contents of the list may change on each successive call to .BR Default (). See the .B DEFAULT_TARGETS list, below, for additional information. Example: .ES if 'foo' in BUILD_TARGETS: print "Don't forget to test the `foo' program!" if 'special/program' in BUILD_TARGETS: SConscript('special') .EE .IP Note that the .B BUILD_TARGETS list only contains targets expected listed on the command line or via calls to the .BR Default () function or method. It does .I not contain all dependent targets that will be built as a result of making the sure the explicitly-specified targets are up to date. '\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" .TP COMMAND_LINE_TARGETS A list of the targets explicitly specified on the command line. If there are no targets specified on the command line, the list is empty. This can be used, for example, to take specific actions only when a certain target or targets is explicitly being built. Example: .ES if 'foo' in COMMAND_LINE_TARGETS: print "Don't forget to test the `foo' program!" if 'special/program' in COMMAND_LINE_TARGETS: SConscript('special') .EE '\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" .TP DEFAULT_TARGETS A list of the target .I nodes that have been specified using the .BR Default () function or method. The elements of the list are nodes, so you need to run them through the Python .B str function to get at the path name for each Node. Example: .ES print str(DEFAULT_TARGETS[0]) if 'foo' in map(str, DEFAULT_TARGETS): print "Don't forget to test the `foo' program!" .EE .IP The contents of the .B DEFAULT_TARGETS list change on on each successive call to the .BR Default () function: .ES print map(str, DEFAULT_TARGETS) # originally [] Default('foo') print map(str, DEFAULT_TARGETS) # now a node ['foo'] Default('bar') print map(str, DEFAULT_TARGETS) # now a node ['foo', 'bar'] Default(None) print map(str, DEFAULT_TARGETS) # back to [] .EE .IP Consequently, be sure to use .B DEFAULT_TARGETS only after you've made all of your .BR Default () calls, or else simply be careful of the order of these statements in your SConscript files so that you don't look for a specific default target before it's actually been added to the list. .SS Construction Variables .\" XXX From Gary Ruben, 23 April 2002: .\" I think it would be good to have an example with each construction .\" variable description in the documentation. .\" eg. .\" CC The C compiler .\" Example: env["CC"] = "c68x" .\" Default: env["CC"] = "cc" .\" .\" CCCOM The command line ... .\" Example: .\" To generate the compiler line c68x -ps -qq -mr -o $TARGET $SOURCES .\" env["CC"] = "c68x" .\" env["CFLAGS"] = "-ps -qq -mr" .\" env["CCCOM"] = "$CC $CFLAGS -o $TARGET $SOURCES .\" Default: .\" (I dunno what this is ;-) A construction environment has an associated dictionary of .I construction variables that are used by built-in or user-supplied build rules. Construction variables must follow the same rules for Python identifiers: the initial character must be an underscore or letter, followed by any number of underscores, letters, or digits. A number of useful construction variables are automatically defined by scons for each supported platform, and additional construction variables can be defined by the user. The following is a list of the automatically defined construction variables: '\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" '\" BEGIN GENERATED CONSTRUCTION VARIABLE DESCRIPTIONS '\" '\" The descriptions below of the various SCons construction variables '\" are generated from the .xml files that live next to the various '\" Python modules in the build enginer library. If you're reading '\" this [gnt]roff file with an eye towards patching this man page, '\" you can still submit a diff against this text, but it will have to '\" be translated to a diff against the underlying .xml file before the '\" patch is actually accepted. If you do that yourself, it will make '\" it easier to integrate the patch. '\" '\" BEGIN GENERATED CONSTRUCTION VARIABLE DESCRIPTIONS '\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" .so variables.man '\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" '\" END GENERATED CONSTRUCTION VARIABLE DESCRIPTIONS '\" '\" The descriptions above of the various SCons construction variables '\" are generated from the .xml files that live next to the various '\" Python modules in the build enginer library. If you're reading '\" this [gnt]roff file with an eye towards patching this man page, '\" you can still submit a diff against this text, but it will have to '\" be translated to a diff against the underlying .xml file before the '\" patch is actually accepted. If you do that yourself, it will make '\" it easier to integrate the patch. '\" '\" END GENERATED CONSTRUCTION VARIABLE DESCRIPTIONS '\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" .LP Construction variables can be retrieved and set using the .B Dictionary method of the construction environment: .ES dict = env.Dictionary() dict["CC"] = "cc" .EE or using the [] operator: .ES env["CC"] = "cc" .EE Construction variables can also be passed to the construction environment constructor: .ES env = Environment(CC="cc") .EE or when copying a construction environment using the .B Clone method: .ES env2 = env.Clone(CC="cl.exe") .EE .SS Configure Contexts .B scons supports .I configure contexts, an integrated mechanism similar to the various AC_CHECK macros in GNU autoconf for testing for the existence of C header files, libraries, etc. In contrast to autoconf, .B scons does not maintain an explicit cache of the tested values, but uses its normal dependency tracking to keep the checked values up to date. However, users may override this behaviour with the .B --config command line option. The following methods can be used to perform checks: .TP .RI Configure( env ", [" custom_tests ", " conf_dir ", " log_file ", " config_h ", " clean ", " help]) .TP .RI env.Configure([ custom_tests ", " conf_dir ", " log_file ", " config_h ", " clean ", " help]) This creates a configure context, which can be used to perform checks. .I env specifies the environment for building the tests. This environment may be modified when performing checks. .I custom_tests is a dictionary containing custom tests. See also the section about custom tests below. By default, no custom tests are added to the configure context. .I conf_dir specifies a directory where the test cases are built. Note that this directory is not used for building normal targets. The default value is the directory #/.sconf_temp. .I log_file specifies a file which collects the output from commands that are executed to check for the existence of header files, libraries, etc. The default is the file #/config.log. If you are using the .BR VariantDir () method, you may want to specify a subdirectory under your variant directory. .I config_h specifies a C header file where the results of tests will be written, e.g. #define HAVE_STDIO_H, #define HAVE_LIBM, etc. The default is to not write a .B config.h file. You can specify the same .B config.h file in multiple calls to Configure, in which case .B scons will concatenate all results in the specified file. Note that SCons uses its normal dependency checking to decide if it's necessary to rebuild the specified .I config_h file. This means that the file is not necessarily re-built each time scons is run, but is only rebuilt if its contents will have changed and some target that depends on the .I config_h file is being built. The optional .B clean and .B help arguments can be used to suppress execution of the configuration tests when the .B -c/--clean or .B -H/-h/--help options are used, respectively. The default behavior is always to execute configure context tests, since the results of the tests may affect the list of targets to be cleaned or the help text. If the configure tests do not affect these, then you may add the .B clean=False or .B help=False arguments (or both) to avoid unnecessary test execution. .EE A created .B Configure instance has the following associated methods: .TP .RI SConf.Finish( context ) .TP .IR sconf .Finish() This method should be called after configuration is done. It returns the environment as modified by the configuration checks performed. After this method is called, no further checks can be performed with this configuration context. However, you can create a new .RI Configure context to perform additional checks. Only one context should be active at a time. The following Checks are predefined. (This list will likely grow larger as time goes by and developers contribute new useful tests.) .TP .RI SConf.CheckHeader( context ", " header ", [" include_quotes ", " language ]) .TP .IR sconf .CheckHeader( header ", [" include_quotes ", " language ]) Checks if .I header is usable in the specified language. .I header may be a list, in which case the last item in the list is the header file to be checked, and the previous list items are header files whose .B #include lines should precede the header line being checked for. The optional argument .I include_quotes must be a two character string, where the first character denotes the opening quote and the second character denotes the closing quote. By default, both characters are " (double quote). The optional argument .I language should be either .B C or .B C++ and selects the compiler to be used for the check. Returns 1 on success and 0 on failure. .TP .RI SConf.CheckCHeader( context ", " header ", [" include_quotes ]) .TP .IR sconf .CheckCHeader( header ", [" include_quotes ]) This is a wrapper around .B SConf.CheckHeader which checks if .I header is usable in the C language. .I header may be a list, in which case the last item in the list is the header file to be checked, and the previous list items are header files whose .B #include lines should precede the header line being checked for. The optional argument .I include_quotes must be a two character string, where the first character denotes the opening quote and the second character denotes the closing quote (both default to \N'34'). Returns 1 on success and 0 on failure. .TP .RI SConf.CheckCXXHeader( context ", " header ", [" include_quotes ]) .TP .IR sconf .CheckCXXHeader( header ", [" include_quotes ]) This is a wrapper around .B SConf.CheckHeader which checks if .I header is usable in the C++ language. .I header may be a list, in which case the last item in the list is the header file to be checked, and the previous list items are header files whose .B #include lines should precede the header line being checked for. The optional argument .I include_quotes must be a two character string, where the first character denotes the opening quote and the second character denotes the closing quote (both default to \N'34'). Returns 1 on success and 0 on failure. .TP .RI SConf.CheckFunc( context, ", " function_name ", [" header ", " language ]) .TP .IR sconf .CheckFunc( function_name ", [" header ", " language ]) Checks if the specified C or C++ function is available. .I function_name is the name of the function to check for. The optional .I header argument is a string that will be placed at the top of the test file that will be compiled to check if the function exists; the default is: .ES #ifdef __cplusplus extern "C" #endif char function_name(); .EE The optional .I language argument should be .B C or .B C++ and selects the compiler to be used for the check; the default is "C". .TP .RI SConf.CheckLib( context ", [" library ", " symbol ", " header ", " language ", " autoadd=1 ]) .TP .IR sconf .CheckLib([ library ", " symbol ", " header ", " language ", " autoadd=1 ]) Checks if .I library provides .IR symbol . If the value of .I autoadd is 1 and the library provides the specified .IR symbol , appends the library to the LIBS construction environment variable. .I library may also be None (the default), in which case .I symbol is checked with the current LIBS variable, or a list of library names, in which case each library in the list will be checked for .IR symbol . If .I symbol is not set or is .BR None , then .BR SConf.CheckLib () just checks if you can link against the specified .IR library . The optional .I language argument should be .B C or .B C++ and selects the compiler to be used for the check; the default is "C". The default value for .I autoadd is 1. This method returns 1 on success and 0 on error. .TP .RI SConf.CheckLibWithHeader( context ", " library ", " header ", " language ", [" call ", " autoadd ]) .TP .IR sconf .CheckLibWithHeader( library ", " header ", " language ", [" call ", " autoadd ]) In contrast to the .RI SConf.CheckLib call, this call provides a more sophisticated way to check against libraries. Again, .I library specifies the library or a list of libraries to check. .I header specifies a header to check for. .I header may be a list, in which case the last item in the list is the header file to be checked, and the previous list items are header files whose .B #include lines should precede the header line being checked for. .I language may be one of 'C','c','CXX','cxx','C++' and 'c++'. .I call can be any valid expression (with a trailing ';'). If .I call is not set, the default simply checks that you can link against the specified .IR library . .I autoadd specifies whether to add the library to the environment (only if the check succeeds). This method returns 1 on success and 0 on error. .TP .RI SConf.CheckType( context ", " type_name ", [" includes ", " language ]) .TP .IR sconf .CheckType( type_name ", [" includes ", " language ]) Checks for the existence of a type defined by .BR typedef . .I type_name specifies the typedef name to check for. .I includes is a string containing one or more .B #include lines that will be inserted into the program that will be run to test for the existence of the type. The optional .I language argument should be .B C or .B C++ and selects the compiler to be used for the check; the default is "C". Example: .ES sconf.CheckType('foo_type', '#include "my_types.h"', 'C++') .EE .TP .RI Configure.CheckCC( self ) Checks whether the C compiler (as defined by the CC construction variable) works by trying to compile a small source file. By default, SCons only detects if there is a program with the correct name, not if it is a functioning compiler. This uses the exact same command than the one used by the object builder for C source file, so it can be used to detect if a particular compiler flag works or not. .TP .RI Configure.CheckCXX( self ) Checks whether the C++ compiler (as defined by the CXX construction variable) works by trying to compile a small source file. By default, SCons only detects if there is a program with the correct name, not if it is a functioning compiler. This uses the exact same command than the one used by the object builder for CXX source files, so it can be used to detect if a particular compiler flag works or not. .TP .RI Configure.CheckSHCC( self ) Checks whether the C compiler (as defined by the SHCC construction variable) works by trying to compile a small source file. By default, SCons only detects if there is a program with the correct name, not if it is a functioning compiler. This uses the exact same command than the one used by the object builder for C source file, so it can be used to detect if a particular compiler flag works or not. This does not check whether the object code can be used to build a shared library, only that the compilation (not link) succeeds. .TP .RI Configure.CheckSHCXX( self ) Checks whether the C++ compiler (as defined by the SHCXX construction variable) works by trying to compile a small source file. By default, SCons only detects if there is a program with the correct name, not if it is a functioning compiler. This uses the exact same command than the one used by the object builder for CXX source files, so it can be used to detect if a particular compiler flag works or not. This does not check whether the object code can be used to build a shared library, only that the compilation (not link) succeeds. .EE Example of a typical Configure usage: .ES env = Environment() conf = Configure( env ) if not conf.CheckCHeader( 'math.h' ): print 'We really need math.h!' Exit(1) if conf.CheckLibWithHeader( 'qt', 'qapp.h', 'c++', 'QApplication qapp(0,0);' ): # do stuff for qt - usage, e.g. conf.env.Append( CPPFLAGS = '-DWITH_QT' ) env = conf.Finish() .EE .TP .RI SConf.CheckTypeSize( context ", " type_name ", [" header ", " language ", " expect ]) .TP .IR sconf .CheckTypeSize( type_name ", [" header ", " language ", " expect ]) Checks for the size of a type defined by .BR typedef . .I type_name specifies the typedef name to check for. The optional .I header argument is a string that will be placed at the top of the test file that will be compiled to check if the function exists; the default is empty. The optional .I language argument should be .B C or .B C++ and selects the compiler to be used for the check; the default is "C". The optional .I expect argument should be an integer. If this argument is used, the function will only check whether the type given in type_name has the expected size (in bytes). For example, .B "CheckTypeSize('short', expect = 2)" will return success only if short is two bytes. .ES .EE .TP .RI SConf.CheckDeclaration( context ", " symbol ", [" includes ", " language ]) .TP .IR sconf .CheckDeclaration( symbol ", [" includes ", " language ]) Checks if the specified .I symbol is declared. .I includes is a string containing one or more .B #include lines that will be inserted into the program that will be run to test for the existence of the type. The optional .I language argument should be .B C or .B C++ and selects the compiler to be used for the check; the default is "C". .TP .RI SConf.Define( context ", " symbol ", [" value ", " comment ]) .TP .IR sconf .Define( symbol ", [" value ", " comment ]) This function does not check for anything, but defines a preprocessor symbol that will be added to the configuration header file. It is the equivalent of AC_DEFINE, and defines the symbol .I name with the optional .B value and the optional comment .BR comment . .IP Examples: .ES env = Environment() conf = Configure( env ) # Puts the following line in the config header file: # #define A_SYMBOL conf.Define('A_SYMBOL') # Puts the following line in the config header file: # #define A_SYMBOL 1 conf.Define('A_SYMBOL', 1) .EE .IP Be careful about quoting string values, though: .ES env = Environment() conf = Configure( env ) # Puts the following line in the config header file: # #define A_SYMBOL YA conf.Define('A_SYMBOL', "YA") # Puts the following line in the config header file: # #define A_SYMBOL "YA" conf.Define('A_SYMBOL', '"YA"') .EE .IP For comment: .ES env = Environment() conf = Configure( env ) # Puts the following lines in the config header file: # /* Set to 1 if you have a symbol */ # #define A_SYMBOL 1 conf.Define('A_SYMBOL', 1, 'Set to 1 if you have a symbol') .EE .EE You can define your own custom checks. in addition to the predefined checks. These are passed in a dictionary to the Configure function. This dictionary maps the names of the checks to user defined Python callables (either Python functions or class instances implementing the .I __call__ method). The first argument of the call is always a .I CheckContext instance followed by the arguments, which must be supplied by the user of the check. These CheckContext instances define the following methods: .TP .RI CheckContext.Message( self ", " text ) Usually called before the check is started. .I text will be displayed to the user, e.g. 'Checking for library X...' .TP .RI CheckContext.Result( self, ", " res ) Usually called after the check is done. .I res can be either an integer or a string. In the former case, 'yes' (res != 0) or 'no' (res == 0) is displayed to the user, in the latter case the given string is displayed. .TP .RI CheckContext.TryCompile( self ", " text ", " extension ) Checks if a file with the specified .I extension (e.g. '.c') containing .I text can be compiled using the environment's .B Object builder. Returns 1 on success and 0 on failure. .TP .RI CheckContext.TryLink( self ", " text ", " extension ) Checks, if a file with the specified .I extension (e.g. '.c') containing .I text can be compiled using the environment's .B Program builder. Returns 1 on success and 0 on failure. .TP .RI CheckContext.TryRun( self ", " text ", " extension ) Checks, if a file with the specified .I extension (e.g. '.c') containing .I text can be compiled using the environment's .B Program builder. On success, the program is run. If the program executes successfully (that is, its return status is 0), a tuple .I (1, outputStr) is returned, where .I outputStr is the standard output of the program. If the program fails execution (its return status is non-zero), then (0, '') is returned. .TP .RI CheckContext.TryAction( self ", " action ", [" text ", " extension ]) Checks if the specified .I action with an optional source file (contents .I text , extension .I extension = '' ) can be executed. .I action may be anything which can be converted to a .B scons .RI Action. On success, .I (1, outputStr) is returned, where .I outputStr is the content of the target file. On failure .I (0, '') is returned. .TP .RI CheckContext.TryBuild( self ", " builder ", [" text ", " extension ]) Low level implementation for testing specific builds; the methods above are based on this method. Given the Builder instance .I builder and the optional .I text of a source file with optional .IR extension , this method returns 1 on success and 0 on failure. In addition, .I self.lastTarget is set to the build target node, if the build was successful. .EE Example for implementing and using custom tests: .ES def CheckQt(context, qtdir): context.Message( 'Checking for qt ...' ) lastLIBS = context.env['LIBS'] lastLIBPATH = context.env['LIBPATH'] lastCPPPATH= context.env['CPPPATH'] context.env.Append(LIBS = 'qt', LIBPATH = qtdir + '/lib', CPPPATH = qtdir + '/include' ) ret = context.TryLink(""" #include int main(int argc, char **argv) { QApplication qapp(argc, argv); return 0; } """) if not ret: context.env.Replace(LIBS = lastLIBS, LIBPATH=lastLIBPATH, CPPPATH=lastCPPPATH) context.Result( ret ) return ret env = Environment() conf = Configure( env, custom_tests = { 'CheckQt' : CheckQt } ) if not conf.CheckQt('/usr/lib/qt'): print 'We really need qt!' Exit(1) env = conf.Finish() .EE .SS Command-Line Construction Variables Often when building software, some variables must be specified at build time. For example, libraries needed for the build may be in non-standard locations, or site-specific compiler options may need to be passed to the compiler. .B scons provides a .B Variables object to support overriding construction variables on the command line: .ES $ scons VARIABLE=foo .EE The variable values can also be specified in a text-based SConscript file. To create a Variables object, call the Variables() function: .TP .RI Variables([ files "], [" args ]) This creates a Variables object that will read construction variables from the file or list of filenames specified in .IR files . If no files are specified, or the .I files argument is .BR None , then no files will be read. The optional argument .I args is a dictionary of values that will override anything read from the specified files; it is primarily intended to be passed the .B ARGUMENTS dictionary that holds variables specified on the command line. Example: .ES vars = Variables('custom.py') vars = Variables('overrides.py', ARGUMENTS) vars = Variables(None, {FOO:'expansion', BAR:7}) .EE Variables objects have the following methods: .TP .RI Add( key ", [" help ", " default ", " validator ", " converter ]) This adds a customizable construction variable to the Variables object. .I key is the name of the variable. .I help is the help text for the variable. .I default is the default value of the variable; if the default value is .B None and there is no explicit value specified, the construction variable will .I not be added to the construction environment. .I validator is called to validate the value of the variable, and should take three arguments: key, value, and environment. The recommended way to handle an invalid value is to raise an exception (see example below). .I converter is called to convert the value before putting it in the environment, and should take either a value, or the value and environment, as parameters. The .I converter must return a value, which will be converted into a string before being validated by the .I validator (if any) and then added to the environment. Examples: .ES vars.Add('CC', 'The C compiler') def validate_color(key, val, env): if not val in ['red', 'blue', 'yellow']: raise Exception("Invalid color value '%s'" % val) vars.Add('COLOR', validator=valid_color) .EE .TP .RI AddVariables( list ) A wrapper script that adds multiple customizable construction variables to a Variables object. .I list is a list of tuple or list objects that contain the arguments for an individual call to the .B Add method. .ES opt.AddVariables( ('debug', '', 0), ('CC', 'The C compiler'), ('VALIDATE', 'An option for testing validation', 'notset', validator, None), ) .EE .TP .RI Update( env ", [" args ]) This updates a construction environment .I env with the customized construction variables. Any specified variables that are .I not configured for the Variables object will be saved and may be retrieved with the .BR UnknownVariables () method, below. Normally this method is not called directly, but is called indirectly by passing the Variables object to the Environment() function: .ES env = Environment(variables=vars) .EE .IP The text file(s) that were specified when the Variables object was created are executed as Python scripts, and the values of (global) Python variables set in the file are added to the construction environment. Example: .ES CC = 'my_cc' .EE .TP .RI UnknownVariables( ) Returns a dictionary containing any variables that were specified either in the files or the dictionary with which the Variables object was initialized, but for which the Variables object was not configured. .ES env = Environment(variables=vars) for key, value in vars.UnknownVariables(): print "unknown variable: %s=%s" % (key, value) .EE .TP .RI Save( filename ", " env ) This saves the currently set variables into a script file named .I filename that can be used on the next invocation to automatically load the current settings. This method combined with the Variables method can be used to support caching of variables between runs. .ES env = Environment() vars = Variables(['variables.cache', 'custom.py']) vars.Add(...) vars.Update(env) vars.Save('variables.cache', env) .EE .TP .RI GenerateHelpText( env ", [" sort ]) This generates help text documenting the customizable construction variables suitable to passing in to the Help() function. .I env is the construction environment that will be used to get the actual values of customizable variables. Calling with an optional .I sort function will cause the output to be sorted by the specified argument. The specific .I sort function should take two arguments and return -1, 0 or 1 (like the standard Python .I cmp function). .ES Help(vars.GenerateHelpText(env)) Help(vars.GenerateHelpText(env, sort=cmp)) .EE .TP .RI FormatVariableHelpText( env ", " opt ", " help ", " default ", " actual ) This method returns a formatted string containing the printable help text for one option. It is normally not called directly, but is called by the .IR GenerateHelpText () method to create the returned help text. It may be overridden with your own function that takes the arguments specified above and returns a string of help text formatted to your liking. Note that the .IR GenerateHelpText () will not put any blank lines or extra characters in between the entries, so you must add those characters to the returned string if you want the entries separated. .ES def my_format(env, opt, help, default, actual): fmt = "\n%s: default=%s actual=%s (%s)\n" return fmt % (opt, default. actual, help) vars.FormatVariableHelpText = my_format .EE To make it more convenient to work with customizable Variables, .B scons provides a number of functions that make it easy to set up various types of Variables: .TP .RI BoolVariable( key ", " help ", " default ) Return a tuple of arguments to set up a Boolean option. The option will use the specified name .IR key , have a default value of .IR default , and display the specified .I help text. The option will interpret the values .BR y , .BR yes , .BR t , .BR true , .BR 1 , .B on and .B all as true, and the values .BR n , .BR no , .BR f , .BR false , .BR 0 , .B off and .B none as false. .TP .RI EnumVariable( key ", " help ", " default ", " allowed_values ", [" map ", " ignorecase ]) Return a tuple of arguments to set up an option whose value may be one of a specified list of legal enumerated values. The option will use the specified name .IR key , have a default value of .IR default , and display the specified .I help text. The option will only support those values in the .I allowed_values list. The optional .I map argument is a dictionary that can be used to convert input values into specific legal values in the .I allowed_values list. If the value of .I ignore_case is .B 0 (the default), then the values are case-sensitive. If the value of .I ignore_case is .BR 1 , then values will be matched case-insensitive. If the value of .I ignore_case is .BR 1 , then values will be matched case-insensitive, and all input values will be converted to lower case. .TP .RI ListVariable( key ", " help ", " default ", " names ", [", map ]) Return a tuple of arguments to set up an option whose value may be one or more of a specified list of legal enumerated values. The option will use the specified name .IR key , have a default value of .IR default , and display the specified .I help text. The option will only support the values .BR all , .BR none , or the values in the .I names list. More than one value may be specified, with all values separated by commas. The default may be a string of comma-separated default values, or a list of the default values. The optional .I map argument is a dictionary that can be used to convert input values into specific legal values in the .I names list. .TP .RI PackageVariable( key ", " help ", " default ) Return a tuple of arguments to set up an option whose value is a path name of a package that may be enabled, disabled or given an explicit path name. The option will use the specified name .IR key , have a default value of .IR default , and display the specified .I help text. The option will support the values .BR yes , .BR true , .BR on , .BR enable or .BR search , in which case the specified .I default will be used, or the option may be set to an arbitrary string (typically the path name to a package that is being enabled). The option will also support the values .BR no , .BR false , .BR off or .BR disable to disable use of the specified option. .TP .RI PathVariable( key ", " help ", " default ", [" validator ]) Return a tuple of arguments to set up an option whose value is expected to be a path name. The option will use the specified name .IR key , have a default value of .IR default , and display the specified .I help text. An additional .I validator may be specified that will be called to verify that the specified path is acceptable. SCons supplies the following ready-made validators: .BR PathVariable.PathExists (the default), which verifies that the specified path exists; .BR PathVariable.PathIsFile , which verifies that the specified path is an existing file; .BR PathVariable.PathIsDir , which verifies that the specified path is an existing directory; .BR PathVariable.PathIsDirCreate , which verifies that the specified path is a directory and will create the specified directory if the path does not exist; and .BR PathVariable.PathAccept , which simply accepts the specific path name argument without validation, and which is suitable if you want your users to be able to specify a directory path that will be created as part of the build process, for example. You may supply your own .I validator function, which must take three arguments .RI ( key , the name of the variable to be set; .IR val , the specified value being checked; and .IR env , the construction environment) and should raise an exception if the specified value is not acceptable. .RE These functions make it convenient to create a number of variables with consistent behavior in a single call to the .B AddVariables method: .ES vars.AddVariables( BoolVariable('warnings', 'compilation with -Wall and similiar', 1), EnumVariable('debug', 'debug output and symbols', 'no' allowed_values=('yes', 'no', 'full'), map={}, ignorecase=0), # case sensitive ListVariable('shared', 'libraries to build as shared libraries', 'all', names = list_of_libs), PackageVariable('x11', 'use X11 installed here (yes = search some places)', 'yes'), PathVariable('qtdir', 'where the root of Qt is installed', qtdir), PathVariable('foopath', 'where the foo library is installed', foopath, PathVariable.PathIsDir), ) .EE .SS File and Directory Nodes The .IR File () and .IR Dir () functions return .I File and .I Dir Nodes, respectively. python objects, respectively. Those objects have several user-visible attributes and methods that are often useful: .IP path The build path of the given file or directory. This path is relative to the top-level directory (where the .B SConstruct file is found). The build path is the same as the source path if .I variant_dir is not being used. .IP abspath The absolute build path of the given file or directory. .IP srcnode() The .IR srcnode () method returns another .I File or .I Dir object representing the .I source path of the given .I File or .IR Dir . The .ES # Get the current build dir's path, relative to top. Dir('.').path # Current dir's absolute path Dir('.').abspath # Next line is always '.', because it is the top dir's path relative to itself. Dir('#.').path File('foo.c').srcnode().path # source path of the given source file. # Builders also return File objects: foo = env.Program('foo.c') print "foo will be built in %s"%foo.path .EE A .I Dir Node or .I File Node can also be used to create file and subdirectory Nodes relative to the generating Node. A .I Dir Node will place the new Nodes within the directory it represents. A .I File node will place the new Nodes within its parent directory (that is, "beside" the file in question). If .I d is a .I Dir (directory) Node and .I f is a .I File (file) Node, then these methods are available: .TP .IR d .Dir( name ) Returns a directory Node for a subdirectory of .I d named .IR name . .TP .IR d .File( name ) Returns a file Node for a file within .I d named .IR name . .TP .IR d .Entry( name ) Returns an unresolved Node within .I d named .IR name . .TP .IR f .Dir( name ) Returns a directory named .I name within the parent directory of .IR f . .TP .IR f .File( name ) Returns a file named .I name within the parent directory of .IR f . .TP .IR f .Entry( name ) Returns an unresolved Node named .I name within the parent directory of .IR f . .RE For example: .ES # Get a Node for a file within a directory incl = Dir('include') f = incl.File('header.h') # Get a Node for a subdirectory within a directory dist = Dir('project-3.2.1) src = dist.Dir('src') # Get a Node for a file in the same directory cfile = File('sample.c') hfile = cfile.File('sample.h') # Combined example docs = Dir('docs') html = docs.Dir('html') index = html.File('index.html') css = index.File('app.css') .EE .SH EXTENDING SCONS .SS Builder Objects .B scons can be extended to build different types of targets by adding new Builder objects to a construction environment. .IR "In general" , you should only need to add a new Builder object when you want to build a new type of file or other external target. If you just want to invoke a different compiler or other tool to build a Program, Object, Library, or any other type of output file for which .B scons already has an existing Builder, it is generally much easier to use those existing Builders in a construction environment that sets the appropriate construction variables (CC, LINK, etc.). Builder objects are created using the .B Builder function. The .B Builder function accepts the following arguments: .IP action The command line string used to build the target from the source. .B action can also be: a list of strings representing the command to be executed and its arguments (suitable for enclosing white space in an argument), a dictionary mapping source file name suffixes to any combination of command line strings (if the builder should accept multiple source file extensions), a Python function; an Action object (see the next section); or a list of any of the above. An action function takes three arguments: .I source - a list of source nodes, .I target - a list of target nodes, .I env - the construction environment. .IP prefix The prefix that will be prepended to the target file name. This may be specified as a: .RS 10 .HP 6 * .IR string , .HP 6 * .I callable object - a function or other callable that takes two arguments (a construction environment and a list of sources) and returns a prefix, .HP 6 * .I dictionary - specifies a mapping from a specific source suffix (of the first source specified) to a corresponding target prefix. Both the source suffix and target prefix specifications may use environment variable substitution, and the target prefix (the 'value' entries in the dictionary) may also be a callable object. The default target prefix may be indicated by a dictionary entry with a key value of None. .RE .P .ES b = Builder("build_it < $SOURCE > $TARGET", prefix = "file-") def gen_prefix(env, sources): return "file-" + env['PLATFORM'] + '-' b = Builder("build_it < $SOURCE > $TARGET", prefix = gen_prefix) b = Builder("build_it < $SOURCE > $TARGET", suffix = { None: "file-", "$SRC_SFX_A": gen_prefix }) .EE .IP suffix The suffix that will be appended to the target file name. This may be specified in the same manner as the prefix above. If the suffix is a string, then .B scons will append a '.' to the beginning of the suffix if it's not already there. The string returned by callable object (or obtained from the dictionary) is untouched and must append its own '.' to the beginning if one is desired. .ES b = Builder("build_it < $SOURCE > $TARGET" suffix = "-file") def gen_suffix(env, sources): return "." + env['PLATFORM'] + "-file" b = Builder("build_it < $SOURCE > $TARGET", suffix = gen_suffix) b = Builder("build_it < $SOURCE > $TARGET", suffix = { None: ".sfx1", "$SRC_SFX_A": gen_suffix }) .EE .IP ensure_suffix When set to any true value, causes .B scons to add the target suffix specified by the .I suffix keyword to any target strings that have a different suffix. (The default behavior is to leave untouched any target file name that looks like it already has any suffix.) .ES b1 = Builder("build_it < $SOURCE > $TARGET" suffix = ".out") b2 = Builder("build_it < $SOURCE > $TARGET" suffix = ".out", ensure_suffix) env = Environment() env['BUILDERS']['B1'] = b1 env['BUILDERS']['B2'] = b2 # Builds "foo.txt" because ensure_suffix is not set. env.B1('foo.txt', 'foo.in') # Builds "bar.txt.out" because ensure_suffix is set. env.B2('bar.txt', 'bar.in') .EE .IP src_suffix The expected source file name suffix. This may be a string or a list of strings. .IP target_scanner A Scanner object that will be invoked to find implicit dependencies for this target file. This keyword argument should be used for Scanner objects that find implicit dependencies based only on the target file and the construction environment, .I not for implicit dependencies based on source files. (See the section "Scanner Objects" below, for information about creating Scanner objects.) .IP source_scanner A Scanner object that will be invoked to find implicit dependencies in any source files used to build this target file. This is where you would specify a scanner to find things like .B #include lines in source files. The pre-built .B DirScanner Scanner object may be used to indicate that this Builder should scan directory trees for on-disk changes to files that .B scons does not know about from other Builder or function calls. (See the section "Scanner Objects" below, for information about creating your own Scanner objects.) .IP target_factory A factory function that the Builder will use to turn any targets specified as strings into SCons Nodes. By default, SCons assumes that all targets are files. Other useful target_factory values include .BR Dir , for when a Builder creates a directory target, and .BR Entry , for when a Builder can create either a file or directory target. Example: .ES MakeDirectoryBuilder = Builder(action=my_mkdir, target_factory=Dir) env = Environment() env.Append(BUILDERS = {'MakeDirectory':MakeDirectoryBuilder}) env.MakeDirectory('new_directory', []) .EE .IP Note that the call to the MakeDirectory Builder needs to specify an empty source list to make the string represent the builder's target; without that, it would assume the argument is the source, and would try to deduce the target name from it, which in the absence of an automatically-added prefix or suffix would lead to a matching target and source name and a circular dependency. .IP source_factory A factory function that the Builder will use to turn any sources specified as strings into SCons Nodes. By default, SCons assumes that all source are files. Other useful source_factory values include .BR Dir , for when a Builder uses a directory as a source, and .BR Entry , for when a Builder can use files or directories (or both) as sources. Example: .ES CollectBuilder = Builder(action=my_mkdir, source_factory=Entry) env = Environment() env.Append(BUILDERS = {'Collect':CollectBuilder}) env.Collect('archive', ['directory_name', 'file_name']) .EE .IP emitter A function or list of functions to manipulate the target and source lists before dependencies are established and the target(s) are actually built. .B emitter can also be a string containing a construction variable to expand to an emitter function or list of functions, or a dictionary mapping source file suffixes to emitter functions. (Only the suffix of the first source file is used to select the actual emitter function from an emitter dictionary.) An emitter function takes three arguments: .I source - a list of source nodes, .I target - a list of target nodes, .I env - the construction environment. An emitter must return a tuple containing two lists, the list of targets to be built by this builder, and the list of sources for this builder. Example: .ES def e(target, source, env): return (target + ['foo.foo'], source + ['foo.src']) # Simple association of an emitter function with a Builder. b = Builder("my_build < $TARGET > $SOURCE", emitter = e) def e2(target, source, env): return (target + ['bar.foo'], source + ['bar.src']) # Simple association of a list of emitter functions with a Builder. b = Builder("my_build < $TARGET > $SOURCE", emitter = [e, e2]) # Calling an emitter function through a construction variable. env = Environment(MY_EMITTER = e) b = Builder("my_build < $TARGET > $SOURCE", emitter = '$MY_EMITTER') # Calling a list of emitter functions through a construction variable. env = Environment(EMITTER_LIST = [e, e2]) b = Builder("my_build < $TARGET > $SOURCE", emitter = '$EMITTER_LIST') # Associating multiple emitters with different file # suffixes using a dictionary. def e_suf1(target, source, env): return (target + ['another_target_file'], source) def e_suf2(target, source, env): return (target, source + ['another_source_file']) b = Builder("my_build < $TARGET > $SOURCE", emitter = {'.suf1' : e_suf1, '.suf2' : e_suf2}) .EE .IP multi Specifies whether this builder is allowed to be called multiple times for the same target file(s). The default is 0, which means the builder can not be called multiple times for the same target file(s). Calling a builder multiple times for the same target simply adds additional source files to the target; it is not allowed to change the environment associated with the target, specify addition environment overrides, or associate a different builder with the target. .IP env A construction environment that can be used to fetch source code using this Builder. (Note that this environment is .I not used for normal builds of normal target files, which use the environment that was used to call the Builder for the target file.) .IP generator A function that returns a list of actions that will be executed to build the target(s) from the source(s). The returned action(s) may be an Action object, or anything that can be converted into an Action object (see the next section). The generator function takes four arguments: .I source - a list of source nodes, .I target - a list of target nodes, .I env - the construction environment, .I for_signature - a Boolean value that specifies whether the generator is being called for generating a build signature (as opposed to actually executing the command). Example: .ES def g(source, target, env, for_signature): return [["gcc", "-c", "-o"] + target + source] b = Builder(generator=g) .EE .IP The .I generator and .I action arguments must not both be used for the same Builder. .IP src_builder Specifies a builder to use when a source file name suffix does not match any of the suffixes of the builder. Using this argument produces a multi-stage builder. .IP single_source Specifies that this builder expects exactly one source file per call. Giving more than one source file without target files results in implicitely calling the builder multiple times (once for each source given). Giving multiple source files together with target files results in a UserError exception. .RE .IP The .I generator and .I action arguments must not both be used for the same Builder. .IP source_ext_match When the specified .I action argument is a dictionary, the default behavior when a builder is passed multiple source files is to make sure that the extensions of all the source files match. If it is legal for this builder to be called with a list of source files with different extensions, this check can be suppressed by setting .B source_ext_match to .B None or some other non-true value. When .B source_ext_match is disable, .B scons will use the suffix of the first specified source file to select the appropriate action from the .I action dictionary. In the following example, the setting of .B source_ext_match prevents .B scons from exiting with an error due to the mismatched suffixes of .B foo.in and .BR foo.extra . .ES b = Builder(action={'.in' : 'build $SOURCES > $TARGET'}, source_ext_match = None) env = Environment(BUILDERS = {'MyBuild':b}) env.MyBuild('foo.out', ['foo.in', 'foo.extra']) .EE .IP env A construction environment that can be used to fetch source code using this Builder. (Note that this environment is .I not used for normal builds of normal target files, which use the environment that was used to call the Builder for the target file.) .ES b = Builder(action="build < $SOURCE > $TARGET") env = Environment(BUILDERS = {'MyBuild' : b}) env.MyBuild('foo.out', 'foo.in', my_arg = 'xyzzy') .EE .IP chdir A directory from which scons will execute the action(s) specified for this Builder. If the .B chdir argument is a string or a directory Node, scons will change to the specified directory. If the .B chdir is not a string or Node and is non-zero, then scons will change to the target file's directory. Note that scons will .I not automatically modify its expansion of construction variables like .B $TARGET and .B $SOURCE when using the chdir keyword argument--that is, the expanded file names will still be relative to the top-level SConstruct directory, and consequently incorrect relative to the chdir directory. Builders created using chdir keyword argument, will need to use construction variable expansions like .B ${TARGET.file} and .B ${SOURCE.file} to use just the filename portion of the targets and source. .ES b = Builder(action="build < ${SOURCE.file} > ${TARGET.file}", chdir=1) env = Environment(BUILDERS = {'MyBuild' : b}) env.MyBuild('sub/dir/foo.out', 'sub/dir/foo.in') .EE .B WARNING: Python only keeps one current directory location for all of the threads. This means that use of the .B chdir argument will .I not work with the SCons .B -j option, because individual worker threads spawned by SCons interfere with each other when they start changing directory. .RE Any additional keyword arguments supplied when a Builder object is created (that is, when the Builder() function is called) will be set in the executing construction environment when the Builder object is called. The canonical example here would be to set a construction variable to the repository of a source code system. Any additional keyword arguments supplied when a Builder .I object is called will only be associated with the target created by that particular Builder call (and any other files built as a result of the call). These extra keyword arguments are passed to the following functions: command generator functions, function Actions, and emitter functions. .SS Action Objects The .BR Builder () function will turn its .B action keyword argument into an appropriate internal Action object. You can also explicity create Action objects using the .BR Action () global function, which can then be passed to the .BR Builder () function. This can be used to configure an Action object more flexibly, or it may simply be more efficient than letting each separate Builder object create a separate Action when multiple Builder objects need to do the same thing. The .BR Action () global function returns an appropriate object for the action represented by the type of the first argument: .IP Action If the first argument is already an Action object, the object is simply returned. .IP String If the first argument is a string, a command-line Action is returned. Note that the command-line string may be preceded by an .B @ (at-sign) to suppress printing of the specified command line, or by a .B \- (hyphen) to ignore the exit status from the specified command: .ES Action('$CC -c -o $TARGET $SOURCES') # Doesn't print the line being executed. Action('@build $TARGET $SOURCES') # Ignores return value Action('-build $TARGET $SOURCES') .EE .\" XXX From Gary Ruben, 23 April 2002: .\" What would be useful is a discussion of how you execute command .\" shell commands ie. what is the process used to spawn the shell, pass .\" environment variables to it etc., whether there is one shell per .\" environment or one per command etc. It might help to look at the Gnu .\" make documentation to see what they think is important to discuss about .\" a build system. I'm sure you can do a better job of organising the .\" documentation than they have :-) .IP List If the first argument is a list, then a list of Action objects is returned. An Action object is created as necessary for each element in the list. If an element .I within the list is itself a list, the internal list is the command and arguments to be executed via the command line. This allows white space to be enclosed in an argument by defining a command in a list within a list: .ES Action([['cc', '-c', '-DWHITE SPACE', '-o', '$TARGET', '$SOURCES']]) .EE .IP Function If the first argument is a Python function, a function Action is returned. The Python function must take three keyword arguments, .B target (a Node object representing the target file), .B source (a Node object representing the source file) and .B env (the construction environment used for building the target file). The .B target and .B source arguments may be lists of Node objects if there is more than one target file or source file. The actual target and source file name(s) may be retrieved from their Node objects via the built-in Python str() function: .ES target_file_name = str(target) source_file_names = map(lambda x: str(x), source) .EE .IP The function should return .B 0 or .B None to indicate a successful build of the target file(s). The function may raise an exception or return a non-zero exit status to indicate an unsuccessful build. .ES def build_it(target = None, source = None, env = None): # build the target from the source return 0 a = Action(build_it) .EE If the action argument is not one of the above, None is returned. .PP The second argument is optional and is used to define the output which is printed when the Action is actually performed. In the absence of this parameter, or if it's an empty string, a default output depending on the type of the action is used. For example, a command-line action will print the executed command. The argument must be either a Python function or a string. In the first case, it's a function that returns a string to be printed to describe the action being executed. The function may also be specified by the .IR strfunction = keyword argument. Like a function to build a file, this function must take three keyword arguments: .B target (a Node object representing the target file), .B source (a Node object representing the source file) and .BR env (a construction environment). The .B target and .B source arguments may be lists of Node objects if there is more than one target file or source file. In the second case, you provide the string itself. The string may also be specified by the .IR cmdstr = keyword argument. The string typically contains variables, notably $TARGET(S) and $SOURCE(S), or consists of just a single variable, which is optionally defined somewhere else. SCons itself heavily uses the latter variant. Examples: .ES def build_it(target, source, env): # build the target from the source return 0 def string_it(target, source, env): return "building '%s' from '%s'" % (target[0], source[0]) # Use a positional argument. f = Action(build_it, string_it) s = Action(build_it, "building '$TARGET' from '$SOURCE'") # Alternatively, use a keyword argument. f = Action(build_it, strfunction=string_it) s = Action(build_it, cmdstr="building '$TARGET' from '$SOURCE'") # You can provide a configurable variable. l = Action(build_it, '$STRINGIT') .EE The third and succeeding arguments, if present, may either be a construction variable or a list of construction variables whose values will be included in the signature of the Action when deciding whether a target should be rebuilt because the action changed. The variables may also be specified by a .IR varlist = keyword parameter; if both are present, they are combined. This is necessary whenever you want a target to be rebuilt when a specific construction variable changes. This is not often needed for a string action, as the expanded variables will normally be part of the command line, but may be needed if a Python function action uses the value of a construction variable when generating the command line. .ES def build_it(target, source, env): # build the target from the 'XXX' construction variable open(target[0], 'w').write(env['XXX']) return 0 # Use positional arguments. a = Action(build_it, '$STRINGIT', ['XXX']) # Alternatively, use a keyword argument. a = Action(build_it, varlist=['XXX']) .EE The .BR Action () global function can be passed the following optional keyword arguments to modify the Action object's behavior: .IP .B chdir The .B chdir keyword argument specifies that scons will execute the action after changing to the specified directory. If the .B chdir argument is a string or a directory Node, scons will change to the specified directory. If the .B chdir argument is not a string or Node and is non-zero, then scons will change to the target file's directory. Note that scons will .I not automatically modify its expansion of construction variables like .B $TARGET and .B $SOURCE when using the chdir keyword argument--that is, the expanded file names will still be relative to the top-level SConstruct directory, and consequently incorrect relative to the chdir directory. Builders created using chdir keyword argument, will need to use construction variable expansions like .B ${TARGET.file} and .B ${SOURCE.file} to use just the filename portion of the targets and source. .ES a = Action("build < ${SOURCE.file} > ${TARGET.file}", chdir=1) .EE .IP .B exitstatfunc The .BR Action () global function also takes an .B exitstatfunc keyword argument which specifies a function that is passed the exit status (or return value) from the specified action and can return an arbitrary or modified value. This can be used, for example, to specify that an Action object's return value should be ignored under special conditions and SCons should, therefore, consider that the action always suceeds: .ES def always_succeed(s): # Always return 0, which indicates success. return 0 a = Action("build < ${SOURCE.file} > ${TARGET.file}", exitstatfunc=always_succeed) .EE .IP .B batch_key The .B batch_key keyword argument can be used to specify that the Action can create multiple target files by processing multiple independent source files simultaneously. (The canonical example is "batch compilation" of multiple object files by passing multiple source files to a single invocation of a compiler such as Microsoft's Visual C / C++ compiler.) If the .B batch_key argument is any non-False, non-callable Python value, the configured Action object will cause .B scons to collect all targets built with the Action object and configured with the same construction environment into single invocations of the Action object's command line or function. Command lines will typically want to use the .BR CHANGED_SOURCES construction variable (and possibly .BR CHANGED_TARGETS as well) to only pass to the command line those sources that have actually changed since their targets were built. Example: .ES a = Action('build $CHANGED_SOURCES', batch_key=True) .EE The .B batch_key argument may also be a callable function that returns a key that will be used to identify different "batches" of target files to be collected for batch building. A .B batch_key function must take the following arguments: .IP action The action object. .IP env The construction environment configured for the target. .IP target The list of targets for a particular configured action. .IP source The list of source for a particular configured action. The returned key should typically be a tuple of values derived from the arguments, using any appropriate logic to decide how multiple invocations should be batched. For example, a .B batch_key function may decide to return the value of a specific construction variable from the .B env argument which will cause .B scons to batch-build targets with matching values of that variable, or perhaps return the .BR id () of the entire construction environment, in which case .B scons will batch-build all targets configured with the same construction environment. Returning .B None indicates that the particular target should .I not be part of any batched build, but instead will be built by a separate invocation of action's command or function. Example: .ES def batch_key(action, env, target, source): tdir = target[0].dir if tdir.name == 'special': # Don't batch-build any target # in the special/ subdirectory. return None return (id(action), id(env), tdir) a = Action('build $CHANGED_SOURCES', batch_key=batch_key) .EE .SS Miscellaneous Action Functions .B scons supplies a number of functions that arrange for various common file and directory manipulations to be performed. These are similar in concept to "tasks" in the Ant build tool, although the implementation is slightly different. These functions do not actually perform the specified action at the time the function is called, but instead return an Action object that can be executed at the appropriate time. (In Object-Oriented terminology, these are actually Action .I Factory functions that return Action objects.) In practice, there are two natural ways that these Action Functions are intended to be used. First, if you need to perform the action at the time the SConscript file is being read, you can use the .B Execute global function to do so: .ES Execute(Touch('file')) .EE Second, you can use these functions to supply Actions in a list for use by the .B Command method. This can allow you to perform more complicated sequences of file manipulation without relying on platform-specific external commands: that .ES env = Environment(TMPBUILD = '/tmp/builddir') env.Command('foo.out', 'foo.in', [Mkdir('$TMPBUILD'), Copy('$TMPBUILD', '${SOURCE.dir}'), "cd $TMPBUILD && make", Delete('$TMPBUILD')]) .EE .TP .RI Chmod( dest ", " mode ) Returns an Action object that changes the permissions on the specified .I dest file or directory to the specified .IR mode . Examples: .ES Execute(Chmod('file', 0755)) env.Command('foo.out', 'foo.in', [Copy('$TARGET', '$SOURCE'), Chmod('$TARGET', 0755)]) .EE .TP .RI Copy( dest ", " src ) Returns an Action object that will copy the .I src source file or directory to the .I dest destination file or directory. Examples: .ES Execute(Copy('foo.output', 'foo.input')) env.Command('bar.out', 'bar.in', Copy('$TARGET', '$SOURCE')) .EE .TP .RI Delete( entry ", [" must_exist ]) Returns an Action that deletes the specified .IR entry , which may be a file or a directory tree. If a directory is specified, the entire directory tree will be removed. If the .I must_exist flag is set, then a Python error will be thrown if the specified entry does not exist; the default is .BR must_exist=0 , that is, the Action will silently do nothing if the entry does not exist. Examples: .ES Execute(Delete('/tmp/buildroot')) env.Command('foo.out', 'foo.in', [Delete('${TARGET.dir}'), MyBuildAction]) Execute(Delete('file_that_must_exist', must_exist=1)) .EE .TP .RI Mkdir( dir ) Returns an Action that creates the specified directory .I dir . Examples: .ES Execute(Mkdir('/tmp/outputdir')) env.Command('foo.out', 'foo.in', [Mkdir('/tmp/builddir'), Copy('/tmp/builddir/foo.in', '$SOURCE'), "cd /tmp/builddir && make", Copy('$TARGET', '/tmp/builddir/foo.out')]) .EE .TP .RI Move( dest ", " src ) Returns an Action that moves the specified .I src file or directory to the specified .I dest file or directory. Examples: .ES Execute(Move('file.destination', 'file.source')) env.Command('output_file', 'input_file', [MyBuildAction, Move('$TARGET', 'file_created_by_MyBuildAction')]) .EE .TP .RI Touch( file ) Returns an Action that updates the modification time on the specified .IR file . Examples: .ES Execute(Touch('file_to_be_touched')) env.Command('marker', 'input_file', [MyBuildAction, Touch('$TARGET')]) .EE .SS Variable Substitution Before executing a command, .B scons performs construction variable interpolation on the strings that make up the command line of builders. Variables are introduced by a .B $ prefix. Besides construction variables, scons provides the following variables for each command execution: .IP CHANGED_SOURCES The file names of all sources of the build command that have changed since the target was last built. .IP CHANGED_TARGETS The file names of all targets that would be built from sources that have changed since the target was last built. .IP SOURCE The file name of the source of the build command, or the file name of the first source if multiple sources are being built. .IP SOURCES The file names of the sources of the build command. .IP TARGET The file name of the target being built, or the file name of the first target if multiple targets are being built. .IP TARGETS The file names of all targets being built. .IP UNCHANGED_SOURCES The file names of all sources of the build command that have .I not changed since the target was last built. .IP UNCHANGED_TARGETS The file names of all targets that would be built from sources that have .I not changed since the target was last built. (Note that the above variables are reserved and may not be set in a construction environment.) .LP For example, given the construction variable CC='cc', targets=['foo'], and sources=['foo.c', 'bar.c']: .ES action='$CC -c -o $TARGET $SOURCES' .EE would produce the command line: .ES cc -c -o foo foo.c bar.c .EE Variable names may be surrounded by curly braces ({}) to separate the name from the trailing characters. Within the curly braces, a variable name may have a Python slice subscript appended to select one or more items from a list. In the previous example, the string: .ES ${SOURCES[1]} .EE would produce: .ES bar.c .EE Additionally, a variable name may have the following special modifiers appended within the enclosing curly braces to modify the interpolated string: .IP base The base path of the file name, including the directory path but excluding any suffix. .IP dir The name of the directory in which the file exists. .IP file The file name, minus any directory portion. .IP filebase Just the basename of the file, minus any suffix and minus the directory. .IP suffix Just the file suffix. .IP abspath The absolute path name of the file. .IP posix The POSIX form of the path, with directories separated by .B / (forward slashes) not backslashes. This is sometimes necessary on Windows systems when a path references a file on other (POSIX) systems. .IP srcpath The directory and file name to the source file linked to this file through .BR VariantDir (). If this file isn't linked, it just returns the directory and filename unchanged. .IP srcdir The directory containing the source file linked to this file through .BR VariantDir (). If this file isn't linked, it just returns the directory part of the filename. .IP rsrcpath The directory and file name to the source file linked to this file through .BR VariantDir (). If the file does not exist locally but exists in a Repository, the path in the Repository is returned. If this file isn't linked, it just returns the directory and filename unchanged. .IP rsrcdir The Repository directory containing the source file linked to this file through .BR VariantDir (). If this file isn't linked, it just returns the directory part of the filename. .LP For example, the specified target will expand as follows for the corresponding modifiers: .ES $TARGET => sub/dir/file.x ${TARGET.base} => sub/dir/file ${TARGET.dir} => sub/dir ${TARGET.file} => file.x ${TARGET.filebase} => file ${TARGET.suffix} => .x ${TARGET.abspath} => /top/dir/sub/dir/file.x SConscript('src/SConscript', variant_dir='sub/dir') $SOURCE => sub/dir/file.x ${SOURCE.srcpath} => src/file.x ${SOURCE.srcdir} => src Repository('/usr/repository') $SOURCE => sub/dir/file.x ${SOURCE.rsrcpath} => /usr/repository/src/file.x ${SOURCE.rsrcdir} => /usr/repository/src .EE Note that curly braces braces may also be used to enclose arbitrary Python code to be evaluated. (In fact, this is how the above modifiers are substituted, they are simply attributes of the Python objects that represent TARGET, SOURCES, etc.) See the section "Python Code Substitution" below, for more thorough examples of how this can be used. Lastly, a variable name may be a callable Python function associated with a construction variable in the environment. The function should take four arguments: .I target - a list of target nodes, .I source - a list of source nodes, .I env - the construction environment, .I for_signature - a Boolean value that specifies whether the function is being called for generating a build signature. SCons will insert whatever the called function returns into the expanded string: .ES def foo(target, source, env, for_signature): return "bar" # Will expand $BAR to "bar baz" env=Environment(FOO=foo, BAR="$FOO baz") .EE You can use this feature to pass arguments to a Python function by creating a callable class that stores one or more arguments in an object, and then uses them when the .B __call__() method is called. Note that in this case, the entire variable expansion must be enclosed by curly braces so that the arguments will be associated with the instantiation of the class: .ES class foo(object): def __init__(self, arg): self.arg = arg def __call__(self, target, source, env, for_signature): return self.arg + " bar" # Will expand $BAR to "my argument bar baz" env=Environment(FOO=foo, BAR="${FOO('my argument')} baz") .EE .LP The special pseudo-variables .B "$(" and .B "$)" may be used to surround parts of a command line that may change .I without causing a rebuild--that is, which are not included in the signature of target files built with this command. All text between .B "$(" and .B "$)" will be removed from the command line before it is added to file signatures, and the .B "$(" and .B "$)" will be removed before the command is executed. For example, the command line: .ES echo Last build occurred $( $TODAY $). > $TARGET .EE .LP would execute the command: .ES echo Last build occurred $TODAY. > $TARGET .EE .LP but the command signature added to any target files would be: .ES echo Last build occurred . > $TARGET .EE .SS Python Code Substitution Any python code within .BR "${" - "}" pairs gets evaluated by python 'eval', with the python globals set to the current environment's set of construction variables. So in the following case: .ES env['COND'] = 0 env.Command('foo.out', 'foo.in', '''echo ${COND==1 and 'FOO' or 'BAR'} > $TARGET''') .EE the command executed will be either .ES echo FOO > foo.out .EE or .ES echo BAR > foo.out .EE according to the current value of env['COND'] when the command is executed. The evaluation occurs when the target is being built, not when the SConscript is being read. So if env['COND'] is changed later in the SConscript, the final value will be used. Here's a more interesting example. Note that all of COND, FOO, and BAR are environment variables, and their values are substituted into the final command. FOO is a list, so its elements are interpolated separated by spaces. .ES env=Environment() env['COND'] = 0 env['FOO'] = ['foo1', 'foo2'] env['BAR'] = 'barbar' env.Command('foo.out', 'foo.in', 'echo ${COND==1 and FOO or BAR} > $TARGET') # Will execute this: # echo foo1 foo2 > foo.out .EE SCons uses the following rules when converting construction variables into command lines: .IP String When the value is a string it is interpreted as a space delimited list of command line arguments. .IP List When the value is a list it is interpreted as a list of command line arguments. Each element of the list is converted to a string. .IP Other Anything that is not a list or string is converted to a string and interpreted as a single command line argument. .IP Newline Newline characters (\\n) delimit lines. The newline parsing is done after all other parsing, so it is not possible for arguments (e.g. file names) to contain embedded newline characters. This limitation will likely go away in a future version of SCons. .SS Scanner Objects You can use the .B Scanner function to define objects to scan new file types for implicit dependencies. The .B Scanner function accepts the following arguments: .IP function This can be either: 1) a Python function that will process the Node (file) and return a list of File Nodes representing the implicit dependencies (file names) found in the contents; or: 2) a dictionary that maps keys (typically the file suffix, but see below for more discussion) to other Scanners that should be called. If the argument is actually a Python function, the function must take three or four arguments: def scanner_function(node, env, path): def scanner_function(node, env, path, arg=None): The .B node argument is the internal SCons node representing the file. Use .B str(node) to fetch the name of the file, and .B node.get_contents() to fetch contents of the file. Note that the file is .I not guaranteed to exist before the scanner is called, so the scanner function should check that if there's any chance that the scanned file might not exist (for example, if it's built from other files). The .B env argument is the construction environment for the scan. Fetch values from it using the .B env.Dictionary() method. The .B path argument is a tuple (or list) of directories that can be searched for files. This will usually be the tuple returned by the .B path_function argument (see below). The .B arg argument is the argument supplied when the scanner was created, if any. .IP name The name of the Scanner. This is mainly used to identify the Scanner internally. .IP argument An optional argument that, if specified, will be passed to the scanner function (described above) and the path function (specified below). .IP skeys An optional list that can be used to determine which scanner should be used for a given Node. In the usual case of scanning for file names, this argument will be a list of suffixes for the different file types that this Scanner knows how to scan. If the argument is a string, then it will be expanded into a list by the current environment. .IP path_function A Python function that takes four or five arguments: a construction environment, a Node for the directory containing the SConscript file in which the first target was defined, a list of target nodes, a list of source nodes, and an optional argument supplied when the scanner was created. The .B path_function returns a tuple of directories that can be searched for files to be returned by this Scanner object. (Note that the .BR FindPathDirs () function can be used to return a ready-made .B path_function for a given construction variable name, instead of having to write your own function from scratch.) .IP node_class The class of Node that should be returned by this Scanner object. Any strings or other objects returned by the scanner function that are not of this class will be run through the .B node_factory function. .IP node_factory A Python function that will take a string or other object and turn it into the appropriate class of Node to be returned by this Scanner object. .IP scan_check An optional Python function that takes two arguments, a Node (file) and a construction environment, and returns whether the Node should, in fact, be scanned for dependencies. This check can be used to eliminate unnecessary calls to the scanner function when, for example, the underlying file represented by a Node does not yet exist. .IP recursive An optional flag that specifies whether this scanner should be re-invoked on the dependency files returned by the scanner. When this flag is not set, the Node subsystem will only invoke the scanner on the file being scanned, and not (for example) also on the files specified by the #include lines in the file being scanned. .I recursive may be a callable function, in which case it will be called with a list of Nodes found and should return a list of Nodes that should be scanned recursively; this can be used to select a specific subset of Nodes for additional scanning. .RE Note that .B scons has a global .B SourceFileScanner object that is used by the .BR Object (), .BR SharedObject (), and .BR StaticObject () builders to decide which scanner should be used for different file extensions. You can using the .BR SourceFileScanner.add_scanner () method to add your own Scanner object to the .B scons infrastructure that builds target programs or libraries from a list of source files of different types: .ES def xyz_scan(node, env, path): contents = node.get_text_contents() # Scan the contents and return the included files. XYZScanner = Scanner(xyz_scan) SourceFileScanner.add_scanner('.xyz', XYZScanner) env.Program('my_prog', ['file1.c', 'file2.f', 'file3.xyz']) .EE .SH SYSTEM-SPECIFIC BEHAVIOR SCons and its configuration files are very portable, due largely to its implementation in Python. There are, however, a few portability issues waiting to trap the unwary. .SS .C file suffix SCons handles the upper-case .B .C file suffix differently, depending on the capabilities of the underlying system. On a case-sensitive system such as Linux or UNIX, SCons treats a file with a .B .C suffix as a C++ source file. On a case-insensitive system such as Windows, SCons treats a file with a .B .C suffix as a C source file. .SS .F file suffix SCons handles the upper-case .B .F file suffix differently, depending on the capabilities of the underlying system. On a case-sensitive system such as Linux or UNIX, SCons treats a file with a .B .F suffix as a Fortran source file that is to be first run through the standard C preprocessor. On a case-insensitive system such as Windows, SCons treats a file with a .B .F suffix as a Fortran source file that should .I not be run through the C preprocessor. .SS Windows: Cygwin Tools and Cygwin Python vs. Windows Pythons Cygwin supplies a set of tools and utilities that let users work on a Windows system using a more POSIX-like environment. The Cygwin tools, including Cygwin Python, do this, in part, by sharing an ability to interpret UNIX-like path names. For example, the Cygwin tools will internally translate a Cygwin path name like /cygdrive/c/mydir to an equivalent Windows pathname of C:/mydir (equivalent to C:\\mydir). Versions of Python that are built for native Windows execution, such as the python.org and ActiveState versions, do not have the Cygwin path name semantics. This means that using a native Windows version of Python to build compiled programs using Cygwin tools (such as gcc, bison, and flex) may yield unpredictable results. "Mixing and matching" in this way can be made to work, but it requires careful attention to the use of path names in your SConscript files. In practice, users can sidestep the issue by adopting the following rules: When using gcc, use the Cygwin-supplied Python interpreter to run SCons; when using Microsoft Visual C/C++ (or some other Windows compiler) use the python.org or ActiveState version of Python to run SCons. .SS Windows: scons.bat file On Windows systems, SCons is executed via a wrapper .B scons.bat file. This has (at least) two ramifications: First, Windows command-line users that want to use variable assignment on the command line may have to put double quotes around the assignments: .ES scons "FOO=BAR" "BAZ=BLEH" .EE Second, the Cygwin shell does not recognize this file as being the same as an .B scons command issued at the command-line prompt. You can work around this either by executing .B scons.bat from the Cygwin command line, or by creating a wrapper shell script named .B scons . .SS MinGW The MinGW bin directory must be in your PATH environment variable or the PATH variable under the ENV construction variable for SCons to detect and use the MinGW tools. When running under the native Windows Python interpreter, SCons will prefer the MinGW tools over the Cygwin tools, if they are both installed, regardless of the order of the bin directories in the PATH variable. If you have both MSVC and MinGW installed and you want to use MinGW instead of MSVC, then you must explictly tell SCons to use MinGW by passing .ES tools=['mingw'] .EE to the Environment() function, because SCons will prefer the MSVC tools over the MinGW tools. .SH EXAMPLES To help you get started using SCons, this section contains a brief overview of some common tasks. .SS Basic Compilation From a Single Source File .ES env = Environment() env.Program(target = 'foo', source = 'foo.c') .EE Note: Build the file by specifying the target as an argument ("scons foo" or "scons foo.exe"). or by specifying a dot ("scons ."). .SS Basic Compilation From Multiple Source Files .ES env = Environment() env.Program(target = 'foo', source = Split('f1.c f2.c f3.c')) .EE .SS Setting a Compilation Flag .ES env = Environment(CCFLAGS = '-g') env.Program(target = 'foo', source = 'foo.c') .EE .SS Search The Local Directory For .h Files Note: You do .I not need to set CCFLAGS to specify -I options by hand. SCons will construct the right -I options from CPPPATH. .ES env = Environment(CPPPATH = ['.']) env.Program(target = 'foo', source = 'foo.c') .EE .SS Search Multiple Directories For .h Files .ES env = Environment(CPPPATH = ['include1', 'include2']) env.Program(target = 'foo', source = 'foo.c') .EE .SS Building a Static Library .ES env = Environment() env.StaticLibrary(target = 'foo', source = Split('l1.c l2.c')) env.StaticLibrary(target = 'bar', source = ['l3.c', 'l4.c']) .EE .SS Building a Shared Library .ES env = Environment() env.SharedLibrary(target = 'foo', source = ['l5.c', 'l6.c']) env.SharedLibrary(target = 'bar', source = Split('l7.c l8.c')) .EE .SS Linking a Local Library Into a Program .ES env = Environment(LIBS = 'mylib', LIBPATH = ['.']) env.Library(target = 'mylib', source = Split('l1.c l2.c')) env.Program(target = 'prog', source = ['p1.c', 'p2.c']) .EE .SS Defining Your Own Builder Object Notice that when you invoke the Builder, you can leave off the target file suffix, and SCons will add it automatically. .ES bld = Builder(action = 'pdftex < $SOURCES > $TARGET' suffix = '.pdf', src_suffix = '.tex') env = Environment(BUILDERS = {'PDFBuilder' : bld}) env.PDFBuilder(target = 'foo.pdf', source = 'foo.tex') # The following creates "bar.pdf" from "bar.tex" env.PDFBuilder(target = 'bar', source = 'bar') .EE Note also that the above initialization overwrites the default Builder objects, so the Environment created above can not be used call Builders like env.Program(), env.Object(), env.StaticLibrary(), etc. .SS Adding Your Own Builder Object to an Environment .ES bld = Builder(action = 'pdftex < $SOURCES > $TARGET' suffix = '.pdf', src_suffix = '.tex') env = Environment() env.Append(BUILDERS = {'PDFBuilder' : bld}) env.PDFBuilder(target = 'foo.pdf', source = 'foo.tex') env.Program(target = 'bar', source = 'bar.c') .EE You also can use other Pythonic techniques to add to the BUILDERS construction variable, such as: .ES env = Environment() env['BUILDERS]['PDFBuilder'] = bld .EE .SS Defining Your Own Scanner Object The following example shows an extremely simple scanner (the .BR kfile_scan () function) that doesn't use a search path at all and simply returns the file names present on any .B include lines in the scanned file. This would implicitly assume that all included files live in the top-level directory: .ES import re '\" Note: the \\ in the following are for the benefit of nroff/troff, '\" not inappropriate doubled escape characters within the r'' raw string. include_re = re.compile(r'^include\\s+(\\S+)$', re.M) def kfile_scan(node, env, path, arg): contents = node.get_text_contents() includes = include_re.findall(contents) return env.File(includes) kscan = Scanner(name = 'kfile', function = kfile_scan, argument = None, skeys = ['.k']) scanners = Environment().Dictionary('SCANNERS') env = Environment(SCANNERS = scanners + [kscan]) env.Command('foo', 'foo.k', 'kprocess < $SOURCES > $TARGET') bar_in = File('bar.in') env.Command('bar', bar_in, 'kprocess $SOURCES > $TARGET') bar_in.target_scanner = kscan .EE It is important to note that you have to return a list of File nodes from the scan function, simple strings for the file names won't do. As in the examples we are showing here, you can use the .BR File() function of your current Environment in order to create nodes on the fly from a sequence of file names with relative paths. Here is a similar but more complete example that searches a path of directories (specified as the .B MYPATH construction variable) for files that actually exist: .ES import re import os include_re = re.compile(r'^include\\s+(\\S+)$', re.M) def my_scan(node, env, path, arg): contents = node.get_text_contents() includes = include_re.findall(contents) if includes == []: return [] results = [] for inc in includes: for dir in path: file = str(dir) + os.sep + inc if os.path.exists(file): results.append(file) break return env.File(results) scanner = Scanner(name = 'myscanner', function = my_scan, argument = None, skeys = ['.x'], path_function = FindPathDirs('MYPATH') ) scanners = Environment().Dictionary('SCANNERS') env = Environment(SCANNERS = scanners + [scanner], MYPATH = ['incs']) env.Command('foo', 'foo.x', 'xprocess < $SOURCES > $TARGET') .EE The .BR FindPathDirs () function used in the previous example returns a function (actually a callable Python object) that will return a list of directories specified in the .B $MYPATH construction variable. It lets SCons detect the file .B incs/foo.inc , even if .B foo.x contains the line .B include foo.inc only. If you need to customize how the search path is derived, you would provide your own .B path_function argument when creating the Scanner object, as follows: .ES # MYPATH is a list of directories to search for files in def pf(env, dir, target, source, arg): top_dir = Dir('#').abspath results = [] if 'MYPATH' in env: for p in env['MYPATH']: results.append(top_dir + os.sep + p) return results scanner = Scanner(name = 'myscanner', function = my_scan, argument = None, skeys = ['.x'], path_function = pf ) .EE .SS Creating a Hierarchical Build Notice that the file names specified in a subdirectory's SConscript file are relative to that subdirectory. .ES SConstruct: env = Environment() env.Program(target = 'foo', source = 'foo.c') SConscript('sub/SConscript') sub/SConscript: env = Environment() # Builds sub/foo from sub/foo.c env.Program(target = 'foo', source = 'foo.c') SConscript('dir/SConscript') sub/dir/SConscript: env = Environment() # Builds sub/dir/foo from sub/dir/foo.c env.Program(target = 'foo', source = 'foo.c') .EE .SS Sharing Variables Between SConscript Files You must explicitly Export() and Import() variables that you want to share between SConscript files. .ES SConstruct: env = Environment() env.Program(target = 'foo', source = 'foo.c') Export("env") SConscript('subdirectory/SConscript') subdirectory/SConscript: Import("env") env.Program(target = 'foo', source = 'foo.c') .EE .SS Building Multiple Variants From the Same Source Use the variant_dir keyword argument to the SConscript function to establish one or more separate variant build directory trees for a given source directory: .ES SConstruct: cppdefines = ['FOO'] Export("cppdefines") SConscript('src/SConscript', variant_dir='foo') cppdefines = ['BAR'] Export("cppdefines") SConscript('src/SConscript', variant_dir='bar') src/SConscript: Import("cppdefines") env = Environment(CPPDEFINES = cppdefines) env.Program(target = 'src', source = 'src.c') .EE Note the use of the Export() method to set the "cppdefines" variable to a different value each time we call the SConscript function. .SS Hierarchical Build of Two Libraries Linked With a Program .ES SConstruct: env = Environment(LIBPATH = ['#libA', '#libB']) Export('env') SConscript('libA/SConscript') SConscript('libB/SConscript') SConscript('Main/SConscript') libA/SConscript: Import('env') env.Library('a', Split('a1.c a2.c a3.c')) libB/SConscript: Import('env') env.Library('b', Split('b1.c b2.c b3.c')) Main/SConscript: Import('env') e = env.Copy(LIBS = ['a', 'b']) e.Program('foo', Split('m1.c m2.c m3.c')) .EE The '#' in the LIBPATH directories specify that they're relative to the top-level directory, so they don't turn into "Main/libA" when they're used in Main/SConscript. Specifying only 'a' and 'b' for the library names allows SCons to append the appropriate library prefix and suffix for the current platform (for example, 'liba.a' on POSIX systems, \&'a.lib' on Windows). .SS Customizing construction variables from the command line. The following would allow the C compiler to be specified on the command line or in the file custom.py. .ES vars = Variables('custom.py') vars.Add('CC', 'The C compiler.') env = Environment(variables=vars) Help(vars.GenerateHelpText(env)) .EE The user could specify the C compiler on the command line: .ES scons "CC=my_cc" .EE or in the custom.py file: .ES CC = 'my_cc' .EE or get documentation on the options: .ES $ scons -h CC: The C compiler. default: None actual: cc .EE .SS Using Microsoft Visual C++ precompiled headers Since windows.h includes everything and the kitchen sink, it can take quite some time to compile it over and over again for a bunch of object files, so Microsoft provides a mechanism to compile a set of headers once and then include the previously compiled headers in any object file. This technology is called precompiled headers. The general recipe is to create a file named "StdAfx.cpp" that includes a single header named "StdAfx.h", and then include every header you want to precompile in "StdAfx.h", and finally include "StdAfx.h" as the first header in all the source files you are compiling to object files. For example: StdAfx.h: .ES #include #include .EE StdAfx.cpp: .ES #include .EE Foo.cpp: .ES #include /* do some stuff */ .EE Bar.cpp: .ES #include /* do some other stuff */ .EE SConstruct: .ES env=Environment() env['PCHSTOP'] = 'StdAfx.h' env['PCH'] = env.PCH('StdAfx.cpp')[0] env.Program('MyApp', ['Foo.cpp', 'Bar.cpp']) .EE For more information see the document for the PCH builder, and the PCH and PCHSTOP construction variables. To learn about the details of precompiled headers consult the MSDN documention for /Yc, /Yu, and /Yp. .SS Using Microsoft Visual C++ external debugging information Since including debugging information in programs and shared libraries can cause their size to increase significantly, Microsoft provides a mechanism for including the debugging information in an external file called a PDB file. SCons supports PDB files through the PDB construction variable. SConstruct: .ES env=Environment() env['PDB'] = 'MyApp.pdb' env.Program('MyApp', ['Foo.cpp', 'Bar.cpp']) .EE For more information see the document for the PDB construction variable. .SH ENVIRONMENT .IP SCONS_LIB_DIR Specifies the directory that contains the SCons Python module directory (e.g. /home/aroach/scons-src-0.01/src/engine). .IP SCONSFLAGS A string of options that will be used by scons in addition to those passed on the command line. .SH "SEE ALSO" .B scons User Manual, .B scons Design Document, .B scons source code. .SH AUTHORS Steven Knight .br Anthony Roach scons-doc-2.3.0/doc/man/scons-time.10000644000175000017500000006031712114661557017714 0ustar dktrkranzdktrkranz.\" Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation .\" .\" 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. .\" .\" doc/man/scons-time.1 2013/03/03 09:48:35 garyo .\" .\" ES - Example Start - indents and turns off line fill .de ES .RS .nf .. .\" EE - Example End - ends indent and turns line fill back on .de EE .RE .fi .. '\"========================================================================== .de SF .B scons-time func [\fB-h\fR] [\fB--chdir=\fIDIR\fR] [\fB-f \fIFILE\fR] [\fB--fmt=\fIFORMAT\fR] [\fB--func=\fINAME\fR] [\fB-p \fISTRING\fR] [\fB-t \fINUMBER\fR] [\fB--title= TITLE\fR] [\fIARGUMENTS\fR] .. '\"-------------------------------------------------------------------------- .de SY .B scons-time mem [\fB-h\fR] [\fB--chdir=\fIDIR\fR] [\fB-f \fIFILE\fR] [\fB--fmt=\fIFORMAT\fR] [\fB-p \fISTRING\fR] [\fB--stage=\fISTAGE\fR] [\fB-t \fINUMBER\fR] [\fB--title=\fITITLE\fR] [\fIARGUMENTS\fR] .. '\"-------------------------------------------------------------------------- .de SO .B scons-time obj [\fB-h\fR] [\fB--chdir=\fIDIR\fR] [\fB-f \fIFILE\fR] [\fB--fmt=\fIFORMAT\fR] [\fB-p \fISTRING\fR] [\fB--stage=\fISTAGE\fR] [\fB-t \fINUMBER\fR] [\fB--title=\fITITLE\fR] [\fIARGUMENTS\fR] .. '\"-------------------------------------------------------------------------- .de SR .B scons-time run [\fB-hnqv\fR] [\fB--aegis=\fIPROJECT\fR] [\fB-f \fIFILE\fR] [\fB--number=\fINUMBER\fR] [\fB--outdir=\fIOUTDIR\fR] [\fB-p \fISTRING\fR] [\fB--python=\fIPYTHON\fR] [\fB-s \fIDIR\fR] [\fB--scons=\fISCONS\fR] [\fB--svn=\fIURL\fR] [\fIARGUMENTS\fR] .. '\"-------------------------------------------------------------------------- .de ST .B scons-time time [\fB-h\fR] [\fB--chdir=\fIDIR\fR] [\fB-f \fIFILE\fR] [\fB--fmt=\fIFORMAT\fR] [\fB-p \fISTRING\fR] [\fB-t \fINUMBER\fR] [\fB--title=\fITITLE\fR] [\fB--which=\fIWHICH\fR] [\fIARGUMENTS\fR] .. .TH SCONS-TIME 1 "March 2013" .SH NAME scons-time \- generate and display SCons timing information '\"========================================================================== .SH SYNOPSIS .B scons-time .IR subcommand [ .IR options ... ] [ .IR arguments ... ] '\"-------------------------------------------------------------------------- .SS "Generating Timing Information" .SR '\"-------------------------------------------------------------------------- .SS "Extracting Function Timings" .SF '\"-------------------------------------------------------------------------- .SS "Extracting Memory Statistics" .SY '\"-------------------------------------------------------------------------- .SS "Extracting Object Counts" .SO '\"-------------------------------------------------------------------------- .SS "Extracting Execution Times" .ST '\"-------------------------------------------------------------------------- .SS "Help Text" .B scons-time help .I SUBCOMMAND [...] '\"========================================================================== .SH DESCRIPTION The .B scons-time command runs an SCons configuration through a standard set of profiled timings and can extract and graph information from the resulting profiles and log files of those timings. The action to be performed by the .B scons-time script is specified by a subcommand, the first argument on the command line. See the .B SUBCOMMANDS section below for information about the operation of specific subcommands. .P The basic way to use .B scons-time is to run the .B scons-time run subcommand (possibly multiple times) to generate profile and log file output, and then use one of the other subcommands to display the results captured in the profiles and log files for a particular kind of information: function timings (the .B scons-time func subcommand), total memory used (the .B scons-time mem subcommand), object counts (the .B scons-time obj subcommand) and overall execution time (the .B scons-time time subcommand). Options exist to place and find the profiles and log files in separate directories, to generate the output in a format suitable for graphing with the .BR gnuplot (1) program, and so on. .P There are two basic ways the .B scons-time run subcommand is intended to be used to gather timing statistics for a configuration. One is to use the .B --svn= option to test a configuration against a list of revisions from the SCons Subversion repository. This will generate a profile and timing log file for every revision listed with the .B --number= option, and can be used to look at the impact of commited changes to the SCons code base on a particular configuration over time. .P The other way is to profile incremental changes to a local SCons code base during a development cycle--that is, to look at the performance impact of changes you're making in the local tree. In this mode, you run the .B scons-time run subcommand .I without the .B --svn= option, in which case it simply looks in the profile/log file output directory (the current directory by default) and automatically figures out the .I next run number for the output profile and log file. Used in this way, the development cycle goes something like: make a change to SCons; run .B scons-time run to profile it against a specific configuration; make another change to SCons; run .B scons-time run again to profile it; etc. '\"========================================================================== .SH OPTIONS The .B scons-time command only supports a few global options: .TP -h, --help Displays the global help text and exits, identical to the .B scons-time help subcommand. .TP -V, --version Displays the .B scons-time version and exits. .P Most functionality is controlled by options to the individual subcommands. See the next section for information about individual subcommand options. '\"========================================================================== .SH SUBCOMMANDS The .B scons-time command supports the following individual subcommands. '\"-------------------------------------------------------------------------- .SS "The func Subcommand" .SF .P The .B scons-time func subcommand displays timing information for a specific Python function within SCons. By default, it extracts information about the .BR _main () function, which includes the Python profiler timing for all of SCons. .P The .B scons-time func subcommand extracts function timing information from all the specified file arguments, which should be Python profiler output files. (Normally, these would be .B *.prof files generated by the .B scons-time run subcommand, but they can actually be generated by any Python profiler invocation.) All file name arguments will be globbed for on-disk files. .P If no arguments are specified, then function timing information will be extracted from all .B *.prof files, or the subset of them with a prefix specified by the .B -p option. .P Options include: .TP -C DIRECTORY, --chdir=DIRECTORY Changes to the specified .I DIRECTORY before looking for the specified files (or files that match the specified patterns). .TP -f FILE, --file=FILE Reads configuration information from the specified .IR FILE . .TP -fmt=FORMAT, --format=FORMAT Reports the output in the specified .IR FORMAT . The formats currently supported are .B ascii (the default) and .BR gnuplot . .TP --func=NAME Extracts timings for the specified function .IR NAME . The default is to report cumulative timings for the .BR _main () function, which contains the entire SCons run. .TP -h, --help Displays help text for the .B scons-time func subcommand. .TP -p STRING, --prefix=STRING Specifies the prefix string for profiles from which to extract function timing information. This will be used to search for profiles if no arguments are specified on the command line. .TP -t NUMBER, --tail=NUMBER Only extracts function timings from the last .I NUMBER files. '\"-------------------------------------------------------------------------- .SS "The help Subcommand" .B scons-time help .I SUBCOMMAND [...] The .B help subcommand prints help text for any other subcommands listed as later arguments on the command line. '\"-------------------------------------------------------------------------- .SS "The mem Subcommand" .SY .P The .B scons-time mem subcommand displays how much memory SCons uses. .P The .B scons-time mem subcommand extracts memory use information from all the specified file arguments, which should be files containing output from running SCons with the .B --debug=memory option. (Normally, these would be .B *.log files generated by the .B scons-time run subcommand.) All file name arguments will be globbed for on-disk files. .P If no arguments are specified, then memory information will be extracted from all .B *.log files, or the subset of them with a prefix specified by the .B -p option. .P .TP -C DIR, --chdir=DIR Changes to the specified .I DIRECTORY before looking for the specified files (or files that match the specified patterns). .TP -f FILE, --file=FILE Reads configuration information from the specified .IR FILE . .TP -fmt=FORMAT, --format=FORMAT Reports the output in the specified .IR FORMAT . The formats currently supported are .B ascii (the default) and .BR gnuplot . .TP -h, --help Displays help text for the .B scons-time mem subcommand. .TP -p STRING, --prefix=STRING Specifies the prefix string for log files from which to extract memory usage information. This will be used to search for log files if no arguments are specified on the command line. .TP --stage=STAGE Prints the memory used at the end of the specified .IR STAGE : .B pre-read (before the SConscript files are read), .B post-read , (after the SConscript files are read), .B pre-build (before any targets are built) or .B post-build (after any targets are built). If no .B --stage option is specified, the default behavior is .BR post-build , which reports the final amount of memory used by SCons during each run. .TP -t NUMBER, --tail=NUMBER Only reports memory statistics from the last .I NUMBER files. '\"-------------------------------------------------------------------------- .SS "The obj Subcommand" .SO .P The .B scons-time obj subcommand displays how many objects of a specific named type are created by SCons. .P The .B scons-time obj subcommand extracts object counts from all the specified file arguments, which should be files containing output from running SCons with the .B --debug=count option. (Normally, these would be .B *.log files generated by the .B scons-time run subcommand.) All file name arguments will be globbed for on-disk files. .P If no arguments are specified, then object counts will be extracted from all .B *.log files, or the subset of them with a prefix specified by the .B -p option. .TP -C DIR, --chdir=DIR Changes to the specified .I DIRECTORY before looking for the specified files (or files that match the specified patterns). .TP -f FILE, --file=FILE Reads configuration information from the specified .IR FILE . .TP -fmt=FORMAT, --format=FORMAT Reports the output in the specified .IR FORMAT . The formats currently supported are .B ascii (the default) and .BR gnuplot . .TP -h, --help Displays help text for the .B scons-time obj subcommand. .TP -p STRING, --prefix=STRING Specifies the prefix string for log files from which to extract object counts. This will be used to search for log files if no arguments are specified on the command line. .TP --stage=STAGE Prints the object count at the end of the specified .IR STAGE : .B pre-read (before the SConscript files are read), .B post-read , (after the SConscript files are read), .B pre-build (before any targets are built) or .B post-build (after any targets are built). If no .B --stage option is specified, the default behavior is .BR post-build , which reports the final object count during each run. .TP -t NUMBER, --tail=NUMBER Only reports object counts from the last .I NUMBER files. '\"-------------------------------------------------------------------------- .SS "The run Subcommand" .SR The .B scons-time run subcommand is the basic subcommand for profiling a specific configuration against a version of SCons. .P The configuration to be tested is specified as a list of files or directories that will be unpacked or copied into a temporary directory in which SCons will be invoked. The .B scons-time run subcommand understands file suffixes like .BR .tar , .BR .tar.gz , .BR .tgz and .BR .zip and will unpack their contents into a temporary directory. If more than one argument is specified, each one will be unpacked or copied into the temporary directory "on top of" the previous archives or directories, so the expectation is that multiple specified archives share the same directory layout. .P Once the file or directory arguments are unpacked or copied to the temporary directory, the .B scons-time run subcommand runs the requested version of SCons against the configuration three times: .TP Startup SCons is run with the .B --help option so that just the SConscript files are read, and then the default help text is printed. This profiles just the perceived "overhead" of starting up SCons and processing the SConscript files. .TP Full build SCons is run to build everything specified in the configuration. Specific targets to be passed in on the command l ine may be specified by the .B targets keyword in a configuration file; see below for details. .TP Rebuild SCons is run again on the same just-built directory. If the dependencies in the SCons configuration are correct, this should be an up-to-date, "do nothing" rebuild. .P Each invocation captures the output log file and a profile. .P The .B scons-time run subcommand supports the following options: .TP --aegis=PROJECT Specifies the Aegis .I PROJECT from which the version(s) of .B scons being timed will be extracted. When .B --aegis is specified, the .BI --number= NUMBER option specifies delta numbers that will be tested. Output from each invocation run will be placed in file names that match the Aegis delta numbers. If the .B --number= option is not specified, then the default behavior is to time the tip of the specified .IR PROJECT . .TP -f FILE, --file=FILE Reads configuration information from the specified .IR FILE . This often provides a more convenient way to specify and collect parameters associated with a specific timing configuration than specifying them on the command line. See the .B CONFIGURATION FILE section below for information about the configuration file parameters. .TP -h, --help Displays help text for the .B scons-time run subcommand. .TP -n, --no-exec Do not execute commands, just printing the command-line equivalents of what would be executed. Note that the .B scons-time script actually executes its actions in Python, where possible, for portability. The commands displayed are UNIX .I equivalents of what it's doing. .TP --number=NUMBER Specifies the run number to be used in the names of the log files and profile outputs generated by this run. .IP When used in conjuction with the .BI --aegis= PROJECT option, .I NUMBER specifies one or more comma-separated Aegis delta numbers that will be retrieved automatically from the specified Aegis .IR PROJECT . .IP When used in conjuction with the .BI --svn= URL option, .I NUMBER specifies one or more comma-separated Subversion revision numbers that will be retrieved automatically from the Subversion repository at the specified .IR URL . Ranges of delta or revision numbers may be specified be separating two numbers with a hyphen .RB ( \- ). .P Example: .ES % scons-time run --svn=http://scons.tigris.org/svn/trunk --num=1247,1249-1252 . .EE .TP -p STRING, --prefix=STRING Specifies the prefix string to be used for all of the log files and profiles generated by this run. The default is derived from the first specified argument: if the first argument is a directory, the default prefix is the name of the directory; if the first argument is an archive (tar or zip file), the default prefix is the the base name of the archive, that is, what remains after stripping the archive suffix .RB ( .tgz ", " .tar.gz " or " .zip ). .TP --python=PYTHON Specifies a path to the Python executable to be used for the timing runs. The default is to use the same Python executable that is running the .B scons-time command itself. .TP -q, --quiet Suppresses display of the command lines being executed. .TP -s DIR, --subdir=DIR Specifies the name of directory or subdirectory from which the commands should be executed. The default is XXX .TP --scons=SCONS Specifies a path to the SCons script to be used for the timing runs. The default is XXX .TP --svn=URL, --subversion=URL Specifies the .I URL of the Subversion repository from which the version(s) of .B scons being timed will be extracted. When .B --svn is specified, the .BI --number= NUMBER option specifies revision numbers that will be tested. Output from each invocation run will be placed in file names that match the Subversion revision numbers. If the .B --number= option is not specified, then the default behavior is to time the .B HEAD of the specified .IR URL . .TP -v, --verbose Displays the output from individual commands to the screen (in addition to capturing the output in log files). '\"-------------------------------------------------------------------------- .SS "The time Subcommand" .ST .P The .B scons-time time subcommand displays SCons execution times as reported by the .B scons --debug=time option. .P The .B scons-time time subcommand extracts SCons timing from all the specified file arguments, which should be files containing output from running SCons with the .B --debug=time option. (Normally, these would be .B *.log files generated by the .B scons-time run subcommand.) All file name arguments will be globbed for on-disk files. .P If no arguments are specified, then execution timings will be extracted from all .B *.log files, or the subset of them with a prefix specified by the .B -p option. .TP -C DIR, --chdir=DIR Changes to the specified .I DIRECTORY before looking for the specified files (or files that match the specified patterns). .TP -f FILE, --file=FILE Reads configuration information from the specified .IR FILE . .TP -fmt=FORMAT, --format=FORMAT Reports the output in the specified .IR FORMAT . The formats currently supported are .B ascii (the default) and .BR gnuplot . .TP -h, --help Displays help text for the .B scons-time time subcommand. .TP -p STRING, --prefix=STRING Specifies the prefix string for log files from which to extract execution timings. This will be used to search for log files if no arguments are specified on the command line. .TP -t NUMBER, --tail=NUMBER Only reports object counts from the last .I NUMBER files. .TP --which=WHICH Prints the execution time for the specified .IR WHICH value: .B total (the total execution time), .B SConscripts (total execution time for the SConscript files themselves), .B SCons (exectuion time in SCons code itself) or .B commands (execution time of the commands and other actions used to build targets). If no .B --which option is specified, the default behavior is .BR total , which reports the total execution time for each run. '\"========================================================================== .SH CONFIGURATION FILE Various .B scons-time subcommands can read information from a specified configuration file when passed the .B \-f or .B \--file options. The configuration file is actually executed as a Python script. Setting Python variables in the configuration file controls the behavior of the .B scons-time script more conveniently than having to specify command-line options or arguments for every run, and provides a handy way to "shrink-wrap" the necessary information for producing (and reporting) consistent timing runs for a given configuration. .TP .B aegis The Aegis executable for extracting deltas. The default is simply .BR aegis . .TP .B aegis_project The Aegis project from which deltas should be extracted. The default is whatever is specified with the .B --aegis= command-line option. .TP .B archive_list A list of archives (files or directories) that will be copied to the temporary directory in which SCons will be invoked. .BR .tar , .BR .tar.gz , .BR .tgz and .BR .zip files will have their contents unpacked in the temporary directory. Directory trees and files will be copied as-is. .TP .B initial_commands A list of commands that will be executed before the actual timed .B scons runs. This can be used for commands that are necessary to prepare the source tree\-for example, creating a configuration file that should not be part of the timed run. .TP .B key_location The location of the key on Gnuplot graphing information generated with the .BR --format=gnuplot option. The default is .BR "bottom left" . .TP .B prefix The file name prefix to be used when running or extracting timing for this configuration. .TP .B python The path name of the Python executable to be used when running or extracting information for this configuration. The default is the same version of Python used to run the SCons .TP .B scons The path name of the SCons script to be used when running or extracting information for this configuration. The default is simply .BR scons . .TP .B scons_flags The .B scons flags used when running SCons to collect timing information. The default value is .BR "--debug=count --debug=memory --debug=time --debug=memoizer" . .TP .B scons_lib_dir .TP .B scons_wrapper .TP .B startup_targets .TP .B subdir The subdirectory of the project into which the .B scons-time script should change before executing the SCons commands to time. .TP .B subversion_url The Subversion URL from .TP .B svn The subversion executable used to check out revisions of SCons to be timed. The default is simple .BR svn . .TP .B svn_co_flag .TP .B tar .TP .B targets A string containing the targets that should be added to the command line of every timed .B scons run. This can be used to restrict what's being timed to a subset of the full build for the configuration. .TP .B targets0 .TP .B targets1 .TP .B targets2 .TP .B title .TP .B unzip .TP .B verbose .TP .B vertical_bars '\"-------------------------------------------------------------------------- .SS Example Here is an example .B scons-time configuration file for a hypothetical sample project: .P .ES # The project doesn't use SCons natively (yet), so we're # timing a separate set of SConscript files that we lay # on top of the vanilla unpacked project tarball. arguments = ['project-1.2.tgz', 'project-SConscripts.tar'] # The subdirectory name contains the project version number, # so tell scons-time to chdir there before building. subdir = 'project-1.2' # Set the prefix so output log files and profiles are named: # project-000-[012].{log,prof} # project-001-[012].{log,prof} # etc. prefix = 'project' # The SConscript files being tested don't do any SConf # configuration, so run their normal ./configure script # before we invoke SCons. initial_commands = [ './configure', ] # Only time building the bin/project executable. targets = 'bin/project' # Time against SCons revisions of the branches/core branch subversion_url = 'http://scons.tigris.org/svn/scons/branches/core' .EE '\"========================================================================== .SH ENVIRONMENT The .B scons-time script uses the following environment variables: .TP .B PRESERVE If this value is set, the .B scons-time script will .I not remove the temporary directory or directories in which it builds the specified configuration or downloads a specific version of SCons. '\"========================================================================== .SH "SEE ALSO" .BR gnuplot (1), .BR scons (1) .SH AUTHORS Steven Knight scons-doc-2.3.0/doc/man/MANIFEST0000644000175000017500000000002312114661557016666 0ustar dktrkranzdktrkranzscons.1 sconsign.1 scons-doc-2.3.0/doc/man/sconsign.10000644000175000017500000001116612114661557017454 0ustar dktrkranzdktrkranz.\" Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation .\" .\" 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. .\" .\" doc/man/sconsign.1 2013/03/03 09:48:35 garyo .\" .\" ES - Example Start - indents and turns off line fill .de ES .RS .nf .. .\" EE - Example End - ends indent and turns line fill back on .de EE .RE .fi .. .TH SCONSIGN 1 "March 2013" .SH NAME sconsign \- print SCons .sconsign file information .SH SYNOPSIS .B sconsign [ .IR options ... ] .IR file [ ... ] .SH DESCRIPTION The .B sconsign command displays the contents of one or more .B .sconsign files specified by the user. By default, .B sconsign dumps the entire contents of the specified file(s). Each entry is printed in the following format: file: signature timestamp length implicit_dependency_1: signature timestamp length implicit_dependency_2: signature timestamp length action_signature [action string] .B None is printed in place of any missing timestamp, bsig, or csig values for any entry or any of its dependencies. If the entry has no implicit dependencies, or no build action, the lines are simply omitted. By default, .B sconsign assumes that any .I file arguments that end with a .B .dbm suffix contains signature entries for more than one directory (that is, was specified by the .B SConsignFile () function). Any .I file argument that does not end in .B .dbm is assumed to be a traditional .B .sconsign file containing the signature entries for a single directory. An explicit format may be specified using the .B -f or .B --file= options. .SH OPTIONS Various options control what information is printed and the format: .TP -a, --act, --action Prints the build action information for all entries or the specified entries. .TP -c, --csig Prints the content signature (csig) information for all entries or the specified entries. .TP -d DIRECTORY, --dir=DIRECTORY When the signatures are being read from a .B .dbm file, or the .B -f dbm or .B --format=dbm options are used, prints information about only the signatures for entries in the specified .IR DIRECTORY . .TP -e ENTRY, --entry=ENTRY Prints information about only the specified .IR ENTRY . Multiple -e options may be used, in which case information about each .I ENTRY is printed in the order in which the options are specified on the command line. .TP -f FORMAT, --format=FORMAT The file(s) to be printed are in the specified .IR FORMAT . Legal values are .B dbm (the DBM format used when the .BR SConsignFile () function is used) or .B sconsign (the default format used for an individual .B .sconsign file in each directory). .TP -h, --help Prints a help message and exits. .TP -i, --implicit Prints the list of cached implicit dependencies for all entries or the the specified entries. .TP --raw Prints a pretty-printed representation of the raw Python dictionary that holds build information about individual entry (both the entry itself or its implicit dependencies). An entry's build action is still printed in its usual format. .TP -r, --readable Prints timestamps in a human-readable string, enclosed in single quotes. .TP -t, --timestamp Prints the timestamp information for all entries or the specified entries. .TP -v, --verbose Prints labels identifying each field being printed. .SH ENVIRONMENT .IP SCONS_LIB_DIR Specifies the directory that contains the SCons Python module directory (e.g. /home/aroach/scons-src-0.01/src/engine). on the command line. .SH "SEE ALSO" .BR scons , .B scons User Manual, .B scons Design Document, .B scons source code. .SH AUTHORS Steven Knight scons-doc-2.3.0/doc/MANIFEST0000644000175000017500000000001212114661557016111 0ustar dktrkranzdktrkranzscons.mod scons-doc-2.3.0/doc/design/0000755000175000017500000000000012114661557016240 5ustar dktrkranzdktrkranzscons-doc-2.3.0/doc/design/issues.xml0000644000175000017500000001253612114661557020304 0ustar dktrkranzdktrkranz No build tools is perfect. Here are some &SCons; issues that do not yet have solutions.
Interaction with SC-config The SC-config tool will be used in the &SCons; installation process to generate an appropriate default construction environment so that building most software works "out of the box" on the installed platform. The SC-config tool will find reasonable default compilers (C, C++, Fortran), linkers/loaders, library archive tools, etc. for specification in the default &SCons; construction environment.
Interaction with test infrastructures &SCons; can be configured to use SC-test (or some other test tool) to provide controlled, automated testing of software. The &Link; method could link a test subdirectory to a build subdirectory: Link('test', 'build') SConscript('test/SConscript') Any test cases checked in with the source code will be linked into the test subdirectory and executed. If &SConscript; files and test cases are written with this in mind, then invoking: % sccons test Would run all the automated test cases that depend on any changed software.
Java dependencies Java dependencies are difficult for an external dependency-based construction tool to accomodate. Determining Java class dependencies is more complicated than the simple pattern-matching of C or C++ #include files. From the point of view of an external build tool, the Java compiler behaves "unpredictably" because it may create or update multiple output class files and directories as a result of its internal class dependencies. An obvious &SCons; implementation would be to have the &Scanner; object parse output from Java -depend -verbose to calculate dependencies, but this has the distinct disadvantage of requiring two separate compiler invocations, thereby slowing down builds.
Limitations of digital signature calculation In practice, calculating digital signatures of a file's contents is a more robust mechanism than time stamps for determining what needs building. However: Developers used to the time stamp model of &Make; can initially find digital signatures counter-intuitive. The assumption that: % touch file.c will cause a rebuild of file is strong... Abstracting dependency calculation into a single digital signature loses a little information: It is no longer possible to tell (without laborious additional calculation) which input file dependency caused a rebuild of a given target file. A feature that could report, "I'm rebuilding file X because it's out-of-date with respect to file Y," would be good, but an digital-signature implementation of such a feature is non-obvious.
Remote execution The ability to use multiple build systems through remote execution of tools would be good. This should be implementable through the &Job; class. Construction environments would need modification to specify build systems.
Conditional builds The ability to check run-time conditions as suggested on the sc-discuss mailing list ("build X only if: the machine is idle / the file system has Y megabytes free space") would also be good, but is not part of the current design.
scons-doc-2.3.0/doc/design/engine.xml0000644000175000017500000013230512114661557020233 0ustar dktrkranzdktrkranz
General Principles
Keyword arguments All methods and functions in this API will support the use of keyword arguments in calls, for the sake of explicitness and readability. For brevity in the hands of experts, most methods and functions will also support positional arguments for their most-commonly-used arguments. As an explicit example, the following two lines will each arrange for an executable program named foo (or foo.exe on a Win32 system) to be compiled from the foo.c source file: env.Program(target = 'foo', source = 'foo.c') env.Program('foo', 'foo.c')
Internal object representation All methods and functions use internal (Python) objects that represent the external objects (files, for example) for which they perform dependency analysis. All methods and functions in this API that accept an external object as an argument will accept either a string description or an object reference. For example, the two following two-line examples are equivalent: env.Object(target = 'foo.o', source = 'foo.c') env.Program(target = 'foo', 'foo.o') # builds foo from foo.o foo_obj = env.Object(target = 'foo.o', source = 'foo.c') env.Program(target = 'foo', foo_obj) # builds foo from foo.o
&ConsEnvs; A &consenv; is the basic means by which a software system interacts with the &SCons; Python API to control a build process. A &consenv; is an object with associated methods for generating target files of various types (&Builder; objects), other associated object methods for automatically determining dependencies from the contents of various types of source files (&Scanner; objects), and a dictionary of values used by these methods. Passing no arguments to the &Environment; instantiation creates a &consenv; with default values for the current platform: env = Environment()
&Consvars; A &consenv; has an associated dictionary of &consvars; that control how the build is performed. By default, the &Environment; method creates a &consenv; with values that make most software build "out of the box" on the host system. These default values will be generated at the time &SCons; is installed using functionality similar to that provided by GNU &Autoconf;. It would be nice if we could avoid re-inventing the wheel here by using some other Python-based tool &Autoconf; replacement--like what was supposed to come out of the Software Carpentry configuration tool contest. It will probably be most efficient to roll our own logic initially and convert if something better does come along. At a minimum, there will be pre-configured sets of default values that will provide reasonable defaults for UNIX and Windows NT. The default &consenv; values may be overridden when a new &consenv; is created by specifying keyword arguments: env = Environment(CC = 'gcc', CCFLAGS = '-g', CPPPATH = ['.', 'src', '/usr/include'], LIBPATH = ['/usr/lib', '.'])
Fetching &consvars; A copy of the dictionary of &consvars; can be returned using the &Dictionary; method: env = Environment() dict = env.Dictionary() If any arguments are supplied, then just the corresponding value(s) are returned: ccflags = env.Dictionary('CCFLAGS') cc, ld = env.Dictionary('CC', 'LD')
Copying a &consenv; A method exists to return a copy of an existing environment, with any overridden values specified as keyword arguments to the method: env = Environment() debug = env.Copy(CCFLAGS = '-g')
Multiple &consenvs; Different external objects often require different build characteristics. Multiple &consenvs; may be defined, each with different values: env = Environment(CCFLAGS = '') debug = Environment(CCFLAGS = '-g') env.Make(target = 'hello', source = 'hello.c') debug.Make(target = 'hello-debug', source = 'hello.c') Dictionaries of values from multiple &consenvs; may be passed to the &Environment; instantiation or the &Copy; method, in which case the last-specified dictionary value wins: env1 = Environment(CCFLAGS = '-O', LDFLAGS = '-d') env2 = Environment(CCFLAGS = '-g') new = Environment(env1.Dictionary(), env2.Dictionary()) The new environment in the above example retains LDFLAGS = '-d' from the env1 environment, and CCFLAGS = '-g' from the env2 environment.
Variable substitution Within a construction command, any variable from the &consenv; may be interpolated by prefixing the name of the construction with $: MyBuilder = Builder(command = "$XX $XXFLAGS -c $_INPUTS -o $target") env.Command(targets = 'bar.out', sources = 'bar.in', command = "sed '1d' < $source > $target") Variable substitution is recursive: the command line is expanded until no more substitutions can be made. Variable names following the $ may be enclosed in braces. This can be used to concatenate an interpolated value with an alphanumeric character: VerboseBuilder = Builder(command = "$XX -${XXFLAGS}v > $target") The variable within braces may contain a pair of parentheses after a Python function name to be evaluated (for example, ${map()}). &SCons; will interpolate the return value from the function (presumably a string): env = Environment(FUNC = myfunc) env.Command(target = 'foo.out', source = 'foo.in', command = "${FUNC($<)}") If a referenced variable is not defined in the &consenv;, the null string is interpolated. The following special variables can also be used: $targets All target file names. If multiple targets are specified in an array, $targets expands to the entire list of targets, separated by a single space. Individual targets from a list may be extracted by enclosing the targets keyword in braces and using the appropriate Python array index or slice: ${targets[0]} # expands to the first target ${targets[1:]} # expands to all but the first target ${targets[1:-1]} # expands to all but the first and last targets $target A synonym for ${targets[0]}, the first target specified. $sources All input file names. Any input file names that are used anywhere else on the current command line (via ${sources[0]}, ${sources{[1]}, etc.) are removed from the expanded list. Any of the above special variables may be enclosed in braces and followed immediately by one of the following attributes to select just a portion of the expanded path name: .base Basename: the directory plus the file name, minus any file suffix. .dir The directory in which the file lives. This is a relative path, where appropriate. .file The file name, minus any directory portion. .suffix The file name suffix (that is, the right-most dot in the file name, and all characters to the right of that). .filebase The file name (no directory portion), minus any file suffix. .abspath The absolute path to the file.
&Builder; Objects By default, &SCons; supplies (and uses) a number of pre-defined &Builder; objects: &Object; compile or assemble an object file &Library; archive files into a library &SharedLibrary; archive files into a shared library &Program; link objects and/or libraries into an executable &MakeBuilder; build according to file suffixes; see below A &consenv; can be explicitly initialized with associated &Builder; objects that will be bound to the &consenv; object: env = Environment(BUILDERS = ['Object', 'Program']) &Builder; objects bound to a &consenv; can be called directly as methods. When invoked, a &Builder; object returns a (list of) objects that it will build: obj = env.Object(target ='hello.o', source = 'hello.c') lib = env.Library(target ='libfoo.a', source = ['aaa.c', 'bbb.c']) slib = env.SharedLibrary(target ='libbar.so', source = ['xxx.c', 'yyy.c']) prog = env.Program(target ='hello', source = ['hello.o', 'libfoo.a', 'libbar.so'])
Specifying multiple inputs Multiple input files that go into creating a target file may be passed in as a single string, with the individual file names separated by white space: env.Library(target = 'foo.a', source = 'aaa.c bbb.c ccc.c') env.Object(target = 'yyy.o', source = 'yyy.c') env.Program(target = 'bar', source = 'xxx.c yyy.o foo.a') Alternatively, multiple input files that go into creating a target file may be passed in as an array. This allows input files to be specified using their object representation: env.Library(target = 'foo.a', source = ['aaa.c', 'bbb.c', 'ccc.c']) yyy_obj = env.Object(target = 'yyy.o', source = 'yyy.c') env.Program(target = 'bar', source = ['xxx.c', yyy_obj, 'foo.a']) Individual string elements within an array of input files are not further split into white-space separated file names. This allows file names that contain white space to be specified by putting the value into an array: env.Program(target = 'foo', source = ['an input file.c'])
Specifying multiple targets Conversely, the generated target may be a string listing multiple files separated by white space: env.Object(target = 'grammar.o y.tab.h', source = 'grammar.y') An array of multiple target files can be used to mix string and object representations, or to accomodate file names that contain white space: env.Program(target = ['my program'], source = 'input.c')
File prefixes and suffixes For portability, if the target file name does not already have an appropriate file prefix or suffix, the &Builder; objects will append one appropriate for the file type on the current system: # builds 'hello.o' on UNIX, 'hello.obj' on Windows NT: obj = env.Object(target ='hello', source = 'hello.c') # builds 'libfoo.a' on UNIX, 'foo.lib' on Windows NT: lib = env.Library(target ='foo', source = ['aaa.c', 'bbb.c']) # builds 'libbar.so' on UNIX, 'bar.dll' on Windows NT: slib = env.SharedLibrary(target ='bar', source = ['xxx.c', 'yyy.c']) # builds 'hello' on UNIX, 'hello.exe' on Windows NT: prog = env.Program(target ='hello', source = ['hello.o', 'libfoo.a', 'libbar.so'])
&Builder; object exceptions &Builder; objects raise the following exceptions on error:
User-defined &Builder; objects Users can define additional &Builder; objects for specific external object types unknown to &SCons;. A &Builder; object may build its target by executing an external command: WebPage = Builder(command = 'htmlgen $HTMLGENFLAGS $sources > $target', suffix = '.html', src_suffix = '.in') Alternatively, a &Builder; object may also build its target by executing a Python function: def update(dest): # [code to update the object] return 1 OtherBuilder1 = Builder(function = update, src_suffix = ['.in', '.input']) An optional argument to pass to the function may be specified: def update_arg(dest, arg): # [code to update the object] return 1 OtherBuilder2 = Builder(function = update_arg, function_arg = 'xyzzy', src_suffix = ['.in', '.input']) Both an external command and an internal function may be specified, in which case the function will be called to build the object first, followed by the command line. User-defined &Builder; objects can be used like the default &Builder; objects to initialize &consenvs;. WebPage = Builder(command = 'htmlgen $HTMLGENFLAGS $sources > $target', suffix = '.html', src_suffix = '.in') env = Environment(BUILDERS = ['WebPage']) env.WebPage(target = 'foo.html', source = 'foo.in') # Builds 'bar.html' on UNIX, 'bar.htm' on Windows NT: env.WebPage(target = 'bar', source = 'bar.in') The command-line specification can interpolate variables from the &consenv;; see "Variable substitution," above. A &Builder; object may optionally be initialized with a list of: the prefix of the target file (e.g., 'lib' for libraries) the suffix of the target file (e.g., '.a' for libraries) the expected suffixes of the input files (e.g., '.o' for object files) These arguments are used in automatic dependency analysis and to generate output file names that don't have suffixes supplied explicitly.
Copying &Builder; Objects A &Copy; method exists to return a copy of an existing &Builder; object, with any overridden values specified as keyword arguments to the method: build = Builder(function = my_build) build_out = build.Copy(suffix = '.out') Typically, &Builder; objects will be supplied by a tool-master or administrator through a shared &consenv;.
Special-purpose build rules A pre-defined &Command; builder exists to associate a target file with a specific command or list of commands for building the file: env.Command(target = 'foo.out', source = command = 'foo.in', "foo.process $sources > $target") commands = [ "bar.process -o .tmpfile $sources", "mv .tmpfile $target" ] env.Command(target = 'bar.out', source = 'bar.in', command = commands) This is useful when it's too cumbersome to create a &Builder; object just to build a single file in a special way.
The &MakeBuilder; &Builder; A pre-defined &Builder; object named &MakeBuilder; exists to make simple builds as easy as possible for users, at the expense of sacrificing some build portability. The following minimal example builds the 'hello' program from the 'hello.c' source file: Environment().Make('hello', 'hello.c') Users of the &MakeBuilder; &Builder; object are not required to understand intermediate steps involved in generating a file--for example, the distinction between compiling source code into an object file, and then linking object files into an executable. The details of intermediate steps are handled by the invoked method. Users that need to, however, can specify intermediate steps explicitly: env = Environment() env.Make(target = 'hello.o', source = 'hello.c') env.Make(target = 'hello', source = 'hello.o') The &MakeBuilder; method understands the file suffixes specified and "does the right thing" to generate the target object and program files, respectively. It does this by examining the specified output suffixes for the &Builder; objects bound to the environment. Because file name suffixes in the target and source file names must be specified, the &MakeBuilder; method can't be used portably across operating systems. In other words, for the example above, the &MakeBuilder; builder will not generate hello.exe on Windows NT.
&Builder; maps The env.Make method "does the right thing" to build different file types because it uses a dictionary from the &consenv; that maps file suffixes to the appropriate &Builder; object. This &BUILDERMAP; can be initialized at instantiation: env = Environment(BUILDERMAP = { '.o' : Object, '.a' : Library, '.html' : WebPage, '' : Program, }) With the &BUILDERMAP; properly initialized, the env.Make method can be used to build additional file types: env.Make(target = 'index.html', source = 'index.input') &Builder; objects referenced in the &BUILDERMAP; do not need to be listed separately in the &BUILDERS; variable. The &consenv; will bind the union of the &Builder; objects listed in both variables.
Dependencies
Automatic dependencies By default, &SCons; assumes that a target file has automatic dependencies on the:
tool used to build the target file contents of the input files command line used to build the target file
If any of these changes, the target file will be rebuilt.
Implicit dependencies Additionally, &SCons; can scan the contents of files for implicit dependencies on other files. For example, &SCons; will scan the contents of a .c file and determine that any object created from it is dependent on any .h files specified via #include. &SCons;, therefore, "does the right thing" without needing to have these dependencies listed explicitly: % cat Construct env = Environment() env.Program('hello', 'hello.c') % cat hello.c #include "hello_string.h" main() { printf("%s\n", STRING); } % cat > hello_string.h #define STRING "Hello, world!\n" % scons . gcc -c hello.c -o hello.o gcc -o hello hello.c % ./hello Hello, world! % cat > hello_string.h #define STRING "Hello, world, hello!\n" % scons . gcc -c hello.c -o hello.o gcc -o hello hello.c % ./hello Hello, world, hello! %
Ignoring dependencies Undesirable automatic dependencies or implicit dependencies may be ignored: env.Program(target = 'bar', source = 'bar.c') env.Ignore('bar', '/usr/bin/gcc', 'version.h') In the above example, the bar program will not be rebuilt if the /usr/bin/gcc compiler or the version.h file change.
Explicit dependencies Dependencies that are unknown to &SCons; may be specified explicitly in an &SCons; configuration file: env.Dependency(target = 'output1', dependency = 'input_1 input_2') env.Dependency(target = 'output2', dependency = ['input_1', 'input_2']) env.Dependency(target = 'output3', dependency = ['white space input']) env.Dependency(target = 'output_a output_b', dependency = 'input_3') env.Dependency(target = ['output_c', 'output_d'], dependency = 'input_4') env.Dependency(target = ['white space output'], dependency = 'input_5') Just like the target keyword argument, the dependency keyword argument may be specified as a string of white-space separated file names, or as an array. A dependency on an &SCons; configuration file itself may be specified explicitly to force a rebuild whenever the configuration file changes: env.Dependency(target = 'archive.tar.gz', dependency = 'SConstruct')
&Scanner; Objects Analagous to the previously-described &Builder; objects, &SCons; supplies (and uses) &Scanner; objects to search the contents of a file for implicit dependency files: CScan scan .{c,C,cc,cxx,cpp} files for #include dependencies A &consenv; can be explicitly initialized with associated &Scanner; objects: env = Environment(SCANNERS = ['CScan', 'M4Scan']) &Scanner; objects bound to a &consenv; can be associated directly with specified files: env.CScan('foo.c', 'bar.c') env.M4Scan('input.m4')
User-defined &Scanner; objects A user may define a &Scanner; object to scan a type of file for implicit dependencies: def scanner1(file_contents): # search for dependencies return dependency_list FirstScan = Scanner(function = scanner1) The scanner function must return a list of dependencies that its finds based on analyzing the file contents it is passed as an argument. The scanner function, when invoked, will be passed the calling environment. The scanner function can use &consenvs; from the passed environment to affect how it performs its dependency scan--the canonical example being to use some sort of search-path construction variable to look for dependency files in other directories: def scanner2(file_contents, env): path = env.{'SCANNERPATH'} # XXX # search for dependencies using 'path' return dependency_list SecondScan = Scanner(function = scanner2) The user may specify an additional argument when the &Scanner; object is created. When the scanner is invoked, the additional argument will be passed to the scanner funciton, which can be used in any way the scanner function sees fit: def scanner3(file_contents, env, arg): # skip 'arg' lines, then search for dependencies return dependency_list Skip_3_Lines_Scan = Scanner(function = scanner2, argument = 3) Skip_6_Lines_Scan = Scanner(function = scanner2, argument = 6)
Copying &Scanner; Objects A method exists to return a copy of an existing &Scanner; object, with any overridden values specified as keyword arguments to the method: scan = Scanner(function = my_scan) scan_path = scan.Copy(path = '%SCANNERPATH') Typically, &Scanner; objects will be supplied by a tool-master or administrator through a shared &consenv;.
&Scanner; maps Each &consenv; has a &SCANNERMAP;, a dictionary that associates different file suffixes with a scanner object that can be used to generate a list of dependencies from the contents of that file. This &SCANNERMAP; can be initialized at instantiation: env = Environment(SCANNERMAP = { '.c' : CScan, '.cc' : CScan, '.m4' : M4Scan, }) &Scanner; objects referenced in the &SCANNERMAP; do not need to be listed separately in the &SCANNERS; variable. The &consenv; will bind the union of the &Scanner; objects listed in both variables.
Targets The methods in the build engine API described so far merely establish associations that describe file dependencies, how a file should be scanned, etc. Since the real point is to actually build files, &SCons; also has methods that actually direct the build engine to build, or otherwise manipulate, target files.
Building targets One or more targets may be built as follows: env.Build(target = ['foo', 'bar']) Note that specifying a directory (or other collective object) will cause all subsidiary/dependent objects to be built as well: env.Build(target = '.') env.Build(target = 'builddir') By default, &SCons; explicitly removes a target file before invoking the underlying function or command(s) to build it.
Removing targets A "cleanup" operation of removing generated (target) files is performed as follows: env.Clean(target = ['foo', 'bar']) Like the &Build; method, the &Clean; method may be passed a directory or other collective object, in which case the subsidiary target objects under the directory will be removed: env.Clean(target = '.') env.Clean(target = 'builddir') (The directories themselves are not removed.)
Suppressing cleanup removal of build-targets By default, &SCons; explicitly removes all build-targets when invoked to perform "cleanup". Files that should not be removed during "cleanup" can be specified via the &NoClean; method: env.Library(target = 'libfoo.a', source = ['aaa.c', 'bbb.c', 'ccc.c']) env.NoClean('libfoo.a') The NoClean operation has precedence over the Clean operation. A target that is specified as both Clean and NoClean, will not be removed during a clean. In the following example, target 'foo' will not be removed during "cleanup": env.Clean(target = 'foo') env.NoClean('foo')
Suppressing build-target removal As mentioned, by default, &SCons; explicitly removes a target file before invoking the underlying function or command(s) to build it. Files that should not be removed before rebuilding can be specified via the &Precious; method: env.Library(target = 'libfoo.a', source = ['aaa.c', 'bbb.c', 'ccc.c']) env.Precious('libfoo.a')
Default targets The user may specify default targets that will be built if there are no targets supplied on the command line: env.Default('install', 'src') Multiple calls to the &Default; method (typically one per &SConscript; file) append their arguments to the list of default targets.
File installation Files may be installed in a destination directory: env.Install('/usr/bin', 'program1', 'program2') Files may be renamed on installation: env.InstallAs('/usr/bin/xyzzy', 'xyzzy.in') Multiple files may be renamed on installation by specifying equal-length lists of target and source files: env.InstallAs(['/usr/bin/foo', '/usr/bin/bar'], ['foo.in', 'bar.in'])
Target aliases In order to provide convenient "shortcut" target names that expand to a specified list of targets, aliases may be established: env.Alias(alias = 'install', targets = ['/sbin', '/usr/lib', '/usr/share/man']) In this example, specifying a target of install will cause all the files in the associated directories to be built (that is, installed). An &Alias; may include one or more other &Aliases; in its list: env.Alias(alias = 'libraries', targets = ['lib']) env.Alias(alias = 'programs', targets = ['libraries', 'src'])
Customizing output The &SCons; API supports the ability to customize, redirect, or suppress its printed output through user-defined functions. &SCons; has several pre-defined points in its build process at which it calls a function to (potentially) print output. User-defined functions can be specified for these call-back points when &Build; or &Clean;is invoked: env.Build(target = '.', on_analysis = dump_dependency, pre_update = my_print_command, post_update = my_error_handler) on_error = my_error_handler) The specific call-back points are: on_analysis Called for every object, immediately after the object has been analyzed to see if it's out-of-date. Typically used to print a trace of considered objects for debugging of unexpected dependencies. pre_update Called for every object that has been determined to be out-of-date before its update function or command is executed. Typically used to print the command being called to update a target. post_update Called for every object after its update function or command has been executed. Typically used to report that a top-level specified target is up-to-date or was not remade. on_error Called for every error returned by an update function or command. Typically used to report errors with some string that will be identifiable to build-analysis tools. Functions for each of these call-back points all take the same arguments: my_dump_dependency(target, level, status, update, dependencies) where the arguments are: target The target object being considered. level Specifies how many levels the dependency analysis has recursed in order to consider the target. A value of 0 specifies a top-level target (that is, one passed to the &Build; or &Clean; method). Objects which a top-level target is directly dependent upon have a level of <1>, their direct dependencies have a level of <2>, etc. Typically used to indent output to reflect the recursive levels. status A string specifying the current status of the target ("unknown", "built", "error", "analyzed", etc.). A complete list will be enumerated and described during implementation. update The command line or function name that will be (or has been) executed to update the target. dependencies A list of direct dependencies of the target.
Separate source and build trees &SCons; allows target files to be built completely separately from the source files by "linking" a build directory to an underlying source directory: env.Link('build', 'src') SConscript('build/SConscript') &SCons; will copy (or hard link) necessary files (including the &SConscript; file) into the build directory hierarchy. This allows the source directory to remain uncluttered by derived files.
Variant builds The &Link; method may be used in conjunction with multiple &consenvs; to support variant builds. The following &SConstruct; and &SConscript; files would build separate debug and production versions of the same program side-by-side: % cat SConstruct env = Environment() env.Link('build/debug', 'src') env.Link('build/production', 'src') flags = '-g' SConscript('build/debug/SConscript', Export(env)) flags = '-O' SConscript('build/production/SConscript', Export(env)) % cat src/SConscript env = Environment(CCFLAGS = flags) env.Program('hello', 'hello.c') The following example would build the appropriate program for the current compilation platform, without having to clean any directories of object or executable files for other architectures: % cat SConstruct build_platform = os.path.join('build', sys.platform) Link(build_platform, 'src') SConscript(os.path.join(build_platform, 'SConscript')) % cat src/SConscript env = Environment env.Program('hello', 'hello.c')
Code repositories &SCons; may use files from one or more shared code repositories in order to build local copies of changed target files. A repository would typically be a central directory tree, maintained by an integrator, with known good libraries and executables. Repository('/home/source/1.1', '/home/source/1.0') Specified repositories will be searched in-order for any file (configuration file, input file, target file) that does not exist in the local directory tree. When building a local target file, &SCons; will rewrite path names in the build command to use the necessary repository files. This includes modifying lists of or flags to specify an appropriate set of include paths for dependency analysis. &SCons; will modify the Python sys.path variable to reflect the addition of repositories to the search path, so that any imported modules or packages necessary for the build can be found in a repository, as well. If an up-to-date target file is found in a code repository, the file will not be rebuilt or copied locally. Files that must exist locally (for example, to run tests) may be specified: Local('program', 'libfoo.a') in which case &SCons; will copy or link an up-to-date copy of the file from the appropriate repository.
Derived-file caching &SCons; can maintain a cache directory of target files which may be shared among multiple builds. This reduces build times by allowing developers working on a project together to share common target files: Cache('/var/tmp/build.cache/i386') When a target file is generated, a copy is added to the cache. When generating a target file, if &SCons; determines that a file that has been built with the exact same dependencies already exists in the specified cache, &SCons; will copy the cached file rather than re-building the target. Command-line options exist to modify the &SCons; caching behavior for a specific build, including disabling caching, building dependencies in random order, and displaying commands as if cached files were built.
Job management A simple API exists to inform the Build Engine how many jobs may be run simultaneously: Jobs(limit = 4)
scons-doc-2.3.0/doc/design/goals.xml0000644000175000017500000001331712114661557020074 0ustar dktrkranzdktrkranz As a next-generation build tool, &SCons; should fundamentally improve on its predecessors. Rather than simply being driven by trying to not be like previous tools, &SCons; aims to satisfy the following goals: Practicality The &SCons; design emphasizes an implementable feature set that lets users get practical, useful work done. &SCons; is helped in this regard by its roots in &Cons;, which has had its feature set honed by several years of input from a dedicated band of users. Portability &SCons; is intended as a portable build tool, able to handle software construction tasks on a variety of operating systems. It should be possible (although not mandatory) to use &SCons; so that the same configuration file builds the same software correctly on, for example, both Linux and Windows NT. Consequently, &SCons; should hide from users operating-system-dependent details such as filename extensions (for example, .o vs. .obj). Usability Novice users should be able to grasp quickly the rudiments of using &SCons; to build their software. This extends to installing &SCons;, too. Installation should be painless, and the installed &SCons; should work "out of the box" to build most software. This goal should be kept in mind during implementation, when there is always a tendency to try to optimize too early. Speed is nice, but not as important as clarity and ease of use. Utility &SCons; should also provide a rich enough set of features to accommodate building more complicated software projects. However, the features required for building complicated software projects should not get in the way of novice users. (See the previous goal.) In other words, complexity should be available when it's needed but not required to get work done. Practically, this implies that &SCons; shouldn't be dumbed down to the point it excludes complicated software builds. Sharability As a key element in balancing the conflicting needs of Usability and Utility, &SCons; should provide mechanisms to allow &SCons; users to share build rules, dependency scanners, and other objects and recipes for constructing software. A good sharing mechanism should support the model wherein most developers on a project use rules and templates that are created and maintained by a local integrator or build-master, Extensibility &SCons; should provide mechanisms for easily extending its capabilities, including building new types of files, adding new types of dependency scanning, being able to accomodate dependencies between objects other than files, etc. Flexibility In addition to providing a useful command-line interface, &SCons; should provide the right architectural framework for embedding its dependency management in other interfaces. &SCons; would help strengthen other GUIs or IDEs and the additional requirements of the other interfaces would help broaden and solidify the core &SCons; dependency management.
Fixing &Make;'s problems
Fixing &Cons;'s problems
scons-doc-2.3.0/doc/design/scons.mod0000644000175000017500000003527312114661557020100 0ustar dktrkranzdktrkranz Aegis"> Ant"> Autoconf"> Automake"> cc"> Cons"> cp"> csh"> gcc"> Jam"> jar"> javac"> javah"> Make"> Make++"> Python"> ranlib"> rmic"> SCons"> scons"> ScCons"> tar"> touch"> zip"> Action"> ActionBase"> CommandAction"> FunctionAction"> ListAction"> Builder"> BuilderBase"> CompositeBuilder"> MultiStepBuilder"> Job"> Jobs"> Serial"> Parallel"> Node"> Node.FS"> Scanner"> Sig"> Signature"> Taskmaster"> TimeStamp"> Walker"> Wrapper"> --debug=explain"> --implicit-cache"> --implicit-deps-changed"> --implicit-deps-unchanged"> -Q"> implicit_cache"> implicit_deps_changed"> implicit_deps_unchanged"> build"> Makefile"> Makefiles"> SConscript"> SConstruct"> Sconstruct"> sconstruct"> .sconsign"> src"> Add"> AddOptions"> Alias"> Aliases"> Append"> BoolOption"> Build"> CacheDir"> Clean"> Clone"> Command"> Configure"> Copy"> Default"> DefaultRules"> Depends"> Dir"> Entry"> EnumOption"> Environment"> Export"> File"> Finish"> GenerateHelpText"> Help"> Ignore"> Import"> Install"> InstallAs"> Link"> ListOption"> Local"> Module"> NoClean"> Objects"> Options"> PackageOption"> PathOption"> Precious"> Prepend"> Replace"> Repository"> Return"> RuleSet"> Salt"> SetBuildSignatureType"> SetContentSignatureType"> SourceSignature"> SourceSignatures"> Split"> TargetSignatures"> Task"> subst"> Message"> Result"> CheckCHeader"> CheckCXXHeader"> CheckFunc"> CheckHeader"> CheckLib"> CheckLibWithHeader"> CheckType"> TryAction"> TryBuild"> TryCompile"> TryLink"> TryRun"> str"> zipfile"> Cache"> ARGUMENTS"> BUILD_TARGETS"> COMMAND_LINE_TARGETS"> DEFAULT_TARGETS"> BUILDERMAP"> BUILDERS"> CC"> CCFLAGS"> CCCOM"> COLOR"> COLORS"> CONFIG"> CPPDEFINES"> ENV"> JAVACLASSDIR"> LIBDIRPREFIX"> LIBDIRSUFFIX"> LIBLINKPREFIX"> LIBLINKSUFFIX"> LIBPATH"> LIBS"> LINK"> LINKCOM"> LINKFLAGS"> RELEASE"> RELEASE_BUILD"> SCANNERMAP"> SCANNERS"> TARFLAGS"> TARSUFFIX"> PATH"> PYTHONPATH"> SCONSFLAGS"> allowed_values"> build_dir"> map"> ignorecase"> options"> exports"> source"> target"> all"> none"> BuildDir"> CFile"> CXXFile"> DVI"> Jar"> Java"> JavaH"> Library"> Object"> PCH"> PDF"> PostScript"> Program"> RES"> RMIC"> SharedLibrary"> SharedObject"> StaticLibrary"> StaticObject"> Tar"> Zip"> Make"> builder function"> builder method"> Configure Contexts"> configure context"> Construction Environment"> Construction Environments"> Construction environment"> Construction environments"> construction environment"> construction environments"> Construction Variable"> Construction Variables"> Construction variable"> Construction variables"> construction variable"> construction variables"> CPPPATH"> Dictionary"> Emitter"> emitter"> Generator"> generator"> Nodes"> signature"> build signature"> true"> false"> typedef"> bar"> common1.c"> common2.c"> custom.py"> goodbye"> goodbye.o"> goodbye.obj"> file.dll"> file.in"> file.lib"> file.o"> file.obj"> file.out"> foo"> foo.o"> foo.obj"> hello"> hello.c"> hello.exe"> hello.h"> hello.o"> hello.obj"> libfile_a"> libfile_so"> new_hello"> new_hello.exe"> prog"> prog1"> prog2"> prog.c"> prog.exe"> stdio.h"> +"> #"> announce@scons.tigris.org"> dev@scons.tigris.org"> users@scons.tigris.org"> scons-doc-2.3.0/doc/design/engine.jpg0000644000175000017500000012041412114661557020211 0ustar dktrkranzdktrkranzÿØÿàJFIFPPÿÛC    $.' ",#(7),01444'9=82<.342ÿÛC  2!!22222222222222222222222222222222222222222222222222ÿÀžé"ÿÄ ÿĵ}!1AQa"q2‘¡#B±ÁRÑð$3br‚ %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyzƒ„…†‡ˆ‰Š’“”•–—˜™š¢£¤¥¦§¨©ª²³´µ¶·¸¹ºÂÃÄÅÆÇÈÉÊÒÓÔÕÖרÙÚáâãäåæçèéêñòóôõö÷øùúÿÄ ÿĵw!1AQaq"2B‘¡±Á #3RðbrÑ $4á%ñ&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz‚ƒ„…†‡ˆ‰Š’“”•–—˜™š¢£¤¥¦§¨©ª²³´µ¶·¸¹ºÂÃÄÅÆÇÈÉÊÒÓÔÕÖרÙÚâãäåæçèéêòóôõö÷øùúÿÚ ?÷ú(¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ +Ÿ—ÄóißÙYxwU¿û « Ó@öÊ›Ìi&™2±ùd^ÔÂCªЙ®ßë/þH ‚Šçÿá!Õ?èL×?ïõ—ÿ$Qÿ ©ÿBf¹ÿ¬¿ù"€: +Ÿÿ„‡Tÿ¡3\ÿ¿Ö_ü‘Gü$:§ý šçýþ²ÿäŠè(®þSþ„ÍsþÿYòEðêŸô&kŸ÷úËÿ’( ¢¹ÿøHuOú5ÏûýeÿÉÂCªЙ®ßë/þH ‚Šçÿá!Õ?èL×?ïõ—ÿ$Qÿ ©ÿBf¹ÿ¬¿ù"€: +Ÿÿ„‡Tÿ¡3\ÿ¿Ö_ü‘Gü$:§ý šçýþ²ÿäŠè(®þSþ„ÍsþÿYòE\Ñu¡¬‹Õk »ì®>Ï4F2Á¼´cvR ȽýhRŠ( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Šçåñ<ßÚwöV^ÕoþÃ*Ã4Ð=²¦óIæL¬~YµðêŸô&kŸ÷úËÿ’( ¢¹ÿøHuOú5ÏûýeÿÉÂCªЙ®ßë/þH ‚Šçÿá!Õ?èL×?ïõ—ÿ$Qÿ ©ÿBf¹ÿ¬¿ù"€: +Ÿÿ„‡Tÿ¡3\ÿ¿Ö_ü‘Gü$:§ý šçýþ²ÿäŠè(®þSþ„ÍsþÿYòEðêŸô&kŸ÷úËÿ’( ¢¹ÿøHuOú5ÏûýeÿÉÂCªЙ®ßë/þH ‚Šçÿá!Õ?èL×?ïõ—ÿ$Qÿ ©ÿBf¹ÿ¬¿ù"€: +Ÿÿ„‡Tÿ¡3\ÿ¿Ö_ü‘W4]hk"õZÂîÆ{+³ÍÑŒ°o-$Ý”‚²/ZÔ¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(Ÿð÷ü‡g?&6·\}ÓéQŸè"Þâàëzo‘n‘<ò}©6IJc,s€Tž¹ã4©Egêzî¢y_ÚÚ­‡Ÿ/íw üc8ÜFq‘ÓÔQ}®èúd²EªØÚIBwIî2±–‚xRÄ.zdã­hQ\Ü:Ðn¼c†mï#šò[!{‘Ê©ä !²\¯ÏŒ}Ï›8®’€ çü=ÿ!ÏØV?ý"µ®‚¹ÿÈsÅŸöÿH­h ¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(Ÿð÷ü‡_hO;îîû™Ý÷yéӚТ³íõÝîÎKËmVÆkX¢óäš;„dHòÃy`p(ã=>Fô4Zëº=ö>£gªØÜXÁ»Î¹†á8ö͹ÀÀ œô¡EeÇâ]m.mR-oM}>Ù-ÚÝ!‰Žó€~eàžãÖ¤þÝÑÿ±ÿµÿµl³?ç÷í äýí¿;~÷zñ@Vöîýý¯ý«cý™ÿ?¿hO'ïmûùÛ÷¸ë׊]Ñä³µ¼U±{[¹DÓ-šBHœ3dÏ€4(¨àž«x®-åŽh%@ñÉWR2#‚ç5%Ïø{þCž,ÿ°¬úEk]sþÿç‹?ì+þ‘ZÐAEPEPEPEPEPEPEPEPEP?áïùx³þ±ÿé­tÏø{þCž,ÿ°¬úEk]x?‰¬/-þ)x¢òkOˆÚÝ}“ìóxb2]°ÛØðØ< tù« »Ó?á'×¾Ës¡j·d_ÅvšÝ¦ùlJˆ×‚¡™“p'©ÁëPÏ—~ñƒ¦êZ÷‡4K¸õ-/ÅØm’×å·,~lqc.TÛ·+× €qÓé¾ðþ­ðªÍl®Ù4û{ö¾“o˜¶òËfVu@ÞÌ«Ï8êz×®Q@8iŸ_Mð‡ƒ|.ð´Úo‹|4ú·„®À}PÓͦ¶÷Kw,ˆò3•ü´PÞù }EW§èz¯‰Çãúèø&›ÿ’« ¯ŸÇ¼A¤Ïã¨õ JvÒ.®õk=:á¤möWq!xÐHHج§ “¹>P9$Ø>Çãúèø&›ÿ’¨ûŒ?è;¡ÿàšoþJ®.ׯ>&žËÂ^ðåµ¥Öµs¡A©Þ^êÒ1‰b('iÞÎÏÔóŒŽIZzoÅ/øŸQ‡Ã:…cmâkO7û]µ ´ƒË;ÍŒY²äzíÎ>a–ö?ÐwCÿÁ4ßü•GØüaÿAÝÿÓòUxü?u èz™½‹½÷‰nàu Þx4èÓÉ.„§ÌÊžfAޤt Þ‘ðÃÇOãòk£cöë+¶‚O±³‘8)(GùÕXd Ývœ€±ö?ÐwCÿÁ4ßü•GØüaÿAÝÿÓòUtP.úOŒQ†óþM)|¨¤‹É]&aï(w2ý§–0`ÍëV>Çãúèø&›ÿ’« ¢€9ÿ±øÃþƒºþ ¦ÿäª>Çãúèø&›ÿ’« ¢€9ÿ±øÃþƒºþ ¦ÿäª>Çãúèø&›ÿ’« ¢€9ÿ±øÃþƒºþ ¦ÿäª>Çãúèø&›ÿ’« ¢€9ÿ±øÃþƒºþ ¦ÿäª>Çãúèø&›ÿ’« ¢€9ÿ±øÃþƒºþ ¦ÿ䪧©\ø«FŠÚîãSÑ®`kÛ[y"K–&+,ñÄHcpÀ=Jë+Ÿñ—ü€í¿ì+¦ÿél4ÐQEQEQEQEQEQEQEQEQEW?ã/ùÛØWMÿÒØk ®Æ_ò¶ÿ°®›ÿ¥°ÐAEPEPEPEPEPEPEPEPEPEPEPEPEPEP?ã/ùÛØWMÿÒØk ®Æ_ò¶ÿ°®›ÿ¥°×A@Q@Q@Q@Q@ÿŒ¿ämÿa]7ÿKa®‚¹ÿÈÛþºoþ–ÃY¯¼Wdš;x]dbר.•,…Á‘ ³–F NÁ…?¼Œà8iEPEPEPEPEPEP\ߎg†×ÃqÜ\K0E©éï$’0UE’I<9®’¹ÿÈÛþºoþ–Ã@ü'~ÿ¡¯CÿÁŒ?üUrooðÖoøƒE›Åºl¶šåì·×;µX$®U²„€¬Š@9éÎGé”P•êzoÃ]JËD‰Çãúèø&›ÿ’¨ ¢¹ÿ±øÃþƒºþ ¦ÿäª>Çãúèø&›ÿ’¨⟉aðÖ…g=í¼fom¥2ÆAo2+˜e…îZ4™²HËÁ9a]Äf·‰®#Ž9Ê"FåÕ[€ÄF{àgÐWâ/k~*³µµÕµ]hm®â»N. ¡èÀÜÊAe ƒÇ¡Ácñ‡ýt?üMÿÉTÐQ\ÿØüaÿAÝÿÓòUcñ‡ýt?üMÿÉTÐQ\ÿØüaÿAÝÿÓòUSÔ®|U£Emwq©è×05í­¼‘G¥Ë–xâ$1¸`ž‡¥u”QEQEQEQEÏøËþ@vßöÓô¶è+›ñÈ™¼7ÛÉsOO¼ˆ]U¾Ù PA#=²3ê(¤¢¹ÿ±øÃþƒºþ ¦ÿäª>Çãúèø&›ÿ’¨ ¢¹ÿ±øÃþƒºþ ¦ÿäª>Çãúèø&›ÿ’¨ ¢¹ÿ±øÃþƒºþ ¦ÿäª>Çãúèø&›ÿ’¨ ¢¹ÿ±øÃþƒºþ ¦ÿäª>Çãúèø&›ÿ’¨ ¢¹ÿ±øÃþƒºþ ¦ÿäª>Çãúèø&›ÿ’¨ ¢¹ÿ±øÃþƒºþ ¦ÿäª>Çãúèø&›ÿ’¨ ¢¹ÿ±øÃþƒºþ ¦ÿäª>Çãúèø&›ÿ’¨ ¢¹ÿ±øÃþƒºþ ¦ÿäª>Çãúèø&›ÿ’¨ ¢¹ÿ±øÃþƒºþ ¦ÿäª>Çãúèø&›ÿ’¨ ®Æ_ò¶ÿ°®›ÿ¥°Ñö?ÐwCÿÁ4ßü•Xþ$¶ñ$z}›_êºTö£UÓ·Ç™$Nßéã g`9ÇðŸN:ÐqEPEPEPEPEPEPEPEPEP\ÿŒ¿ämÿa]7ÿKa®‚¹ÿÈÛþºoþ–Ã@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@ÿŒ¿ämÿa]7ÿKa®‚¹ÿÈÛþºoþ–Ã]QEÏøËþ@vßöÓô¶è+Ÿñ—ü€í¿ì+¦ÿél4ÐQErÿ=V;mÖOpFDb@rIÝØÏ–ÙÁ*¬®Æ_ò¶ÿ°®›ÿ¥°×A\ߎg†×ÃqÜ\K0E©éï$’0UE’I<9 ’Šâáø±à‹Péðx‚ѤGe’GqH?7™&ÕpHl,Nà@Ú Ÿðø?þ†½ÿ0ÿñTÐQ\ÿü'~ÿ¡¯CÿÁŒ?üUðø?þ†½ÿ0ÿñTÐQ\ÿü'~ÿ¡¯CÿÁŒ?üUðø?þ†½ÿ0ÿñTÐQ\ÿü'~ÿ¡¯CÿÁŒ?üUðø?þ†½ÿ0ÿñTÐW?ã/ùÛØWMÿÒØhÿ„ÇG½ÿGе cP~#¶´»GÇûr2çËŒwb= ÅU²üWs©[YÙZêÇ]i’iw~;‚çM’ÙÛZ˫ۑiÉ€FøÞv´QíÞÌP0EvƒÇZ8‰[Ri÷ºIº2©E›tb=Êš#’;Æ*KxwSÔã°°Ô>×$’˜h!’HA” œ/–X '³Ç­yݵ„4¯iϤøþÒÊÞ4¼žk›[û¿{ ¶EŒÄ¨#(V&llûÊ 9Å\Ñ4O…ž× ÕtŸéVþD­4vÿÚ®ªÍˆ1˜® ;L›wsŠì-¾!xróh¶žúi$!M.èÈñ>vÊ©åîhøÆð ‚@'$f=7â‡}o¦‰¥’+Ë»{Id‚(emšä)$‘SjX»nx8ÁÍ­·€ ¼Óol|•ö§Ã¦Áq­m“a†×Vq`NWª)psN-át¥Ž¡‹ôÕžÑ,×&þÑ̆×h‹2–BB(o, `9ÜZøóÃ×¶í5½ÍÛ‘:!Óî“,ÊЦéÈr€ðŒOš±aâýRMÖ×r7ØC%¼±Êd,Ë(rI2ØÀòäÉ{Eðø[Ù$9´·žÆÞÎ [˜uku’/³ B7÷X²Í"°`TƒÐT‘]ø)<[a­Kã-*f²´hÄ“kJÏq33áÝCI‚àc÷ì@ФQ\ÿü'~ÿ¡¯CÿÁŒ?üUðø?þ†½ÿ0ÿñTÐQ\ÿü'~ÿ¡¯CÿÁŒ?üUðø?þ†½ÿ0ÿñTÐQ\ÿü'~ÿ¡¯CÿÁŒ?üUðø?þ†½ÿ0ÿñTÐQXö>,ðÞ§y‡ˆ4«»©3²/c‘Û“…'øVÅQEÏøËþ@vßöÓô¶è+Ÿñ—ü€í¿ì+¦ÿél4ÐQEQEQEQEQEQEQEQEQEQEQEQEQEQEÏøËþ@vßöÓô¶è+Ÿñ—ü€í¿ì+¦ÿél5ÐPEP\ÿŒ¿ämÿa]7ÿKa®‚¹ÿÈÛþºoþ–Ã@rÿ<7yâïjZ„GuuålyØ„eG9 ÑOjê( +ðïÃÍzéî©eá2 1'+jñµÃH›0û€’8äcœŒýá·®l\iž%dºlºLW©sóMrš„q©VòÔFË9]»úņzg‚ü7yáÏøH~Ù$ý¥­Üêù,NØäÛ´6@ÃprG½uP\ÿŒ¿ämÿa]7ÿKa®‚¹ÿÈÛþºoþ–Ã@,|+¡iw‘Ýišd|‰œ‹mÒL‚?x‰…“8ÞÜ’0MlQ^gà«3c©Eª¾©X^.™pÿSÓnÎ(%ób…î¢hÀ”¾ÆcÝûµ¶: ö‘x[F‚¡ŽÏlqEi 5ÎÙÌ¿ÂÄŸ~ùžŸ<1¶­Œé¥Ø½¶·[éÄ̰d‡–¼“À\rF1@öš#ø«Añ+^]ùWZŽ¡yb¹º/44ÂÎf„˜‘ÞE„(Ì«åA$aŒ‚*8üyáé^d7jð>Û•“O¸Ck• mÈ<¤*rö©Ž~VÀIErzßÄ-Bº‚;©d[sq$ «["‘ÈYvl‘÷G³b¶íÄŒeH­Í'Z²ÖâžK&Ÿý_&džÚHj¾ HªßuÔôï@QEQEQEQEsþ2ÿ·ý…tßý-†º çüeÿ ;oû é¿ú[ tQEW?ã/ùÛØWMÿÒØk ®Æ_ò¶ÿ°®›ÿ¥°ÐAEPEPEPEPEPEPEPEPEPEPEPUï¯ìôË9//îà´µæžA.H,x$ƬW?ã/ùÛØWMÿÒØhÿ„ïÁÿô5èø1‡ÿŠ£þ¿ÿÐסÿàÆþ*º (Ÿÿ„ïÁÿô5èø1‡ÿŠ£þ¿ÿÐסÿàÆþ*º (Æþ.ëšV·¢ißðŽø§F]Kí±[½Âjqƒ èŸb@ÅnÅ”P„ôÝ^<ko½¿‰|?  HãŽþTP0°bºJ(Ÿÿ„ïÁÿô5èø1‡ÿŠ£þ¿ÿÐסÿàÆþ*º (Ÿÿ„ïÁÿô5èø1‡ÿŠ© ñ§…n®"··ñ.4ò¸HãŽþ&gbp²IZ5 êóßꯦx–M:ÏVxžéÔ4ñ²¢Ææ w,´hƒ,¯´Ëƒ@ç-ôýJÖÎ÷GÔ­…ÝïØ­¥™í“Îo4E½#3y¬™`rîØÁ¢?Å,ÓmÐ5‘g§ý˜×̰¬F_8C¹A—{&öpS߸ aÞ|/¸–ü]Zßhvó.ªÚšÜ¶„åžf¼Þh%A eB’ ã õðŒÄû7íŸóþÒó<¯ú}ûVÌgþœûãµg¶¡â-cGñîŒ|‹‹{··Òáš8Ù'û;&ï›#Ì‘e%†#§$ôšN¥³£Xê–ë"Á{oÄk ‚º†à‘œS\Ý›k™¯ÙCmö‹¥Ô$—L³¿–ëu&õi'*’É |Á#Ý´dg¤ÒtØtmÇK·i +xíãi,U(' àz ¹EPEPEPEPEP\ߎg†×ÃqÜ\K0E©éï$’0UE’I<9®’¹ÿÈÛþºoþ–Ã@püXðEΨtøд»Èî´Í2 >DÎEˆ6é&A¼DÂÉŒœonI&¶(ÍåË­mÿ ô a©}¡žÉuˆ<¨žu+#¦~nw;lf(‹ÈŽö/‡×šÞ§ªÂsi êÈ"Ô`‹V·ò®¡‘ˆÙNp6«a”«1ðÃ#™Ey>µ¤|8×¢¹µ½ñÜMšîKá`º½¿•ë‘ ÇÌìûwlÜO˃ŠÜð¾¥à/ Ù\ÚØø¿Ft¹¸7ºòÒ0b&b€aEë’z×yEsÿðø?þ†½ÿ0ÿñTÂwàÿúô?üÃÿÅWAEsÿðø?þ†½ÿ0ÿñUbÇÅžÔï#³°ñ•wu&vCìr;`p äà Ø®Æ_ò¶ÿ°®›ÿ¥°ÐAEPEP?ã/ùÛØWMÿÒØk ®Æ_ò¶ÿ°®›ÿ¥°×A@Q@sþ2ÿ·ý…tßý-†º çüeÿ ;oû é¿ú[ tQEQEQEQEQEQEQEQEQEQEQEÏøËþ@vßöÓô¶è+Ÿñ—ü€í¿ì+¦ÿél4ÐV\Þ%ÐmµA¥Ï­é±jÕ£Ý"ÊY±´l'99ç"µ+æÍV×CŽ÷Ä÷ñjÖtùïf¼»±Ö"–ÃRŠ@åÞ™#j‚x-‘´|À€}'Exx‘|aâ)´ýCÆ÷~³´Ñí&Ó¢ŠùÈóA…¦™ØJQ‚AaÈ ‡'3Ç/ÖnšÕmüY<Íi¢C4âÂù4±çÉÈ$ùÈ{Ã-å¢&Ðȶ@>¢¾µ»×|M¦øÇQ¹ñV¹k&›¢iÚŒQÙ]Q¦k#dò©l’©´óÈܲ}_Æ>/Ñtû¯k6^x6Úþàé×ó—-¤)9ä¨Á €{%òE¿Žüm¯Agey¬_@°iïöI—VLóÎý¢Y$“|W‚vAÞO§é7ž"ñwü7 ¾ Õt¨å𥾣t4`Ï ›ï…ù£]ÇcgnJ|„Äh®Æ_ò¶ÿ°®›ÿ¥°×A\ߎDÍá¸ÖÞHãœêzxäBê­öÈpJ‚ 푟Q@%æz^—ñQ|@³kZæ›%»»¼1ÚÚù–öçý`ó!Œª?z2rØ*­]gØüaÿAÝÿÓòUtW?ö?ÐwCÿÁ4ßü•GØüaÿAÝÿÓòUtW?ö?ÐwCÿÁ4ßü•GØüaÿAÝÿÓòUtW?ö?ÐwCÿÁ4ßü•GØüaÿAÝÿÓòUtW?ö?ÐwCÿÁ4ßü•GØüaÿAÝÿÓòUtW?ö?ÐwCÿÁ4ßü•GØüaÿAÝÿÓòUtW?ö?ÐwCÿÁ4ßü•GØüaÿAÝÿÓòUtW?ö?ÐwCÿÁ4ßü•GØüaÿAÝÿÓòUtW?ö?ÐwCÿÁ4ßü•GØüaÿAÝÿÓòUtËøšþÏPðý¼¶Wp\Æ5]3/ ÀÝwnëÈõVV¡èj=JË^6êu››MOK ݦ`ðÉ$x9È2Èe@pZ% \2ÜÇ&_‹îlõ+2ëIo=£Ô4ßµÝÛÊ<–…®¡hãc‚$lºº‚ªÅ·(, EPEPEPEPEPEPEPEPEP\ÿŒ¿ämÿa]7ÿKa®‚¹ÿÈÛþºoþ–Ã@W¾²‹P³’Ög#|dÁ;ÂãaÓ±ç§J±Eaø. ­| áë{ˆ¤†x´Ëd’9«#”AäxÅnQEQEQEÏøËþ@vßöÓô¶è+Ÿñ—ü€í¿ì+¦ÿél4ÐQEQEÏøËþ@vßöÓô¶è+Ÿñ—ü€í¿ì+¦ÿél5ÐPEP\ÿŒ¿ämÿa]7ÿKa®‚¹ÿÈÛþºoþ–Ã@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@sþ2ÿ·ý…tßý-†º æüs<6¾ŽââXá‚-OOy$‘‚ª(¼„’Ià9Ít•—7†´Pj“èšlº€uqvö¨Ò†\m;ÈÎFxÀªðø?þ†½ÿ0ÿñTÂwàÿúô?üÃÿÅPÍKÃZ³p·¦‰¦ßN¨dºµIX.IÀ, ÆI8÷4j^Ðu›…¸Õ4M6úu@‹%ÕªJÁrN`N2Iǹªðø?þ†½ÿ0ÿñTÂwàÿúô?üÃÿÅP„:oÌPéV1ÇuÁp‰n€K®ÅF|Êåð*H46Öâ+‹}>Òâ·±É*¬ƒ‘ d <íéYðø?þ†½ÿ0ÿñTÂwàÿúô?üÃÿÅP‰¼'ị;k9¼?¥Ikk»ìð½”e"ÜrÛTŒ.O'kCìÚ?Ú?dƒíÞW‘öŸ,yž^wlÝ×nyÇLÖ?ü'~ÿ¡¯CÿÁŒ?üUðø?þ†½ÿ0ÿñTÐW?ã/ùÛØWMÿÒØhÿ„ïÁÿô5èø1‡ÿЬx³ÃzžŸggaâ *îêMWNÙ ±Èí‹ÈIƒ“€ ü(¸®_AñeƯ«Em>•ö{KÛG¾Ó.ÒàH.`YrÊB´lVHœ/<> ¤WQ\Þ‘àÛm6ZêZ“måµ±ó¤I „R0b‘’™` G3~jçŠ5É|9áëÍZ-6}CìÑ<­2"mUFbÌ\Œ/;C7<)­ŠÏÔ´”ÕtSMº¹œÛê<-·h1#ǰ„;~­ónål¡@Q@Q@Q@Q@Q@Q@Q@s~,‚mÝ Š8ëU(,×г;–$“Ü’k¤®Æ_ò¶ÿ°®›ÿ¥°ÐAEPEPEPEPEPEPEPEPEP\ÿŒ¿ämÿa]7ÿKa®‚¹¿Ï ¯†ã¸¸–8`‹SÓÞI$`ªŠ/!$’xs@%sóø×A·Øêv’K¹¬â†;9¤’YbÏ›± :®. ¨#Íðø?þ†½ÿ0ÿñUÇë_ 5¨,ÒçźW™gwuu ¦úÒ\‡g‘JH6\·RFÕÁÎIì4Oè>"ò™u<‘ÜnM-œÐÇ1\åQݳ ¬v‚NŽ0§í¾!xróh¶žúi$!M.èÈñ>vÊ©åîhøÆð ‚@'$g‹ð^ðÍ„ÜxæÒhì¯n¥³´¹Õ­^8ròF’)\6ZÉ]Ûs#¡º\[oAy¦ÞØøþ +í;O‡M‚â ZÛ&à ®¬ 6âÀœ¯TR6àä¬Ô¼k¦Y›d´2_¼÷°‡¶ŠGDòF ´Ê¦5;d°$þð5cHñ~‡®Þý“M»’áÊJé ·•b•cpŽc”¨IfQ•'­rö_‚o5K{M ÅѪ3Ú¿öUž¥ É;Zí1¬Á (FDc=3Zžøo¢xCVký)§_ÝK Bë$‘\‚á‚€ îØPaEP\ÿŒ¿ämÿa]7ÿKa®‚¹ÿÈÛþºoþ–Ã@Q@Q@ÿŒ¿ämÿa]7ÿKa®‚¹ÿÈÛþºoþ–Ã]QEÏøËþ@vßöÓô¶è+Ÿñ—ü€í¿ì+¦ÿél4ÐQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEW?ã/ùÛØWMÿÒØk ®Æ_ò¶ÿ°®›ÿ¥°ÐAEPEPEPEPEPEPEPEPEP\ÿŒ¿ämÿa]7ÿKa®‚¹ÿÈÛþºoþ–Ã@U{ë(µ 9-fyÒ7ÆL¼.0AáІ;zt  V‚àš×À¾·¸ŠHg‹L¶I#‘J²0‰AAŒVåsþ2ÿ·ý…tßý-†º çüeÿ ;oû é¿ú[ tQEW?ã/ùÛØWMÿÒØk ®Æ_ò¶ÿ°®›ÿ¥°ÐAEPEP?ã/ùÛØWMÿÒØk ®Æ_ò¶ÿ°®›ÿ¥°×A@Q@sþ2ÿ·ý…tßý-†º çüeÿ ;oû é¿ú[ tQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE—â *mgIû%½ÌvӭŽÄrÉ•CE2JPÊH%1Ôu­J(ŸûŒ?è;¡ÿàšoþJ£ì~0ÿ î‡ÿ‚i¿ù*º çücíÖ‡öM:{ènåXocãWû1É1Ðày|0+æn®?áíoÆä¥%Ôt;Uš/µØ?ö\Ì.íKYnA €ŒÉƒ°KN[ ûŒ?è;¡ÿàšoþJ¬;íwQÕoâŸLðž²5 "õ"›|¶j6:#K>X¤Wy‰?tŠï(ŸûŒ?è;¡ÿàšoþJ£ì~0ÿ î‡ÿ‚i¿ù*º (—¾oéÖr]]x‡CŽÀ$h“1$UEÉ,Ä€I$ 5ž—¾8‚[_íKÝÆ¬,rfK ŽRÄ,Râç Ì6`‚Ê\²nÎÃ'Y©i°êvë$RFâH." I€ g‚ ‚URA÷ûgˆç»ÓuO!mtéE½ôpgmô…@?v’!dÉ,Ä¡%ù >Çãúèø&›ÿ’¨ûŒ?è;¡ÿàšoþJ®‚ŠçþÇãúèø&›ÿ’ª½óxŸN³’êëÄ:p¦#D™‰$€ªª.If$€I I®¢©êZl:ºÇ#I‘¸’ ˆˆA BAÁ ‚ •`T@94½ñÄÚÿj^èv0Ýac“û2Yr–!b—8Va³Rå“vv6>Çãúèø&›ÿ’ª½¿ÛÇãúèø&›ÿ’ª½Þ‡âMMmà¿Öô¦µŽîÞåÖ *Hݼ©RP¡Ã’€g­uPEPEP?ã/ùÛØWMÿÒØk ®Æ_ò¶ÿ°®›ÿ¥°×A@Q@sþ2ÿ·ý…tßý-†º çüeÿ ;oû é¿ú[ tQEQEsþ2ÿ·ý…tßý-†º çüeÿ ;oû é¿ú[ tQEW?ã/ùÛØWMÿÒØk ®Æ_ò¶ÿ°®›ÿ¥°ÐAEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEP?áïùx³þ±ÿé­tÏø{þCž,ÿ°¬úEk]QEÏø{þCž,ÿ°¬úEk]sþÿç‹?ì+þ‘ZÐAEPEP?áïùx³þ±ÿé­tÏø{þCž,ÿ°¬úEk]QEÅéþ%ÐtoxªßTÖôÛÛSÖ;«¤‰ŠýŽØd ã Œûí( þ¿ÿÐסÿàÆþ*øNüÿC^‡ÿƒøªè( þ¿ÿÐסÿàÆþ*øNüÿC^‡ÿƒøªè( þ¿ÿÐסÿàÆþ*øNüÿC^‡ÿƒøªè( Äž,ðÞ§§ÙÙØxƒJ»º“UÓ¶Cìr;bòp äà î(¢€ (¢€ çüeÿ ;oû é¿ú[ tÏøËþ@vßöÓô¶è(¢Š(¢Šçüeÿ ;oû é¿ú[ tÏøËþ@vßöÓô¶è(¢Š(®Æ_ò¶ÿ°®›ÿ¥°×A\ÿŒ¿ämÿa]7ÿKa ‚Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Ãßòñgý…cÿÒ+Zè+Ÿð÷ü‡·æÿdê¶7þN<ϲ\$»3œgi8Î_C@V~™®èúß›ý“ªØßù8ó>Ép’ìÎq¤ã8=} f»£ë~oöN«cäãÌû%ÂK³9Æv“Œàõô4¡Egéšî­ù¿Ù:­ÿ“3ì— .ÌçÚN3ƒ×ÐѦkº>·æÿdê¶7þN<ϲ\$»3œgi8Î_C@V~™®èúß›ý“ªØßù8ó>Ép’ìÎq¤ã8=} f»£ë~oöN«cäãÌû%ÂK³9Æv“Œàõô4¡Egéšî­ù¿Ù:­ÿ“3ì— .ÌçÚN3ƒ×ÐѦkº>·æÿdê¶7þN<ϲ\$»3œgi8Î_C@V~™®èúß›ý“ªØßù8ó>Ép’ìÎq¤ã8=} f»£ë~oöN«cäãÌû%ÂK³9Æv“Œàõô4¡Egéšî­ù¿Ù:­ÿ“3ì— .ÌçÚN3ƒ×ÐѦkº>·æÿdê¶7þN<ϲ\$»3œgi8Î_C@÷ŸòPôoû_ÿèÛJè+·×t}oâ—ý“ªØßù:U÷™öK„—fe´Æv“Œàõô5¹â]Jmº¾©n±´öVS\F²T²!`8Èõ©Eyü_-à³ÒaºÒ5[ÝZóD·Õš *ÌÌ…!FìŒOÍÆÞ$€sîþ/E&¹áÒ´Ëë­#ZŠYg•l^IWnWlaO-+ òà®r(Ô(®‹~ºñZe¼:”ÖÒÞ>=V;mÖOpFDb@rI»ðýÖ´Ë©[Ú[§Y­Hó%ÉB0%\mm»¾ëƒœ+íôJ+ƒâ߇FÕµ BKK“KtIìoí¼«†i´aS$À6x IÂóQ‹Úé×sϦk–÷Ö×pÙ.k·o$À´aS8ù‚± ü½9\€zÃÿÂÔпáþÛû&«»ûCû3û;ì‡íjÏú­™Æìs÷½¾÷ËQŸ‹~_¦¦ÐêBåï[OM(Û¦µÂ1pH ¤ó¸¼vÐyEyûübð´:ê·úÝWP}Õ¬°=œ‡y̱ç!pŒr»c Ðj"tð ÿˆ­-'‚htù®£¶¿·hŽÙàŽF=A ƒ@ãz_Ä¿K¦®­qà;ÈÉïdÓ-/åŽôªÄd(Þ¨Œ„du< ñÅ>$ÔtÓ<^Ô¬o"gž-&äÇwa‚£t±Là•àíÔò†õŠ(¢€9ÿÈÛþºoþ–Ã]sþ2ÿ·ý…tßý-†º (¢Šçï?ä¡èßö ¿ÿѶ•ÐW?yÿ%Fÿ°Uÿþ´«ž%ԦѼ+«ê–ëOee5Äk %K"àƒŒQ@”WŸÅñBÞ =&­#U½Õ¯4K}Y Ò¬ÌÀ‰RnÈÁ$üÜ`}âH>ïâôRkž]+L¾ºÒ5¨¥–yVÅä•våvÆòѲ±ß. ç"€=BŠààø·á˯E¦[éMm-èÓãÕc¶Ýd÷dF$$“ÇLwû¿5W³øÓáK¿ÝkLº•½¥ºušÔ2\‘ä#Q¥ÆÖÛ»î¸9¾ÐD¢¸8þ-øqtm[PÔ!Ô´¹4·DžÆþÛʸf‘KF2A,`g€¤œ/5ø½¡.w<úf¹o}mw ™Òæ±+vòL F3˜+’ËÓ•È Q\?ü-M þí¿²j»¿´?³?³¾È~×ö¬ÿªÙœnÇ?{Ûï|µø·áÅðújm¤.^õ´ôÒ·úk\)£ç€ÊO8€ûÇmw”WŸ¿Æ/ C¡®«p/­Õu§ÝZËÙÈwœËrÇ+¸ðF2¡â'OßøŠÒÒx&‡Ošê;kûv‰ÑÑí‘ä`àóÔ4ÐQ^Gkã¿Y?„µ kNðüÚ/ˆn ZÄʲÂfPSvö#8$ð|„ddÏÔ>)øª;½½ÿ‚­ Ò/n­ã´¿šT»b'P>°àtË8 l¢©é7“j:5퍖sÜ[Ç,–Ògt,Ê CA8è:tr€9ÿÈÛþºoþ–Ã]sþ2ÿ·ý…tßý-†º (¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š+?YþÑû Ùññö»mÿwýOœžwÞãý^ÿNqZŸ¬ÿhý†?ì¿øøû]¶ÿ»þ§ÎO;ïqþ¯¿§8  (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€9ÿÈsÅŸöÿH­k ®Ãßòñgý…cÿÒ+Zè(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(Ÿ¼ÿ’‡£Ø*ÿÿFÚUÏé³k>Õô»vg½²šÞ6¡ ‚p ÆO¡ªwŸòPôoû_ÿèÛJè(ƒðׂµ-ÅZF©q=£Aeáxti7bÆdpņT ˜rµaèŸÕô»vg½²šÞ6¡ ‚p ÆO¡ªwŸòPôoû_ÿèÛJè(ƒðׂµ-ÅZF©q=£Aeáxti7bÆdpņT ˜rµaèŸ>×m¿îÿ©ó“ÎûÜ«ßïéÎ+B³õŸí°Çý—ÿk¶ß÷ÔùÉç}î?Õï÷ôç¡EPEPEPEPEPEPEPEPEPEPEP?áïùx³þ±ÿé­tÏø{þCž,ÿ°¬úEk]QEQEQEQEQEQEQEaë>¥u¬Øêš^£ii=­¼öì·Vm:ºÊÑ6FÙ‚ #¹êj?±øÃþƒºþ ¦ÿäªè( ì~0ÿ î‡ÿ‚i¿ù*±øÃþƒºþ ¦ÿäªè( ì~0ÿ î‡ÿ‚i¿ù*±øÃþƒºþ ¦ÿäªè( ì~0ÿ î‡ÿ‚i¿ù*±øÃþƒºþ ¦ÿäªè( ì~0ÿ î‡ÿ‚i¿ù*±øÃþƒºþ ¦ÿäªè( ì~0ÿ î‡ÿ‚i¿ù*±øÃþƒºþ ¦ÿäªè( ì~0ÿ î‡ÿ‚i¿ù*±øÃþƒºþ ¦ÿäªè( ì~0ÿ î‡ÿ‚i¿ù*±øÃþƒºþ ¦ÿäªè( ^ïCñ&¦¶ð_ëzSZÇworë•$nÞT©(PÆá€É@3ƒÖºŠ( Š(  =cGÔ®µ›SKÔm-'µ·žÝ–êͧWYZ&ÈÛ"A„w=MGö?ÐwCÿÁ4ßü•]ÏýÆôÐÿðM7ÿ%Qö?ÐwCÿÁ4ßü•]ÏýÆôÐÿðM7ÿ%Qö?ÐwCÿÁ4ßü•]ÏýÆôÐÿðM7ÿ%Qö?ÐwCÿÁ4ßü•]ÇèW0Öü=¦jßÚú?n´ŠçÊþÈ™¶o@Ûsö‘œgÀ­±øÃþƒºþ ¦ÿäªÐе?í¿iš·“äýºÒ+Ÿ+ví›Ð6ÜàgÆp+B€9ÿ±øÃþƒºþ ¦ÿäª>Çãúèø&›ÿ’« ¢€9ÿ±øÃþƒºþ ¦ÿäª>Çãúèø&›ÿ’« ¢€9ÿ±øÃþƒºþ ¦ÿäª>Çãúèø&›ÿ’« ¢€9{½ÄššÛÁ­éMkݽˬT‘»yR¤¡C†%ÎZê(¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ ÏÖ´~Ãö_ü|}®ÛÝÿSç'÷¸ÿW¿ßÓœV…gë?Ú?aû/þ>>×m¿îÿ©ó“ÎûÜ«ßïéÎ(BŠ( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Ãßòñgý…cÿÒ+Zè+Ÿð÷ü‡VíÛ7 m¹ÀÎ3ŒàV…QEQEQEQEQEQEQEQEQEQEQEQEŸ¬ÿhý†?ì¿øøû]¶ÿ»þ§ÎO;ïqþ¯¿§8­ äüuâ[¿ X[ϼ6Ic([lN’ËVà–·K–ÎF<¼¹–€:Ê*8 Ío\Gs”D˪¶9ˆŒ÷ÀÏ ©(®~óþJÿ`«ÿýi]s÷ŸòPôoû_ÿèÛJè(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š+Ÿð'ü“Ï Ø*×ÿE-tÏøþI熿ìkÿ¢–€: (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€3ô-OûoÃÚf­äù?n´ŠçÊÝ»fô ·8Æqœ Ьý SþÛðö™«y>OÛ­"¹ò·nÙ½mÎqœg´(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(®].¼I©ëZÔ†•ikav–ȳéòNí˜"”±a:²‘Œv®¢¹ÿÈsÅŸöÿH­hûŒ?è;¡ÿàšoþJ¬½sž!ñ ½´úÞŒRÞánf‘0É«+µa‘ÑH «°÷¥ÏýÆôÐÿðM7ÿ%Qö?ÐwCÿÁ4ßü•]ÏýÆôÐÿðM7ÿ%U94K¬Ûj¯èÞ}½¼Öè£G—iY6bÒsœÄ¸ç¹ëÛ¬¢€9ÿ±øÃþƒºþ ¦ÿäª>Çãúèø&›ÿ’« ¢€9ÿ±øÃþƒºþ ¦ÿäª>Çãúèø&›ÿ’« ¢€9ÿ±øÃþƒºþ ¦ÿ䪧©\ø«FŠÚîãSÑ®`kÛ[y"K–&+,ñÄHcpÀ=Jë+Ÿñ—ü€í¿ì+¦ÿél4ÐQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEâ}BóLÑDöº’îÖÙẍ¾lñÄX¨e'ÉÆGJ¯ö?ÐwCÿÁ4ßü•GŒ¿ämÿa]7ÿKa®‚€9ÿ±øÃþƒºþ ¦ÿäª>Çãúèø&›ÿ’« ¢€9ÿ±øÃþƒºþ ¦ÿäª>Çãúèø&›ÿ’« ¢€9ÿ±øÃþƒºþ ¦ÿ䪧¤è>*Ñ´k.ß_Ñš +xíãi4yKE ÅÈÀôÖQ@ÿØüaÿAÝÿÓòUcñ‡ýt?üMÿÉUÐQ@ÿØüaÿAÝÿÓòUcñ‡ýt?üMÿÉUÐQ@ÿØüaÿAÝÿÓòUIáÛýJê]bÓT–ÒiôûÑn²ÚÀЫ©‚rUÈ9”޽…nW?áïùx³þ±ÿé­tQEQEQEQEgèZŸö߇´Í[Éò~ÝiÏ•»vÍènp3Œã8¡Yú§ý·áí3Vò|Ÿ·ZEsånݳzÛœ ã8ÎhPEPEPEPEPEPEPEPEPEPEP{ëû=2ÎKËû¸--cÆù§F‹’ËIñ¬øNüÿC^‡ÿƒøªÕô»vg½²šÞ6¡ ‚p ÆO¡ ;Ó¾-^jžðíÊXAm©Ýkvzf¡m8?,s!q4cp`®¸*[#ï›;š×Å¿hšÍÖžðêWqؼi}gmæÛÙ3¶Ð%pr=@žX1ï~ÜÈþ ¼³¸´·¼ÒÇûUC8KÁnªÆ ¨ªYA!ðH ’÷À~,´¼ñŸ êšQð÷ˆå’kµÔ#s=«Ì ÌbÙ€ÜFãØ9f±â/ŒZ«’'Ô¤³´2ûh ö0ÌÊÞRI"â`«Çldp?Åý/OÒt³wg}¨jsiQjWÐi6þh³FX´™a±~lòN ÆA8rü.ñ‡ômcÃ~ÖtÓáýQ3 Õ}¢eÙ VD*C($p:F㟫üÔ'ºÓoí±¯çe½¥í¶¥-ÂD²Åq‡‰¡Úħ'œ€ ‰ã_¾‰ðÎçÅz'‘sû¨&¶3£lt‘ÐW*ßuóÚ¹ý/Ç^$Hµ;ÛýGÀÚµ­†Ÿ=ãÛèš„†Ý®àpC ¤áOLn'2j¾ñ çÃ}KÁPÝé¯h–öpé·¼ÂF1²4ÆlïÚ )Ø« ëÂe×ü#––:¥,F8ïì¬!œƒÈ+†\•={p@à_ˆž)ñ&£¦™âðæ¥cy<ñi7&;» ¥Šg¨'h> Ÿ”7¬W‘Ùü7ñ6¥¬éWž Â6¯a{¡%ö•bßk¼•$HÄ(Ãå˜>ðW®P\ÿŒ¿ämÿa]7ÿKa®‚¹ÿÈÛþºoþ–Ã@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@ÿŒ¿ämÿa]7ÿKa®‚¹ÿÈÛþºoþ–Ã]ç~'ø“7…þ%ØhWvq¾‹qe×kÖ¬ó„ŽÄí°ã³“ŸD®/Uð2ëž?¸Õu8­.t[ û2[g-½ŸÏó7c aÈ Œf€)Ù|J·³ÐüO«øà··Òu»6Ý`S¾p›v(ŽéOL2p4/Æ/ G§jwˆ¾Ò®ôý¾f›¨@"»“pvG“»9õã«aH5‡að›Z¶ðUæ™.½kPë§ZÓõ *ª„3äÄãv -‚ «ü1ñ7Нá$Öµm6ËÅ)é_Ù±±µC™¥óbK1 Ã}Úаø·iuâûËKÈdÒt›ÞÞ JÙẂa2¨F †GFP ’\sÅnxOâ6—âíE¬-ì5[ ¯²-ìI¨[y^| qæ!‚¹#ž3ž3ƒŽ>ãá—‹º—bæ]VG[xš!ƒ¡ˆ8<•îv5/ øúËÆ~&Ö¨ð³Ñ¡híáXŒ€Ø9bđޣ99Àï(®/Oñ™¥x“ÅV·È÷m©Ç µµ‚K‰ö}ŽØo1Ƭá21»ÎrEv•ÇØèZ>·®xŸû[J±¿òuUòþ×n’ìÍ•®q¸g§   x£GñE™¹Òo ¸Uå‘%FtRNÂʤ”Ü@l7b f»£ë~oöN«cäãÌû%ÂK³9Æv“Œàõô5bÆÊ->Î;X^w3ƒ<ï3œ’yw%^çŽ*Ågéšî­ù¿Ù:­ÿ“3ì— .ÌçÚN3ƒ×ÐѦkº>·æÿdê¶7þN<ϲ\$»3œgi8Î_CZP~™®èúß›ý“ªØßù8ó>Ép’ìÎq¤ã8=} f»£ë~oöN«cäãÌû%ÂK³9Æv“Œàõô5¡Egéšî­ù¿Ù:­ÿ“3ì— .ÌçÚN3ƒ×ÐѦkº>·æÿdê¶7þN<ϲ\$»3œgi8Î_CZP~…©ÿmø{LÕ¼Ÿ'íÖ‘\ù[·lÞ¶ç8Î3ZQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@ÿŒ¿ämÿa]7ÿKa«~Òï¼Q§xŽx3©iñK/8ÁÝŽ[pœ ìq’¯ã/ùÛØWMÿÒØk  ¹ÿÈsÅŸöÿH­k ®lèšõ®­ª]ézÆ› …ÂÜ4WZkÌÈÂâÀeˆÍt”W?ö?ÐwCÿÁ4ßü•GØüaÿAÝÿÓòUtW?ö?ÐwCÿÁ4ßü•GØüaÿAÝÿÓòUtW?ö?ÐwCÿÁ4ßü•\üºßŒ"ñiçQÐÍ‚K¤·ßÙsÓ¡‘ce7<)Sß»—š4’v€zÏýÆôÐÿðM7ÿ%Qö?ÐwCÿÁ4ßü•@ÏýÆôÐÿðM7ÿ%Qö?ÐwCÿÁ4ßü•@sþ2ÿ·ý…tßý-†±øÃþƒºþ ¦ÿ䪯w¡ø“S[x/õ½)­c»·¹uƒJ’7o*T”(cpÀd Áë@EQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@ÿŒ¿ämÿa]7ÿKa®‚²üA¥M¬é?d·¹ŽÚu¸·¸ŽY"2¨h¦I@*I¦:޵Oì~0ÿ î‡ÿ‚i¿ù*€: +ŸûŒ?è;¡ÿàšoþJ£ì~0ÿ î‡ÿ‚i¿ù*€: +ŸûŒ?è;¡ÿàšoþJ£ì~0ÿ î‡ÿ‚i¿ù*€: +Ïì5¿^k’Ù>£¡ÅfòÍ•çö\Ä\I¨TÜŒÌ 2Kdnz±øÃþƒºþ ¦ÿäªè(®ì~0ÿ î‡ÿ‚i¿ù*±øÃþƒºþ ¦ÿäªè(®ì~0ÿ î‡ÿ‚i¿ù*±øÃþƒºþ ¦ÿäªè+Ÿð÷ü‡Ÿz-Ö[Xu0C.J³¹2‘×°­Êçü=ÿ!ÏØV?ý"µ ‚Š( Š( Š( Š( Š( Š( Š( Š( Š(  {ïxoL¼’ÎÿÄU¥Ôxß ÷±ÆëÊ“‘Aük/VñO‚uûK¸ñnŒ°^ÛÉo#G©B+©RFIÁô5sÃßòñgý…cÿÒ+Zè(ƒñg<+s£Û¤%Ñ¥q©Ø9T¿‰ˆU»…˜ðÝ‚Iì5¹ÿ ߃ÿèkÐÿðcÿ]ÏÿÂwàÿúô?üÃÿÅV…ãO ìxåñ.Œ‰6¦5ü@:ý’Ýr¿7#r°Èîí]åÏÿÂwàÿúô?üÃÿÅTxÓ·W[Ûø—Fšy\$qÇ3±8Ù$ž1[•ÏøËþ@vßöÓô¶è(¢Š(¢Š(¢Š+ûÅžÓ/$³¿ñ•iu7Ã=ìqºä2¤ädØ®Ãßòñgý…cÿÒ+Z?á;ðý zþ aÿâ¨ÿ„ïÁÿô5èø1‡ÿŠ®‚Šçÿá;ðý zþ aÿâ«Á~4ð­¯|=oqâ]âÓ-’H俉YD ‚ dxÅw”P?ÿ ߃ÿèkÐÿðcÿGü'~ÿ¡¯CÿÁŒ?üUtP4ð­ÕÄVöþ%ѦžW qßÄÌìN6I'ŒVåsþ2ÿ·ý…tßý-†º (¢Š(¢Š(¢Š(¢Š(¢Š(¢Š*½õýž™g%åýÜ–±ã|ÓÈ#EÉe$øÕŠçüeÿ ;oû é¿ú[ ðø?þ†½ÿ0ÿñTÂwàÿúô?üÃÿÅWAEsÿðø?þ†½ÿ0ÿñTÂwàÿúô?üÃÿÅWAEp~,ñ§…nt{tƒÄº4®5;*—ñ1 ·p³ PI=€&·?á;ðý zþ aÿâ« ¢€9ÿøNüÿC^‡ÿƒøª?á;ðý zþ aÿâ« ¢€9ÿøNüÿC^‡ÿƒøª’xVêâ+{èÓO+„Ž8ïâfv'$“Æ+r¹ÿÈÛþºoþ–Ã@Q@Q@ï¯ìôË9//îà´µæžA.H,x$Ʊÿá;ðý zþ aÿâ¨ñ—ü€í¿ì+¦ÿél5ÐP?ÿ ߃ÿèkÐÿðcÿGü'~ÿ¡¯CÿÁŒ?üUtP?ÿ ߃ÿèkÐÿðcÿGü'~ÿ¡¯CÿÁŒ?üUtPkãO ¯Žµk†ñ.Œ }2ÉCÖe–è°v ”‘Ûpõ­ÏøNüÿC^‡ÿƒøªè( þ¿ÿÐסÿàÆþ*øNüÿC^‡ÿƒøªè( þ¿ÿÐסÿàÆþ*øNüÿC^‡ÿƒøªè( þ¿ÿÐסÿàÆþ*µ4Ý[MÖmÚãKÔ-/ W(ÒZ̲¨l‚T‘œqî*åsþÿç‹?ì+þ‘ZÐAEPEP=÷‹<7¦^Igâ *Òê,ðÞ§y‡ˆ4«»©3²/c‘Û“…'øUÿÉ<ñ/ý‚®¿ôSWŒOÿWöwÂÏìŸì¯øH´4ï´ý‹o™· æùÛ?‹~Ï¿ógv?Š€=þû;‹Ë›8nà’ê×oÚ!Ix· ®å®G#=jÅ|ÿâ»R‰Þ ‡HÕgÓ.®üA¡Û}¢pÚQó.@uÎ SÁÇ5îšUƒiš]½‹ß]ßgÚ.ÙZW·Ž3Œœdää •ÏøËþ@vßöÓô¶è+Ÿñ—ü€í¿ì+¦ÿél4ÐQEQEQEW?áïùx³þ±ÿé­tÏø{þCž,ÿ°¬úEk@G<ðÚÛËqq,pÁ’I*¢’I<9©+Ÿñßü“ÏÿØ*ëÿE5X±ñg†õ;ÈììÉ ës“ý¿ýPhܱ|ÍÛK®;W!áÛ«Ë|0¼Ó­þÑ}¦¿-¼; ù’*HUvŽNH“@GÑ_8|3ño‰ï¼i ÜÜê—×ÑêRÜE{êðL!ÙYmI ¦Ì“Ü / ¾ Æ_ò¶ÿ°®›ÿ¥°×A\ÿŒ¿ämÿa]7ÿKa®‚€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ çüeÿ ;oû é¿ú[ tÏøËþ@vßöÓô¶è(¢ŠÇ¾ñg†ôËÉ,ïüA¥Z]GðÏ{n¹Œ©9Æ´,oìõ;8ï,.໵“;&‚A"6  88 ¼ßâ¾…£ÉyáKÉ4«º»ñ-”35ºš2vÆYpÁãX®bðþ©¥ø÷‘èð25õΟ¦\C¤ÌbCtä.r„$…bO €dšþÎÞòÚÎk¸#ººÝöx^@]£-µO-ÉÇJ&¿³·¼¶³šîî®·}ž—hËmSË`rqÒ¾|MRï\Òþ^k&“M–TÕ"ŸVy#_Ýýöà; Øò äõ,¼K­\ë•nã×/ô;zÎÆéc.o„VˆÑ3b\± prF9'$€}E|áðÏž'¾ñ¦ƒssª__G©Kqì«Á0|‡eeµ$4 ›2Op0¼0Vú>€ çüeÿ ;oû é¿ú[ tÍøäLÞmäŽ9Χ§ˆÞD.ªßl‡¨ ‘žÙõÒQ^g¥éÄ 6µ®i²[»»Ã­¯™onpÖ2ÁÚ£÷£'-‚ªÕÖ}ÆôÐÿðM7ÿ%PAEsÿcñ‡ýt?üMÿÉT}ÆôÐÿðM7ÿ%Pã/ùÛØWMÿÒØk ®Ä–Þ$O³kýWJžÔjºvøàÓ$‰Ûý2aŒì8þéÇZî(¬½Kĺp¶ú¦·¦ØÎÈcººH˜®HÈ AÆAö5©^7ã]'RÖ~6Co¥éþ¾|8®Ñë°´°ûK ¨PNüô-@±¦êÚn³n×^¡i}¹F’Öe•C`¤ŒàƒqW+ç‹cTð—Áï >É©ÜÚj²YňmIà›lsx"–ä8ÁÚFŸã-q|A…µ/Ýéš¡q²_^-BâÝ@  º„Ê36Œ6–Þõàú†³yck¦h ñyü9s­Íþ"…ËÍb(¥ŽÝ®Ê³¸/’0;*ºõZ°øyãÛ;ÝêvMŘÓ5Äœ™\K"4‘ùêpå$üÇ8(÷ʯcg©ÙÇyawݬ™Ù4 °H8aÁÁ~á÷sê:w5m'Äßõ].=&ÑotéÝ!y#€ò9P1,k eüÇ"œ+g¸ø%ÿ$‡Bÿ·ý(’€=Š( ¹ÿÈsÅŸöÿH­k ¯/Õl|{}®x<«éZ|cUC;]ÄL™VØ•u*sÓh ¨ä‚@õ +Ñ,|jº<}bÆ)¾bë¥3ÌX±$±Žñ““È v€@@Ú4>Çãúèø&›ÿ’¨ ¢¹ÿ±øÃþƒºþ ¦ÿäª>Çãúèø&›ÿ’¨ð÷ü‡¼oÝëZ–£®ÚÜßi³Ëo “ª/XB—)·£ä2€ôQEsþÿç‹?ì+þ‘Z×A\ÿ‡¿ä9âÏû Çÿ¤VµÐPEPEPEPEPEPEP?áïùx³þ±ÿé­tÅéþ%ÐtoxªßTÖôÛÛSÖ;«¤‰ŠýŽØd ã ŒûÔÿ„ïÁÿô5èø1‡ÿŠ  Éà†êÞ[{ˆ£š P¤‘È¡•ÔŒAà‚8ÅeØøOÃzeäw–Ò­.£ÎÉ ²Ž7\‚ Œ‚GãUÿá;ðý zþ aÿâ¨ÿ„ïÁÿô5èø1‡ÿŠ  t-{Ƽ—J±’饎v™íй’0Dn[Ü Qž+B¹ÿøNüÿC^‡ÿƒøª?á;ðý zþ aÿ⨠®Æ_ò¶ÿ°®›ÿ¥°Ñÿ ߃ÿèkÐÿðcÿXþ$ñg†õ=>ÎÎÃÄUÝÔš®²/c‘Û“…'øPqEPEPEP\ÿ‡¿ä9âÏû Çÿ¤VµÐW§ø—AѼIâ«}S[ÓlgmN7Xî®’&+ö;aƒŒ‚3ìh´¨ç‚«ymî"Žh%B’G"†WR0A‚ã‡ÿ ߃ÿèkÐÿðcÿGü'~ÿ¡¯CÿÁŒ?üUnA6¶ñ[ÛÅ0D#Ž5 ¨ `Æ*¾…£Ú}ìÚUŒ?aßöO.ÝìûþþÌ—w|c=ë?þ¿ÿÐסÿàÆþ*øNüÿC^‡ÿƒøªÐµÐ´{F}FÏJ±·¾Ÿws º$’n;›s“’9êkB¹ÿøNüÿC^‡ÿƒøª?á;ðý zþ aÿâ¨ñ—ü€í¿ì+¦ÿél5ÐWâOxoSÓìììoŸöo±Gåù˜Û¿n1»g®*¿ü'~ÿ¡¯CÿÁŒ?üUðø?þ†½ÿ0ÿñT¡ý…£ÿcÿdeXÿfÏ—ÙÓÉûÛ¾æ6ýîzuæì-ûû#û*Çû3þ|¾ÎžOÞÝ÷1·ïsÓ¯5Ÿÿ ߃ÿèkÐÿðcÿGü'~ÿ¡¯CÿÁŒ?üUhjz­ù_ÚÚUÿ“Ÿ/ívé.Ìã8Ü3ÓÐU‹ =2Î;; H--cÎÈ`ŒF‹’IÂŽI'ñ¬øNüÿC^‡ÿƒøª?á;ðý zþ aÿ⨠¢¹ÿøNüÿC^‡ÿƒøª?á;ðý zþ aÿ⨠®oE3.§ã·Ž9'š˜ÒG(¬ßb¶À, g¾= Iÿ ߃ÿèkÐÿðcÿUü!g©ê)¼°»‚îÖMU6M‚DlYÛ†Gá@žÔ¯µÙê–•&•w:{)$ÞÑ ¹8•ÁÁŒàò+RŠ(¬{=BòOêºdƵ‚Òֿܤe]|Æ™YX–!¹‡ €¿{ã5±Y餤~!›Y[™Ä“ZGk$/–ÁÙ[îîÜ<Ç{=2  ÿÈsÅŸöÿH­k ®/Oñ.ƒ£x“ÅVú¦·¦ØÎÚœn±Ý]$LWìvà 1gØÖ§ü'~ÿ¡¯CÿÁŒ?üUnO7VòÛÜEÐJ…$ŽE ®¤`‚Æ(‚mmâ··Š8`‰GjQ@ÀŒVü'~ÿ¡¯CÿÁŒ?üUðø?þ†½ÿ0ÿñTrO h3ipérèškéð¾ø­Õ HÜò©Œó7 w>µat5^É×O´`…,ØB¹·R¡H`cŠËÿ„ïÁÿô5èø1‡ÿŠ£þ¿ÿÐסÿàÆþ*€,Â'á¿ííøGô¯·y¾Ú~Å™ægvýØÎìóž¹­Šçÿá;ðý zþ aÿâ¨ÿ„ïÁÿô5èø1‡ÿŠ ‚Šçÿá;ðý zþ aÿâ¨ÿ„ïÁÿô5èø1‡ÿŠ Ãßòñgý…cÿÒ+Zè+—ð…ýž§¨x¦òÂî »Y5TÙ4 ±glppA…uQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEÿÙscons-doc-2.3.0/doc/design/overview.xml0000644000175000017500000003163512114661557020640 0ustar dktrkranzdktrkranz
Architecture The heart of &SCons; is its Build Engine. The &SCons; Build Engine is a Python module that manages dependencies between external objects such as files or database records. The Build Engine is designed to be interface-neutral and easily embeddable in any software system that needs dependency analysis between updatable objects. The key parts of the Build Engine architecture are captured in the following quasi-UML diagram: The point of &SCons; is to manage dependencies between arbitrary external objects. Consequently, the Build Engine does not restrict or specify the nature of the external objects it manages, but instead relies on subclass of the &Node; class to interact with the external system or systems (file systems, database management systems) that maintain the objects being examined or updated. The Build Engine presents to the software system in which it is embedded a Python API for specifying source (input) and target (output) objects, rules for building/updating objects, rules for scanning objects for dependencies, etc. Above its Python API, the Build Engine is completely interface-independent, and can be encapsulated by any other software that supports embedded Python. Software that chooses to use the Build Engine for dependency management interacts with it through Construction Environments. A Construction Environment consists of a dictionary of environment variables, and one or more associated &Scanner; objects and &Builder; objects. The Python API is used to form these associations. A &Scanner; object specifies how to examine a type of source object (C source file, database record) for dependency information. A &Scanner; object may use variables from the associated Construction Environment to modify how it scans an object: specifying a search path for included files, which field in a database record to consult, etc. A &Builder; object specifies how to update a type of target object: executable program, object file, database field, etc. Like a &Scanner; object, a &Builder; object may use variables from the associated Construction Environment to modify how it builds an object: specifying flags to a compiler, using a different update function, etc. &Scanner; and &Builder; objects will return one or more &Node; objects that represent external objects. &Node; objects are the means by which the Build Engine tracks dependencies: A &Node; may represent a source (input) object that should already exist, or a target (output) object which may be built, or both. The &Node; class is sub-classed to represent external objects of specific type: files, directories, database fields or records, etc. Because dependency information, however, is tracked by the top-level &Node; methods and attributes, dependencies can exist between nodes representing different external object types. For example, building a file could be made dependent on the value of a given field in a database record, or a database table could depend on the contents of an external file. The Build Engine uses a &Job; class (not displayed) to manage the actual work of updating external target objects: spawning commands to build files, submitting the necessary commands to update a database record, etc. The &Job; class has sub-classes to handle differences between spawning jobs in parallel and serially. The Build Engine also uses a &Signature; class (not displayed) to maintain information about whether an external object is up-to-date. Target objects with out-of-date signatures are updated using the appropriate &Builder; object.
Build Engine More detailed discussion of some of the Build Engine's characteristics:
Python API The Build Engine can be embedded in any other software that supports embedding Python: in a GUI, in a wrapper script that interprets classic Makefile syntax, or in any other software that can translate its dependency representation into the appropriate calls to the Build Engine API. describes in detail the specification for a "Native Python" interface that will drive the &SCons; implementation effort.
Single-image execution When building/updating the objects, the Build Engine operates as a single executable with a complete Directed Acyclic Graph (DAG) of the dependencies in the entire build tree. This is in stark contrast to the commonplace recursive use of Make to handle hierarchical directory-tree builds.
Dependency analysis Dependency analysis is carried out via digital signatures (a.k.a. "fingerprints"). Contents of object are examined and reduced to a number that can be stored and compared to see if the object has changed. Additionally, &SCons; uses the same signature technique on the command-lines that are executed to update an object. If the command-line has changed since the last time, then the object must be rebuilt.
Customized output The output of Build Engine is customizable through user-defined functions. This could be used to print additional desired information about what &SCons; is doing, or tailor output to a specific build analyzer, GUI, or IDE.
Build failures &SCons; detects build failures via the exit status from the tools used to build the target files. By default, a failed exit status (non-zero on UNIX systems) terminates the build with an appropriate error message. An appropriate class from the Python library will interpret build-tool failures via an OS-independent API. If multiple tasks are executing in a parallel build, and one tool returns failure, &SCons; will not initiate any further build tasks, but allow the other build tasks to complete before terminating. A command-line option may be used to ignore errors and continue building other targets. In no case will a target that depends on a failed build be rebuilt.
Interfaces As previously described, the &SCons; Build Engine is interface-independent above its Python API, and can be embedded in any software system that can translate its dependency requirements into the necessary Python calls. The "main" &SCons; interface for implementation purposes, uses Python scripts as configuration files. Because this exposes the Build Engine's Python API to the user, it is current called the "Native Python" interface. This section will also discuss how &SCons; will function in the context of two other interfaces: the &Makefile; interface of the classic &Make; utility, and a hypothetical graphical user interface (GUI).
Native Python interface The Native Python interface is intended to be the primary interface by which users will know &SCons;--that is, it is the interface they will use if they actually type &SCons; at a command-line prompt. In the Native Python interface, &SCons; configuration files are simply Python scripts that directly invoke methods from the Build Engine's Python API to specify target files to be built, rules for building the target files, and dependencies. Additional methods, specific to this interface, are added to handle functionality that is specific to the Native Python interface: reading a subsidiary configuration file; copying target files to an installation directory; etc. Because configuration files are Python scripts, Python flow control can be used to provide very flexible manipulation of objects and dependencies. For example, a function could be used to invoke a common set of methods on a file, and called iteratively over an array of files. As an additional advantage, syntax errors in &SCons; Native Python configuration files will be caught by the Python parser. Target-building does not begin until after all configuration files are read, so a syntax error will not cause a build to fail half-way.
Makefile interface An alternate &SCons; interface would provide backwards compatibility with the classic &Make; utility. This would be done by embedding the &SCons; Build Engine in a Python script that can translate existing &Makefile;s into the underlying calls to the Build Engine's Python API for building and tracking dependencies. Here are approaches to solving some of the issues that arise from marrying these two pieces: &Makefile; suffix rules can be translated into an appropriate &Builder; object with suffix maps from the Construction Environment. Long lists of static dependences appended to a &Makefile; by various "make depend" schemes can be preserved but supplemented by the more accurate dependency information provided by &Scanner; objects. Recursive invocations of &Make; can be avoided by reading up the subsidiary &Makefile; instead. Lest this seem like too outlandish an undertaking, there is a working example of this approach: Gary Holt's &Makepp; utility is a Perl script that provides admirably complete parsing of complicated &Makefile;s around an internal build engine inspired, in part, by the classic Cons utility.
Graphical interfaces The &SCons; Build Engine is designed from the ground up to be embedded into multiple interfaces. Consequently, embedding the dependency capabilities of &SCons; into graphical interface would be a matter of mapping the GUI's dependency representation (either implicit or explicit) into corresponding calls to the Python API of the &SCons; Build Engine. Note, however, that this proposal leaves the problem of designed a good graphical interface for representing software build dependencies to people with actual GUI design experience...
scons-doc-2.3.0/doc/design/MANIFEST0000644000175000017500000000023212114661557017366 0ustar dktrkranzdktrkranzacks.xml bground.xml copyright.xml engine.fig engine.jpg engine.xml goals.xml install.xml intro.xml issues.xml main.xml native.xml overview.xml scons.mod scons-doc-2.3.0/doc/design/copyright.xml0000644000175000017500000000301012114661557020764 0ustar dktrkranzdktrkranz
Copyright (c) 2001 Steven Knight Portions of this document, by the same author, were previously published Copyright 2000 by CodeSourcery LLC, under the Software Carpentry Open Publication License, the terms of which are available at http://www.software-carpentry.com/openpub-license.html .
scons-doc-2.3.0/doc/design/engine.fig0000644000175000017500000001453412114661557020203 0ustar dktrkranzdktrkranz#FIG 3.2 Landscape Center Inches Letter 100.00 Single -2 1200 2 6 2100 8700 3600 9300 2 2 0 1 0 7 100 0 -1 4.000 0 0 -1 0 0 5 2100 8700 3600 8700 3600 9300 2100 9300 2100 8700 4 0 0 100 0 18 14 0.0000 4 165 900 2400 9075 Node.FS\001 -6 6 7050 6900 9000 7500 2 2 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 5 7050 6900 9000 6900 9000 7500 7050 7500 7050 6900 4 0 0 100 0 18 14 0.0000 4 165 1530 7200 7275 Intercessor.FS\001 -6 6 9450 6900 11400 7500 2 2 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 5 9450 6900 11400 6900 11400 7500 9450 7500 9450 6900 4 0 0 100 0 18 14 0.0000 4 165 1560 9600 7275 Intercessor.DB\001 -6 6 1200 4200 2400 4800 2 2 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 5 1200 4200 2400 4200 2400 4800 1200 4800 1200 4200 4 0 0 100 0 18 14 0.0000 4 165 870 1350 4575 Scanner\001 -6 6 2400 3300 3600 3900 2 2 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 5 2400 3300 3600 3300 3600 3900 2400 3900 2400 3300 4 0 0 100 0 18 14 0.0000 4 165 750 2625 3675 Builder\001 -6 6 8700 1650 10500 2250 2 2 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 5 8700 1650 10500 1650 10500 2250 8700 2250 8700 1650 4 0 0 100 0 18 14 0.0000 4 165 1185 9000 2025 Intercessor\001 -6 6 1500 1650 3300 2250 2 2 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 5 1500 1650 3300 1650 3300 2250 1500 2250 1500 1650 4 0 0 100 0 18 14 0.0000 4 165 1320 1725 2025 Environment\001 -6 6 7800 8700 9300 9300 2 2 0 1 0 7 100 0 -1 4.000 0 0 -1 0 0 5 7800 8700 9300 8700 9300 9300 7800 9300 7800 8700 4 0 0 100 0 18 14 0.0000 4 165 930 8100 9075 Node.DB\001 -6 6 1500 10200 2400 10800 2 2 0 1 0 7 100 0 -1 4.000 0 0 -1 0 0 5 1500 10200 2400 10200 2400 10800 1500 10800 1500 10200 4 0 0 100 0 18 14 0.0000 4 165 315 1800 10575 Dir\001 -6 6 3300 10200 4200 10800 2 2 0 1 0 7 100 0 -1 4.000 0 0 -1 0 0 5 3300 10200 4200 10200 4200 10800 3300 10800 3300 10200 4 0 0 100 0 18 14 0.0000 4 165 375 3600 10575 File\001 -6 6 6000 10200 7200 10800 2 2 0 1 0 7 100 0 -1 4.000 0 0 -1 0 0 5 6000 10200 7200 10200 7200 10800 6000 10800 6000 10200 4 0 0 100 0 18 14 0.0000 4 165 555 6300 10575 Table\001 -6 6 7800 10200 9300 10800 2 2 0 1 0 7 100 0 -1 4.000 0 0 -1 0 0 5 7800 10200 9300 10200 9300 10800 7800 10800 7800 10200 4 0 0 100 0 18 14 0.0000 4 165 765 8100 10575 Record\001 -6 6 9900 10200 11100 10800 2 2 0 1 0 7 100 0 -1 4.000 0 0 -1 0 0 5 9900 10200 11100 10200 11100 10800 9900 10800 9900 10200 4 0 0 100 0 18 14 0.0000 4 165 510 10200 10575 Field\001 -6 2 3 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 5 6900 5250 6825 5175 6900 5100 6975 5175 6900 5250 2 3 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 5 6300 5250 6225 5175 6300 5100 6375 5175 6300 5250 2 3 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 5 5700 5250 5625 5175 5700 5100 5775 5175 5700 5250 2 2 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 5 4800 2700 7200 2700 7200 5100 4800 5100 4800 2700 2 3 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 4 5100 5100 5025 5250 5175 5250 5100 5100 2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 1 0 5 0 0 1.00 60.00 120.00 6300 5250 6300 5700 8400 5700 8400 4200 7200 4200 2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 1 0 5 0 0 1.00 60.00 120.00 5700 5250 5700 6000 9000 6000 9000 3600 7200 3600 2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 2 5100 5250 5100 8100 2 3 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 5 4725 3675 4650 3600 4725 3525 4800 3600 4725 3675 2 3 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 5 4725 4575 4650 4500 4725 4425 4800 4500 4725 4575 2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 1 0 2 0 0 1.00 60.00 120.00 4650 3600 3600 3600 2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 1 0 2 0 0 1.00 60.00 120.00 4650 4500 2400 4500 2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 1 0 2 0 0 1.00 60.00 120.00 1800 2400 1800 4200 2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 1 0 2 0 0 1.00 60.00 120.00 3000 2400 3000 3300 2 1 1 1 0 7 100 0 -1 4.000 0 0 7 0 0 2 5850 1950 5850 2700 2 3 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 5 3000 2400 2925 2325 3000 2250 3075 2325 3000 2400 2 3 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 5 1800 2400 1725 2325 1800 2250 1875 2325 1800 2400 2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 2 3300 1950 8700 1950 2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 2 9600 2400 9600 6600 2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 4 7950 6900 7950 6600 10350 6600 10350 6900 2 3 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 4 9600 2250 9525 2400 9675 2400 9600 2250 2 1 0 1 0 7 100 0 -1 4.000 0 0 7 0 0 2 4800 3000 7200 3000 2 1 0 1 0 7 100 0 -1 4.000 0 0 -1 0 0 2 4800 3300 7200 3300 2 3 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 4 2850 9300 2775 9450 2925 9450 2850 9300 2 1 0 1 0 7 100 0 -1 4.000 0 0 -1 0 0 4 2100 10200 2100 9900 3750 9900 3750 10200 2 1 0 1 0 7 100 0 -1 4.000 0 0 -1 0 0 5 6600 10200 6600 9900 10500 9900 10500 10200 10500 10125 2 1 0 1 0 7 100 0 -1 4.000 0 0 -1 0 0 2 2850 9450 2850 9900 2 1 0 1 0 7 100 0 -1 4.000 0 0 -1 0 0 2 8475 9450 8475 10200 2 3 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 4 8475 9300 8400 9450 8550 9450 8475 9300 2 1 0 1 0 7 100 0 -1 4.000 0 0 -1 0 0 1 2775 6825 2 1 0 1 0 7 100 0 -1 4.000 0 0 -1 1 0 3 0 0 1.00 60.00 120.00 1800 10200 1800 9000 2100 9000 2 1 0 1 0 7 100 0 -1 4.000 0 0 -1 1 0 2 0 0 1.00 60.00 120.00 9900 10500 9300 10500 2 1 0 1 0 7 100 0 -1 4.000 0 0 -1 1 0 2 0 0 1.00 60.00 120.00 7800 10500 7200 10500 2 1 0 1 0 7 100 0 -1 4.000 0 0 7 0 0 4 2850 8700 2850 8100 8550 8100 8550 8700 2 1 1 1 0 7 100 0 -1 4.000 0 0 -1 1 0 3 0 0 1.00 60.00 120.00 10350 7500 10350 9000 9300 9000 2 1 1 1 0 7 100 0 -1 4.000 0 0 -1 1 0 3 0 0 1.00 60.00 120.00 7050 7200 2400 7200 2400 8700 2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 1 0 5 0 0 1.00 60.00 120.00 6900 5250 6900 5400 7800 5400 7800 4800 7200 4800 4 0 0 100 0 18 14 0.0000 4 165 555 4950 2925 Node\001 4 0 0 100 0 16 10 0.0000 4 150 825 7350 3525 dependency\001 4 0 0 100 0 16 10 0.0000 4 45 60 7425 3825 *\001 4 0 0 100 0 16 10 0.0000 4 120 555 7350 4125 srcnode\001 4 0 0 100 0 16 10 0.0000 4 120 90 7425 4425 1\001 4 0 0 100 0 16 10 0.0000 4 150 570 7350 4725 repnode\001 4 0 0 100 0 16 10 0.0000 4 120 90 7425 5025 1\001 4 0 0 100 0 16 10 0.0000 4 120 270 2550 4725 0..1\001 4 0 0 100 0 16 10 0.0000 4 120 270 3750 3825 0..1\001 4 0 0 100 0 0 12 0.0000 4 75 90 1875 4050 *\001 4 0 0 100 0 0 12 0.0000 4 75 90 3075 3150 *\001 4 0 0 100 0 16 14 0.0000 4 210 600 5100 3750 build()\001 4 0 0 100 0 16 14 0.0000 4 210 630 5100 4260 scan()\001 4 0 0 100 0 0 12 0.0000 4 135 90 9750 10725 1\001 4 0 0 100 0 16 10 0.0000 4 120 90 1650 10125 1\001 4 0 0 100 0 16 10 0.0000 4 45 60 1875 9225 *\001 4 0 0 100 0 16 10 0.0000 4 120 90 7650 10725 1\001 4 0 0 100 0 16 10 0.0000 4 45 60 7275 10725 *\001 4 0 0 100 0 16 10 0.0000 4 45 60 9375 10725 *\001 scons-doc-2.3.0/doc/design/intro.xml0000644000175000017500000001001512114661557020112 0ustar dktrkranzdktrkranz The &SCons; tool provides an easy-to-use, feature-rich interface for constructing software. Architecturally, &SCons; separates its dependency analysis and external object management into an interface-independent Build Engine that could be embedded in any software system that can run Python. At the command line, &SCons; presents an easily-grasped tool where configuration files are Python scripts, reducing the need to learn new build-tool syntax. Inexperienced users can use intelligent methods that ``do the right thing'' to build software with a minimum of fuss. Sophisticated users can use a rich set of underlying features for finer control of the build process, including mechanisms for easily extending the build process to new file types. Dependencies are tracked using digital signatures, which provide more robust dependency analysis than file time stamps. Implicit dependencies are determined automatically by scanning the contents of source files, avoiding the need for laborious and fragile maintenance of static lists of dependencies in configuration files. The &SCons; tool supports use of files from one or more central code repositories, a mechanism for caching derived files, and parallel builds. The tool also includes a framework for sharing build environments, which allows system administrators or integrators to define appropriate build parameters for use by other users.
About This Document This document is an ongoing work-in-progress to write down the ideas and tradeoffs that have gone, and will go into, the &SCons; design. As such, this is intended primarily for use by developers and others working on &SCons;, although it is also intended to serve as a detailed overview of &SCons; for other interested parties. It will be continually updated and evolve, and will likely overlap with other documentation produced by the project. Sections of this document that deal with syntax, for example, may move or be copied into a user guide or reference manual. So please don't assume that everything mentioned here has been decided and carved in stone. If you have ideas for improvements, or questions about things that don't seem to make any sense, please help improve the design by speaking up about them.
scons-doc-2.3.0/doc/design/install.xml0000644000175000017500000000222412114661557020430 0ustar dktrkranzdktrkranz scons-doc-2.3.0/doc/design/bground.xml0000644000175000017500000000676612114661557020441 0ustar dktrkranzdktrkranz Most of the ideas in &SCons; originate with &Cons;, a Perl-based software construction utility that has been in use by a small but growing community since its development by Bob Sidebotham at FORE Systems in 1996. The &Cons; copyright was transferred in 2000 from Marconi (who purchased FORE Systems) to the Free Software Foundation. I've been a principal implementer and maintainer of &Cons; for several years. &Cons; was originally designed to handle complicated software build problems (multiple directories, variant builds) while keeping the input files simple and maintainable. The general philosophy is that the build tool should ``do the right thing'' with minimal input from an unsophisticated user, while still providing a rich set of underlying functionality for more complicated software construction tasks needed by experts. In 2000, the Software Carpentry sought entries in a contest for a new, Python-based build tool that would provide an improvement over Make for physical scientists and other non-programmers struggling to use their computers more effectively. Prior to that, the idea of combining the superior build architecture of &Cons; with the easier syntax of Python had come up several times on the cons-discuss mailing list. The Software Carpentry contest provided the right motivation to spend some actual time working on a design document. After two rounds of competition, the submitted design, named ScCons, won the competition. Software Carpentry, however, did not immediately fund implementation of the build tool, instead contracting for additional, more detailed draft(s) of the design document. This proved to be not as strong motivation as actual coding, and after several months of inactivity, I essentially resigned from the Software Carpentry effort in early 2001 to start working on the tool independently. After half a year of prototyping some of the important infrastructure, I accumulated enough code to take the project public at SourceForge, renaming it &SCons; to distinguish it slightly from the version of the design that won the Software Carpentry contest while still honoring its roots there and in the original &Cons; utility. And also because it would be a teensy bit easier to type. scons-doc-2.3.0/doc/design/acks.xml0000644000175000017500000001227412114661557017711 0ustar dktrkranzdktrkranz I'm grateful to the following people for their influence, knowing or not, on the design of &SCons;: Bob Sidebotham First, as the original author of &Cons;, Bob did the real heavy lifting of creating the underlying model for dependency management and software construction, as well as implementing it in Perl. During the first years of &Cons;' existence, Bob did a skillful job of integrating input and code from the first users, and consequently is a source of practical wisdom and insight into the problems of real-world software construction. His continuing advice has been invaluable. The &SCons; Development Team A big round of thanks go to those brave souls who have gotten in on the ground floor: David Abrahams, Charles Crain, Steven Leblanc. Anthony Roach, and Steven Shaw. Their contributions, through their general knowledge of software build issues in general Python in particular, have made &SCons; what it is today. The &Cons; Community The real-world build problems that the users of &Cons; share on the cons-discuss mailing list have informed much of the thinking that has gone into the &SCons; design. In particular, Rajesh Vaidheeswarran, the current maintainer of &Cons;, has been a very steady influence. I've also picked up valuable insight from mailing-list participants Johan Holmberg, Damien Neil, Gary Oberbrunner, Wayne Scott, and Greg Spencer. Peter Miller Peter has indirectly influenced two aspects of the &SCons; design: Miller's influential paper Recursive Make Considered Harmful was what led me, indirectly, to my involvement with &Cons; in the first place. Experimenting with the single-Makefile approach he describes in RMCH led me to conclude that while it worked as advertised, it was not an extensible scheme. This solidified my frustration with Make and led me to try &Cons;, which at its core shares the single-process, universal-DAG model of the "RMCH" single-Makefile technique. The testing framework that Miller created for his Aegis change management system changed the way I approach software development by providing a framework for rigorous, repeatable testing during development. It was my success at using Aegis for personal projects that led me to begin my involvement with &Cons; by creating the cons-test regression suite. Stuart Stanley An experienced Python programmer, Stuart provided valuable advice and insight into some of the more useful Python idioms at my disposal during the original ScCons; design for the Software Carpentry contest. Gary Holt I don't know which came first, the first-round Software Carpentry contest entry or the tool itself, but Gary's design for &Makepp; showed me that it is possible to marry the strengths of &Cons;-like dependency management with backwards compatibility for &Makefile;s. Striving to support both &Makefile; compatibility and a native Python interface cleaned up the &SCons; design immeasurably by factoring out the common elements into the Build Engine. scons-doc-2.3.0/doc/design/native.xml0000644000175000017500000002152612114661557020256 0ustar dktrkranzdktrkranz The "Native Python" interface is the interface that the actual &SCons; utility will present to users. Because it exposes the Python Build Engine API, &SCons; users will have direct access to the complete functionality of the Build Engine. In contrast, a different user interface such as a GUI may choose to only use, and present to the end-user, a subset of the Build Engine functionality.
Configuration files &SCons; configuration files are simply Python scripts that invoke methods to specify target files to be built, rules for building the target files, and dependencies. Common build rules are available by default and need not be explicitly specified in the configuration files. By default, the &SCons; utility searches for a file named &SConstruct;, &Sconstruct; or &sconstruct; (in that order) in the current directory, and reads its configuration from the first file found. A command-line option exists to read a different file name.
Python syntax Because &SCons; configuration files are Python scripts, normal Python syntax can be used to generate or manipulate lists of targets or dependencies: sources = ['aaa.c', 'bbb.c', 'ccc.c'] env.Make('bar', sources) Python flow-control can be used to iterate through invocations of build rules: objects = ['aaa.o', 'bbb.o', 'ccc.o'] for obj in objects: src = replace(obj, '.o', '.c') env.Make(obj, src) or to handle more complicated conditional invocations: # only build 'foo' on Linux systems if sys.platform == 'linux1': env.Make('foo', 'foo.c') Because &SCons; configuration files are Python scripts, syntax errors will be caught by the Python parser. Target-building does not begin until after all configuration files are read, so a syntax error will not cause a build to fail half-way.
Subsidiary configuration Files A configuration file can instruct &SCons; to read up subsidiary configuration files. Subsidiary files are specified explicitly in a configuration file via the &SConscript; method. As usual, multiple file names may be specified with white space separation, or in an array: SConscript('other_file') SConscript('file1 file2') SConscript(['file3', 'file4']) SConscript(['file name with white space']) An explicit sconscript keyword may be used: SConscript(sconscript = 'other_file') Including subsidiary configuration files is recursive: a configuration file included via &SConscript; may in turn &SConscript; other configuration files.
Variable scoping in subsidiary files When a subsidiary configuration file is read, it is given its own namespace; it does not have automatic access to variables from the parent configuration file. Any variables (not just &SCons; objects) that are to be shared between configuration files must be explicitly passed in the &SConscript; call using the &Export; method: env = Environment() debug = Environment(CCFLAGS = '-g') installdir = '/usr/bin' SConscript('src/SConscript', Export(env=env, debug=debug, installdir=installdir)) Which may be specified explicitly using a keyword argument: env = Environment() debug = Environment(CCFLAGS = '-g') installdir = '/usr/bin' SConscript(sconscript = 'src/SConscript', export = Export(env=env, debug=debug, installdir=installdir)) Explicit variable-passing provides control over exactly what is available to a subsidiary file, and avoids unintended side effects of changes in one configuration file affecting other far-removed configuration files (a very hard-to-debug class of build problem).
Hierarchical builds The &SConscript; method is so named because, by convention, subsidiary configuration files in subdirectories are named &SConscript;: SConscript('src/SConscript') SConscript('lib/build_me') When a subsidiary configuration file is read from a subdirectory, all of that configuration file's targets and build rules are interpreted relative to that directory (as if &SCons; had changed its working directory to that subdirectory). This allows for easy support of hierarchical builds of directory trees for large projects.
Sharing &consenvs; &SCons; will allow users to share &consenvs;, as well as other &SCons; objects and Python variables, by importing them from a central, shared repository using normal Python syntax: from LocalEnvironments import optimized, debug optimized.Make('foo', 'foo.c') debug.Make('foo-d', 'foo.c') The expectation is that some local tool-master, integrator or administrator will be responsible for assembling environments (creating the &Builder; objects that specify the tools, options, etc.) and make these available for sharing by all users. The modules containing shared &consenvs; (LocalEnvironments in the above example) can be checked in and controlled with the rest of the source files. This allows a project to track the combinations of tools and command-line options that work on different platforms, at different times, and with different tool versions, by using already-familiar revision control tools.
Help The &SCons; utility provides a &Help; function to allow the writer of a &SConstruct; file to provide help text that is specific to the local build tree: Help(""" Type: scons . build and test everything scons test build the software scons src run the tests scons web build the web pages """) This help text is displayed in response to the command-line option. Calling the &Help; function more than once is an error.
Debug &SCons; supports several command-line options for printing extra information with which to debug build problems. See the -d, -p, -pa, and -pw options in the , below. All of these options make use of call-back functions to printed by the Build Engine.
scons-doc-2.3.0/doc/design/main.xml0000644000175000017500000001041512114661557017707 0ustar dktrkranzdktrkranz %version; --> %scons; ]> SCons Design version &buildversion; Steven Knight Revision &buildrevision; (&builddate;) 2001 2001 Steven Knight ©right; version &buildversion; Introduction &intro; Goals &goals; Overview &overview; Build Engine API &engine; Native Python Interface &native; Other Issues &issues; Background &bground; Summary &SCons; offers a robust and feature-rich design for an SC-build tool. With a Build Engine based on the proven design of the &Cons; utility, it offers increased simplification of the user interface for unsophisticated users with the addition of the "do-the-right-thing" env.Make method, increased flexibility for sophisticated users with the addition of &Builder; and &Scanner; objects, a mechanism to allow tool-masters (and users) to share working construction environments, and embeddability to provide reliable dependency management in a variety of environments and interfaces. Acknowledgements &acks; scons-doc-2.3.0/doc/reference/0000755000175000017500000000000012114661557016725 5ustar dktrkranzdktrkranzscons-doc-2.3.0/doc/reference/PostScript.xml0000644000175000017500000000232112114661557021557 0ustar dktrkranzdktrkranz X
The &PostScript; Method X
scons-doc-2.3.0/doc/reference/CXXFile.xml0000644000175000017500000000231612114661557020713 0ustar dktrkranzdktrkranz X
The &CXXFile; Method X
scons-doc-2.3.0/doc/reference/preface.xml0000644000175000017500000000301312114661557021051 0ustar dktrkranzdktrkranz X
Why &SCons;? X
History X
Conventions X
Acknowledgements X
Contact X
scons-doc-2.3.0/doc/reference/StaticObject.xml0000644000175000017500000000232312114661557022025 0ustar dktrkranzdktrkranz X
The &StaticObject; Method X
scons-doc-2.3.0/doc/reference/Alias.xml0000644000175000017500000000231412114661557020500 0ustar dktrkranzdktrkranz X
The &Alias; Method X
scons-doc-2.3.0/doc/reference/PCH.xml0000644000175000017500000000231212114661557020057 0ustar dktrkranzdktrkranz X
The &PCH; Method X
scons-doc-2.3.0/doc/reference/Library.xml0000644000175000017500000001167612114661557021066 0ustar dktrkranzdktrkranz
Linking With a Library env = Environment(CC = 'gcc', LIBS = 'world') env.Program('hello.c') % scons gcc -c hello.c -o hello.o gcc -c world.c -o world.o gcc -o hello hello.o -lworld
Creating a Library env = Environment(CC = 'gcc', LIBS = 'world') env.Program('hello.c') env.Library('world.c') % scons gcc -c hello.c -o hello.o gcc -c world.c -o world.o ar r libworld.a world.o ar: creating libworld.a ranlib libworld.a gcc -o hello hello.o libworld.a
The &Library; Builder X
scons-doc-2.3.0/doc/reference/PDF.xml0000644000175000017500000000231212114661557020056 0ustar dktrkranzdktrkranz X
The &PDF; Method X
scons-doc-2.3.0/doc/reference/SharedObject.xml0000644000175000017500000000232312114661557022004 0ustar dktrkranzdktrkranz X
The &SharedObject; Method X
scons-doc-2.3.0/doc/reference/errors.xml0000644000175000017500000000227312114661557020767 0ustar dktrkranzdktrkranz X
X X
scons-doc-2.3.0/doc/reference/InstallAs.xml0000644000175000017500000000232012114661557021336 0ustar dktrkranzdktrkranz X
The &InstallAs; Method X
scons-doc-2.3.0/doc/reference/StaticLibrary.xml0000644000175000017500000000232412114661557022224 0ustar dktrkranzdktrkranz X
The &StaticLibrary; Method X
scons-doc-2.3.0/doc/reference/Object.xml0000644000175000017500000000516712114661557020666 0ustar dktrkranzdktrkranz X
The &Object; Method X
scons-doc-2.3.0/doc/reference/SharedLibrary.xml0000644000175000017500000000232412114661557022203 0ustar dktrkranzdktrkranz X
The &SharedLibrary; Method X
scons-doc-2.3.0/doc/reference/CFile.xml0000644000175000017500000000231412114661557020431 0ustar dktrkranzdktrkranz X
The &CFile; Method X
scons-doc-2.3.0/doc/reference/MANIFEST0000644000175000017500000000040412114661557020054 0ustar dktrkranzdktrkranzAlias.xml CFile.xml CXXFile.xml Command.xml Install.xml InstallAs.xml Library.xml Object.xml PCH.xml PDF.xml PostScript.xml Program.xml RES.xml SharedLibrary.xml SharedObject.xml StaticLibrary.xml StaticObject.xml copyright.xml errors.xml main.xml preface.xml scons-doc-2.3.0/doc/reference/copyright.xml0000644000175000017500000000227712114661557021467 0ustar dktrkranzdktrkranz
SCons User's Guide Copyright (c) 2003 Steven Knight
scons-doc-2.3.0/doc/reference/RES.xml0000644000175000017500000000231212114661557020076 0ustar dktrkranzdktrkranz X
The &RES; Method X
scons-doc-2.3.0/doc/reference/Install.xml0000644000175000017500000000231612114661557021057 0ustar dktrkranzdktrkranz X
The &Install; Method X
scons-doc-2.3.0/doc/reference/Command.xml0000644000175000017500000000440512114661557021030 0ustar dktrkranzdktrkranz X
The &Command; Method X
scons-doc-2.3.0/doc/reference/Program.xml0000644000175000017500000000556612114661557021072 0ustar dktrkranzdktrkranz X
The &Program; Builder X
scons-doc-2.3.0/doc/reference/main.xml0000644000175000017500000001221612114661557020375 0ustar dktrkranzdktrkranz %version; %scons; ]> SCons Reference Manual &buildversion; Steven Knight Revision &buildrevision; (&builddate;) 2003 2003 Steven Knight ©right; version &buildversion; Preface &preface; Builder Reference
The Alias Builder &Alias_file;
The CFile Builder &CFile_file;
The Command Builder &Command_file;
The CXXFile Builder &CXXFile_file;
The Install Builder &Install_file;
The InstallAs Builder &InstallAs_file;
The Library Builder &Library_file;
The Object Builder &Object_file;
The PCH Builder &PCH_file;
The PDF Builder &PDF_file;
The PDF Builder &PostScript_file;
The Program Builder &Program_file;
The RES Builder &RES_file;
The SharedLibrary Builder &SharedLibrary_file;
The SharedObject Builder &SharedObject_file;
The StaticLibrary Builder &StaticLibrary_file;
The StaticObject Builder &StaticObject_file;
&ConsVar; Reference
AR X
Errors Generated by &SCons; &errors;
scons-doc-2.3.0/LICENSE0000644000175000017500000000216012114661557015226 0ustar dktrkranzdktrkranzCopyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation 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. scons-doc-2.3.0/SConstruct0000644000175000017500000014370112114661557016262 0ustar dktrkranzdktrkranz# # SConstruct file to build scons packages during development. # # See the README.rst file for an overview of how SCons is built and tested. # When this gets changed, you must also change the copyright_years string # in QMTest/TestSCons.py so the test scripts look for the right string. copyright_years = '2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013' # This gets inserted into the man pages to reflect the month of release. month_year = 'March 2013' # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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. # import distutils.util import fnmatch import os import os.path import re import stat import sys import tempfile project = 'scons' default_version = '2.3.0' copyright = "Copyright (c) %s The SCons Foundation" % copyright_years platform = distutils.util.get_platform() SConsignFile() # # An internal "whereis" routine to figure out if a given program # is available on this system. # def whereis(file): exts = [''] if platform == "win32": exts += ['.exe'] for dir in os.environ['PATH'].split(os.pathsep): f = os.path.join(dir, file) for ext in exts: f_ext = f + ext if os.path.isfile(f_ext): try: st = os.stat(f_ext) except: continue if stat.S_IMODE(st[stat.ST_MODE]) & 0111: return f_ext return None # # We let the presence or absence of various utilities determine whether # or not we bother to build certain pieces of things. This should allow # people to still do SCons packaging work even if they don't have all # of the utilities installed (e.g. RPM). # dh_builddeb = whereis('dh_builddeb') fakeroot = whereis('fakeroot') gzip = whereis('gzip') rpmbuild = whereis('rpmbuild') or whereis('rpm') hg = os.path.exists('.hg') and whereis('hg') svn = os.path.exists('.svn') and whereis('svn') unzip = whereis('unzip') zip = whereis('zip') # # Now grab the information that we "build" into the files. # date = ARGUMENTS.get('DATE') if not date: import time date = time.strftime("%Y/%m/%d %H:%M:%S", time.localtime(time.time())) developer = ARGUMENTS.get('DEVELOPER') if not developer: for variable in ['USERNAME', 'LOGNAME', 'USER']: developer = os.environ.get(variable) if developer: break build_system = ARGUMENTS.get('BUILD_SYSTEM') if not build_system: import socket build_system = socket.gethostname().split('.')[0] version = ARGUMENTS.get('VERSION', '') if not version: version = default_version hg_status_lines = [] svn_status_lines = [] if hg: cmd = "%s status --all 2> /dev/null" % hg hg_status_lines = os.popen(cmd, "r").readlines() if svn: cmd = "%s status --verbose 2> /dev/null" % svn svn_status_lines = os.popen(cmd, "r").readlines() revision = ARGUMENTS.get('REVISION', '') def generate_build_id(revision): return revision if not revision and hg: hg_heads = os.popen("%s heads 2> /dev/null" % hg, "r").read() cs = re.search('changeset:\s+(\S+)', hg_heads) if cs: revision = cs.group(1) b = re.search('branch:\s+(\S+)', hg_heads) if b: revision = b.group(1) + ':' + revision def generate_build_id(revision): result = revision if [l for l in hg_status_lines if l[0] in 'AMR!']: result = result + '[MODIFIED]' return result if not revision and svn: svn_info = os.popen("%s info 2> /dev/null" % svn, "r").read() m = re.search('Revision: (\d+)', svn_info) if m: revision = m.group(1) def generate_build_id(revision): result = 'r' + revision if [l for l in svn_status_lines if l[0] in 'ACDMR']: result = result + '[MODIFIED]' return result checkpoint = ARGUMENTS.get('CHECKPOINT', '') if checkpoint: if checkpoint == 'd': import time checkpoint = time.strftime('%Y%m%d', time.localtime(time.time())) elif checkpoint == 'r': checkpoint = 'r' + revision version = version + '.beta.' + checkpoint build_id = ARGUMENTS.get('BUILD_ID') if build_id is None: if revision: build_id = generate_build_id(revision) else: build_id = '' python_ver = sys.version[0:3] # Re-exporting LD_LIBRARY_PATH is necessary if the Python version was # built with the --enable-shared option. ENV = { 'PATH' : os.environ['PATH'] } for key in ['LOGNAME', 'PYTHONPATH', 'LD_LIBRARY_PATH']: if key in os.environ: ENV[key] = os.environ[key] build_dir = ARGUMENTS.get('BUILDDIR', 'build') if not os.path.isabs(build_dir): build_dir = os.path.normpath(os.path.join(os.getcwd(), build_dir)) command_line_variables = [ ("BUILDDIR=", "The directory in which to build the packages. " + "The default is the './build' subdirectory."), ("BUILD_ID=", "An identifier for the specific build." + "The default is the Subversion revision number."), ("BUILD_SYSTEM=", "The system on which the packages were built. " + "The default is whatever hostname is returned " + "by socket.gethostname()."), ("CHECKPOINT=", "The specific checkpoint release being packaged, " + "which will be appended to the VERSION string. " + "A value of CHECKPOINT=d will generate a string " + "of 'd' plus today's date in the format YYYMMDD. " + "A value of CHECKPOINT=r will generate a " + "string of 'r' plus the Subversion revision " + "number. Any other CHECKPOINT= string will be " + "used as is. There is no default value."), ("DATE=", "The date string representing when the packaging " + "build occurred. The default is the day and time " + "the SConstruct file was invoked, in the format " + "YYYY/MM/DD HH:MM:SS."), ("DEVELOPER=", "The developer who created the packages. " + "The default is the first set environment " + "variable from the list $USERNAME, $LOGNAME, $USER."), ("REVISION=", "The revision number of the source being built. " + "The default is the Subversion revision returned " + "'svn info', with an appended string of " + "'[MODIFIED]' if there are any changes in the " + "working copy."), ("VERSION=", "The SCons version being packaged. The default " + "is the hard-coded value '%s' " % default_version + "from this SConstruct file."), ] Default('.', build_dir) packaging_flavors = [ ('deb', "A .deb package. (This is currently not supported.)"), ('rpm', "A RedHat Package Manager file."), ('tar-gz', "The normal .tar.gz file for end-user installation."), ('src-tar-gz', "A .tar.gz file containing all the source " + "(including tests and documentation)."), ('local-tar-gz', "A .tar.gz file for dropping into other software " + "for local use."), ('zip', "The normal .zip file for end-user installation."), ('src-zip', "A .zip file containing all the source " + "(including tests and documentation)."), ('local-zip', "A .zip file for dropping into other software " + "for local use."), ] test_deb_dir = os.path.join(build_dir, "test-deb") test_rpm_dir = os.path.join(build_dir, "test-rpm") test_tar_gz_dir = os.path.join(build_dir, "test-tar-gz") test_src_tar_gz_dir = os.path.join(build_dir, "test-src-tar-gz") test_local_tar_gz_dir = os.path.join(build_dir, "test-local-tar-gz") test_zip_dir = os.path.join(build_dir, "test-zip") test_src_zip_dir = os.path.join(build_dir, "test-src-zip") test_local_zip_dir = os.path.join(build_dir, "test-local-zip") unpack_tar_gz_dir = os.path.join(build_dir, "unpack-tar-gz") unpack_zip_dir = os.path.join(build_dir, "unpack-zip") if platform == "win32": tar_hflag = '' python_project_subinst_dir = os.path.join("Lib", "site-packages", project) project_script_subinst_dir = 'Scripts' else: tar_hflag = 'h' python_project_subinst_dir = os.path.join("lib", project) project_script_subinst_dir = 'bin' import textwrap indent_fmt = ' %-26s ' Help("""\ The following aliases build packages of various types, and unpack the contents into build/test-$PACKAGE subdirectories, which can be used by the runtest.py -p option to run tests against what's been actually packaged: """) aliases = sorted(packaging_flavors + [('doc', 'The SCons documentation.')]) for alias, help_text in aliases: tw = textwrap.TextWrapper( width = 78, initial_indent = indent_fmt % alias, subsequent_indent = indent_fmt % '' + ' ', ) Help(tw.fill(help_text) + '\n') Help(""" The following command-line variables can be set: """) for variable, help_text in command_line_variables: tw = textwrap.TextWrapper( width = 78, initial_indent = indent_fmt % variable, subsequent_indent = indent_fmt % '' + ' ', ) Help(tw.fill(help_text) + '\n') zcat = 'gzip -d -c' # # Figure out if we can handle .zip files. # zipit = None unzipit = None try: import zipfile def zipit(env, target, source): print "Zipping %s:" % str(target[0]) def visit(arg, dirname, names): for name in names: path = os.path.join(dirname, name) if os.path.isfile(path): arg.write(path) zf = zipfile.ZipFile(str(target[0]), 'w') olddir = os.getcwd() os.chdir(env['CD']) try: os.path.walk(env['PSV'], visit, zf) finally: os.chdir(olddir) zf.close() def unzipit(env, target, source): print "Unzipping %s:" % str(source[0]) zf = zipfile.ZipFile(str(source[0]), 'r') for name in zf.namelist(): dest = os.path.join(env['UNPACK_ZIP_DIR'], name) dir = os.path.dirname(dest) try: os.makedirs(dir) except: pass print dest,name # if the file exists, then delete it before writing # to it so that we don't end up trying to write to a symlink: if os.path.isfile(dest) or os.path.islink(dest): os.unlink(dest) if not os.path.isdir(dest): open(dest, 'wb').write(zf.read(name)) except: if unzip and zip: zipit = "cd $CD && $ZIP $ZIPFLAGS $( ${TARGET.abspath} $) $PSV" unzipit = "$UNZIP $UNZIPFLAGS $SOURCES" def SCons_revision(target, source, env): """Interpolate specific values from the environment into a file. This is used to copy files into a tree that gets packaged up into the source file package. """ t = str(target[0]) s = source[0].rstr() contents = open(s, 'rb').read() # Note: We construct the __*__ substitution strings here # so that they don't get replaced when this file gets # copied into the tree for packaging. contents = contents.replace('__BUILD' + '__', env['BUILD']) contents = contents.replace('__BUILDSYS' + '__', env['BUILDSYS']) contents = contents.replace('__COPYRIGHT' + '__', env['COPYRIGHT']) contents = contents.replace('__DATE' + '__', env['DATE']) contents = contents.replace('__DEVELOPER' + '__', env['DEVELOPER']) contents = contents.replace('__FILE' + '__', str(source[0]).replace('\\', '/')) contents = contents.replace('__MONTH_YEAR'+ '__', env['MONTH_YEAR']) contents = contents.replace('__REVISION' + '__', env['REVISION']) contents = contents.replace('__VERSION' + '__', env['VERSION']) contents = contents.replace('__NULL' + '__', '') open(t, 'wb').write(contents) os.chmod(t, os.stat(s)[0]) revbuilder = Builder(action = Action(SCons_revision, varlist=['COPYRIGHT', 'VERSION'])) def soelim(target, source, env): """ Interpolate files included in [gnt]roff source files using the .so directive. This behaves somewhat like the soelim(1) wrapper around groff, but makes us independent of whether the actual underlying implementation includes an soelim() command or the corresponding command-line option to groff(1). The key behavioral difference is that this doesn't recursively include .so files from the include file. Not yet, anyway. """ t = str(target[0]) s = str(source[0]) dir, f = os.path.split(s) tfp = open(t, 'w') sfp = open(s, 'r') for line in sfp.readlines(): if line[:4] in ['.so ', "'so "]: sofile = os.path.join(dir, line[4:-1]) tfp.write(open(sofile, 'r').read()) else: tfp.write(line) sfp.close() tfp.close() def soscan(node, env, path): c = node.get_text_contents() return re.compile(r"^[\.']so\s+(\S+)", re.M).findall(c) soelimbuilder = Builder(action = Action(soelim), source_scanner = Scanner(soscan)) # When copying local files from a Repository (Aegis), # just make copies, don't symlink them. SetOption('duplicate', 'copy') env = Environment( ENV = ENV, BUILD = build_id, BUILDDIR = build_dir, BUILDSYS = build_system, COPYRIGHT = copyright, DATE = date, DEVELOPER = developer, DISTDIR = os.path.join(build_dir, 'dist'), MONTH_YEAR = month_year, REVISION = revision, VERSION = version, DH_COMPAT = 2, TAR_HFLAG = tar_hflag, ZIP = zip, ZIPFLAGS = '-r', UNZIP = unzip, UNZIPFLAGS = '-o -d $UNPACK_ZIP_DIR', ZCAT = zcat, RPMBUILD = rpmbuild, RPM2CPIO = 'rpm2cpio', TEST_DEB_DIR = test_deb_dir, TEST_RPM_DIR = test_rpm_dir, TEST_SRC_TAR_GZ_DIR = test_src_tar_gz_dir, TEST_SRC_ZIP_DIR = test_src_zip_dir, TEST_TAR_GZ_DIR = test_tar_gz_dir, TEST_ZIP_DIR = test_zip_dir, UNPACK_TAR_GZ_DIR = unpack_tar_gz_dir, UNPACK_ZIP_DIR = unpack_zip_dir, BUILDERS = { 'SCons_revision' : revbuilder, 'SOElim' : soelimbuilder }, PYTHON = '"%s"' % sys.executable, PYTHONFLAGS = '-tt', ) Version_values = [Value(version), Value(build_id)] # # Define SCons packages. # # In the original, more complicated packaging scheme, we were going # to have separate packages for: # # python-scons only the build engine # scons-script only the script # scons the script plus the build engine # # We're now only delivering a single "scons" package, but this is still # "built" as two sub-packages (the build engine and the script), so # the definitions remain here, even though we're not using them for # separate packages. # python_scons = { 'pkg' : 'python-' + project, 'src_subdir' : 'engine', 'inst_subdir' : os.path.join('lib', 'python1.5', 'site-packages'), 'rpm_dir' : '/usr/lib/scons', 'debian_deps' : [ 'debian/changelog', 'debian/control', 'debian/copyright', 'debian/dirs', 'debian/docs', 'debian/postinst', 'debian/prerm', 'debian/rules', ], 'files' : [ 'LICENSE.txt', 'README.txt', 'setup.cfg', 'setup.py', ], 'filemap' : { 'LICENSE.txt' : '../LICENSE.txt' }, 'buildermap' : {}, 'extra_rpm_files' : [], 'explicit_deps' : { 'SCons/__init__.py' : Version_values, }, } # Figure out the name of a .egg-info file that might be generated # as part of the RPM package. There are two complicating factors. # # First, the RPM spec file we generate will just execute "python", not # necessarily the one in sys.executable. If *that* version of python has # a distutils that knows about Python eggs, then setup.py will generate a # .egg-info file, so we have to execute any distutils logic in a subshell. # # Second, we can't just have the subshell check for the existence of the # distutils.command.install_egg_info module and generate the expected # file name by hand, the way we used to, because different systems can # have slightly different .egg-info naming conventions. (Specifically, # Ubuntu overrides the default behavior to remove the Python version # string from the .egg-info file name.) The right way to do this is to # actually call into the install_egg_info() class to have it generate # the expected name for us. # # This is all complicated enough that we do it by writing an in-line # script to a temporary file and then feeding it to a separate invocation # of "python" to tell us the actual name of the generated .egg-info file. print_egg_info_name = """ try: from distutils.dist import Distribution from distutils.command.install_egg_info import install_egg_info except ImportError: pass else: dist = Distribution({'name' : "scons", 'version' : '%s'}) i = install_egg_info(dist) i.finalize_options() import os.path print os.path.split(i.outputs[0])[1] """ % version try: fd, tfname = tempfile.mkstemp() tfp = os.fdopen(fd, "w") tfp.write(print_egg_info_name) tfp.close() egg_info_file = os.popen("python %s" % tfname).read()[:-1] if egg_info_file: python_scons['extra_rpm_files'].append(egg_info_file) finally: try: os.unlink(tfname) except EnvironmentError: pass # # The original packaging scheme would have have required us to push # the Python version number into the package name (python1.5-scons, # python2.0-scons, etc.), which would have required a definition # like the following. Leave this here in case we ever decide to do # this in the future, but note that this would require some modification # to src/engine/setup.py before it would really work. # #python2_scons = { # 'pkg' : 'python2-' + project, # 'src_subdir' : 'engine', # 'inst_subdir' : os.path.join('lib', 'python2.2', 'site-packages'), # # 'debian_deps' : [ # 'debian/changelog', # 'debian/control', # 'debian/copyright', # 'debian/dirs', # 'debian/docs', # 'debian/postinst', # 'debian/prerm', # 'debian/rules', # ], # # 'files' : [ # 'LICENSE.txt', # 'README.txt', # 'setup.cfg', # 'setup.py', # ], # 'filemap' : { # 'LICENSE.txt' : '../LICENSE.txt', # }, # 'buildermap' : {}, #} # scons_script = { 'pkg' : project + '-script', 'src_subdir' : 'script', 'inst_subdir' : 'bin', 'rpm_dir' : '/usr/bin', 'debian_deps' : [ 'debian/changelog', 'debian/control', 'debian/copyright', 'debian/dirs', 'debian/docs', 'debian/postinst', 'debian/prerm', 'debian/rules', ], 'files' : [ 'LICENSE.txt', 'README.txt', 'setup.cfg', 'setup.py', ], 'filemap' : { 'LICENSE.txt' : '../LICENSE.txt', 'scons' : 'scons.py', 'sconsign' : 'sconsign.py', 'scons-time' : 'scons-time.py', }, 'buildermap' : {}, 'extra_rpm_files' : [ 'scons-' + version, 'sconsign-' + version, 'scons-time-' + version, ], 'explicit_deps' : { 'scons' : Version_values, 'sconsign' : Version_values, }, } scons = { 'pkg' : project, 'debian_deps' : [ 'debian/changelog', 'debian/control', 'debian/copyright', 'debian/dirs', 'debian/docs', 'debian/postinst', 'debian/prerm', 'debian/rules', ], 'files' : [ 'CHANGES.txt', 'LICENSE.txt', 'README.txt', 'RELEASE.txt', 'scons.1', 'sconsign.1', 'scons-time.1', 'script/scons.bat', #'script/scons-post-install.py', 'setup.cfg', 'setup.py', ], 'filemap' : { 'scons.1' : '$BUILDDIR/doc/man/scons.1', 'sconsign.1' : '$BUILDDIR/doc/man/sconsign.1', 'scons-time.1' : '$BUILDDIR/doc/man/scons-time.1', }, 'buildermap' : { 'scons.1' : env.SOElim, 'sconsign.1' : env.SOElim, 'scons-time.1' : env.SOElim, }, 'subpkgs' : [ python_scons, scons_script ], 'subinst_dirs' : { 'python-' + project : python_project_subinst_dir, project + '-script' : project_script_subinst_dir, }, } scripts = ['scons', 'sconsign', 'scons-time'] src_deps = [] src_files = [] for p in [ scons ]: # # Initialize variables with the right directories for this package. # pkg = p['pkg'] pkg_version = "%s-%s" % (pkg, version) src = 'src' if 'src_subdir' in p: src = os.path.join(src, p['src_subdir']) build = os.path.join(build_dir, pkg) tar_gz = os.path.join(build, 'dist', "%s.tar.gz" % pkg_version) platform_tar_gz = os.path.join(build, 'dist', "%s.%s.tar.gz" % (pkg_version, platform)) zip = os.path.join(build, 'dist', "%s.zip" % pkg_version) platform_zip = os.path.join(build, 'dist', "%s.%s.zip" % (pkg_version, platform)) if platform == "win-amd64": win32_exe = os.path.join(build, 'dist', "%s.win-amd64.exe" % pkg_version) else: win32_exe = os.path.join(build, 'dist', "%s.win32.exe" % pkg_version) # # Update the environment with the relevant information # for this package. # # We can get away with calling setup.py using a directory path # like this because we put a preamble in it that will chdir() # to the directory in which setup.py exists. # setup_py = os.path.join(build, 'setup.py') env.Replace(PKG = pkg, PKG_VERSION = pkg_version, SETUP_PY = '"%s"' % setup_py) Local(setup_py) # # Read up the list of source files from our MANIFEST.in. # This list should *not* include LICENSE.txt, MANIFEST, # README.txt, or setup.py. Make a copy of the list for the # destination files. # manifest_in = File(os.path.join(src, 'MANIFEST.in')).rstr() src_files = [x[:-1] for x in open(manifest_in).readlines()] raw_files = src_files[:] dst_files = src_files[:] rpm_files = [] MANIFEST_in_list = [] if 'subpkgs' in p: # # This package includes some sub-packages. Read up their # MANIFEST.in files, and add them to our source and destination # file lists, modifying them as appropriate to add the # specified subdirs. # for sp in p['subpkgs']: ssubdir = sp['src_subdir'] isubdir = p['subinst_dirs'][sp['pkg']] MANIFEST_in = File(os.path.join(src, ssubdir, 'MANIFEST.in')).rstr() MANIFEST_in_list.append(MANIFEST_in) files = [x[:-1] for x in open(MANIFEST_in).readlines()] raw_files.extend(files) src_files.extend([os.path.join(ssubdir, x) for x in files]) for f in files: r = os.path.join(sp['rpm_dir'], f) rpm_files.append(r) if f[-3:] == ".py": rpm_files.append(r + 'c') for f in sp.get('extra_rpm_files', []): r = os.path.join(sp['rpm_dir'], f) rpm_files.append(r) files = [os.path.join(isubdir, x) for x in files] dst_files.extend(files) for k, f in sp['filemap'].items(): if f: k = os.path.join(ssubdir, k) p['filemap'][k] = os.path.join(ssubdir, f) for f, deps in sp['explicit_deps'].items(): f = os.path.join(build, ssubdir, f) env.Depends(f, deps) # # Now that we have the "normal" source files, add those files # that are standard for each distribution. Note that we don't # add these to dst_files, because they don't get installed. # And we still have the MANIFEST to add. # src_files.extend(p['files']) # # Now run everything in src_file through the sed command we # concocted to expand SConstruct, 2.3.0, etc. # for b in src_files: s = p['filemap'].get(b, b) if not s[0] == '$' and not os.path.isabs(s): s = os.path.join(src, s) builder = p['buildermap'].get(b, env.SCons_revision) x = builder(os.path.join(build, b), s) Local(x) # # NOW, finally, we can create the MANIFEST, which we do # by having Python spit out the contents of the src_files # array we've carefully created. After we've added # MANIFEST itself to the array, of course. # src_files.append("MANIFEST") MANIFEST_in_list.append(os.path.join(src, 'MANIFEST.in')) def write_src_files(target, source, **kw): global src_files src_files.sort() f = open(str(target[0]), 'wb') for file in src_files: f.write(file + "\n") f.close() return 0 env.Command(os.path.join(build, 'MANIFEST'), MANIFEST_in_list, write_src_files) # # Now go through and arrange to create whatever packages we can. # build_src_files = [os.path.join(build, x) for x in src_files] Local(*build_src_files) distutils_formats = [] distutils_targets = [ win32_exe ] dist_distutils_targets = env.Install('$DISTDIR', distutils_targets) Local(dist_distutils_targets) AddPostAction(dist_distutils_targets, Chmod(dist_distutils_targets, 0644)) if not gzip: print "gzip not found in %s; skipping .tar.gz package for %s." % (os.environ['PATH'], pkg) else: distutils_formats.append('gztar') src_deps.append(tar_gz) distutils_targets.extend([ tar_gz, platform_tar_gz ]) dist_tar_gz = env.Install('$DISTDIR', tar_gz) dist_platform_tar_gz = env.Install('$DISTDIR', platform_tar_gz) Local(dist_tar_gz, dist_platform_tar_gz) AddPostAction(dist_tar_gz, Chmod(dist_tar_gz, 0644)) AddPostAction(dist_platform_tar_gz, Chmod(dist_platform_tar_gz, 0644)) # # Unpack the tar.gz archive created by the distutils into # build/unpack-tar-gz/scons-{version}. # # We'd like to replace the last three lines with the following: # # tar zxf $SOURCES -C $UNPACK_TAR_GZ_DIR # # but that gives heartburn to Cygwin's tar, so work around it # with separate zcat-tar-rm commands. # unpack_tar_gz_files = [os.path.join(unpack_tar_gz_dir, pkg_version, x) for x in src_files] env.Command(unpack_tar_gz_files, dist_tar_gz, [ Delete(os.path.join(unpack_tar_gz_dir, pkg_version)), "$ZCAT $SOURCES > .temp", "tar xf .temp -C $UNPACK_TAR_GZ_DIR", Delete(".temp"), ]) # # Run setup.py in the unpacked subdirectory to "install" everything # into our build/test subdirectory. The runtest.py script will set # PYTHONPATH so that the tests only look under build/test-{package}, # and under QMTest (for the testing modules TestCmd.py, TestSCons.py, # etc.). This makes sure that our tests pass with what # we really packaged, not because of something hanging around in # the development directory. # # We can get away with calling setup.py using a directory path # like this because we put a preamble in it that will chdir() # to the directory in which setup.py exists. # dfiles = [os.path.join(test_tar_gz_dir, x) for x in dst_files] env.Command(dfiles, unpack_tar_gz_files, [ Delete(os.path.join(unpack_tar_gz_dir, pkg_version, 'build')), Delete("$TEST_TAR_GZ_DIR"), '$PYTHON $PYTHONFLAGS "%s" install "--prefix=$TEST_TAR_GZ_DIR" --standalone-lib' % \ os.path.join(unpack_tar_gz_dir, pkg_version, 'setup.py'), ]) # # Generate portage files for submission to Gentoo Linux. # gentoo = os.path.join(build, 'gentoo') ebuild = os.path.join(gentoo, 'scons-%s.ebuild' % version) digest = os.path.join(gentoo, 'files', 'digest-scons-%s' % version) env.Command(ebuild, os.path.join('gentoo', 'scons.ebuild.in'), SCons_revision) def Digestify(target, source, env): import md5 src = source[0].rfile() contents = open(str(src)).read() sig = md5.new(contents).hexdigest() bytes = os.stat(str(src))[6] open(str(target[0]), 'w').write("MD5 %s %s %d\n" % (sig, src.name, bytes)) env.Command(digest, tar_gz, Digestify) if not zipit: print "zip not found; skipping .zip package for %s." % pkg else: distutils_formats.append('zip') src_deps.append(zip) distutils_targets.extend([ zip, platform_zip ]) dist_zip = env.Install('$DISTDIR', zip) dist_platform_zip = env.Install('$DISTDIR', platform_zip) Local(dist_zip, dist_platform_zip) AddPostAction(dist_zip, Chmod(dist_zip, 0644)) AddPostAction(dist_platform_zip, Chmod(dist_platform_zip, 0644)) # # Unpack the zip archive created by the distutils into # build/unpack-zip/scons-{version}. # unpack_zip_files = [os.path.join(unpack_zip_dir, pkg_version, x) for x in src_files] env.Command(unpack_zip_files, dist_zip, [ Delete(os.path.join(unpack_zip_dir, pkg_version)), unzipit, ]) # # Run setup.py in the unpacked subdirectory to "install" everything # into our build/test subdirectory. The runtest.py script will set # PYTHONPATH so that the tests only look under build/test-{package}, # and under QMTest (for the testing modules TestCmd.py, TestSCons.py, # etc.). This makes sure that our tests pass with what # we really packaged, not because of something hanging around in # the development directory. # # We can get away with calling setup.py using a directory path # like this because we put a preamble in it that will chdir() # to the directory in which setup.py exists. # dfiles = [os.path.join(test_zip_dir, x) for x in dst_files] env.Command(dfiles, unpack_zip_files, [ Delete(os.path.join(unpack_zip_dir, pkg_version, 'build')), Delete("$TEST_ZIP_DIR"), '$PYTHON $PYTHONFLAGS "%s" install "--prefix=$TEST_ZIP_DIR" --standalone-lib' % \ os.path.join(unpack_zip_dir, pkg_version, 'setup.py'), ]) if not rpmbuild: msg = "@echo \"Warning: Can not build 'rpm': no rpmbuild utility found\"" AlwaysBuild(Alias('rpm', [], msg)) else: topdir = os.path.join(build, 'build', 'bdist.' + platform, 'rpm') buildroot = os.path.join(build_dir, 'rpm-buildroot') BUILDdir = os.path.join(topdir, 'BUILD', pkg + '-' + version) RPMSdir = os.path.join(topdir, 'RPMS', 'noarch') SOURCESdir = os.path.join(topdir, 'SOURCES') SPECSdir = os.path.join(topdir, 'SPECS') SRPMSdir = os.path.join(topdir, 'SRPMS') specfile_in = os.path.join('rpm', "%s.spec.in" % pkg) specfile = os.path.join(SPECSdir, "%s-1.spec" % pkg_version) sourcefile = os.path.join(SOURCESdir, "%s.tar.gz" % pkg_version); noarch_rpm = os.path.join(RPMSdir, "%s-1.noarch.rpm" % pkg_version) src_rpm = os.path.join(SRPMSdir, "%s-1.src.rpm" % pkg_version) def spec_function(target, source, env): """Generate the RPM .spec file from the template file. This fills in the %files portion of the .spec file with a list generated from our MANIFEST(s), so we don't have to maintain multiple lists. """ c = open(str(source[0]), 'rb').read() c = c.replace('__VERSION' + '__', env['VERSION']) c = c.replace('__RPM_FILES' + '__', env['RPM_FILES']) open(str(target[0]), 'wb').write(c) rpm_files.sort() rpm_files_str = "\n".join(rpm_files) + "\n" rpm_spec_env = env.Clone(RPM_FILES = rpm_files_str) rpm_spec_action = Action(spec_function, varlist=['RPM_FILES']) rpm_spec_env.Command(specfile, specfile_in, rpm_spec_action) env.InstallAs(sourcefile, tar_gz) Local(sourcefile) targets = [ noarch_rpm, src_rpm ] cmd = "$RPMBUILD --define '_topdir $(%s$)' --buildroot %s -ba $SOURCES" % (topdir, buildroot) if not os.path.isdir(BUILDdir): cmd = ("$( mkdir -p %s; $)" % BUILDdir) + cmd t = env.Command(targets, specfile, cmd) env.Depends(t, sourcefile) dist_noarch_rpm = env.Install('$DISTDIR', noarch_rpm) dist_src_rpm = env.Install('$DISTDIR', src_rpm) Local(dist_noarch_rpm, dist_src_rpm) AddPostAction(dist_noarch_rpm, Chmod(dist_noarch_rpm, 0644)) AddPostAction(dist_src_rpm, Chmod(dist_src_rpm, 0644)) dfiles = [os.path.join(test_rpm_dir, 'usr', x) for x in dst_files] env.Command(dfiles, dist_noarch_rpm, "$RPM2CPIO $SOURCES | (cd $TEST_RPM_DIR && cpio -id)") if dh_builddeb and fakeroot: # Our Debian packaging builds directly into build/dist, # so we don't need to Install() the .debs. deb = os.path.join(build_dir, 'dist', "%s_%s_all.deb" % (pkg, version)) for d in p['debian_deps']: b = env.SCons_revision(os.path.join(build, d), d) env.Depends(deb, b) Local(b) env.Command(deb, build_src_files, [ "cd %s && fakeroot make -f debian/rules PYTHON=$PYTHON BUILDDEB_OPTIONS=--destdir=../../build/dist binary" % build, ]) old = os.path.join('lib', 'scons', '') new = os.path.join('lib', 'python' + python_ver, 'site-packages', '') def xxx(s, old=old, new=new): if s[:len(old)] == old: s = new + s[len(old):] return os.path.join('usr', s) dfiles = [os.path.join(test_deb_dir, xxx(x)) for x in dst_files] env.Command(dfiles, deb, "dpkg --fsys-tarfile $SOURCES | (cd $TEST_DEB_DIR && tar -xf -)") # # Use the Python distutils to generate the appropriate packages. # commands = [ Delete(os.path.join(build, 'build', 'lib')), Delete(os.path.join(build, 'build', 'scripts')), ] if distutils_formats: commands.append(Delete(os.path.join(build, 'build', 'bdist.' + platform, 'dumb'))) for format in distutils_formats: commands.append("$PYTHON $PYTHONFLAGS $SETUP_PY bdist_dumb -f %s" % format) commands.append("$PYTHON $PYTHONFLAGS $SETUP_PY sdist --formats=%s" % \ ','.join(distutils_formats)) commands.append("$PYTHON $PYTHONFLAGS $SETUP_PY bdist_wininst --plat-name win32 --user-access-control auto") env.Command(distutils_targets, build_src_files, commands) # # Now create local packages for people who want to let people # build their SCons-buildable packages without having to # install SCons. # s_l_v = '%s-local-%s' % (pkg, version) local = pkg + '-local' build_dir_local = os.path.join(build_dir, local) build_dir_local_slv = os.path.join(build_dir, local, s_l_v) dist_local_tar_gz = os.path.join("$DISTDIR/%s.tar.gz" % s_l_v) dist_local_zip = os.path.join("$DISTDIR/%s.zip" % s_l_v) AddPostAction(dist_local_tar_gz, Chmod(dist_local_tar_gz, 0644)) AddPostAction(dist_local_zip, Chmod(dist_local_zip, 0644)) commands = [ Delete(build_dir_local), '$PYTHON $PYTHONFLAGS $SETUP_PY install "--install-script=%s" "--install-lib=%s" --no-install-man --no-compile --standalone-lib --no-version-script' % \ (build_dir_local, build_dir_local_slv), ] for script in scripts: # add .py extension for scons-local scripts on non-windows platforms if platform == "win32": break local_script = os.path.join(build_dir_local, script) commands.append(Move(local_script + '.py', local_script)) rf = [x for x in raw_files if not x in scripts] rf = [os.path.join(s_l_v, x) for x in rf] for script in scripts: rf.append("%s.py" % script) local_targets = [os.path.join(build_dir_local, x) for x in rf] env.Command(local_targets, build_src_files, commands) scons_LICENSE = os.path.join(build_dir_local, 'scons-LICENSE') l = env.SCons_revision(scons_LICENSE, 'LICENSE-local') local_targets.append(l) Local(l) scons_README = os.path.join(build_dir_local, 'scons-README') l = env.SCons_revision(scons_README, 'README-local') local_targets.append(l) Local(l) if gzip: if platform == "win32": # avoid problem with tar interpreting c:/ as a remote machine tar_cargs = '-cz --force-local -f' else: tar_cargs = '-czf' env.Command(dist_local_tar_gz, local_targets, "cd %s && tar %s $( ${TARGET.abspath} $) *" % (build_dir_local, tar_cargs)) unpack_targets = [os.path.join(test_local_tar_gz_dir, x) for x in rf] commands = [Delete(test_local_tar_gz_dir), Mkdir(test_local_tar_gz_dir), "cd %s && tar xzf $( ${SOURCE.abspath} $)" % test_local_tar_gz_dir] env.Command(unpack_targets, dist_local_tar_gz, commands) if zipit: env.Command(dist_local_zip, local_targets, zipit, CD = build_dir_local, PSV = '.') unpack_targets = [os.path.join(test_local_zip_dir, x) for x in rf] commands = [Delete(test_local_zip_dir), Mkdir(test_local_zip_dir), unzipit] env.Command(unpack_targets, dist_local_zip, unzipit, UNPACK_ZIP_DIR = test_local_zip_dir) # # # Export('build_dir', 'env') SConscript('QMTest/SConscript') # # # files = [ 'runtest.py', ] def copy(target, source, env): t = str(target[0]) s = str(source[0]) open(t, 'wb').write(open(s, 'rb').read()) for file in files: # Guarantee that real copies of these files always exist in # build/. If there's a symlink there, then this is an Aegis # build and we blow them away now so that they'll get "built" later. p = os.path.join(build_dir, file) if os.path.islink(p): os.unlink(p) if not os.path.isabs(p): p = '#' + p sp = env.Command(p, file, copy) Local(sp) # # Documentation. # Export('build_dir', 'env', 'whereis') SConscript('doc/SConscript') # # If we're running in a Subversion working directory, pack up a complete # source archive from the project files and files in the change. # sfiles = None if hg_status_lines: slines = [l for l in hg_status_lines if l[0] in 'ACM'] sfiles = [l.split()[-1] for l in slines] elif svn_status_lines: slines = [l for l in svn_status_lines if l[0] in ' MA'] sentries = [l.split()[-1] for l in slines] sfiles = list(filter(os.path.isfile, sentries)) else: print "Not building in a Mercurial or Subversion tree; skipping building src package." if sfiles: remove_patterns = [ '.hgt/*', '.svnt/*', '*.aeignore', '*.hgignore', 'www/*', ] for p in remove_patterns: sfiles = [s for s in sfiles if not fnmatch.fnmatch(s, p)] if sfiles: ps = "%s-src" % project psv = "%s-%s" % (ps, version) b_ps = os.path.join(build_dir, ps) b_psv = os.path.join(build_dir, psv) b_psv_stamp = b_psv + '-stamp' src_tar_gz = os.path.join(build_dir, 'dist', '%s.tar.gz' % psv) src_zip = os.path.join(build_dir, 'dist', '%s.zip' % psv) Local(src_tar_gz, src_zip) for file in sfiles: env.SCons_revision(os.path.join(b_ps, file), file) b_ps_files = [os.path.join(b_ps, x) for x in sfiles] cmds = [ Delete(b_psv), Copy(b_psv, b_ps), Touch("$TARGET"), ] env.Command(b_psv_stamp, src_deps + b_ps_files, cmds) Local(*b_ps_files) if gzip: env.Command(src_tar_gz, b_psv_stamp, "tar cz${TAR_HFLAG} -f $TARGET -C build %s" % psv) # # Unpack the archive into build/unpack/scons-{version}. # unpack_tar_gz_files = [os.path.join(unpack_tar_gz_dir, psv, x) for x in sfiles] # # We'd like to replace the last three lines with the following: # # tar zxf $SOURCES -C $UNPACK_TAR_GZ_DIR # # but that gives heartburn to Cygwin's tar, so work around it # with separate zcat-tar-rm commands. env.Command(unpack_tar_gz_files, src_tar_gz, [ Delete(os.path.join(unpack_tar_gz_dir, psv)), "$ZCAT $SOURCES > .temp", "tar xf .temp -C $UNPACK_TAR_GZ_DIR", Delete(".temp"), ]) # # Run setup.py in the unpacked subdirectory to "install" everything # into our build/test subdirectory. The runtest.py script will set # PYTHONPATH so that the tests only look under build/test-{package}, # and under QMTest (for the testing modules TestCmd.py, # TestSCons.py, etc.). This makes sure that our tests pass with # what we really packaged, not because of something hanging around # in the development directory. # # We can get away with calling setup.py using a directory path # like this because we put a preamble in it that will chdir() # to the directory in which setup.py exists. # dfiles = [os.path.join(test_src_tar_gz_dir, x) for x in dst_files] scons_lib_dir = os.path.join(unpack_tar_gz_dir, psv, 'src', 'engine') ENV = env.Dictionary('ENV').copy() ENV['SCONS_LIB_DIR'] = scons_lib_dir ENV['USERNAME'] = developer env.Command(dfiles, unpack_tar_gz_files, [ Delete(os.path.join(unpack_tar_gz_dir, psv, 'build', 'scons', 'build')), Delete("$TEST_SRC_TAR_GZ_DIR"), 'cd "%s" && $PYTHON $PYTHONFLAGS "%s" "%s" VERSION="$VERSION"' % \ (os.path.join(unpack_tar_gz_dir, psv), os.path.join('src', 'script', 'scons.py'), os.path.join('build', 'scons')), '$PYTHON $PYTHONFLAGS "%s" install "--prefix=$TEST_SRC_TAR_GZ_DIR" --standalone-lib' % \ os.path.join(unpack_tar_gz_dir, psv, 'build', 'scons', 'setup.py'), ], ENV = ENV) if zipit: env.Command(src_zip, b_psv_stamp, zipit, CD = 'build', PSV = psv) # # Unpack the archive into build/unpack/scons-{version}. # unpack_zip_files = [os.path.join(unpack_zip_dir, psv, x) for x in sfiles] env.Command(unpack_zip_files, src_zip, [ Delete(os.path.join(unpack_zip_dir, psv)), unzipit ]) # # Run setup.py in the unpacked subdirectory to "install" everything # into our build/test subdirectory. The runtest.py script will set # PYTHONPATH so that the tests only look under build/test-{package}, # and under QMTest (for the testing modules TestCmd.py, # TestSCons.py, etc.). This makes sure that our tests pass with # what we really packaged, not because of something hanging # around in the development directory. # # We can get away with calling setup.py using a directory path # like this because we put a preamble in it that will chdir() # to the directory in which setup.py exists. # dfiles = [os.path.join(test_src_zip_dir, x) for x in dst_files] scons_lib_dir = os.path.join(unpack_zip_dir, psv, 'src', 'engine') ENV = env.Dictionary('ENV').copy() ENV['SCONS_LIB_DIR'] = scons_lib_dir ENV['USERNAME'] = developer env.Command(dfiles, unpack_zip_files, [ Delete(os.path.join(unpack_zip_dir, psv, 'build', 'scons', 'build')), Delete("$TEST_SRC_ZIP_DIR"), 'cd "%s" && $PYTHON $PYTHONFLAGS "%s" "%s" VERSION="$VERSION"' % \ (os.path.join(unpack_zip_dir, psv), os.path.join('src', 'script', 'scons.py'), os.path.join('build', 'scons')), '$PYTHON $PYTHONFLAGS "%s" install "--prefix=$TEST_SRC_ZIP_DIR" --standalone-lib' % \ os.path.join(unpack_zip_dir, psv, 'build', 'scons', 'setup.py'), ], ENV = ENV) for pf, help_text in packaging_flavors: Alias(pf, [ os.path.join(build_dir, 'test-'+pf), os.path.join(build_dir, 'QMTest'), os.path.join(build_dir, 'runtest.py'), ]) scons-doc-2.3.0/src/0000755000175000017500000000000012114661560015003 5ustar dktrkranzdktrkranzscons-doc-2.3.0/src/test_pychecker.py0000644000175000017500000001052612114661560020375 0ustar dktrkranzdktrkranz#!/usr/bin/env python # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/test_pychecker.py 2013/03/03 09:48:35 garyo" """ Use pychecker to catch various Python coding errors. """ import os import os.path import sys import TestSCons test = TestSCons.TestSCons() test.skip_test('Not ready for clean pychecker output; skipping test.\n') try: import pychecker except ImportError: pychecker = test.where_is('pychecker') if not pychecker: test.skip_test("Could not find 'pychecker'; skipping test(s).\n") program = pychecker default_arguments = [] else: pychecker = os.path.join(os.path.split(pychecker.__file__)[0], 'checker.py') program = sys.executable default_arguments = [pychecker] try: cwd = os.environ['SCONS_CWD'] except KeyError: src_engine = os.environ['SCONS_LIB_DIR'] else: src_engine = os.path.join(cwd, 'build', 'scons-src', 'src', 'engine') if not os.path.exists(src_engine): src_engine = os.path.join(cwd, 'src', 'engine') src_engine_ = os.path.join(src_engine, '') MANIFEST = os.path.join(src_engine, 'MANIFEST.in') files = open(MANIFEST).read().split() files = [f for f in files if f[-3:] == '.py'] ignore = [ 'SCons/compat/__init__.py', 'SCons/compat/_scons_UserString.py', 'SCons/compat/_scons_hashlib.py', 'SCons/compat/_scons_sets.py', 'SCons/compat/_scons_subprocess.py', 'SCons/compat/builtins.py', ] u = {} for file in files: u[file] = 1 for file in ignore: try: del u[file] except KeyError: pass files = sorted(u.keys()) mismatches = [] default_arguments.extend([ '--quiet', '--limit=1000', # Suppress warnings about unused arguments to functions and methods. # We have too many wrapper functions that intentionally only use some # of their arguments. '--no-argsused', ]) if sys.platform == 'win32': default_arguments.extend([ '--blacklist', '"pywintypes,pywintypes.error"', ]) per_file_arguments = { #'SCons/__init__.py' : [ # '--varlist', # '"__revision__,__version__,__build__,__buildsys__,__date__,__developer__"', #], } pywintypes_warning = "warning: couldn't find real module for class pywintypes.error (module name: pywintypes)\n" os.environ['PYTHONPATH'] = src_engine for file in files: args = (default_arguments + per_file_arguments.get(file, []) + [os.path.join(src_engine, file)]) test.run(program=program, arguments=args, status=None, stderr=None) stdout = test.stdout() stdout = stdout.replace(src_engine_, '') stderr = test.stderr() stderr = stderr.replace(src_engine_, '') stderr = stderr.replace(pywintypes_warning, '') if test.status or stdout or stderr: mismatches.append('\n') mismatches.append(' '.join([program] + args) + '\n') mismatches.append('STDOUT =====================================\n') mismatches.append(stdout) if stderr: mismatches.append('STDERR =====================================\n') mismatches.append(stderr) if mismatches: print ''.join(mismatches[1:]) test.fail_test() test.pass_test() # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/test_files.py0000644000175000017500000000604112114661560017517 0ustar dktrkranzdktrkranz#!/usr/bin/env python # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/test_files.py 2013/03/03 09:48:35 garyo" """ Verify that we have certain important files in our distribution packages. Note that this is a packaging test, not a functional test, so the name of this script doesn't end in *Tests.py. """ import os import os.path import re import TestSCons test = TestSCons.TestSCons() try: cwd = os.environ['SCONS_CWD'] except KeyError: cwd = os.getcwd() def build_path(*args): return os.path.join(cwd, 'build', *args) build_scons_tar_gz = build_path('unpack-tar-gz', 'scons-'+test.scons_version) build_scons_zip = build_path('unpack-zip', 'scons-'+test.scons_version) build_local_tar_gz = build_path('test-local-tar-gz') build_local_zip = build_path('test-local-zip') scons_files = [ 'CHANGES.txt', 'LICENSE.txt', 'README.txt', 'RELEASE.txt', ] local_files = [ 'scons-LICENSE', 'scons-README', ] # Map each directory to search (dictionary keys) to a list of its # subsidiary files and directories to exclude from copyright checks. check = { build_scons_tar_gz : scons_files, build_scons_zip : scons_files, build_local_tar_gz : local_files, build_local_zip : local_files, } missing = [] no_result = [] for directory, check_list in check.items(): if os.path.exists(directory): for c in check_list: f = os.path.join(directory, c) if not os.path.isfile(f): missing.append(f) else: no_result.append(directory) if missing: print "Missing the following files:\n" print "\t" + "\n\t".join(missing) test.fail_test(1) if no_result: print "Cannot check files, the following have apparently not been built:" print "\t" + "\n\t".join(no_result) test.no_result(1) test.pass_test() # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/test_interrupts.py0000644000175000017500000001172712114661560020643 0ustar dktrkranzdktrkranz#!/usr/bin/env python # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/test_interrupts.py 2013/03/03 09:48:35 garyo" """ Verify that the SCons source code contains only correct handling of keyboard interrupts (e.g. Ctrl-C). """ import os import os.path import re import time import TestSCons test = TestSCons.TestSCons() # We do not want statements of the form: # try: # # do something, e.g. # return a['x'] # except: # # do the exception handling # a['x'] = getx() # return a['x'] # # The code above may catch a KeyboardInterrupt exception, which was not # intended by the programmer. We check for these situations in all python # source files. try: cwd = os.environ['SCONS_CWD'] except KeyError: scons_lib_dir = os.environ['SCONS_LIB_DIR'] MANIFEST = os.path.join(scons_lib_dir, 'MANIFEST.in') else: #cwd = os.getcwd() scons_lib_dir = os.path.join(cwd, 'build', 'scons') MANIFEST = os.path.join(scons_lib_dir, 'MANIFEST') # We expect precisely this many uncaught KeyboardInterrupt exceptions # from the files in the following dictionary. expected_uncaught = { 'engine/SCons/Job.py' : 5, 'engine/SCons/Script/Main.py' : 1, 'engine/SCons/Taskmaster.py' : 3, } try: fp = open(MANIFEST) except IOError: test.skip_test('%s does not exist; skipping test.\n' % MANIFEST) else: files = fp.read().split() files = [f for f in files if f[-3:] == '.py'] # some regexps to parse the python files tryexc_pat = re.compile( r'^(?P(?P *)(try|except)( [^\n]*)?:.*)',re.MULTILINE) keyboardint_pat = re.compile(r' *except +([^,],)*KeyboardInterrupt([ ,][^\n]*)?:[^\n]*') exceptall_pat = re.compile(r' *except(?: *| +Exception *, *[^: ]+):[^\n]*') uncaughtKeyboardInterrupt = 0 for f in files: contents = open(os.path.join(scons_lib_dir, f)).read() try_except_lines = {} lastend = 0 while True: match = tryexc_pat.search( contents, lastend ) if match is None: break #print match.groups() lastend = match.end() try: indent_list = try_except_lines[match.group('indent')] except: indent_list = [] line_num = 1 + contents[:match.start()].count('\n') indent_list.append( (line_num, match.group('try_or_except') ) ) try_except_lines[match.group('indent')] = indent_list uncaught_this_file = [] for indent in try_except_lines.keys(): exc_keyboardint_seen = 0 exc_all_seen = 0 for (l,statement) in try_except_lines[indent] + [(-1,indent + 'try')]: #print "%4d %s" % (l,statement), m1 = keyboardint_pat.match(statement) m2 = exceptall_pat.match(statement) if statement.find(indent + 'try') == 0: if exc_all_seen and not exc_keyboardint_seen: uncaught_this_file.append(line) exc_keyboardint_seen = 0 exc_all_seen = 0 line = l #print " -> reset" elif m1 is not None: exc_keyboardint_seen = 1 #print " -> keyboard -> ", m1.groups() elif m2 is not None: exc_all_seen = 1 #print " -> all -> ", m2.groups() else: pass #print "Warning: unknown statement %s" % statement expected_num = expected_uncaught.get(f, 0) if expected_num != len(uncaught_this_file): uncaughtKeyboardInterrupt = 1 msg = "%s: expected %d uncaught interrupts, got %d:" print msg % (f, expected_num, len(uncaught_this_file)) for line in uncaught_this_file: print " File %s:%d: Uncaught KeyboardInterrupt!" % (f,line) test.fail_test(uncaughtKeyboardInterrupt) test.pass_test() # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/test_aegistests.py0000644000175000017500000000514312114661560020572 0ustar dktrkranzdktrkranz#!/usr/bin/env python # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/test_aegistests.py 2013/03/03 09:48:35 garyo" """ Verify that we have proper Copyright notices on all the right files in our distributions. Note that this is a packaging test, not a functional test, so the name of this script doesn't end in *Tests.py. """ import os import popen2 import re import sys import TestSCons test = TestSCons.TestSCons() try: popen2.Popen3 except AttributeError: def get_stdout(command): (tochild, fromchild, childerr) = os.popen3(command) tochild.close() return fromchild.read() else: def get_stdout(command): p = popen2.Popen3(command, 1) p.tochild.close() return p.fromchild.read() output = get_stdout('aegis -list -unformatted pf') +\ get_stdout('aegis -list -unformatted cf') lines = output.split('\n')[:-1] sources = [x for x in lines if x[:7] == 'source '] re1 = re.compile(r' src/.*Tests\.py') re2 = re.compile(r' src/test_.*\.py') re3 = re.compile(r' test/.*\.py') def filename_is_a_test(x): return re1.search(x) or re2.search(x) or re3.search(x) test_files = list(filter(filename_is_a_test, sources)) if test_files: sys.stderr.write("Found the following files with test names not marked as Aegis tests:\n") sys.stderr.write('\t' + '\n\t'.join(test_files) + '\n') test.fail_test(1) test.pass_test() # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/test_setup.py0000644000175000017500000002700612114661560017561 0ustar dktrkranzdktrkranz#!/usr/bin/env python # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/test_setup.py 2013/03/03 09:48:35 garyo" """ Test how the setup.py script installs SCons. Note that this is an installation test, not a functional test, so the name of this script doesn't end in *Tests.py. """ import os import os.path import shutil import sys try: WindowsError except NameError: WindowsError = OSError import TestSCons version = TestSCons.TestSCons.scons_version scons_version = 'scons-%s' % version python = TestSCons.python class MyTestSCons(TestSCons.TestSCons): _lib_modules = [ # A representative smattering of build engine modules. '__init__.py', 'Action.py', 'Builder.py', 'Environment.py', 'Util.py', ] _base_scripts = [ 'scons', 'sconsign', ] _version_scripts = [ 'scons-%s' % version, 'sconsign-%s' % version, ] _bat_scripts = [ 'scons.bat', ] _bat_version_scripts = [ 'scons-%s.bat' % version, ] _man_pages = [ 'scons.1', 'sconsign.1', ] def __init__(self): TestSCons.TestSCons.__init__(self) self.root = self.workpath('root') self.prefix = self.root + os.path.splitdrive(sys.prefix)[1] if sys.platform == 'win32': self.bin_dir = os.path.join(self.prefix, 'Scripts') self.bat_dir = self.prefix self.standalone_lib = os.path.join(self.prefix, 'scons') self.standard_lib = os.path.join(self.prefix, 'Lib', 'site-packages', '') self.version_lib = os.path.join(self.prefix, scons_version) self.man_dir = os.path.join(self.prefix, 'Doc') else: self.bin_dir = os.path.join(self.prefix, 'bin') self.bat_dir = self.bin_dir self.lib_dir = os.path.join(self.prefix, 'lib') self.standalone_lib = os.path.join(self.lib_dir, 'scons') self.standard_lib = os.path.join(self.lib_dir, 'python%s' % sys.version[:3], 'site-packages', '') self.version_lib = os.path.join(self.lib_dir, scons_version) self.man_dir = os.path.join(self.prefix, 'man', 'man1') self.prepend_bin_dir = lambda p: os.path.join(self.bin_dir, p) self.prepend_bat_dir = lambda p: os.path.join(self.bat_dir, p) self.prepend_man_dir = lambda p: os.path.join(self.man_dir, p) def run(self, *args, **kw): kw['chdir'] = scons_version kw['program'] = python kw['stderr'] = None return TestSCons.TestSCons.run(self, *args, **kw) def remove(self, dir): try: shutil.rmtree(dir) except (OSError, WindowsError): pass def stdout_lines(self): return self.stdout().split('\n') def lib_line(self, lib): return 'Installed SCons library modules into %s' % lib def lib_paths(self, lib_dir): return [os.path.join(lib_dir, 'SCons', p) for p in self._lib_modules] def scripts_line(self): return 'Installed SCons scripts into %s' % self.bin_dir def base_script_paths(self): scripts = self._base_scripts return list(map(self.prepend_bin_dir, scripts)) def version_script_paths(self): scripts = self._version_scripts return list(map(self.prepend_bin_dir, scripts)) def bat_script_paths(self): scripts = self._bat_scripts + self._bat_version_scripts return list(map(self.prepend_bat_dir, scripts)) def man_page_line(self): return 'Installed SCons man pages into %s' % self.man_dir def man_page_paths(self): return list(map(self.prepend_man_dir, self._man_pages)) def must_have_installed(self, paths): for p in paths: self.must_exist(p) def must_not_have_installed(self, paths): for p in paths: self.must_not_exist(p) try: cwd = os.environ['SCONS_CWD'] except KeyError: cwd = os.getcwd() test = MyTestSCons() test.subdir(test.root) tar_gz = os.path.join(cwd, 'build', 'dist', '%s.tar.gz' % scons_version) zip = os.path.join(cwd, 'build', 'dist', '%s.zip' % scons_version) if os.path.isfile(zip): try: import zipfile except ImportError: pass else: zf = zipfile.ZipFile(zip, 'r') for name in zf.namelist(): dir = os.path.dirname(name) try: os.makedirs(dir) except: pass # if the file exists, then delete it before writing # to it so that we don't end up trying to write to a symlink: if os.path.isfile(name) or os.path.islink(name): os.unlink(name) if not os.path.isdir(name): open(name, 'w').write(zf.read(name)) if not os.path.isdir(scons_version) and os.path.isfile(tar_gz): # Unpack the .tar.gz file. This should create the scons_version/ # subdirectory from which we execute the setup.py script therein. os.system("gunzip -c %s | tar xf -" % tar_gz) if not os.path.isdir(scons_version): print "Cannot test package installation, found none of the following packages:" print "\t" + tar_gz print "\t" + zip test.no_result(1) # Verify that a virgin installation installs the version library, # the scripts and (on UNIX/Linux systems) the man pages. test.run(arguments = 'setup.py install --root=%s' % test.root) test.fail_test(not test.lib_line(test.version_lib) in test.stdout_lines()) test.must_have_installed(test.lib_paths(test.version_lib)) # Verify that --standard-lib installs into the Python standard library. test.run(arguments = 'setup.py install --root=%s --standard-lib' % test.root) test.fail_test(not test.lib_line(test.standard_lib) in test.stdout_lines()) test.must_have_installed(test.lib_paths(test.standard_lib)) # Verify that --standalone-lib installs the standalone library. test.run(arguments = 'setup.py install --root=%s --standalone-lib' % test.root) test.fail_test(not test.lib_line(test.standalone_lib) in test.stdout_lines()) test.must_have_installed(test.lib_paths(test.standalone_lib)) # Verify that --version-lib installs into a version-specific library directory. test.run(arguments = 'setup.py install --root=%s --version-lib' % test.root) test.fail_test(not test.lib_line(test.version_lib) in test.stdout_lines()) # Now that all of the libraries are in place, # verify that a default installation still installs the version library. test.run(arguments = 'setup.py install --root=%s' % test.root) test.fail_test(not test.lib_line(test.version_lib) in test.stdout_lines()) test.remove(test.version_lib) # Now with only the standard and standalone libraries in place, # verify that a default installation still installs the version library. test.run(arguments = 'setup.py install --root=%s' % test.root) test.fail_test(not test.lib_line(test.version_lib) in test.stdout_lines()) test.remove(test.version_lib) test.remove(test.standalone_lib) # Now with only the standard libraries in place, # verify that a default installation still installs the version library. test.run(arguments = 'setup.py install --root=%s' % test.root) test.fail_test(not test.lib_line(test.version_lib) in test.stdout_lines()) # test.run(arguments = 'setup.py install --root=%s' % test.root) test.fail_test(not test.scripts_line() in test.stdout_lines()) if sys.platform == 'win32': test.must_have_installed(test.base_script_paths()) test.must_have_installed(test.version_script_paths()) test.must_have_installed(test.bat_script_paths()) else: test.must_have_installed(test.base_script_paths()) test.must_have_installed(test.version_script_paths()) test.must_not_have_installed(test.bat_script_paths()) test.remove(test.prefix) test.run(arguments = 'setup.py install --root=%s --no-install-bat' % test.root) test.fail_test(not test.scripts_line() in test.stdout_lines()) test.must_have_installed(test.base_script_paths()) test.must_have_installed(test.version_script_paths()) test.must_not_have_installed(test.bat_script_paths()) test.remove(test.prefix) test.run(arguments = 'setup.py install --root=%s --install-bat' % test.root) test.fail_test(not test.scripts_line() in test.stdout_lines()) test.must_have_installed(test.base_script_paths()) test.must_have_installed(test.version_script_paths()) test.must_have_installed(test.bat_script_paths()) test.remove(test.prefix) test.run(arguments = 'setup.py install --root=%s --no-scons-script' % test.root) test.fail_test(not test.scripts_line() in test.stdout_lines()) test.must_not_have_installed(test.base_script_paths()) test.must_have_installed(test.version_script_paths()) # Doesn't matter whether we installed the .bat scripts or not. test.remove(test.prefix) test.run(arguments = 'setup.py install --root=%s --no-version-script' % test.root) test.fail_test(not test.scripts_line() in test.stdout_lines()) test.must_have_installed(test.base_script_paths()) test.must_not_have_installed(test.version_script_paths()) # Doesn't matter whether we installed the .bat scripts or not. test.remove(test.man_dir) test.run(arguments = 'setup.py install --root=%s' % test.root) if sys.platform == 'win32': test.fail_test(test.man_page_line() in test.stdout_lines()) test.must_not_have_installed(test.man_page_paths()) else: test.fail_test(not test.man_page_line() in test.stdout_lines()) test.must_have_installed(test.man_page_paths()) test.remove(test.man_dir) test.run(arguments = 'setup.py install --root=%s --no-install-man' % test.root) test.fail_test(test.man_page_line() in test.stdout_lines()) test.must_not_have_installed(test.man_page_paths()) test.remove(test.man_dir) test.run(arguments = 'setup.py install --root=%s --install-man' % test.root) test.fail_test(not test.man_page_line() in test.stdout_lines()) test.must_have_installed(test.man_page_paths()) # Verify that we don't warn about the directory in which we've # installed the modules when using a non-standard prefix. other_prefix = test.workpath('other-prefix') test.subdir(other_prefix) test.run(arguments = 'setup.py install --prefix=%s' % other_prefix) test.fail_test(test.stderr().find("you'll have to change the search path yourself") != -1) # All done. test.pass_test() # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/script/0000755000175000017500000000000012114661560016307 5ustar dktrkranzdktrkranzscons-doc-2.3.0/src/script/MANIFEST.in0000644000175000017500000000003212114661560020040 0ustar dktrkranzdktrkranzscons sconsign scons-time scons-doc-2.3.0/src/script/scons-time.py0000644000175000017500000014250312114661560020747 0ustar dktrkranzdktrkranz#!/usr/bin/env python # # scons-time - run SCons timings and collect statistics # # A script for running a configuration through SCons with a standard # set of invocations to collect timing and memory statistics and to # capture the results in a consistent set of output files for display # and analysis. # # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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. from __future__ import division from __future__ import nested_scopes __revision__ = "src/script/scons-time.py 2013/03/03 09:48:35 garyo" import getopt import glob import os import re import shutil import sys import tempfile import time try: sorted except NameError: # Pre-2.4 Python has no sorted() function. # # The pre-2.4 Python list.sort() method does not support # list.sort(key=) nor list.sort(reverse=) keyword arguments, so # we must implement the functionality of those keyword arguments # by hand instead of passing them to list.sort(). def sorted(iterable, cmp=None, key=None, reverse=False): if key is not None: result = [(key(x), x) for x in iterable] else: result = iterable[:] if cmp is None: # Pre-2.3 Python does not support list.sort(None). result.sort() else: result.sort(cmp) if key is not None: result = [t1 for t0,t1 in result] if reverse: result.reverse() return result if os.environ.get('SCONS_HORRIBLE_REGRESSION_TEST_HACK') is not None: # We can't apply the 'callable' fixer until the floor is 2.6, but the # '-3' option to Python 2.6 and 2.7 generates almost ten thousand # warnings. This hack allows us to run regression tests with the '-3' # option by replacing the callable() built-in function with a hack # that performs the same function but doesn't generate the warning. # Note that this hack is ONLY intended to be used for regression # testing, and should NEVER be used for real runs. from types import ClassType def callable(obj): if hasattr(obj, '__call__'): return True if isinstance(obj, (ClassType, type)): return True return False def make_temp_file(**kw): try: result = tempfile.mktemp(**kw) try: result = os.path.realpath(result) except AttributeError: # Python 2.1 has no os.path.realpath() method. pass except TypeError: try: save_template = tempfile.template prefix = kw['prefix'] del kw['prefix'] tempfile.template = prefix result = tempfile.mktemp(**kw) finally: tempfile.template = save_template return result def HACK_for_exec(cmd, *args): ''' For some reason, Python won't allow an exec() within a function that also declares an internal function (including lambda functions). This function is a hack that calls exec() in a function with no internal functions. ''' if not args: exec(cmd) elif len(args) == 1: exec cmd in args[0] else: exec cmd in args[0], args[1] class Plotter(object): def increment_size(self, largest): """ Return the size of each horizontal increment line for a specified maximum value. This returns a value that will provide somewhere between 5 and 9 horizontal lines on the graph, on some set of boundaries that are multiples of 10/100/1000/etc. """ i = largest // 5 if not i: return largest multiplier = 1 while i >= 10: i = i // 10 multiplier = multiplier * 10 return i * multiplier def max_graph_value(self, largest): # Round up to next integer. largest = int(largest) + 1 increment = self.increment_size(largest) return ((largest + increment - 1) // increment) * increment class Line(object): def __init__(self, points, type, title, label, comment, fmt="%s %s"): self.points = points self.type = type self.title = title self.label = label self.comment = comment self.fmt = fmt def print_label(self, inx, x, y): if self.label: print 'set label %s "%s" at %s,%s right' % (inx, self.label, x, y) def plot_string(self): if self.title: title_string = 'title "%s"' % self.title else: title_string = 'notitle' return "'-' %s with lines lt %s" % (title_string, self.type) def print_points(self, fmt=None): if fmt is None: fmt = self.fmt if self.comment: print '# %s' % self.comment for x, y in self.points: # If y is None, it usually represents some kind of break # in the line's index number. We might want to represent # this some way rather than just drawing the line straight # between the two points on either side. if not y is None: print fmt % (x, y) print 'e' def get_x_values(self): return [ p[0] for p in self.points ] def get_y_values(self): return [ p[1] for p in self.points ] class Gnuplotter(Plotter): def __init__(self, title, key_location): self.lines = [] self.title = title self.key_location = key_location def line(self, points, type, title=None, label=None, comment=None, fmt='%s %s'): if points: line = Line(points, type, title, label, comment, fmt) self.lines.append(line) def plot_string(self, line): return line.plot_string() def vertical_bar(self, x, type, label, comment): if self.get_min_x() <= x and x <= self.get_max_x(): points = [(x, 0), (x, self.max_graph_value(self.get_max_y()))] self.line(points, type, label, comment) def get_all_x_values(self): result = [] for line in self.lines: result.extend(line.get_x_values()) return [r for r in result if not r is None] def get_all_y_values(self): result = [] for line in self.lines: result.extend(line.get_y_values()) return [r for r in result if not r is None] def get_min_x(self): try: return self.min_x except AttributeError: try: self.min_x = min(self.get_all_x_values()) except ValueError: self.min_x = 0 return self.min_x def get_max_x(self): try: return self.max_x except AttributeError: try: self.max_x = max(self.get_all_x_values()) except ValueError: self.max_x = 0 return self.max_x def get_min_y(self): try: return self.min_y except AttributeError: try: self.min_y = min(self.get_all_y_values()) except ValueError: self.min_y = 0 return self.min_y def get_max_y(self): try: return self.max_y except AttributeError: try: self.max_y = max(self.get_all_y_values()) except ValueError: self.max_y = 0 return self.max_y def draw(self): if not self.lines: return if self.title: print 'set title "%s"' % self.title print 'set key %s' % self.key_location min_y = self.get_min_y() max_y = self.max_graph_value(self.get_max_y()) incr = (max_y - min_y) / 10.0 start = min_y + (max_y / 2.0) + (2.0 * incr) position = [ start - (i * incr) for i in range(5) ] inx = 1 for line in self.lines: line.print_label(inx, line.points[0][0]-1, position[(inx-1) % len(position)]) inx += 1 plot_strings = [ self.plot_string(l) for l in self.lines ] print 'plot ' + ', \\\n '.join(plot_strings) for line in self.lines: line.print_points() def untar(fname): import tarfile tar = tarfile.open(name=fname, mode='r') for tarinfo in tar: tar.extract(tarinfo) tar.close() def unzip(fname): import zipfile zf = zipfile.ZipFile(fname, 'r') for name in zf.namelist(): dir = os.path.dirname(name) try: os.makedirs(dir) except: pass open(name, 'w').write(zf.read(name)) def read_tree(dir): for dirpath, dirnames, filenames in os.walk(dir): for fn in filenames: fn = os.path.join(dirpath, fn) if os.path.isfile(fn): open(fn, 'rb').read() def redirect_to_file(command, log): return '%s > %s 2>&1' % (command, log) def tee_to_file(command, log): return '%s 2>&1 | tee %s' % (command, log) class SConsTimer(object): """ Usage: scons-time SUBCOMMAND [ARGUMENTS] Type "scons-time help SUBCOMMAND" for help on a specific subcommand. Available subcommands: func Extract test-run data for a function help Provides help mem Extract --debug=memory data from test runs obj Extract --debug=count data from test runs time Extract --debug=time data from test runs run Runs a test configuration """ name = 'scons-time' name_spaces = ' '*len(name) def makedict(**kw): return kw default_settings = makedict( aegis = 'aegis', aegis_project = None, chdir = None, config_file = None, initial_commands = [], key_location = 'bottom left', orig_cwd = os.getcwd(), outdir = None, prefix = '', python = '"%s"' % sys.executable, redirect = redirect_to_file, scons = None, scons_flags = '--debug=count --debug=memory --debug=time --debug=memoizer', scons_lib_dir = None, scons_wrapper = None, startup_targets = '--help', subdir = None, subversion_url = None, svn = 'svn', svn_co_flag = '-q', tar = 'tar', targets = '', targets0 = None, targets1 = None, targets2 = None, title = None, unzip = 'unzip', verbose = False, vertical_bars = [], unpack_map = { '.tar.gz' : (untar, '%(tar)s xzf %%s'), '.tgz' : (untar, '%(tar)s xzf %%s'), '.tar' : (untar, '%(tar)s xf %%s'), '.zip' : (unzip, '%(unzip)s %%s'), }, ) run_titles = [ 'Startup', 'Full build', 'Up-to-date build', ] run_commands = [ '%(python)s %(scons_wrapper)s %(scons_flags)s --profile=%(prof0)s %(targets0)s', '%(python)s %(scons_wrapper)s %(scons_flags)s --profile=%(prof1)s %(targets1)s', '%(python)s %(scons_wrapper)s %(scons_flags)s --profile=%(prof2)s %(targets2)s', ] stages = [ 'pre-read', 'post-read', 'pre-build', 'post-build', ] stage_strings = { 'pre-read' : 'Memory before reading SConscript files:', 'post-read' : 'Memory after reading SConscript files:', 'pre-build' : 'Memory before building targets:', 'post-build' : 'Memory after building targets:', } memory_string_all = 'Memory ' default_stage = stages[-1] time_strings = { 'total' : 'Total build time', 'SConscripts' : 'Total SConscript file execution time', 'SCons' : 'Total SCons execution time', 'commands' : 'Total command execution time', } time_string_all = 'Total .* time' # def __init__(self): self.__dict__.update(self.default_settings) # Functions for displaying and executing commands. def subst(self, x, dictionary): try: return x % dictionary except TypeError: # x isn't a string (it's probably a Python function), # so just return it. return x def subst_variables(self, command, dictionary): """ Substitutes (via the format operator) the values in the specified dictionary into the specified command. The command can be an (action, string) tuple. In all cases, we perform substitution on strings and don't worry if something isn't a string. (It's probably a Python function to be executed.) """ try: command + '' except TypeError: action = command[0] string = command[1] args = command[2:] else: action = command string = action args = (()) action = self.subst(action, dictionary) string = self.subst(string, dictionary) return (action, string, args) def _do_not_display(self, msg, *args): pass def display(self, msg, *args): """ Displays the specified message. Each message is prepended with a standard prefix of our name plus the time. """ if callable(msg): msg = msg(*args) else: msg = msg % args if msg is None: return fmt = '%s[%s]: %s\n' sys.stdout.write(fmt % (self.name, time.strftime('%H:%M:%S'), msg)) def _do_not_execute(self, action, *args): pass def execute(self, action, *args): """ Executes the specified action. The action is called if it's a callable Python function, and otherwise passed to os.system(). """ if callable(action): action(*args) else: os.system(action % args) def run_command_list(self, commands, dict): """ Executes a list of commands, substituting values from the specified dictionary. """ commands = [ self.subst_variables(c, dict) for c in commands ] for action, string, args in commands: self.display(string, *args) sys.stdout.flush() status = self.execute(action, *args) if status: sys.exit(status) def log_display(self, command, log): command = self.subst(command, self.__dict__) if log: command = self.redirect(command, log) return command def log_execute(self, command, log): command = self.subst(command, self.__dict__) output = os.popen(command).read() if self.verbose: sys.stdout.write(output) open(log, 'wb').write(output) # def archive_splitext(self, path): """ Splits an archive name into a filename base and extension. This is like os.path.splitext() (which it calls) except that it also looks for '.tar.gz' and treats it as an atomic extensions. """ if path.endswith('.tar.gz'): return path[:-7], path[-7:] else: return os.path.splitext(path) def args_to_files(self, args, tail=None): """ Takes a list of arguments, expands any glob patterns, and returns the last "tail" files from the list. """ files = [] for a in args: files.extend(sorted(glob.glob(a))) if tail: files = files[-tail:] return files def ascii_table(self, files, columns, line_function, file_function=lambda x: x, *args, **kw): header_fmt = ' '.join(['%12s'] * len(columns)) line_fmt = header_fmt + ' %s' print header_fmt % columns for file in files: t = line_function(file, *args, **kw) if t is None: t = [] diff = len(columns) - len(t) if diff > 0: t += [''] * diff t.append(file_function(file)) print line_fmt % tuple(t) def collect_results(self, files, function, *args, **kw): results = {} for file in files: base = os.path.splitext(file)[0] run, index = base.split('-')[-2:] run = int(run) index = int(index) value = function(file, *args, **kw) try: r = results[index] except KeyError: r = [] results[index] = r r.append((run, value)) return results def doc_to_help(self, obj): """ Translates an object's __doc__ string into help text. This strips a consistent number of spaces from each line in the help text, essentially "outdenting" the text to the left-most column. """ doc = obj.__doc__ if doc is None: return '' return self.outdent(doc) def find_next_run_number(self, dir, prefix): """ Returns the next run number in a directory for the specified prefix. Examines the contents the specified directory for files with the specified prefix, extracts the run numbers from each file name, and returns the next run number after the largest it finds. """ x = re.compile(re.escape(prefix) + '-([0-9]+).*') matches = [x.match(e) for e in os.listdir(dir)] matches = [_f for _f in matches if _f] if not matches: return 0 run_numbers = [int(m.group(1)) for m in matches] return int(max(run_numbers)) + 1 def gnuplot_results(self, results, fmt='%s %.3f'): """ Prints out a set of results in Gnuplot format. """ gp = Gnuplotter(self.title, self.key_location) for i in sorted(results.keys()): try: t = self.run_titles[i] except IndexError: t = '??? %s ???' % i results[i].sort() gp.line(results[i], i+1, t, None, t, fmt=fmt) for bar_tuple in self.vertical_bars: try: x, type, label, comment = bar_tuple except ValueError: x, type, label = bar_tuple comment = label gp.vertical_bar(x, type, label, comment) gp.draw() def logfile_name(self, invocation): """ Returns the absolute path of a log file for the specificed invocation number. """ name = self.prefix_run + '-%d.log' % invocation return os.path.join(self.outdir, name) def outdent(self, s): """ Strip as many spaces from each line as are found at the beginning of the first line in the list. """ lines = s.split('\n') if lines[0] == '': lines = lines[1:] spaces = re.match(' *', lines[0]).group(0) def strip_initial_spaces(l, s=spaces): if l.startswith(spaces): l = l[len(spaces):] return l return '\n'.join([ strip_initial_spaces(l) for l in lines ]) + '\n' def profile_name(self, invocation): """ Returns the absolute path of a profile file for the specified invocation number. """ name = self.prefix_run + '-%d.prof' % invocation return os.path.join(self.outdir, name) def set_env(self, key, value): os.environ[key] = value # def get_debug_times(self, file, time_string=None): """ Fetch times from the --debug=time strings in the specified file. """ if time_string is None: search_string = self.time_string_all else: search_string = time_string contents = open(file).read() if not contents: sys.stderr.write('file %s has no contents!\n' % repr(file)) return None result = re.findall(r'%s: ([\d\.]*)' % search_string, contents)[-4:] result = [ float(r) for r in result ] if not time_string is None: try: result = result[0] except IndexError: sys.stderr.write('file %s has no results!\n' % repr(file)) return None return result def get_function_profile(self, file, function): """ Returns the file, line number, function name, and cumulative time. """ try: import pstats except ImportError, e: sys.stderr.write('%s: func: %s\n' % (self.name, e)) sys.stderr.write('%s This version of Python is missing the profiler.\n' % self.name_spaces) sys.stderr.write('%s Cannot use the "func" subcommand.\n' % self.name_spaces) sys.exit(1) statistics = pstats.Stats(file).stats matches = [ e for e in statistics.items() if e[0][2] == function ] r = matches[0] return r[0][0], r[0][1], r[0][2], r[1][3] def get_function_time(self, file, function): """ Returns just the cumulative time for the specified function. """ return self.get_function_profile(file, function)[3] def get_memory(self, file, memory_string=None): """ Returns a list of integers of the amount of memory used. The default behavior is to return all the stages. """ if memory_string is None: search_string = self.memory_string_all else: search_string = memory_string lines = open(file).readlines() lines = [ l for l in lines if l.startswith(search_string) ][-4:] result = [ int(l.split()[-1]) for l in lines[-4:] ] if len(result) == 1: result = result[0] return result def get_object_counts(self, file, object_name, index=None): """ Returns the counts of the specified object_name. """ object_string = ' ' + object_name + '\n' lines = open(file).readlines() line = [ l for l in lines if l.endswith(object_string) ][0] result = [ int(field) for field in line.split()[:4] ] if index is not None: result = result[index] return result # command_alias = {} def execute_subcommand(self, argv): """ Executes the do_*() function for the specified subcommand (argv[0]). """ if not argv: return cmdName = self.command_alias.get(argv[0], argv[0]) try: func = getattr(self, 'do_' + cmdName) except AttributeError: return self.default(argv) try: return func(argv) except TypeError, e: sys.stderr.write("%s %s: %s\n" % (self.name, cmdName, e)) import traceback traceback.print_exc(file=sys.stderr) sys.stderr.write("Try '%s help %s'\n" % (self.name, cmdName)) def default(self, argv): """ The default behavior for an unknown subcommand. Prints an error message and exits. """ sys.stderr.write('%s: Unknown subcommand "%s".\n' % (self.name, argv[0])) sys.stderr.write('Type "%s help" for usage.\n' % self.name) sys.exit(1) # def do_help(self, argv): """ """ if argv[1:]: for arg in argv[1:]: try: func = getattr(self, 'do_' + arg) except AttributeError: sys.stderr.write('%s: No help for "%s"\n' % (self.name, arg)) else: try: help = getattr(self, 'help_' + arg) except AttributeError: sys.stdout.write(self.doc_to_help(func)) sys.stdout.flush() else: help() else: doc = self.doc_to_help(self.__class__) if doc: sys.stdout.write(doc) sys.stdout.flush() return None # def help_func(self): help = """\ Usage: scons-time func [OPTIONS] FILE [...] -C DIR, --chdir=DIR Change to DIR before looking for files -f FILE, --file=FILE Read configuration from specified FILE --fmt=FORMAT, --format=FORMAT Print data in specified FORMAT --func=NAME, --function=NAME Report time for function NAME -h, --help Print this help and exit -p STRING, --prefix=STRING Use STRING as log file/profile prefix -t NUMBER, --tail=NUMBER Only report the last NUMBER files --title=TITLE Specify the output plot TITLE """ sys.stdout.write(self.outdent(help)) sys.stdout.flush() def do_func(self, argv): """ """ format = 'ascii' function_name = '_main' tail = None short_opts = '?C:f:hp:t:' long_opts = [ 'chdir=', 'file=', 'fmt=', 'format=', 'func=', 'function=', 'help', 'prefix=', 'tail=', 'title=', ] opts, args = getopt.getopt(argv[1:], short_opts, long_opts) for o, a in opts: if o in ('-C', '--chdir'): self.chdir = a elif o in ('-f', '--file'): self.config_file = a elif o in ('--fmt', '--format'): format = a elif o in ('--func', '--function'): function_name = a elif o in ('-?', '-h', '--help'): self.do_help(['help', 'func']) sys.exit(0) elif o in ('--max',): max_time = int(a) elif o in ('-p', '--prefix'): self.prefix = a elif o in ('-t', '--tail'): tail = int(a) elif o in ('--title',): self.title = a if self.config_file: exec open(self.config_file, 'rU').read() in self.__dict__ if self.chdir: os.chdir(self.chdir) if not args: pattern = '%s*.prof' % self.prefix args = self.args_to_files([pattern], tail) if not args: if self.chdir: directory = self.chdir else: directory = os.getcwd() sys.stderr.write('%s: func: No arguments specified.\n' % self.name) sys.stderr.write('%s No %s*.prof files found in "%s".\n' % (self.name_spaces, self.prefix, directory)) sys.stderr.write('%s Type "%s help func" for help.\n' % (self.name_spaces, self.name)) sys.exit(1) else: args = self.args_to_files(args, tail) cwd_ = os.getcwd() + os.sep if format == 'ascii': for file in args: try: f, line, func, time = \ self.get_function_profile(file, function_name) except ValueError, e: sys.stderr.write("%s: func: %s: %s\n" % (self.name, file, e)) else: if f.startswith(cwd_): f = f[len(cwd_):] print "%.3f %s:%d(%s)" % (time, f, line, func) elif format == 'gnuplot': results = self.collect_results(args, self.get_function_time, function_name) self.gnuplot_results(results) else: sys.stderr.write('%s: func: Unknown format "%s".\n' % (self.name, format)) sys.exit(1) # def help_mem(self): help = """\ Usage: scons-time mem [OPTIONS] FILE [...] -C DIR, --chdir=DIR Change to DIR before looking for files -f FILE, --file=FILE Read configuration from specified FILE --fmt=FORMAT, --format=FORMAT Print data in specified FORMAT -h, --help Print this help and exit -p STRING, --prefix=STRING Use STRING as log file/profile prefix --stage=STAGE Plot memory at the specified stage: pre-read, post-read, pre-build, post-build (default: post-build) -t NUMBER, --tail=NUMBER Only report the last NUMBER files --title=TITLE Specify the output plot TITLE """ sys.stdout.write(self.outdent(help)) sys.stdout.flush() def do_mem(self, argv): format = 'ascii' logfile_path = lambda x: x stage = self.default_stage tail = None short_opts = '?C:f:hp:t:' long_opts = [ 'chdir=', 'file=', 'fmt=', 'format=', 'help', 'prefix=', 'stage=', 'tail=', 'title=', ] opts, args = getopt.getopt(argv[1:], short_opts, long_opts) for o, a in opts: if o in ('-C', '--chdir'): self.chdir = a elif o in ('-f', '--file'): self.config_file = a elif o in ('--fmt', '--format'): format = a elif o in ('-?', '-h', '--help'): self.do_help(['help', 'mem']) sys.exit(0) elif o in ('-p', '--prefix'): self.prefix = a elif o in ('--stage',): if not a in self.stages: sys.stderr.write('%s: mem: Unrecognized stage "%s".\n' % (self.name, a)) sys.exit(1) stage = a elif o in ('-t', '--tail'): tail = int(a) elif o in ('--title',): self.title = a if self.config_file: HACK_for_exec(open(self.config_file, 'rU').read(), self.__dict__) if self.chdir: os.chdir(self.chdir) logfile_path = lambda x: os.path.join(self.chdir, x) if not args: pattern = '%s*.log' % self.prefix args = self.args_to_files([pattern], tail) if not args: if self.chdir: directory = self.chdir else: directory = os.getcwd() sys.stderr.write('%s: mem: No arguments specified.\n' % self.name) sys.stderr.write('%s No %s*.log files found in "%s".\n' % (self.name_spaces, self.prefix, directory)) sys.stderr.write('%s Type "%s help mem" for help.\n' % (self.name_spaces, self.name)) sys.exit(1) else: args = self.args_to_files(args, tail) cwd_ = os.getcwd() + os.sep if format == 'ascii': self.ascii_table(args, tuple(self.stages), self.get_memory, logfile_path) elif format == 'gnuplot': results = self.collect_results(args, self.get_memory, self.stage_strings[stage]) self.gnuplot_results(results) else: sys.stderr.write('%s: mem: Unknown format "%s".\n' % (self.name, format)) sys.exit(1) return 0 # def help_obj(self): help = """\ Usage: scons-time obj [OPTIONS] OBJECT FILE [...] -C DIR, --chdir=DIR Change to DIR before looking for files -f FILE, --file=FILE Read configuration from specified FILE --fmt=FORMAT, --format=FORMAT Print data in specified FORMAT -h, --help Print this help and exit -p STRING, --prefix=STRING Use STRING as log file/profile prefix --stage=STAGE Plot memory at the specified stage: pre-read, post-read, pre-build, post-build (default: post-build) -t NUMBER, --tail=NUMBER Only report the last NUMBER files --title=TITLE Specify the output plot TITLE """ sys.stdout.write(self.outdent(help)) sys.stdout.flush() def do_obj(self, argv): format = 'ascii' logfile_path = lambda x: x stage = self.default_stage tail = None short_opts = '?C:f:hp:t:' long_opts = [ 'chdir=', 'file=', 'fmt=', 'format=', 'help', 'prefix=', 'stage=', 'tail=', 'title=', ] opts, args = getopt.getopt(argv[1:], short_opts, long_opts) for o, a in opts: if o in ('-C', '--chdir'): self.chdir = a elif o in ('-f', '--file'): self.config_file = a elif o in ('--fmt', '--format'): format = a elif o in ('-?', '-h', '--help'): self.do_help(['help', 'obj']) sys.exit(0) elif o in ('-p', '--prefix'): self.prefix = a elif o in ('--stage',): if not a in self.stages: sys.stderr.write('%s: obj: Unrecognized stage "%s".\n' % (self.name, a)) sys.stderr.write('%s Type "%s help obj" for help.\n' % (self.name_spaces, self.name)) sys.exit(1) stage = a elif o in ('-t', '--tail'): tail = int(a) elif o in ('--title',): self.title = a if not args: sys.stderr.write('%s: obj: Must specify an object name.\n' % self.name) sys.stderr.write('%s Type "%s help obj" for help.\n' % (self.name_spaces, self.name)) sys.exit(1) object_name = args.pop(0) if self.config_file: HACK_for_exec(open(self.config_file, 'rU').read(), self.__dict__) if self.chdir: os.chdir(self.chdir) logfile_path = lambda x: os.path.join(self.chdir, x) if not args: pattern = '%s*.log' % self.prefix args = self.args_to_files([pattern], tail) if not args: if self.chdir: directory = self.chdir else: directory = os.getcwd() sys.stderr.write('%s: obj: No arguments specified.\n' % self.name) sys.stderr.write('%s No %s*.log files found in "%s".\n' % (self.name_spaces, self.prefix, directory)) sys.stderr.write('%s Type "%s help obj" for help.\n' % (self.name_spaces, self.name)) sys.exit(1) else: args = self.args_to_files(args, tail) cwd_ = os.getcwd() + os.sep if format == 'ascii': self.ascii_table(args, tuple(self.stages), self.get_object_counts, logfile_path, object_name) elif format == 'gnuplot': stage_index = 0 for s in self.stages: if stage == s: break stage_index = stage_index + 1 results = self.collect_results(args, self.get_object_counts, object_name, stage_index) self.gnuplot_results(results) else: sys.stderr.write('%s: obj: Unknown format "%s".\n' % (self.name, format)) sys.exit(1) return 0 # def help_run(self): help = """\ Usage: scons-time run [OPTIONS] [FILE ...] --aegis=PROJECT Use SCons from the Aegis PROJECT --chdir=DIR Name of unpacked directory for chdir -f FILE, --file=FILE Read configuration from specified FILE -h, --help Print this help and exit -n, --no-exec No execute, just print command lines --number=NUMBER Put output in files for run NUMBER --outdir=OUTDIR Put output files in OUTDIR -p STRING, --prefix=STRING Use STRING as log file/profile prefix --python=PYTHON Time using the specified PYTHON -q, --quiet Don't print command lines --scons=SCONS Time using the specified SCONS --svn=URL, --subversion=URL Use SCons from Subversion URL -v, --verbose Display output of commands """ sys.stdout.write(self.outdent(help)) sys.stdout.flush() def do_run(self, argv): """ """ run_number_list = [None] short_opts = '?f:hnp:qs:v' long_opts = [ 'aegis=', 'file=', 'help', 'no-exec', 'number=', 'outdir=', 'prefix=', 'python=', 'quiet', 'scons=', 'svn=', 'subdir=', 'subversion=', 'verbose', ] opts, args = getopt.getopt(argv[1:], short_opts, long_opts) for o, a in opts: if o in ('--aegis',): self.aegis_project = a elif o in ('-f', '--file'): self.config_file = a elif o in ('-?', '-h', '--help'): self.do_help(['help', 'run']) sys.exit(0) elif o in ('-n', '--no-exec'): self.execute = self._do_not_execute elif o in ('--number',): run_number_list = self.split_run_numbers(a) elif o in ('--outdir',): self.outdir = a elif o in ('-p', '--prefix'): self.prefix = a elif o in ('--python',): self.python = a elif o in ('-q', '--quiet'): self.display = self._do_not_display elif o in ('-s', '--subdir'): self.subdir = a elif o in ('--scons',): self.scons = a elif o in ('--svn', '--subversion'): self.subversion_url = a elif o in ('-v', '--verbose'): self.redirect = tee_to_file self.verbose = True self.svn_co_flag = '' if not args and not self.config_file: sys.stderr.write('%s: run: No arguments or -f config file specified.\n' % self.name) sys.stderr.write('%s Type "%s help run" for help.\n' % (self.name_spaces, self.name)) sys.exit(1) if self.config_file: exec open(self.config_file, 'rU').read() in self.__dict__ if args: self.archive_list = args archive_file_name = os.path.split(self.archive_list[0])[1] if not self.subdir: self.subdir = self.archive_splitext(archive_file_name)[0] if not self.prefix: self.prefix = self.archive_splitext(archive_file_name)[0] prepare = None if self.subversion_url: prepare = self.prep_subversion_run elif self.aegis_project: prepare = self.prep_aegis_run for run_number in run_number_list: self.individual_run(run_number, self.archive_list, prepare) def split_run_numbers(self, s): result = [] for n in s.split(','): try: x, y = n.split('-') except ValueError: result.append(int(n)) else: result.extend(list(range(int(x), int(y)+1))) return result def scons_path(self, dir): return os.path.join(dir, 'src', 'script', 'scons.py') def scons_lib_dir_path(self, dir): return os.path.join(dir, 'src', 'engine') def prep_aegis_run(self, commands, removals): self.aegis_tmpdir = make_temp_file(prefix = self.name + '-aegis-') removals.append((shutil.rmtree, 'rm -rf %%s', self.aegis_tmpdir)) self.aegis_parent_project = os.path.splitext(self.aegis_project)[0] self.scons = self.scons_path(self.aegis_tmpdir) self.scons_lib_dir = self.scons_lib_dir_path(self.aegis_tmpdir) commands.extend([ 'mkdir %(aegis_tmpdir)s', (lambda: os.chdir(self.aegis_tmpdir), 'cd %(aegis_tmpdir)s'), '%(aegis)s -cp -ind -p %(aegis_parent_project)s .', '%(aegis)s -cp -ind -p %(aegis_project)s -delta %(run_number)s .', ]) def prep_subversion_run(self, commands, removals): self.svn_tmpdir = make_temp_file(prefix = self.name + '-svn-') removals.append((shutil.rmtree, 'rm -rf %%s', self.svn_tmpdir)) self.scons = self.scons_path(self.svn_tmpdir) self.scons_lib_dir = self.scons_lib_dir_path(self.svn_tmpdir) commands.extend([ 'mkdir %(svn_tmpdir)s', '%(svn)s co %(svn_co_flag)s -r %(run_number)s %(subversion_url)s %(svn_tmpdir)s', ]) def individual_run(self, run_number, archive_list, prepare=None): """ Performs an individual run of the default SCons invocations. """ commands = [] removals = [] if prepare: prepare(commands, removals) save_scons = self.scons save_scons_wrapper = self.scons_wrapper save_scons_lib_dir = self.scons_lib_dir if self.outdir is None: self.outdir = self.orig_cwd elif not os.path.isabs(self.outdir): self.outdir = os.path.join(self.orig_cwd, self.outdir) if self.scons is None: self.scons = self.scons_path(self.orig_cwd) if self.scons_lib_dir is None: self.scons_lib_dir = self.scons_lib_dir_path(self.orig_cwd) if self.scons_wrapper is None: self.scons_wrapper = self.scons if not run_number: run_number = self.find_next_run_number(self.outdir, self.prefix) self.run_number = str(run_number) self.prefix_run = self.prefix + '-%03d' % run_number if self.targets0 is None: self.targets0 = self.startup_targets if self.targets1 is None: self.targets1 = self.targets if self.targets2 is None: self.targets2 = self.targets self.tmpdir = make_temp_file(prefix = self.name + '-') commands.extend([ 'mkdir %(tmpdir)s', (os.chdir, 'cd %%s', self.tmpdir), ]) for archive in archive_list: if not os.path.isabs(archive): archive = os.path.join(self.orig_cwd, archive) if os.path.isdir(archive): dest = os.path.split(archive)[1] commands.append((shutil.copytree, 'cp -r %%s %%s', archive, dest)) else: suffix = self.archive_splitext(archive)[1] unpack_command = self.unpack_map.get(suffix) if not unpack_command: dest = os.path.split(archive)[1] commands.append((shutil.copyfile, 'cp %%s %%s', archive, dest)) else: commands.append(unpack_command + (archive,)) commands.extend([ (os.chdir, 'cd %%s', self.subdir), ]) commands.extend(self.initial_commands) commands.extend([ (lambda: read_tree('.'), 'find * -type f | xargs cat > /dev/null'), (self.set_env, 'export %%s=%%s', 'SCONS_LIB_DIR', self.scons_lib_dir), '%(python)s %(scons_wrapper)s --version', ]) index = 0 for run_command in self.run_commands: setattr(self, 'prof%d' % index, self.profile_name(index)) c = ( self.log_execute, self.log_display, run_command, self.logfile_name(index), ) commands.append(c) index = index + 1 commands.extend([ (os.chdir, 'cd %%s', self.orig_cwd), ]) if not os.environ.get('PRESERVE'): commands.extend(removals) commands.append((shutil.rmtree, 'rm -rf %%s', self.tmpdir)) self.run_command_list(commands, self.__dict__) self.scons = save_scons self.scons_lib_dir = save_scons_lib_dir self.scons_wrapper = save_scons_wrapper # def help_time(self): help = """\ Usage: scons-time time [OPTIONS] FILE [...] -C DIR, --chdir=DIR Change to DIR before looking for files -f FILE, --file=FILE Read configuration from specified FILE --fmt=FORMAT, --format=FORMAT Print data in specified FORMAT -h, --help Print this help and exit -p STRING, --prefix=STRING Use STRING as log file/profile prefix -t NUMBER, --tail=NUMBER Only report the last NUMBER files --which=TIMER Plot timings for TIMER: total, SConscripts, SCons, commands. """ sys.stdout.write(self.outdent(help)) sys.stdout.flush() def do_time(self, argv): format = 'ascii' logfile_path = lambda x: x tail = None which = 'total' short_opts = '?C:f:hp:t:' long_opts = [ 'chdir=', 'file=', 'fmt=', 'format=', 'help', 'prefix=', 'tail=', 'title=', 'which=', ] opts, args = getopt.getopt(argv[1:], short_opts, long_opts) for o, a in opts: if o in ('-C', '--chdir'): self.chdir = a elif o in ('-f', '--file'): self.config_file = a elif o in ('--fmt', '--format'): format = a elif o in ('-?', '-h', '--help'): self.do_help(['help', 'time']) sys.exit(0) elif o in ('-p', '--prefix'): self.prefix = a elif o in ('-t', '--tail'): tail = int(a) elif o in ('--title',): self.title = a elif o in ('--which',): if not a in self.time_strings.keys(): sys.stderr.write('%s: time: Unrecognized timer "%s".\n' % (self.name, a)) sys.stderr.write('%s Type "%s help time" for help.\n' % (self.name_spaces, self.name)) sys.exit(1) which = a if self.config_file: HACK_for_exec(open(self.config_file, 'rU').read(), self.__dict__) if self.chdir: os.chdir(self.chdir) logfile_path = lambda x: os.path.join(self.chdir, x) if not args: pattern = '%s*.log' % self.prefix args = self.args_to_files([pattern], tail) if not args: if self.chdir: directory = self.chdir else: directory = os.getcwd() sys.stderr.write('%s: time: No arguments specified.\n' % self.name) sys.stderr.write('%s No %s*.log files found in "%s".\n' % (self.name_spaces, self.prefix, directory)) sys.stderr.write('%s Type "%s help time" for help.\n' % (self.name_spaces, self.name)) sys.exit(1) else: args = self.args_to_files(args, tail) cwd_ = os.getcwd() + os.sep if format == 'ascii': columns = ("Total", "SConscripts", "SCons", "commands") self.ascii_table(args, columns, self.get_debug_times, logfile_path) elif format == 'gnuplot': results = self.collect_results(args, self.get_debug_times, self.time_strings[which]) self.gnuplot_results(results, fmt='%s %.6f') else: sys.stderr.write('%s: time: Unknown format "%s".\n' % (self.name, format)) sys.exit(1) if __name__ == '__main__': opts, args = getopt.getopt(sys.argv[1:], 'h?V', ['help', 'version']) ST = SConsTimer() for o, a in opts: if o in ('-?', '-h', '--help'): ST.do_help(['help']) sys.exit(0) elif o in ('-V', '--version'): sys.stdout.write('scons-time version\n') sys.exit(0) if not args: sys.stderr.write('Type "%s help" for usage.\n' % ST.name) sys.exit(1) ST.execute_subcommand(args) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/script/scons.bat0000644000175000017500000000303712114661560020127 0ustar dktrkranzdktrkranz@REM Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation @REM src/script/scons.bat 2013/03/03 09:48:35 garyo @echo off set SCONS_ERRORLEVEL= if "%OS%" == "Windows_NT" goto WinNT @REM for 9x/Me you better not have more than 9 args python -c "from os.path import join; import sys; sys.path = [ join(sys.prefix, 'Lib', 'site-packages', 'scons-2.3.0'), join(sys.prefix, 'Lib', 'site-packages', 'scons'), join(sys.prefix, 'scons-2.3.0'), join(sys.prefix, 'scons')] + sys.path; import SCons.Script; SCons.Script.main()" %1 %2 %3 %4 %5 %6 %7 %8 %9 @REM no way to set exit status of this script for 9x/Me goto endscons @REM Credit where credit is due: we return the exit code despite our @REM use of setlocal+endlocal using a technique from Bear's Journal: @REM http://code-bear.com/bearlog/2007/06/01/getting-the-exit-code-from-a-batch-file-that-is-run-from-a-python-program/ :WinNT setlocal @REM ensure the script will be executed with the Python it was installed for set path=%~dp0;%~dp0..;%path% @REM try the script named as the .bat file in current dir, then in Scripts subdir set scriptname=%~dp0%~n0.py if not exist "%scriptname%" set scriptname=%~dp0Scripts\%~n0.py python "%scriptname%" %* endlocal & set SCONS_ERRORLEVEL=%ERRORLEVEL% if NOT "%COMSPEC%" == "%SystemRoot%\system32\cmd.exe" goto returncode if errorlevel 9009 echo you do not have python in your PATH goto endscons :returncode exit /B %SCONS_ERRORLEVEL% :endscons call :returncode %SCONS_ERRORLEVEL% scons-doc-2.3.0/src/script/scons.py0000644000175000017500000001521112114661560020006 0ustar dktrkranzdktrkranz#! /usr/bin/env python # # SCons - a Software Constructor # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/script/scons.py 2013/03/03 09:48:35 garyo" __version__ = "2.3.0" __build__ = "" __buildsys__ = "reepicheep" __date__ = "2013/03/03 09:48:35" __developer__ = "garyo" import os import sys ############################################################################## # BEGIN STANDARD SCons SCRIPT HEADER # # This is the cut-and-paste logic so that a self-contained script can # interoperate correctly with different SCons versions and installation # locations for the engine. If you modify anything in this section, you # should also change other scripts that use this same header. ############################################################################## # Strip the script directory from sys.path() so on case-insensitive # (WIN32) systems Python doesn't think that the "scons" script is the # "SCons" package. Replace it with our own library directories # (version-specific first, in case they installed by hand there, # followed by generic) so we pick up the right version of the build # engine modules if they're in either directory. if sys.version_info >= (3,0,0): msg = "scons: *** SCons version %s does not run under Python version %s.\n\ Python 3 is not yet supported.\n" sys.stderr.write(msg % (__version__, sys.version.split()[0])) sys.exit(1) script_dir = sys.path[0] if script_dir in sys.path: sys.path.remove(script_dir) libs = [] if "SCONS_LIB_DIR" in os.environ: libs.append(os.environ["SCONS_LIB_DIR"]) local_version = 'scons-local-' + __version__ local = 'scons-local' if script_dir: local_version = os.path.join(script_dir, local_version) local = os.path.join(script_dir, local) libs.append(os.path.abspath(local_version)) libs.append(os.path.abspath(local)) scons_version = 'scons-%s' % __version__ # preferred order of scons lookup paths prefs = [] try: import pkg_resources except ImportError: pass else: # when running from an egg add the egg's directory try: d = pkg_resources.get_distribution('scons') except pkg_resources.DistributionNotFound: pass else: prefs.append(d.location) if sys.platform == 'win32': # sys.prefix is (likely) C:\Python*; # check only C:\Python*. prefs.append(sys.prefix) prefs.append(os.path.join(sys.prefix, 'Lib', 'site-packages')) else: # On other (POSIX) platforms, things are more complicated due to # the variety of path names and library locations. Try to be smart # about it. if script_dir == 'bin': # script_dir is `pwd`/bin; # check `pwd`/lib/scons*. prefs.append(os.getcwd()) else: if script_dir == '.' or script_dir == '': script_dir = os.getcwd() head, tail = os.path.split(script_dir) if tail == "bin": # script_dir is /foo/bin; # check /foo/lib/scons*. prefs.append(head) head, tail = os.path.split(sys.prefix) if tail == "usr": # sys.prefix is /foo/usr; # check /foo/usr/lib/scons* first, # then /foo/usr/local/lib/scons*. prefs.append(sys.prefix) prefs.append(os.path.join(sys.prefix, "local")) elif tail == "local": h, t = os.path.split(head) if t == "usr": # sys.prefix is /foo/usr/local; # check /foo/usr/local/lib/scons* first, # then /foo/usr/lib/scons*. prefs.append(sys.prefix) prefs.append(head) else: # sys.prefix is /foo/local; # check only /foo/local/lib/scons*. prefs.append(sys.prefix) else: # sys.prefix is /foo (ends in neither /usr or /local); # check only /foo/lib/scons*. prefs.append(sys.prefix) temp = [os.path.join(x, 'lib') for x in prefs] temp.extend([os.path.join(x, 'lib', 'python' + sys.version[:3], 'site-packages') for x in prefs]) prefs = temp # Add the parent directory of the current python's library to the # preferences. On SuSE-91/AMD64, for example, this is /usr/lib64, # not /usr/lib. try: libpath = os.__file__ except AttributeError: pass else: # Split /usr/libfoo/python*/os.py to /usr/libfoo/python*. libpath, tail = os.path.split(libpath) # Split /usr/libfoo/python* to /usr/libfoo libpath, tail = os.path.split(libpath) # Check /usr/libfoo/scons*. prefs.append(libpath) # Look first for 'scons-__version__' in all of our preference libs, # then for 'scons'. libs.extend([os.path.join(x, scons_version) for x in prefs]) libs.extend([os.path.join(x, 'scons') for x in prefs]) sys.path = libs + sys.path ############################################################################## # END STANDARD SCons SCRIPT HEADER ############################################################################## if __name__ == "__main__": try: import SCons.Script except: ROOT = os.path.join(os.path.abspath(os.path.dirname(__file__)), '..', 'engine') if os.path.exists(ROOT): sys.path += [ROOT] print("SCons import failed. Trying to run from source directory") import SCons.Script # this does all the work, and calls sys.exit # with the proper exit status when done. SCons.Script.main() # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/script/sconsign.py0000644000175000017500000004076012114661560020513 0ustar dktrkranzdktrkranz#! /usr/bin/env python # # SCons - a Software Constructor # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/script/sconsign.py 2013/03/03 09:48:35 garyo" __version__ = "2.3.0" __build__ = "" __buildsys__ = "reepicheep" __date__ = "2013/03/03 09:48:35" __developer__ = "garyo" import os import sys ############################################################################## # BEGIN STANDARD SCons SCRIPT HEADER # # This is the cut-and-paste logic so that a self-contained script can # interoperate correctly with different SCons versions and installation # locations for the engine. If you modify anything in this section, you # should also change other scripts that use this same header. ############################################################################## # Strip the script directory from sys.path() so on case-insensitive # (WIN32) systems Python doesn't think that the "scons" script is the # "SCons" package. Replace it with our own library directories # (version-specific first, in case they installed by hand there, # followed by generic) so we pick up the right version of the build # engine modules if they're in either directory. script_dir = sys.path[0] if script_dir in sys.path: sys.path.remove(script_dir) libs = [] if "SCONS_LIB_DIR" in os.environ: libs.append(os.environ["SCONS_LIB_DIR"]) local_version = 'scons-local-' + __version__ local = 'scons-local' if script_dir: local_version = os.path.join(script_dir, local_version) local = os.path.join(script_dir, local) libs.append(os.path.abspath(local_version)) libs.append(os.path.abspath(local)) scons_version = 'scons-%s' % __version__ # preferred order of scons lookup paths prefs = [] try: import pkg_resources except ImportError: pass else: # when running from an egg add the egg's directory try: d = pkg_resources.get_distribution('scons') except pkg_resources.DistributionNotFound: pass else: prefs.append(d.location) if sys.platform == 'win32': # sys.prefix is (likely) C:\Python*; # check only C:\Python*. prefs.append(sys.prefix) prefs.append(os.path.join(sys.prefix, 'Lib', 'site-packages')) else: # On other (POSIX) platforms, things are more complicated due to # the variety of path names and library locations. Try to be smart # about it. if script_dir == 'bin': # script_dir is `pwd`/bin; # check `pwd`/lib/scons*. prefs.append(os.getcwd()) else: if script_dir == '.' or script_dir == '': script_dir = os.getcwd() head, tail = os.path.split(script_dir) if tail == "bin": # script_dir is /foo/bin; # check /foo/lib/scons*. prefs.append(head) head, tail = os.path.split(sys.prefix) if tail == "usr": # sys.prefix is /foo/usr; # check /foo/usr/lib/scons* first, # then /foo/usr/local/lib/scons*. prefs.append(sys.prefix) prefs.append(os.path.join(sys.prefix, "local")) elif tail == "local": h, t = os.path.split(head) if t == "usr": # sys.prefix is /foo/usr/local; # check /foo/usr/local/lib/scons* first, # then /foo/usr/lib/scons*. prefs.append(sys.prefix) prefs.append(head) else: # sys.prefix is /foo/local; # check only /foo/local/lib/scons*. prefs.append(sys.prefix) else: # sys.prefix is /foo (ends in neither /usr or /local); # check only /foo/lib/scons*. prefs.append(sys.prefix) temp = [os.path.join(x, 'lib') for x in prefs] temp.extend([os.path.join(x, 'lib', 'python' + sys.version[:3], 'site-packages') for x in prefs]) prefs = temp # Add the parent directory of the current python's library to the # preferences. On SuSE-91/AMD64, for example, this is /usr/lib64, # not /usr/lib. try: libpath = os.__file__ except AttributeError: pass else: # Split /usr/libfoo/python*/os.py to /usr/libfoo/python*. libpath, tail = os.path.split(libpath) # Split /usr/libfoo/python* to /usr/libfoo libpath, tail = os.path.split(libpath) # Check /usr/libfoo/scons*. prefs.append(libpath) # Look first for 'scons-__version__' in all of our preference libs, # then for 'scons'. libs.extend([os.path.join(x, scons_version) for x in prefs]) libs.extend([os.path.join(x, 'scons') for x in prefs]) sys.path = libs + sys.path ############################################################################## # END STANDARD SCons SCRIPT HEADER ############################################################################## import SCons.compat # so pickle will import cPickle instead import whichdb import time import pickle import imp import SCons.SConsign def my_whichdb(filename): if filename[-7:] == ".dblite": return "SCons.dblite" try: f = open(filename + ".dblite", "rb") f.close() return "SCons.dblite" except IOError: pass return _orig_whichdb(filename) _orig_whichdb = whichdb.whichdb whichdb.whichdb = my_whichdb def my_import(mname): if '.' in mname: i = mname.rfind('.') parent = my_import(mname[:i]) fp, pathname, description = imp.find_module(mname[i+1:], parent.__path__) else: fp, pathname, description = imp.find_module(mname) return imp.load_module(mname, fp, pathname, description) class Flagger(object): default_value = 1 def __setitem__(self, item, value): self.__dict__[item] = value self.default_value = 0 def __getitem__(self, item): return self.__dict__.get(item, self.default_value) Do_Call = None Print_Directories = [] Print_Entries = [] Print_Flags = Flagger() Verbose = 0 Readable = 0 def default_mapper(entry, name): try: val = eval("entry."+name) except: val = None return str(val) def map_action(entry, name): try: bact = entry.bact bactsig = entry.bactsig except AttributeError: return None return '%s [%s]' % (bactsig, bact) def map_timestamp(entry, name): try: timestamp = entry.timestamp except AttributeError: timestamp = None if Readable and timestamp: return "'" + time.ctime(timestamp) + "'" else: return str(timestamp) def map_bkids(entry, name): try: bkids = entry.bsources + entry.bdepends + entry.bimplicit bkidsigs = entry.bsourcesigs + entry.bdependsigs + entry.bimplicitsigs except AttributeError: return None result = [] for i in range(len(bkids)): result.append(nodeinfo_string(bkids[i], bkidsigs[i], " ")) if result == []: return None return "\n ".join(result) map_field = { 'action' : map_action, 'timestamp' : map_timestamp, 'bkids' : map_bkids, } map_name = { 'implicit' : 'bkids', } def field(name, entry, verbose=Verbose): if not Print_Flags[name]: return None fieldname = map_name.get(name, name) mapper = map_field.get(fieldname, default_mapper) val = mapper(entry, name) if verbose: val = name + ": " + val return val def nodeinfo_raw(name, ninfo, prefix=""): # This just formats the dictionary, which we would normally use str() # to do, except that we want the keys sorted for deterministic output. d = ninfo.__dict__ try: keys = ninfo.field_list + ['_version_id'] except AttributeError: keys = sorted(d.keys()) l = [] for k in keys: l.append('%s: %s' % (repr(k), repr(d.get(k)))) if '\n' in name: name = repr(name) return name + ': {' + ', '.join(l) + '}' def nodeinfo_cooked(name, ninfo, prefix=""): try: field_list = ninfo.field_list except AttributeError: field_list = [] if '\n' in name: name = repr(name) outlist = [name+':'] + [_f for _f in [field(x, ninfo, Verbose) for x in field_list] if _f] if Verbose: sep = '\n ' + prefix else: sep = ' ' return sep.join(outlist) nodeinfo_string = nodeinfo_cooked def printfield(name, entry, prefix=""): outlist = field("implicit", entry, 0) if outlist: if Verbose: print " implicit:" print " " + outlist outact = field("action", entry, 0) if outact: if Verbose: print " action: " + outact else: print " " + outact def printentries(entries, location): if Print_Entries: for name in Print_Entries: try: entry = entries[name] except KeyError: sys.stderr.write("sconsign: no entry `%s' in `%s'\n" % (name, location)) else: try: ninfo = entry.ninfo except AttributeError: print name + ":" else: print nodeinfo_string(name, entry.ninfo) printfield(name, entry.binfo) else: for name in sorted(entries.keys()): entry = entries[name] try: ninfo = entry.ninfo except AttributeError: print name + ":" else: print nodeinfo_string(name, entry.ninfo) printfield(name, entry.binfo) class Do_SConsignDB(object): def __init__(self, dbm_name, dbm): self.dbm_name = dbm_name self.dbm = dbm def __call__(self, fname): # The *dbm modules stick their own file suffixes on the names # that are passed in. This is causes us to jump through some # hoops here to be able to allow the user try: # Try opening the specified file name. Example: # SPECIFIED OPENED BY self.dbm.open() # --------- ------------------------- # .sconsign => .sconsign.dblite # .sconsign.dblite => .sconsign.dblite.dblite db = self.dbm.open(fname, "r") except (IOError, OSError), e: print_e = e try: # That didn't work, so try opening the base name, # so that if the actually passed in 'sconsign.dblite' # (for example), the dbm module will put the suffix back # on for us and open it anyway. db = self.dbm.open(os.path.splitext(fname)[0], "r") except (IOError, OSError): # That didn't work either. See if the file name # they specified just exists (independent of the dbm # suffix-mangling). try: open(fname, "r") except (IOError, OSError), e: # Nope, that file doesn't even exist, so report that # fact back. print_e = e sys.stderr.write("sconsign: %s\n" % (print_e)) return except KeyboardInterrupt: raise except pickle.UnpicklingError: sys.stderr.write("sconsign: ignoring invalid `%s' file `%s'\n" % (self.dbm_name, fname)) return except Exception, e: sys.stderr.write("sconsign: ignoring invalid `%s' file `%s': %s\n" % (self.dbm_name, fname, e)) return if Print_Directories: for dir in Print_Directories: try: val = db[dir] except KeyError: sys.stderr.write("sconsign: no dir `%s' in `%s'\n" % (dir, args[0])) else: self.printentries(dir, val) else: for dir in sorted(db.keys()): self.printentries(dir, db[dir]) def printentries(self, dir, val): print '=== ' + dir + ':' printentries(pickle.loads(val), dir) def Do_SConsignDir(name): try: fp = open(name, 'rb') except (IOError, OSError), e: sys.stderr.write("sconsign: %s\n" % (e)) return try: sconsign = SCons.SConsign.Dir(fp) except KeyboardInterrupt: raise except pickle.UnpicklingError: sys.stderr.write("sconsign: ignoring invalid .sconsign file `%s'\n" % (name)) return except Exception, e: sys.stderr.write("sconsign: ignoring invalid .sconsign file `%s': %s\n" % (name, e)) return printentries(sconsign.entries, args[0]) ############################################################################## import getopt helpstr = """\ Usage: sconsign [OPTIONS] FILE [...] Options: -a, --act, --action Print build action information. -c, --csig Print content signature information. -d DIR, --dir=DIR Print only info about DIR. -e ENTRY, --entry=ENTRY Print only info about ENTRY. -f FORMAT, --format=FORMAT FILE is in the specified FORMAT. -h, --help Print this message and exit. -i, --implicit Print implicit dependency information. -r, --readable Print timestamps in human-readable form. --raw Print raw Python object representations. -s, --size Print file sizes. -t, --timestamp Print timestamp information. -v, --verbose Verbose, describe each field. """ opts, args = getopt.getopt(sys.argv[1:], "acd:e:f:hirstv", ['act', 'action', 'csig', 'dir=', 'entry=', 'format=', 'help', 'implicit', 'raw', 'readable', 'size', 'timestamp', 'verbose']) for o, a in opts: if o in ('-a', '--act', '--action'): Print_Flags['action'] = 1 elif o in ('-c', '--csig'): Print_Flags['csig'] = 1 elif o in ('-d', '--dir'): Print_Directories.append(a) elif o in ('-e', '--entry'): Print_Entries.append(a) elif o in ('-f', '--format'): Module_Map = {'dblite' : 'SCons.dblite', 'sconsign' : None} dbm_name = Module_Map.get(a, a) if dbm_name: try: dbm = my_import(dbm_name) except: sys.stderr.write("sconsign: illegal file format `%s'\n" % a) print helpstr sys.exit(2) Do_Call = Do_SConsignDB(a, dbm) else: Do_Call = Do_SConsignDir elif o in ('-h', '--help'): print helpstr sys.exit(0) elif o in ('-i', '--implicit'): Print_Flags['implicit'] = 1 elif o in ('--raw',): nodeinfo_string = nodeinfo_raw elif o in ('-r', '--readable'): Readable = 1 elif o in ('-s', '--size'): Print_Flags['size'] = 1 elif o in ('-t', '--timestamp'): Print_Flags['timestamp'] = 1 elif o in ('-v', '--verbose'): Verbose = 1 if Do_Call: for a in args: Do_Call(a) else: for a in args: dbm_name = whichdb.whichdb(a) if dbm_name: Map_Module = {'SCons.dblite' : 'dblite'} dbm = my_import(dbm_name) Do_SConsignDB(Map_Module.get(dbm_name, dbm_name), dbm)(a) else: Do_SConsignDir(a) sys.exit(0) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/0000755000175000017500000000000012114661557016256 5ustar dktrkranzdktrkranzscons-doc-2.3.0/src/engine/MANIFEST.in0000644000175000017500000001022512114661557020014 0ustar dktrkranzdktrkranzSCons/__init__.py SCons/Action.py SCons/Builder.py SCons/compat/__init__.py SCons/compat/_scons_builtins.py SCons/compat/_scons_collections.py SCons/compat/_scons_dbm.py SCons/compat/_scons_hashlib.py SCons/compat/_scons_io.py SCons/compat/_scons_sets.py SCons/compat/_scons_subprocess.py SCons/CacheDir.py SCons/Conftest.py SCons/cpp.py SCons/dblite.py SCons/Debug.py SCons/Defaults.py SCons/Environment.py SCons/Errors.py SCons/Executor.py SCons/Job.py SCons/exitfuncs.py SCons/Memoize.py SCons/Node/__init__.py SCons/Node/Alias.py SCons/Node/FS.py SCons/Node/Python.py SCons/Options/__init__.py SCons/Options/BoolOption.py SCons/Options/EnumOption.py SCons/Options/ListOption.py SCons/Options/PackageOption.py SCons/Options/PathOption.py SCons/PathList.py SCons/Platform/__init__.py SCons/Platform/aix.py SCons/Platform/cygwin.py SCons/Platform/darwin.py SCons/Platform/hpux.py SCons/Platform/irix.py SCons/Platform/os2.py SCons/Platform/posix.py SCons/Platform/sunos.py SCons/Platform/win32.py SCons/Scanner/__init__.py SCons/Scanner/C.py SCons/Scanner/D.py SCons/Scanner/Dir.py SCons/Scanner/Fortran.py SCons/Scanner/IDL.py SCons/Scanner/LaTeX.py SCons/Scanner/Prog.py SCons/Scanner/RC.py SCons/SConf.py SCons/SConsign.py SCons/Script/__init__.py SCons/Script/Interactive.py SCons/Script/Main.py SCons/Script/SConscript.py SCons/Script/SConsOptions.py SCons/Sig.py SCons/Subst.py SCons/Taskmaster.py SCons/Tool/__init__.py SCons/Tool/386asm.py SCons/Tool/aixc++.py SCons/Tool/aixcc.py SCons/Tool/aixf77.py SCons/Tool/aixlink.py SCons/Tool/applelink.py SCons/Tool/ar.py SCons/Tool/as.py SCons/Tool/bcc32.py SCons/Tool/BitKeeper.py SCons/Tool/c++.py SCons/Tool/cc.py SCons/Tool/cvf.py SCons/Tool/CVS.py SCons/Tool/default.py SCons/Tool/dmd.py SCons/Tool/dvi.py SCons/Tool/dvipdf.py SCons/Tool/dvips.py SCons/Tool/f77.py SCons/Tool/f90.py SCons/Tool/f95.py SCons/Tool/f03.py SCons/Tool/filesystem.py SCons/Tool/fortran.py SCons/Tool/FortranCommon.py SCons/Tool/g++.py SCons/Tool/g77.py SCons/Tool/gas.py SCons/Tool/gcc.py SCons/Tool/gfortran.py SCons/Tool/gnulink.py SCons/Tool/gs.py SCons/Tool/hpc++.py SCons/Tool/hpcc.py SCons/Tool/hplink.py SCons/Tool/icc.py SCons/Tool/icl.py SCons/Tool/ifl.py SCons/Tool/ifort.py SCons/Tool/ilink.py SCons/Tool/ilink32.py SCons/Tool/install.py SCons/Tool/intelc.py SCons/Tool/ipkg.py SCons/Tool/jar.py SCons/Tool/JavaCommon.py SCons/Tool/javac.py SCons/Tool/javah.py SCons/Tool/latex.py SCons/Tool/lex.py SCons/Tool/link.py SCons/Tool/linkloc.py SCons/Tool/MSCommon/__init__.py SCons/Tool/MSCommon/arch.py SCons/Tool/MSCommon/common.py SCons/Tool/MSCommon/netframework.py SCons/Tool/MSCommon/sdk.py SCons/Tool/MSCommon/vs.py SCons/Tool/MSCommon/vc.py SCons/Tool/m4.py SCons/Tool/masm.py SCons/Tool/midl.py SCons/Tool/mingw.py SCons/Tool/mslib.py SCons/Tool/mslink.py SCons/Tool/mssdk.py SCons/Tool/msvc.py SCons/Tool/msvs.py SCons/Tool/mwcc.py SCons/Tool/mwld.py SCons/Tool/nasm.py SCons/Tool/packaging/__init__.py SCons/Tool/packaging/ipk.py SCons/Tool/packaging/msi.py SCons/Tool/packaging/rpm.py SCons/Tool/packaging/src_tarbz2.py SCons/Tool/packaging/src_targz.py SCons/Tool/packaging/src_zip.py SCons/Tool/packaging/tarbz2.py SCons/Tool/packaging/targz.py SCons/Tool/packaging/zip.py SCons/Tool/pdf.py SCons/Tool/pdflatex.py SCons/Tool/pdftex.py SCons/Tool/Perforce.py SCons/Tool/PharLapCommon.py SCons/Tool/qt.py SCons/Tool/RCS.py SCons/Tool/rmic.py SCons/Tool/rpcgen.py SCons/Tool/rpm.py SCons/Tool/rpmutils.py SCons/Tool/SCCS.py SCons/Tool/sgiar.py SCons/Tool/sgic++.py SCons/Tool/sgicc.py SCons/Tool/sgilink.py SCons/Tool/Subversion.py SCons/Tool/sunar.py SCons/Tool/sunc++.py SCons/Tool/suncc.py SCons/Tool/sunf77.py SCons/Tool/sunf90.py SCons/Tool/sunf95.py SCons/Tool/sunlink.py SCons/Tool/swig.py SCons/Tool/tar.py SCons/Tool/tex.py SCons/Tool/textfile.py SCons/Tool/tlib.py SCons/Tool/wix.py SCons/Tool/yacc.py SCons/Tool/zip.py SCons/Util.py SCons/Variables/__init__.py SCons/Variables/BoolVariable.py SCons/Variables/EnumVariable.py SCons/Variables/ListVariable.py SCons/Variables/PackageVariable.py SCons/Variables/PathVariable.py SCons/Warnings.py SCons/Tool/GettextCommon.py SCons/Tool/gettext.py SCons/Tool/msgfmt.py SCons/Tool/msginit.py SCons/Tool/msgmerge.py SCons/Tool/xgettext.py scons-doc-2.3.0/src/engine/SCons/0000755000175000017500000000000012114661560017275 5ustar dktrkranzdktrkranzscons-doc-2.3.0/src/engine/SCons/TaskmasterTests.py0000644000175000017500000011026312114661557023021 0ustar dktrkranzdktrkranz# # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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. # from __future__ import division __revision__ = "src/engine/SCons/TaskmasterTests.py 2013/03/03 09:48:35 garyo" import SCons.compat import copy import sys import unittest import SCons.Taskmaster import SCons.Errors built_text = None cache_text = [] visited_nodes = [] executed = None scan_called = 0 class Node(object): def __init__(self, name, kids = [], scans = []): self.name = name self.kids = kids self.scans = scans self.cached = 0 self.scanned = 0 self.scanner = None self.targets = [self] self.prerequisites = [] class Builder(object): def targets(self, node): return node.targets self.builder = Builder() self.bsig = None self.csig = None self.state = SCons.Node.no_state self.prepared = None self.ref_count = 0 self.waiting_parents = set() self.waiting_s_e = set() self.side_effect = 0 self.side_effects = [] self.alttargets = [] self.postprocessed = None self._bsig_val = None self._current_val = 0 self.always_build = None def disambiguate(self): return self def push_to_cache(self): pass def retrieve_from_cache(self): global cache_text if self.cached: cache_text.append(self.name + " retrieved") return self.cached def make_ready(self): pass def prepare(self): self.prepared = 1 self.get_binfo() def build(self): global built_text built_text = self.name + " built" def remove(self): pass # The following four methods new_binfo(), del_binfo(), # get_binfo(), clear() as well as its calls have been added # to support the cached_execute() test (issue #2720). # They are full copies (or snippets) of their actual # counterparts in the Node class... def new_binfo(self): binfo = "binfo" return binfo def del_binfo(self): """Delete the build info from this node.""" try: delattr(self, 'binfo') except AttributeError: pass def get_binfo(self): """Fetch a node's build information.""" try: return self.binfo except AttributeError: pass binfo = self.new_binfo() self.binfo = binfo return binfo def clear(self): # The del_binfo() call here isn't necessary for normal execution, # but is for interactive mode, where we might rebuild the same # target and need to start from scratch. self.del_binfo() def built(self): global built_text if not self.cached: built_text = built_text + " really" # Clear the implicit dependency caches of any Nodes # waiting for this Node to be built. for parent in self.waiting_parents: parent.implicit = None self.clear() def has_builder(self): return not self.builder is None def is_derived(self): return self.has_builder or self.side_effect def alter_targets(self): return self.alttargets, None def visited(self): global visited_nodes visited_nodes.append(self.name) def children(self): if not self.scanned: self.scan() self.scanned = 1 return self.kids def scan(self): global scan_called scan_called = scan_called + 1 self.kids = self.kids + self.scans self.scans = [] def scanner_key(self): return self.name def add_to_waiting_parents(self, node): wp = self.waiting_parents if node in wp: return 0 wp.add(node) return 1 def get_state(self): return self.state def set_state(self, state): self.state = state def set_bsig(self, bsig): self.bsig = bsig def set_csig(self, csig): self.csig = csig def store_csig(self): pass def store_bsig(self): pass def is_pseudo_derived(self): pass def is_up_to_date(self): return self._current_val def depends_on(self, nodes): for node in nodes: if node in self.kids: return 1 return 0 def __str__(self): return self.name def postprocess(self): self.postprocessed = 1 self.waiting_parents = set() def get_executor(self): if not hasattr(self, 'executor'): class Executor(object): def prepare(self): pass def get_action_targets(self): return self.targets def get_all_targets(self): return self.targets def get_all_children(self): result = [] for node in self.targets: result.extend(node.children()) return result def get_all_prerequisites(self): return [] def get_action_side_effects(self): return [] self.executor = Executor() self.executor.targets = self.targets return self.executor class OtherError(Exception): pass class MyException(Exception): pass class TaskmasterTestCase(unittest.TestCase): def test_next_task(self): """Test fetching the next task """ global built_text n1 = Node("n1") tm = SCons.Taskmaster.Taskmaster([n1, n1]) t = tm.next_task() t.prepare() t.execute() t = tm.next_task() assert t is None n1 = Node("n1") n2 = Node("n2") n3 = Node("n3", [n1, n2]) tm = SCons.Taskmaster.Taskmaster([n3]) t = tm.next_task() t.prepare() t.execute() assert built_text == "n1 built", built_text t.executed() t.postprocess() t = tm.next_task() t.prepare() t.execute() assert built_text == "n2 built", built_text t.executed() t.postprocess() t = tm.next_task() t.prepare() t.execute() assert built_text == "n3 built", built_text t.executed() t.postprocess() assert tm.next_task() is None built_text = "up to date: " top_node = n3 class MyTask(SCons.Taskmaster.Task): def execute(self): global built_text if self.targets[0].get_state() == SCons.Node.up_to_date: if self.top: built_text = self.targets[0].name + " up-to-date top" else: built_text = self.targets[0].name + " up-to-date" else: self.targets[0].build() n1.set_state(SCons.Node.no_state) n1._current_val = 1 n2.set_state(SCons.Node.no_state) n2._current_val = 1 n3.set_state(SCons.Node.no_state) n3._current_val = 1 tm = SCons.Taskmaster.Taskmaster(targets = [n3], tasker = MyTask) t = tm.next_task() t.prepare() t.execute() assert built_text == "n1 up-to-date", built_text t.executed() t.postprocess() t = tm.next_task() t.prepare() t.execute() assert built_text == "n2 up-to-date", built_text t.executed() t.postprocess() t = tm.next_task() t.prepare() t.execute() assert built_text == "n3 up-to-date top", built_text t.executed() t.postprocess() assert tm.next_task() is None n1 = Node("n1") n2 = Node("n2") n3 = Node("n3", [n1, n2]) n4 = Node("n4") n5 = Node("n5", [n3, n4]) tm = SCons.Taskmaster.Taskmaster([n5]) t1 = tm.next_task() assert t1.get_target() == n1 t2 = tm.next_task() assert t2.get_target() == n2 t4 = tm.next_task() assert t4.get_target() == n4 t4.executed() t4.postprocess() t1.executed() t1.postprocess() t2.executed() t2.postprocess() t3 = tm.next_task() assert t3.get_target() == n3 t3.executed() t3.postprocess() t5 = tm.next_task() assert t5.get_target() == n5, t5.get_target() t5.executed() t5.postprocess() assert tm.next_task() is None n4 = Node("n4") n4.set_state(SCons.Node.executed) tm = SCons.Taskmaster.Taskmaster([n4]) assert tm.next_task() is None n1 = Node("n1") n2 = Node("n2", [n1]) tm = SCons.Taskmaster.Taskmaster([n2,n2]) t = tm.next_task() t.executed() t.postprocess() t = tm.next_task() assert tm.next_task() is None n1 = Node("n1") n2 = Node("n2") n3 = Node("n3", [n1], [n2]) tm = SCons.Taskmaster.Taskmaster([n3]) t = tm.next_task() target = t.get_target() assert target == n1, target t.executed() t.postprocess() t = tm.next_task() target = t.get_target() assert target == n2, target t.executed() t.postprocess() t = tm.next_task() target = t.get_target() assert target == n3, target t.executed() t.postprocess() assert tm.next_task() is None n1 = Node("n1") n2 = Node("n2") n3 = Node("n3", [n1, n2]) n4 = Node("n4", [n3]) n5 = Node("n5", [n3]) global scan_called scan_called = 0 tm = SCons.Taskmaster.Taskmaster([n4]) t = tm.next_task() assert t.get_target() == n1 t.executed() t.postprocess() t = tm.next_task() assert t.get_target() == n2 t.executed() t.postprocess() t = tm.next_task() assert t.get_target() == n3 t.executed() t.postprocess() t = tm.next_task() assert t.get_target() == n4 t.executed() t.postprocess() assert tm.next_task() is None assert scan_called == 4, scan_called tm = SCons.Taskmaster.Taskmaster([n5]) t = tm.next_task() assert t.get_target() == n5, t.get_target() t.executed() assert tm.next_task() is None assert scan_called == 5, scan_called n1 = Node("n1") n2 = Node("n2") n3 = Node("n3") n4 = Node("n4", [n1,n2,n3]) n5 = Node("n5", [n4]) n3.side_effect = 1 n1.side_effects = n2.side_effects = n3.side_effects = [n4] tm = SCons.Taskmaster.Taskmaster([n1,n2,n3,n4,n5]) t = tm.next_task() assert t.get_target() == n1 assert n4.state == SCons.Node.executing, n4.state t.executed() t.postprocess() t = tm.next_task() assert t.get_target() == n2 t.executed() t.postprocess() t = tm.next_task() assert t.get_target() == n3 t.executed() t.postprocess() t = tm.next_task() assert t.get_target() == n4 t.executed() t.postprocess() t = tm.next_task() assert t.get_target() == n5 assert not tm.next_task() t.executed() t.postprocess() n1 = Node("n1") n2 = Node("n2") n3 = Node("n3") n4 = Node("n4", [n1,n2,n3]) def reverse(dependencies): dependencies.reverse() return dependencies tm = SCons.Taskmaster.Taskmaster([n4], order=reverse) t = tm.next_task() assert t.get_target() == n3, t.get_target() t.executed() t.postprocess() t = tm.next_task() assert t.get_target() == n2, t.get_target() t.executed() t.postprocess() t = tm.next_task() assert t.get_target() == n1, t.get_target() t.executed() t.postprocess() t = tm.next_task() assert t.get_target() == n4, t.get_target() t.executed() t.postprocess() n5 = Node("n5") n6 = Node("n6") n7 = Node("n7") n6.alttargets = [n7] tm = SCons.Taskmaster.Taskmaster([n5]) t = tm.next_task() assert t.get_target() == n5 t.executed() t.postprocess() tm = SCons.Taskmaster.Taskmaster([n6]) t = tm.next_task() assert t.get_target() == n7 t.executed() t.postprocess() t = tm.next_task() assert t.get_target() == n6 t.executed() t.postprocess() n1 = Node("n1") n2 = Node("n2", [n1]) n1.set_state(SCons.Node.failed) tm = SCons.Taskmaster.Taskmaster([n2]) assert tm.next_task() is None n1 = Node("n1") n2 = Node("n2") n1.targets = [n1, n2] n1._current_val = 1 tm = SCons.Taskmaster.Taskmaster([n1]) t = tm.next_task() t.executed() t.postprocess() s = n1.get_state() assert s == SCons.Node.executed, s s = n2.get_state() assert s == SCons.Node.executed, s def test_make_ready_out_of_date(self): """Test the Task.make_ready() method's list of out-of-date Nodes """ ood = [] def TaskGen(tm, targets, top, node, ood=ood): class MyTask(SCons.Taskmaster.Task): def make_ready(self): SCons.Taskmaster.Task.make_ready(self) self.ood.extend(self.out_of_date) t = MyTask(tm, targets, top, node) t.ood = ood return t n1 = Node("n1") c2 = Node("c2") c2._current_val = 1 n3 = Node("n3") c4 = Node("c4") c4._current_val = 1 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[:] t = tm.next_task() assert ood == [n1], ood del ood[:] t = tm.next_task() assert ood == [], ood del ood[:] t = tm.next_task() assert ood == [n3], ood del ood[:] 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() """ class MyTask(SCons.Taskmaster.Task): def make_ready(self): raise MyException("from make_ready()") n1 = Node("n1") tm = SCons.Taskmaster.Taskmaster(targets = [n1], tasker = MyTask) t = tm.next_task() exc_type, exc_value, exc_tb = t.exception assert exc_type == MyException, repr(exc_type) assert str(exc_value) == "from make_ready()", exc_value def test_make_ready_all(self): """Test the make_ready_all() method""" class MyTask(SCons.Taskmaster.Task): make_ready = SCons.Taskmaster.Task.make_ready_all n1 = Node("n1") c2 = Node("c2") c2._current_val = 1 n3 = Node("n3") c4 = Node("c4") c4._current_val = 1 tm = SCons.Taskmaster.Taskmaster(targets = [n1, c2, n3, c4]) t = tm.next_task() target = t.get_target() assert target is n1, target assert target.state == SCons.Node.executing, target.state t = tm.next_task() target = t.get_target() assert target is c2, target assert target.state == SCons.Node.up_to_date, target.state t = tm.next_task() target = t.get_target() assert target is n3, target assert target.state == SCons.Node.executing, target.state t = tm.next_task() target = t.get_target() assert target is c4, target assert target.state == SCons.Node.up_to_date, target.state t = tm.next_task() assert t is None n1 = Node("n1") c2 = Node("c2") n3 = Node("n3") c4 = Node("c4") tm = SCons.Taskmaster.Taskmaster(targets = [n1, c2, n3, c4], tasker = MyTask) t = tm.next_task() target = t.get_target() assert target is n1, target assert target.state == SCons.Node.executing, target.state t = tm.next_task() target = t.get_target() assert target is c2, target assert target.state == SCons.Node.executing, target.state t = tm.next_task() target = t.get_target() assert target is n3, target assert target.state == SCons.Node.executing, target.state t = tm.next_task() target = t.get_target() assert target is c4, target assert target.state == SCons.Node.executing, target.state t = tm.next_task() assert t is None def test_children_errors(self): """Test errors when fetching the children of a node. """ class StopNode(Node): def children(self): raise SCons.Errors.StopError("stop!") class ExitNode(Node): def children(self): sys.exit(77) n1 = StopNode("n1") tm = SCons.Taskmaster.Taskmaster([n1]) t = tm.next_task() exc_type, exc_value, exc_tb = t.exception assert exc_type == SCons.Errors.StopError, repr(exc_type) assert str(exc_value) == "stop!", exc_value n2 = ExitNode("n2") tm = SCons.Taskmaster.Taskmaster([n2]) t = tm.next_task() exc_type, exc_value = t.exception assert exc_type == SCons.Errors.ExplicitExit, repr(exc_type) assert exc_value.node == n2, exc_value.node assert exc_value.status == 77, exc_value.status def test_cycle_detection(self): """Test detecting dependency cycles """ n1 = Node("n1") n2 = Node("n2", [n1]) n3 = Node("n3", [n2]) n1.kids = [n3] tm = SCons.Taskmaster.Taskmaster([n3]) try: t = tm.next_task() except SCons.Errors.UserError, e: assert str(e) == "Dependency cycle: n3 -> n1 -> n2 -> n3", str(e) else: assert 'Did not catch expected UserError' def test_next_top_level_candidate(self): """Test the next_top_level_candidate() method """ n1 = Node("n1") n2 = Node("n2", [n1]) n3 = Node("n3", [n2]) tm = SCons.Taskmaster.Taskmaster([n3]) t = tm.next_task() assert t.targets == [n1], t.targets t.fail_stop() assert t.targets == [n3], list(map(str, t.targets)) assert t.top == 1, t.top def test_stop(self): """Test the stop() method Both default and overridden in a subclass. """ global built_text n1 = Node("n1") n2 = Node("n2") n3 = Node("n3", [n1, n2]) tm = SCons.Taskmaster.Taskmaster([n3]) t = tm.next_task() t.prepare() t.execute() assert built_text == "n1 built", built_text t.executed() t.postprocess() assert built_text == "n1 built really", built_text tm.stop() assert tm.next_task() is None class MyTM(SCons.Taskmaster.Taskmaster): def stop(self): global built_text built_text = "MyTM.stop()" SCons.Taskmaster.Taskmaster.stop(self) n1 = Node("n1") n2 = Node("n2") n3 = Node("n3", [n1, n2]) built_text = None tm = MyTM([n3]) tm.next_task().execute() assert built_text == "n1 built" tm.stop() assert built_text == "MyTM.stop()" assert tm.next_task() is None def test_executed(self): """Test when a task has been executed """ global built_text global visited_nodes n1 = Node("n1") tm = SCons.Taskmaster.Taskmaster([n1]) t = tm.next_task() built_text = "xxx" visited_nodes = [] n1.set_state(SCons.Node.executing) t.executed() s = n1.get_state() assert s == SCons.Node.executed, s assert built_text == "xxx really", built_text assert visited_nodes == ['n1'], visited_nodes n2 = Node("n2") tm = SCons.Taskmaster.Taskmaster([n2]) t = tm.next_task() built_text = "should_not_change" visited_nodes = [] n2.set_state(None) t.executed() s = n2.get_state() assert s is None, s assert built_text == "should_not_change", built_text assert visited_nodes == ['n2'], visited_nodes n3 = Node("n3") n4 = Node("n4") n3.targets = [n3, n4] tm = SCons.Taskmaster.Taskmaster([n3]) t = tm.next_task() visited_nodes = [] n3.set_state(SCons.Node.up_to_date) n4.set_state(SCons.Node.executing) t.executed() s = n3.get_state() assert s == SCons.Node.up_to_date, s s = n4.get_state() assert s == SCons.Node.executed, s assert visited_nodes == ['n3', 'n4'], visited_nodes def test_prepare(self): """Test preparation of multiple Nodes for a task """ n1 = Node("n1") n2 = Node("n2") tm = SCons.Taskmaster.Taskmaster([n1, n2]) t = tm.next_task() # This next line is moderately bogus. We're just reaching # in and setting the targets for this task to an array. The # "right" way to do this would be to have the next_task() call # set it up by having something that approximates a real Builder # return this list--but that's more work than is probably # warranted right now. n1.get_executor().targets = [n1, n2] t.prepare() assert n1.prepared assert n2.prepared n3 = Node("n3") n4 = Node("n4") tm = SCons.Taskmaster.Taskmaster([n3, n4]) t = tm.next_task() # More bogus reaching in and setting the targets. n3.set_state(SCons.Node.up_to_date) n3.get_executor().targets = [n3, n4] t.prepare() assert n3.prepared assert n4.prepared # If the Node has had an exception recorded while it was getting # prepared, then prepare() should raise that exception. class MyException(Exception): pass built_text = None n5 = Node("n5") tm = SCons.Taskmaster.Taskmaster([n5]) t = tm.next_task() t.exception_set((MyException, "exception value")) exc_caught = None try: t.prepare() except MyException, e: exc_caught = 1 except: pass assert exc_caught, "did not catch expected MyException" assert str(e) == "exception value", e assert built_text is None, built_text # Regression test, make sure we prepare not only # all targets, but their side effects as well. n6 = Node("n6") n7 = Node("n7") n8 = Node("n8") n9 = Node("n9") n10 = Node("n10") n6.side_effects = [ n8 ] n7.side_effects = [ n9, n10 ] tm = SCons.Taskmaster.Taskmaster([n6, n7]) t = tm.next_task() # More bogus reaching in and setting the targets. n6.get_executor().targets = [n6, n7] t.prepare() assert n6.prepared assert n7.prepared assert n8.prepared assert n9.prepared assert n10.prepared # Make sure we call an Executor's prepare() method. class ExceptionExecutor(object): def prepare(self): raise Exception("Executor.prepare() exception") def get_all_targets(self): return self.nodes def get_all_children(self): result = [] for node in self.nodes: result.extend(node.children()) return result def get_all_prerequisites(self): return [] def get_action_side_effects(self): return [] n11 = Node("n11") n11.executor = ExceptionExecutor() n11.executor.nodes = [n11] tm = SCons.Taskmaster.Taskmaster([n11]) t = tm.next_task() try: t.prepare() except Exception, e: assert str(e) == "Executor.prepare() exception", e else: raise AssertionError("did not catch expected exception") def test_execute(self): """Test executing a task """ global built_text global cache_text n1 = Node("n1") tm = SCons.Taskmaster.Taskmaster([n1]) t = tm.next_task() t.execute() assert built_text == "n1 built", built_text def raise_UserError(): raise SCons.Errors.UserError n2 = Node("n2") n2.build = raise_UserError tm = SCons.Taskmaster.Taskmaster([n2]) t = tm.next_task() try: t.execute() except SCons.Errors.UserError: pass else: raise TestFailed("did not catch expected UserError") def raise_BuildError(): raise SCons.Errors.BuildError n3 = Node("n3") n3.build = raise_BuildError tm = SCons.Taskmaster.Taskmaster([n3]) t = tm.next_task() try: t.execute() except SCons.Errors.BuildError: pass else: raise TestFailed("did not catch expected BuildError") # On a generic (non-BuildError) exception from a Builder, # the target should throw a BuildError exception with the # args set to the exception value, instance, and traceback. def raise_OtherError(): raise OtherError n4 = Node("n4") n4.build = raise_OtherError tm = SCons.Taskmaster.Taskmaster([n4]) t = tm.next_task() try: t.execute() except SCons.Errors.BuildError, e: assert e.node == n4, e.node assert e.errstr == "OtherError : ", e.errstr assert len(e.exc_info) == 3, e.exc_info exc_traceback = sys.exc_info()[2] assert isinstance(e.exc_info[2], type(exc_traceback)), e.exc_info[2] else: raise TestFailed("did not catch expected BuildError") built_text = None cache_text = [] n5 = Node("n5") n6 = Node("n6") n6.cached = 1 tm = SCons.Taskmaster.Taskmaster([n5]) t = tm.next_task() # This next line is moderately bogus. We're just reaching # in and setting the targets for this task to an array. The # "right" way to do this would be to have the next_task() call # set it up by having something that approximates a real Builder # return this list--but that's more work than is probably # warranted right now. t.targets = [n5, n6] t.execute() assert built_text == "n5 built", built_text assert cache_text == [], cache_text built_text = None cache_text = [] n7 = Node("n7") n8 = Node("n8") n7.cached = 1 n8.cached = 1 tm = SCons.Taskmaster.Taskmaster([n7]) t = tm.next_task() # This next line is moderately bogus. We're just reaching # in and setting the targets for this task to an array. The # "right" way to do this would be to have the next_task() call # set it up by having something that approximates a real Builder # return this list--but that's more work than is probably # warranted right now. t.targets = [n7, n8] t.execute() assert built_text is None, built_text assert cache_text == ["n7 retrieved", "n8 retrieved"], cache_text def test_cached_execute(self): """Test executing a task with cached targets """ # In issue #2720 Alexei Klimkin detected that the previous # workflow for execute() led to problems in a multithreaded build. # We have: # task.prepare() # task.execute() # task.executed() # -> node.visited() # for the Serial flow, but # - Parallel - - Worker - # task.prepare() # requestQueue.put(task) # task = requestQueue.get() # task.execute() # resultQueue.put(task) # task = resultQueue.get() # task.executed() # ->node.visited() # in parallel. Since execute() used to call built() when a target # was cached, it could unblock dependent nodes before the binfo got # restored again in visited(). This resulted in spurious # "file not found" build errors, because files fetched from cache would # be seen as not up to date and wouldn't be scanned for implicit # dependencies. # # The following test ensures that execute() only marks targets as cached, # but the actual call to built() happens in executed() only. # Like this, the binfo should still be intact after calling execute()... global cache_text n1 = Node("n1") # Mark the node as being cached n1.cached = 1 tm = SCons.Taskmaster.Taskmaster([n1]) t = tm.next_task() t.prepare() t.execute() assert cache_text == ["n1 retrieved"], cache_text # If no binfo exists anymore, something has gone wrong... has_binfo = hasattr(n1, 'binfo') assert has_binfo == True, has_binfo def test_exception(self): """Test generic Taskmaster exception handling """ n1 = Node("n1") tm = SCons.Taskmaster.Taskmaster([n1]) t = tm.next_task() t.exception_set((1, 2)) exc_type, exc_value = t.exception assert exc_type == 1, exc_type assert exc_value == 2, exc_value t.exception_set(3) assert t.exception == 3 try: 1//0 except: pass t.exception_set(None) exc_type, exc_value, exc_tb = t.exception assert exc_type is ZeroDivisionError, exc_type exception_values = [ "integer division or modulo", "integer division or modulo by zero", ] assert str(exc_value) in exception_values, exc_value class Exception1(Exception): pass t.exception_set((Exception1, None)) try: t.exception_raise() except: exc_type, exc_value = sys.exc_info()[:2] assert exc_type == Exception1, exc_type assert str(exc_value) == '', exc_value else: assert 0, "did not catch expected exception" class Exception2(Exception): pass t.exception_set((Exception2, "xyzzy")) try: t.exception_raise() except: exc_type, exc_value = sys.exc_info()[:2] assert exc_type == Exception2, exc_type assert str(exc_value) == "xyzzy", exc_value else: assert 0, "did not catch expected exception" class Exception3(Exception): pass try: 1//0 except: tb = sys.exc_info()[2] t.exception_set((Exception3, "arg", tb)) try: t.exception_raise() except: exc_type, exc_value, exc_tb = sys.exc_info() assert exc_type == Exception3, exc_type assert str(exc_value) == "arg", exc_value import traceback x = traceback.extract_tb(tb)[-1] y = traceback.extract_tb(exc_tb)[-1] assert x == y, "x = %s, y = %s" % (x, y) else: assert 0, "did not catch expected exception" def test_postprocess(self): """Test postprocessing targets to give them a chance to clean up """ n1 = Node("n1") tm = SCons.Taskmaster.Taskmaster([n1]) t = tm.next_task() assert not n1.postprocessed t.postprocess() assert n1.postprocessed n2 = Node("n2") n3 = Node("n3") tm = SCons.Taskmaster.Taskmaster([n2, n3]) assert not n2.postprocessed assert not n3.postprocessed t = tm.next_task() t.postprocess() assert n2.postprocessed assert not n3.postprocessed t = tm.next_task() t.postprocess() assert n2.postprocessed assert n3.postprocessed def test_trace(self): """Test Taskmaster tracing """ import io trace = io.StringIO() n1 = Node("n1") n2 = Node("n2") n3 = Node("n3", [n1, n2]) tm = SCons.Taskmaster.Taskmaster([n1, n1, n3], trace=trace) t = tm.next_task() t.prepare() t.execute() t.postprocess() n1.set_state(SCons.Node.executed) t = tm.next_task() t.prepare() t.execute() t.postprocess() n2.set_state(SCons.Node.executed) t = tm.next_task() t.prepare() t.execute() t.postprocess() t = tm.next_task() assert t is None value = trace.getvalue() expect = """\ Taskmaster: Looking for a node to evaluate Taskmaster: Considering node and its children: Taskmaster: Evaluating Task.make_ready_current(): node Task.prepare(): node Task.execute(): node Task.postprocess(): node Taskmaster: Looking for a node to evaluate Taskmaster: Considering node and its children: Taskmaster: already handled (executed) Taskmaster: Considering node and its children: Taskmaster: Taskmaster: Taskmaster: adjusted ref count: , child 'n2' Taskmaster: Considering node and its children: Taskmaster: Evaluating Task.make_ready_current(): node Task.prepare(): node Task.execute(): node Task.postprocess(): node Task.postprocess(): removing Task.postprocess(): adjusted parent ref count Taskmaster: Looking for a node to evaluate Taskmaster: Considering node and its children: Taskmaster: Taskmaster: Taskmaster: Evaluating Task.make_ready_current(): node Task.prepare(): node Task.execute(): node Task.postprocess(): node Taskmaster: Looking for a node to evaluate Taskmaster: No candidate anymore. """ assert value == expect, value if __name__ == "__main__": suite = unittest.makeSuite(TaskmasterTestCase, 'test_') if not unittest.TextTestRunner().run(suite).wasSuccessful(): sys.exit(1) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Subst.py0000644000175000017500000010372612114661557020766 0ustar dktrkranzdktrkranz"""SCons.Subst SCons string substitution. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Subst.py 2013/03/03 09:48:35 garyo" import collections import re import SCons.Errors from SCons.Util import is_String, is_Sequence # Indexed by the SUBST_* constants below. _strconv = [SCons.Util.to_String_for_subst, SCons.Util.to_String_for_subst, SCons.Util.to_String_for_signature] AllowableExceptions = (IndexError, NameError) def SetAllowableExceptions(*excepts): global AllowableExceptions AllowableExceptions = [_f for _f in excepts if _f] def raise_exception(exception, target, s): name = exception.__class__.__name__ msg = "%s `%s' trying to evaluate `%s'" % (name, exception, s) if target: raise SCons.Errors.BuildError(target[0], msg) else: raise SCons.Errors.UserError(msg) class Literal(object): """A wrapper for a string. If you use this object wrapped around a string, then it will be interpreted as literal. When passed to the command interpreter, all special characters will be escaped.""" def __init__(self, lstr): self.lstr = lstr def __str__(self): return self.lstr def escape(self, escape_func): return escape_func(self.lstr) def for_signature(self): return self.lstr def is_literal(self): return 1 class SpecialAttrWrapper(object): """This is a wrapper for what we call a 'Node special attribute.' This is any of the attributes of a Node that we can reference from Environment variable substitution, such as $TARGET.abspath or $SOURCES[1].filebase. We implement the same methods as Literal so we can handle special characters, plus a for_signature method, such that we can return some canonical string during signature calculation to avoid unnecessary rebuilds.""" def __init__(self, lstr, for_signature=None): """The for_signature parameter, if supplied, will be the canonical string we return from for_signature(). Else we will simply return lstr.""" self.lstr = lstr if for_signature: self.forsig = for_signature else: self.forsig = lstr def __str__(self): return self.lstr def escape(self, escape_func): return escape_func(self.lstr) def for_signature(self): return self.forsig def is_literal(self): return 1 def quote_spaces(arg): """Generic function for putting double quotes around any string that has white space in it.""" if ' ' in arg or '\t' in arg: return '"%s"' % arg else: return str(arg) class CmdStringHolder(collections.UserString): """This is a special class used to hold strings generated by scons_subst() and scons_subst_list(). It defines a special method escape(). When passed a function with an escape algorithm for a particular platform, it will return the contained string with the proper escape sequences inserted. """ def __init__(self, cmd, literal=None): collections.UserString.__init__(self, cmd) self.literal = literal def is_literal(self): return self.literal def escape(self, escape_func, quote_func=quote_spaces): """Escape the string with the supplied function. The function is expected to take an arbitrary string, then return it with all special characters escaped and ready for passing to the command interpreter. After calling this function, the next call to str() will return the escaped string. """ if self.is_literal(): return escape_func(self.data) elif ' ' in self.data or '\t' in self.data: return quote_func(self.data) else: return self.data def escape_list(mylist, escape_func): """Escape a list of arguments by running the specified escape_func on every object in the list that has an escape() method.""" def escape(obj, escape_func=escape_func): try: e = obj.escape except AttributeError: return obj else: return e(escape_func) return list(map(escape, mylist)) class NLWrapper(object): """A wrapper class that delays turning a list of sources or targets into a NodeList until it's needed. The specified function supplied when the object is initialized is responsible for turning raw nodes into proxies that implement the special attributes like .abspath, .source, etc. This way, we avoid creating those proxies just "in case" someone is going to use $TARGET or the like, and only go through the trouble if we really have to. In practice, this might be a wash performance-wise, but it's a little cleaner conceptually... """ def __init__(self, list, func): self.list = list self.func = func def _return_nodelist(self): return self.nodelist def _gen_nodelist(self): mylist = self.list if mylist is None: mylist = [] elif not is_Sequence(mylist): mylist = [mylist] # The map(self.func) call is what actually turns # a list into appropriate proxies. self.nodelist = SCons.Util.NodeList(list(map(self.func, mylist))) self._create_nodelist = self._return_nodelist return self.nodelist _create_nodelist = _gen_nodelist class Targets_or_Sources(collections.UserList): """A class that implements $TARGETS or $SOURCES expansions by in turn wrapping a NLWrapper. This class handles the different methods used to access the list, calling the NLWrapper to create proxies on demand. Note that we subclass collections.UserList purely so that the is_Sequence() function will identify an object of this class as a list during variable expansion. We're not really using any collections.UserList methods in practice. """ def __init__(self, nl): self.nl = nl def __getattr__(self, attr): nl = self.nl._create_nodelist() return getattr(nl, attr) def __getitem__(self, i): nl = self.nl._create_nodelist() return nl[i] def __getslice__(self, i, j): nl = self.nl._create_nodelist() i = max(i, 0); j = max(j, 0) return nl[i:j] def __str__(self): nl = self.nl._create_nodelist() return str(nl) def __repr__(self): nl = self.nl._create_nodelist() return repr(nl) class Target_or_Source(object): """A class that implements $TARGET or $SOURCE expansions by in turn wrapping a NLWrapper. This class handles the different methods used to access an individual proxy Node, calling the NLWrapper to create a proxy on demand. """ def __init__(self, nl): self.nl = nl def __getattr__(self, attr): nl = self.nl._create_nodelist() try: nl0 = nl[0] except IndexError: # If there is nothing in the list, then we have no attributes to # pass through, so raise AttributeError for everything. raise AttributeError("NodeList has no attribute: %s" % attr) return getattr(nl0, attr) def __str__(self): nl = self.nl._create_nodelist() if nl: return str(nl[0]) return '' def __repr__(self): nl = self.nl._create_nodelist() if nl: return repr(nl[0]) return '' class NullNodeList(SCons.Util.NullSeq): def __call__(self, *args, **kwargs): return '' def __str__(self): return '' NullNodesList = NullNodeList() def subst_dict(target, source): """Create a dictionary for substitution of special construction variables. This translates the following special arguments: target - the target (object or array of objects), used to generate the TARGET and TARGETS construction variables source - the source (object or array of objects), used to generate the SOURCES and SOURCE construction variables """ dict = {} if target: def get_tgt_subst_proxy(thing): try: subst_proxy = thing.get_subst_proxy() except AttributeError: subst_proxy = thing # probably a string, just return it return subst_proxy tnl = NLWrapper(target, get_tgt_subst_proxy) dict['TARGETS'] = Targets_or_Sources(tnl) dict['TARGET'] = Target_or_Source(tnl) # This is a total cheat, but hopefully this dictionary goes # away soon anyway. We just let these expand to $TARGETS # because that's "good enough" for the use of ToolSurrogates # (see test/ToolSurrogate.py) to generate documentation. dict['CHANGED_TARGETS'] = '$TARGETS' dict['UNCHANGED_TARGETS'] = '$TARGETS' else: dict['TARGETS'] = NullNodesList dict['TARGET'] = NullNodesList if source: def get_src_subst_proxy(node): try: rfile = node.rfile except AttributeError: pass else: node = rfile() try: return node.get_subst_proxy() except AttributeError: return node # probably a String, just return it snl = NLWrapper(source, get_src_subst_proxy) dict['SOURCES'] = Targets_or_Sources(snl) dict['SOURCE'] = Target_or_Source(snl) # This is a total cheat, but hopefully this dictionary goes # away soon anyway. We just let these expand to $TARGETS # because that's "good enough" for the use of ToolSurrogates # (see test/ToolSurrogate.py) to generate documentation. dict['CHANGED_SOURCES'] = '$SOURCES' dict['UNCHANGED_SOURCES'] = '$SOURCES' else: dict['SOURCES'] = NullNodesList dict['SOURCE'] = NullNodesList return dict # Constants for the "mode" parameter to scons_subst_list() and # scons_subst(). SUBST_RAW gives the raw command line. SUBST_CMD # gives a command line suitable for passing to a shell. SUBST_SIG # gives a command line appropriate for calculating the signature # of a command line...if this changes, we should rebuild. SUBST_CMD = 0 SUBST_RAW = 1 SUBST_SIG = 2 _rm = re.compile(r'\$[()]') _remove = re.compile(r'\$\([^\$]*(\$[^\)][^\$]*)*\$\)') # Indexed by the SUBST_* constants above. _regex_remove = [ _rm, None, _remove ] def _rm_list(list): #return [ l for l in list if not l in ('$(', '$)') ] return [l for l in list if not l in ('$(', '$)')] def _remove_list(list): result = [] do_append = result.append for l in list: if l == '$(': do_append = lambda x: None elif l == '$)': do_append = result.append else: do_append(l) return result # Indexed by the SUBST_* constants above. _list_remove = [ _rm_list, None, _remove_list ] # Regular expressions for splitting strings and handling substitutions, # for use by the scons_subst() and scons_subst_list() functions: # # The first expression compiled matches all of the $-introduced tokens # that we need to process in some way, and is used for substitutions. # The expressions it matches are: # # "$$" # "$(" # "$)" # "$variable" [must begin with alphabetic or underscore] # "${any stuff}" # # The second expression compiled is used for splitting strings into tokens # to be processed, and it matches all of the tokens listed above, plus # the following that affect how arguments do or don't get joined together: # # " " [white space] # "non-white-space" [without any dollar signs] # "$" [single dollar sign] # _dollar_exps_str = r'\$[\$\(\)]|\$[_a-zA-Z][\.\w]*|\${[^}]*}' _dollar_exps = re.compile(r'(%s)' % _dollar_exps_str) _separate_args = re.compile(r'(%s|\s+|[^\s\$]+|\$)' % _dollar_exps_str) # This regular expression is used to replace strings of multiple white # space characters in the string result from the scons_subst() function. _space_sep = re.compile(r'[\t ]+(?![^{]*})') def scons_subst(strSubst, env, mode=SUBST_RAW, target=None, source=None, gvars={}, lvars={}, conv=None): """Expand a string or list containing construction variable substitutions. This is the work-horse function for substitutions in file names and the like. The companion scons_subst_list() function (below) handles separating command lines into lists of arguments, so see that function if that's what you're looking for. """ if isinstance(strSubst, str) and strSubst.find('$') < 0: return strSubst class StringSubber(object): """A class to construct the results of a scons_subst() call. This binds a specific construction environment, mode, target and source with two methods (substitute() and expand()) that handle the expansion. """ def __init__(self, env, mode, conv, gvars): self.env = env self.mode = mode self.conv = conv self.gvars = gvars def expand(self, s, lvars): """Expand a single "token" as necessary, returning an appropriate string containing the expansion. This handles expanding different types of things (strings, lists, callables) appropriately. It calls the wrapper substitute() method to re-expand things as necessary, so that the results of expansions of side-by-side strings still get re-evaluated separately, not smushed together. """ if is_String(s): try: s0, s1 = s[:2] except (IndexError, ValueError): return s if s0 != '$': return s if s1 == '$': return '$' elif s1 in '()': return s else: key = s[1:] if key[0] == '{' or key.find('.') >= 0: if key[0] == '{': key = key[1:-1] try: s = eval(key, self.gvars, lvars) except KeyboardInterrupt: raise except Exception, e: if e.__class__ in AllowableExceptions: return '' raise_exception(e, lvars['TARGETS'], s) else: if key in lvars: s = lvars[key] elif key in self.gvars: s = self.gvars[key] elif not NameError in AllowableExceptions: raise_exception(NameError(key), lvars['TARGETS'], s) else: return '' # Before re-expanding the result, handle # recursive expansion by copying the local # variable dictionary and overwriting a null # string for the value of the variable name # we just expanded. # # This could potentially be optimized by only # copying lvars when s contains more expansions, # but lvars is usually supposed to be pretty # small, and deeply nested variable expansions # are probably more the exception than the norm, # so it should be tolerable for now. lv = lvars.copy() var = key.split('.')[0] lv[var] = '' return self.substitute(s, lv) elif is_Sequence(s): def func(l, conv=self.conv, substitute=self.substitute, lvars=lvars): return conv(substitute(l, lvars)) return list(map(func, s)) elif callable(s): try: s = s(target=lvars['TARGETS'], source=lvars['SOURCES'], env=self.env, for_signature=(self.mode != SUBST_CMD)) except TypeError: # This probably indicates that it's a callable # object that doesn't match our calling arguments # (like an Action). if self.mode == SUBST_RAW: return s s = self.conv(s) return self.substitute(s, lvars) elif s is None: return '' else: return s def substitute(self, args, lvars): """Substitute expansions in an argument or list of arguments. This serves as a wrapper for splitting up a string into separate tokens. """ if is_String(args) and not isinstance(args, CmdStringHolder): args = str(args) # In case it's a UserString. try: def sub_match(match): return self.conv(self.expand(match.group(1), lvars)) result = _dollar_exps.sub(sub_match, args) except TypeError: # If the internal conversion routine doesn't return # strings (it could be overridden to return Nodes, for # example), then the 1.5.2 re module will throw this # exception. Back off to a slower, general-purpose # algorithm that works for all data types. args = _separate_args.findall(args) result = [] for a in args: result.append(self.conv(self.expand(a, lvars))) if len(result) == 1: result = result[0] else: result = ''.join(map(str, result)) return result else: return self.expand(args, lvars) if conv is None: conv = _strconv[mode] # Doing this every time is a bit of a waste, since the Executor # has typically already populated the OverrideEnvironment with # $TARGET/$SOURCE variables. We're keeping this (for now), though, # because it supports existing behavior that allows us to call # an Action directly with an arbitrary target+source pair, which # we use in Tool/tex.py to handle calling $BIBTEX when necessary. # If we dropped that behavior (or found another way to cover it), # we could get rid of this call completely and just rely on the # Executor setting the variables. if 'TARGET' not in lvars: d = subst_dict(target, source) if d: lvars = lvars.copy() lvars.update(d) # We're (most likely) going to eval() things. If Python doesn't # find a __builtins__ value in the global dictionary used for eval(), # it copies the current global values for you. Avoid this by # setting it explicitly and then deleting, so we don't pollute the # construction environment Dictionary(ies) that are typically used # for expansion. gvars['__builtins__'] = __builtins__ ss = StringSubber(env, mode, conv, gvars) result = ss.substitute(strSubst, lvars) try: del gvars['__builtins__'] except KeyError: pass if is_String(result): # Remove $(-$) pairs and any stuff in between, # if that's appropriate. remove = _regex_remove[mode] if remove: result = remove.sub('', result) if mode != SUBST_RAW: # Compress strings of white space characters into # a single space. result = _space_sep.sub(' ', result).strip() elif is_Sequence(result): remove = _list_remove[mode] if remove: result = remove(result) return result #Subst_List_Strings = {} def scons_subst_list(strSubst, env, mode=SUBST_RAW, target=None, source=None, gvars={}, lvars={}, conv=None): """Substitute construction variables in a string (or list or other object) and separate the arguments into a command list. The companion scons_subst() function (above) handles basic substitutions within strings, so see that function instead if that's what you're looking for. """ # try: # Subst_List_Strings[strSubst] = Subst_List_Strings[strSubst] + 1 # except KeyError: # Subst_List_Strings[strSubst] = 1 # import SCons.Debug # SCons.Debug.caller_trace(1) class ListSubber(collections.UserList): """A class to construct the results of a scons_subst_list() call. Like StringSubber, this class binds a specific construction environment, mode, target and source with two methods (substitute() and expand()) that handle the expansion. In addition, however, this class is used to track the state of the result(s) we're gathering so we can do the appropriate thing whenever we have to append another word to the result--start a new line, start a new word, append to the current word, etc. We do this by setting the "append" attribute to the right method so that our wrapper methods only need ever call ListSubber.append(), and the rest of the object takes care of doing the right thing internally. """ def __init__(self, env, mode, conv, gvars): collections.UserList.__init__(self, []) self.env = env self.mode = mode self.conv = conv self.gvars = gvars if self.mode == SUBST_RAW: self.add_strip = lambda x: self.append(x) else: self.add_strip = lambda x: None self.in_strip = None self.next_line() def expand(self, s, lvars, within_list): """Expand a single "token" as necessary, appending the expansion to the current result. This handles expanding different types of things (strings, lists, callables) appropriately. It calls the wrapper substitute() method to re-expand things as necessary, so that the results of expansions of side-by-side strings still get re-evaluated separately, not smushed together. """ if is_String(s): try: s0, s1 = s[:2] except (IndexError, ValueError): self.append(s) return if s0 != '$': self.append(s) return if s1 == '$': self.append('$') elif s1 == '(': self.open_strip('$(') elif s1 == ')': self.close_strip('$)') else: key = s[1:] if key[0] == '{' or key.find('.') >= 0: if key[0] == '{': key = key[1:-1] try: s = eval(key, self.gvars, lvars) except KeyboardInterrupt: raise except Exception, e: if e.__class__ in AllowableExceptions: return raise_exception(e, lvars['TARGETS'], s) else: if key in lvars: s = lvars[key] elif key in self.gvars: s = self.gvars[key] elif not NameError in AllowableExceptions: raise_exception(NameError(), lvars['TARGETS'], s) else: return # Before re-expanding the result, handle # recursive expansion by copying the local # variable dictionary and overwriting a null # string for the value of the variable name # we just expanded. lv = lvars.copy() var = key.split('.')[0] lv[var] = '' self.substitute(s, lv, 0) self.this_word() elif is_Sequence(s): for a in s: self.substitute(a, lvars, 1) self.next_word() elif callable(s): try: s = s(target=lvars['TARGETS'], source=lvars['SOURCES'], env=self.env, for_signature=(self.mode != SUBST_CMD)) except TypeError: # This probably indicates that it's a callable # object that doesn't match our calling arguments # (like an Action). if self.mode == SUBST_RAW: self.append(s) return s = self.conv(s) self.substitute(s, lvars, within_list) elif s is None: self.this_word() else: self.append(s) def substitute(self, args, lvars, within_list): """Substitute expansions in an argument or list of arguments. This serves as a wrapper for splitting up a string into separate tokens. """ if is_String(args) and not isinstance(args, CmdStringHolder): args = str(args) # In case it's a UserString. args = _separate_args.findall(args) for a in args: if a[0] in ' \t\n\r\f\v': if '\n' in a: self.next_line() elif within_list: self.append(a) else: self.next_word() else: self.expand(a, lvars, within_list) else: self.expand(args, lvars, within_list) def next_line(self): """Arrange for the next word to start a new line. This is like starting a new word, except that we have to append another line to the result.""" collections.UserList.append(self, []) self.next_word() def this_word(self): """Arrange for the next word to append to the end of the current last word in the result.""" self.append = self.add_to_current_word def next_word(self): """Arrange for the next word to start a new word.""" self.append = self.add_new_word def add_to_current_word(self, x): """Append the string x to the end of the current last word in the result. If that is not possible, then just add it as a new word. Make sure the entire concatenated string inherits the object attributes of x (in particular, the escape function) by wrapping it as CmdStringHolder.""" if not self.in_strip or self.mode != SUBST_SIG: try: current_word = self[-1][-1] except IndexError: self.add_new_word(x) else: # All right, this is a hack and it should probably # be refactored out of existence in the future. # The issue is that we want to smoosh words together # and make one file name that gets escaped if # we're expanding something like foo$EXTENSION, # but we don't want to smoosh them together if # it's something like >$TARGET, because then we'll # treat the '>' like it's part of the file name. # So for now, just hard-code looking for the special # command-line redirection characters... try: last_char = str(current_word)[-1] except IndexError: last_char = '\0' if last_char in '<>|': self.add_new_word(x) else: y = current_word + x # We used to treat a word appended to a literal # as a literal itself, but this caused problems # with interpreting quotes around space-separated # targets on command lines. Removing this makes # none of the "substantive" end-to-end tests fail, # so we'll take this out but leave it commented # for now in case there's a problem not covered # by the test cases and we need to resurrect this. #literal1 = self.literal(self[-1][-1]) #literal2 = self.literal(x) y = self.conv(y) if is_String(y): #y = CmdStringHolder(y, literal1 or literal2) y = CmdStringHolder(y, None) self[-1][-1] = y def add_new_word(self, x): if not self.in_strip or self.mode != SUBST_SIG: literal = self.literal(x) x = self.conv(x) if is_String(x): x = CmdStringHolder(x, literal) self[-1].append(x) self.append = self.add_to_current_word def literal(self, x): try: l = x.is_literal except AttributeError: return None else: return l() def open_strip(self, x): """Handle the "open strip" $( token.""" self.add_strip(x) self.in_strip = 1 def close_strip(self, x): """Handle the "close strip" $) token.""" self.add_strip(x) self.in_strip = None if conv is None: conv = _strconv[mode] # Doing this every time is a bit of a waste, since the Executor # has typically already populated the OverrideEnvironment with # $TARGET/$SOURCE variables. We're keeping this (for now), though, # because it supports existing behavior that allows us to call # an Action directly with an arbitrary target+source pair, which # we use in Tool/tex.py to handle calling $BIBTEX when necessary. # If we dropped that behavior (or found another way to cover it), # we could get rid of this call completely and just rely on the # Executor setting the variables. if 'TARGET' not in lvars: d = subst_dict(target, source) if d: lvars = lvars.copy() lvars.update(d) # We're (most likely) going to eval() things. If Python doesn't # find a __builtins__ value in the global dictionary used for eval(), # it copies the current global values for you. Avoid this by # setting it explicitly and then deleting, so we don't pollute the # construction environment Dictionary(ies) that are typically used # for expansion. gvars['__builtins__'] = __builtins__ ls = ListSubber(env, mode, conv, gvars) ls.substitute(strSubst, lvars, 0) try: del gvars['__builtins__'] except KeyError: pass return ls.data def scons_subst_once(strSubst, env, key): """Perform single (non-recursive) substitution of a single construction variable keyword. This is used when setting a variable when copying or overriding values in an Environment. We want to capture (expand) the old value before we override it, so people can do things like: env2 = env.Clone(CCFLAGS = '$CCFLAGS -g') We do this with some straightforward, brute-force code here... """ if isinstance(strSubst, str) and strSubst.find('$') < 0: return strSubst matchlist = ['$' + key, '${' + key + '}'] val = env.get(key, '') def sub_match(match, val=val, matchlist=matchlist): a = match.group(1) if a in matchlist: a = val if is_Sequence(a): return ' '.join(map(str, a)) else: return str(a) if is_Sequence(strSubst): result = [] for arg in strSubst: if is_String(arg): if arg in matchlist: arg = val if is_Sequence(arg): result.extend(arg) else: result.append(arg) else: result.append(_dollar_exps.sub(sub_match, arg)) else: result.append(arg) return result elif is_String(strSubst): return _dollar_exps.sub(sub_match, strSubst) else: return strSubst # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Action.xml0000644000175000017500000000601312114661557021242 0ustar dktrkranzdktrkranz Controls whether or not SCons will add implicit dependencies for the commands executed to build targets. By default, SCons will add to each target an implicit dependency on the command represented by the first argument on any command line it executes. The specific file for the dependency is found by searching the PATH variable in the ENV environment used to execute the command. If the construction variable &cv-IMPLICIT_COMMAND_DEPENDENCIES; is set to a false value (None, False, 0, etc.), then the implicit dependency will not be added to the targets built with that construction environment. env = Environment(IMPLICIT_COMMAND_DEPENDENCIES = 0) A Python function used to print the command lines as they are executed (assuming command printing is not disabled by the or options or their equivalents). The function should take four arguments: s, the command being executed (a string), target, the target being built (file node, list, or string name(s)), source, the source(s) used (file node, list, or string name(s)), and env, the environment being used. The function must do the printing itself. The default implementation, used if this variable is not set or is None, is: def print_cmd_line(s, target, source, env): sys.stdout.write(s + "\n") Here's an example of a more interesting function: def print_cmd_line(s, target, source, env): sys.stdout.write("Building %s -> %s...\n" % (' and '.join([str(x) for x in source]), ' and '.join([str(x) for x in target]))) env=Environment(PRINT_CMD_LINE_FUNC=print_cmd_line) env.Program('foo', 'foo.c') This just prints "Building targetname from sourcename..." instead of the actual commands. Such a function could also log the actual commands to a log file, for example. A command interpreter function that will be called to execute command line strings. The function must expect the following arguments: def spawn(shell, escape, cmd, args, env): sh is a string naming the shell program to use. escape is a function that can be called to escape shell special characters in the command line. cmd is the path to the command to be executed. args is the arguments to the command. env is a dictionary of the environment variables in which the command should be executed. scons-doc-2.3.0/src/engine/SCons/Tool/0000755000175000017500000000000012114661560020212 5ustar dktrkranzdktrkranzscons-doc-2.3.0/src/engine/SCons/Tool/link.py0000644000175000017500000001670212114661560021527 0ustar dktrkranzdktrkranz"""SCons.Tool.link Tool-specific initialization for the generic Posix linker. There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Tool.Tool() selection method. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/link.py 2013/03/03 09:48:35 garyo" import re import SCons.Defaults import SCons.Tool import SCons.Util import SCons.Warnings from SCons.Tool.FortranCommon import isfortran cplusplus = __import__('c++', globals(), locals(), []) issued_mixed_link_warning = False def smart_link(source, target, env, for_signature): has_cplusplus = cplusplus.iscplusplus(source) has_fortran = isfortran(env, source) if has_cplusplus and has_fortran: global issued_mixed_link_warning if not issued_mixed_link_warning: msg = "Using $CXX to link Fortran and C++ code together.\n\t" + \ "This may generate a buggy executable if the '%s'\n\t" + \ "compiler does not know how to deal with Fortran runtimes." SCons.Warnings.warn(SCons.Warnings.FortranCxxMixWarning, msg % env.subst('$CXX')) issued_mixed_link_warning = True return '$CXX' elif has_fortran: return '$FORTRAN' elif has_cplusplus: return '$CXX' return '$CC' def shlib_emitter(target, source, env): Verbose = False platform = env.subst('$PLATFORM') for tgt in target: tgt.attributes.shared = 1 try: # target[0] comes in as libtest.so. Add the version extensions version = env.subst('$SHLIBVERSION') if version: version_names = shlib_emitter_names(target, source, env) # change the name of the target to include the version number target[0].name = version_names[0] for name in version_names: env.SideEffect(name, target[0]) env.Clean(target[0], name) if Verbose: print "shlib_emitter: add side effect - ",name except KeyError: version = None return (target, source) def shlib_emitter_names(target, source, env): """Return list of file names that are side effects for a versioned library build. The first name in the list is the new name for the target""" Verbose = False platform = env.subst('$PLATFORM') version_names = [] try: # target[0] comes in as libtest.so. Add the version extensions version = env.subst('$SHLIBVERSION') if version.count(".") != 2: # We need a version of the form x.y.z to proceed raise ValueError if version: if platform == 'posix': versionparts = version.split('.') name = target[0].name # generate library name with the version number version_name = target[0].name + '.' + version if Verbose: print "shlib_emitter_names: target is ", version_name print "shlib_emitter_names: side effect: ", name # add version_name to list of names to be a Side effect version_names.append(version_name) if Verbose: print "shlib_emitter_names: versionparts ",versionparts for ver in versionparts[0:-1]: name = name + '.' + ver if Verbose: print "shlib_emitter_names: side effect: ", name # add name to list of names to be a Side effect version_names.append(name) elif platform == 'darwin': shlib_suffix = env.subst('$SHLIBSUFFIX') name = target[0].name # generate library name with the version number suffix_re = re.escape(shlib_suffix) version_name = re.sub(suffix_re, '.' + version + shlib_suffix, name) if Verbose: print "shlib_emitter_names: target is ", version_name print "shlib_emitter_names: side effect: ", name # add version_name to list of names to be a Side effect version_names.append(version_name) except KeyError: version = None return version_names def generate(env): """Add Builders and construction variables for gnulink to an Environment.""" SCons.Tool.createSharedLibBuilder(env) SCons.Tool.createProgBuilder(env) env['SHLINK'] = '$LINK' env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -shared') env['SHLINKCOM'] = '$SHLINK -o $TARGET $SHLINKFLAGS $__RPATH $SOURCES $_LIBDIRFLAGS $_LIBFLAGS' # don't set up the emitter, cause AppendUnique will generate a list # starting with None :-( env.Append(SHLIBEMITTER = [shlib_emitter]) env['SMARTLINK'] = smart_link env['LINK'] = "$SMARTLINK" env['LINKFLAGS'] = SCons.Util.CLVar('') # __RPATH is only set to something ($_RPATH typically) on platforms that support it. env['LINKCOM'] = '$LINK -o $TARGET $LINKFLAGS $__RPATH $SOURCES $_LIBDIRFLAGS $_LIBFLAGS' env['LIBDIRPREFIX']='-L' env['LIBDIRSUFFIX']='' env['_LIBFLAGS']='${_stripixes(LIBLINKPREFIX, LIBS, LIBLINKSUFFIX, LIBPREFIXES, LIBSUFFIXES, __env__)}' env['LIBLINKPREFIX']='-l' env['LIBLINKSUFFIX']='' if env['PLATFORM'] == 'hpux': env['SHLIBSUFFIX'] = '.sl' elif env['PLATFORM'] == 'aix': env['SHLIBSUFFIX'] = '.a' # For most platforms, a loadable module is the same as a shared # library. Platforms which are different can override these, but # setting them the same means that LoadableModule works everywhere. SCons.Tool.createLoadableModuleBuilder(env) env['LDMODULE'] = '$SHLINK' # don't set up the emitter, cause AppendUnique will generate a list # starting with None :-( env.Append(LDMODULEEMITTER='$SHLIBEMITTER') env['LDMODULEPREFIX'] = '$SHLIBPREFIX' env['LDMODULESUFFIX'] = '$SHLIBSUFFIX' env['LDMODULEFLAGS'] = '$SHLINKFLAGS' env['LDMODULECOM'] = '$LDMODULE -o $TARGET $LDMODULEFLAGS $__RPATH $SOURCES $_LIBDIRFLAGS $_LIBFLAGS' def exists(env): # This module isn't really a Tool on its own, it's common logic for # other linkers. return None # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/rpmutils.py0000644000175000017500000004046712114661560022456 0ustar dktrkranzdktrkranz"""SCons.Tool.rpmutils.py RPM specific helper routines for general usage in the test framework and SCons core modules. Since we check for the RPM package target name in several places, we have to know which machine/system name RPM will use for the current hardware setup. The following dictionaries and functions try to mimic the exact naming rules of the RPM source code. They were directly derived from the file "rpmrc.in" of the version rpm-4.9.1.3. For updating to a more recent version of RPM, this Python script can be used standalone. The usage() function below shows the exact syntax. """ # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/rpmutils.py 2013/03/03 09:48:35 garyo" import platform # Start of rpmrc dictionaries (Marker, don't change or remove!) os_canon = { 'AIX' : ['AIX','5'], 'AmigaOS' : ['AmigaOS','5'], 'BSD_OS' : ['bsdi','12'], 'CYGWIN32_95' : ['cygwin32','15'], 'CYGWIN32_NT' : ['cygwin32','14'], 'Darwin' : ['darwin','21'], 'FreeBSD' : ['FreeBSD','8'], 'HP-UX' : ['hpux10','6'], 'IRIX' : ['Irix','2'], 'IRIX64' : ['Irix64','10'], 'Linux' : ['Linux','1'], 'Linux/390' : ['OS/390','20'], 'Linux/ESA' : ['VM/ESA','20'], 'MacOSX' : ['macosx','21'], 'MiNT' : ['FreeMiNT','17'], 'NEXTSTEP' : ['NextStep','11'], 'OS/390' : ['OS/390','18'], 'OSF1' : ['osf1','7'], 'SCO_SV' : ['SCO_SV3.2v5.0.2','9'], 'SunOS4' : ['SunOS','4'], 'SunOS5' : ['solaris','3'], 'UNIX_SV' : ['MP_RAS','16'], 'VM/ESA' : ['VM/ESA','19'], 'machten' : ['machten','13'], 'osf3.2' : ['osf1','7'], 'osf4.0' : ['osf1','7'], } buildarch_compat = { 'alpha' : ['noarch'], 'alphaev5' : ['alpha'], 'alphaev56' : ['alphaev5'], 'alphaev6' : ['alphapca56'], 'alphaev67' : ['alphaev6'], 'alphapca56' : ['alphaev56'], 'amd64' : ['x86_64'], 'armv3l' : ['noarch'], 'armv4b' : ['noarch'], 'armv4l' : ['armv3l'], 'armv4tl' : ['armv4l'], 'armv5tejl' : ['armv5tel'], 'armv5tel' : ['armv4tl'], 'armv6l' : ['armv5tejl'], 'armv7l' : ['armv6l'], 'atariclone' : ['m68kmint','noarch'], 'atarist' : ['m68kmint','noarch'], 'atariste' : ['m68kmint','noarch'], 'ataritt' : ['m68kmint','noarch'], 'athlon' : ['i686'], 'falcon' : ['m68kmint','noarch'], 'geode' : ['i586'], 'hades' : ['m68kmint','noarch'], 'hppa1.0' : ['parisc'], 'hppa1.1' : ['hppa1.0'], 'hppa1.2' : ['hppa1.1'], 'hppa2.0' : ['hppa1.2'], 'i386' : ['noarch','fat'], 'i486' : ['i386'], 'i586' : ['i486'], 'i686' : ['i586'], 'ia32e' : ['x86_64'], 'ia64' : ['noarch'], 'm68k' : ['noarch'], 'milan' : ['m68kmint','noarch'], 'mips' : ['noarch'], 'mipsel' : ['noarch'], 'parisc' : ['noarch'], 'pentium3' : ['i686'], 'pentium4' : ['pentium3'], 'ppc' : ['noarch','fat'], 'ppc32dy4' : ['noarch'], 'ppc64' : ['noarch','fat'], 'ppc64iseries' : ['ppc64'], 'ppc64pseries' : ['ppc64'], 'ppc8260' : ['noarch'], 'ppc8560' : ['noarch'], 'ppciseries' : ['noarch'], 'ppcpseries' : ['noarch'], 's390' : ['noarch'], 's390x' : ['noarch'], 'sh3' : ['noarch'], 'sh4' : ['noarch'], 'sh4a' : ['sh4'], 'sparc' : ['noarch'], 'sparc64' : ['sparcv9v'], 'sparc64v' : ['sparc64'], 'sparcv8' : ['sparc'], 'sparcv9' : ['sparcv8'], 'sparcv9v' : ['sparcv9'], 'sun4c' : ['noarch'], 'sun4d' : ['noarch'], 'sun4m' : ['noarch'], 'sun4u' : ['noarch'], 'x86_64' : ['noarch'], } os_compat = { 'BSD_OS' : ['bsdi'], 'Darwin' : ['MacOSX'], 'FreeMiNT' : ['mint','MiNT','TOS'], 'IRIX64' : ['IRIX'], 'MiNT' : ['FreeMiNT','mint','TOS'], 'TOS' : ['FreeMiNT','MiNT','mint'], 'bsdi4.0' : ['bsdi'], 'hpux10.00' : ['hpux9.07'], 'hpux10.01' : ['hpux10.00'], 'hpux10.10' : ['hpux10.01'], 'hpux10.20' : ['hpux10.10'], 'hpux10.30' : ['hpux10.20'], 'hpux11.00' : ['hpux10.30'], 'hpux9.05' : ['hpux9.04'], 'hpux9.07' : ['hpux9.05'], 'mint' : ['FreeMiNT','MiNT','TOS'], 'ncr-sysv4.3' : ['ncr-sysv4.2'], 'osf4.0' : ['osf3.2','osf1'], 'solaris2.4' : ['solaris2.3'], 'solaris2.5' : ['solaris2.3','solaris2.4'], 'solaris2.6' : ['solaris2.3','solaris2.4','solaris2.5'], 'solaris2.7' : ['solaris2.3','solaris2.4','solaris2.5','solaris2.6'], } arch_compat = { 'alpha' : ['axp','noarch'], 'alphaev5' : ['alpha'], 'alphaev56' : ['alphaev5'], 'alphaev6' : ['alphapca56'], 'alphaev67' : ['alphaev6'], 'alphapca56' : ['alphaev56'], 'amd64' : ['x86_64','athlon','noarch'], 'armv3l' : ['noarch'], 'armv4b' : ['noarch'], 'armv4l' : ['armv3l'], 'armv4tl' : ['armv4l'], 'armv5tejl' : ['armv5tel'], 'armv5tel' : ['armv4tl'], 'armv6l' : ['armv5tejl'], 'armv7l' : ['armv6l'], 'atariclone' : ['m68kmint','noarch'], 'atarist' : ['m68kmint','noarch'], 'atariste' : ['m68kmint','noarch'], 'ataritt' : ['m68kmint','noarch'], 'athlon' : ['i686'], 'falcon' : ['m68kmint','noarch'], 'geode' : ['i586'], 'hades' : ['m68kmint','noarch'], 'hppa1.0' : ['parisc'], 'hppa1.1' : ['hppa1.0'], 'hppa1.2' : ['hppa1.1'], 'hppa2.0' : ['hppa1.2'], 'i370' : ['noarch'], 'i386' : ['noarch','fat'], 'i486' : ['i386'], 'i586' : ['i486'], 'i686' : ['i586'], 'ia32e' : ['x86_64','athlon','noarch'], 'ia64' : ['noarch'], 'milan' : ['m68kmint','noarch'], 'mips' : ['noarch'], 'mipsel' : ['noarch'], 'osfmach3_i386' : ['i486'], 'osfmach3_i486' : ['i486','osfmach3_i386'], 'osfmach3_i586' : ['i586','osfmach3_i486'], 'osfmach3_i686' : ['i686','osfmach3_i586'], 'osfmach3_ppc' : ['ppc'], 'parisc' : ['noarch'], 'pentium3' : ['i686'], 'pentium4' : ['pentium3'], 'powerpc' : ['ppc'], 'powerppc' : ['ppc'], 'ppc' : ['rs6000'], 'ppc32dy4' : ['ppc'], 'ppc64' : ['ppc'], 'ppc64iseries' : ['ppc64'], 'ppc64pseries' : ['ppc64'], 'ppc8260' : ['ppc'], 'ppc8560' : ['ppc'], 'ppciseries' : ['ppc'], 'ppcpseries' : ['ppc'], 'rs6000' : ['noarch','fat'], 's390' : ['noarch'], 's390x' : ['s390','noarch'], 'sh3' : ['noarch'], 'sh4' : ['noarch'], 'sh4a' : ['sh4'], 'sparc' : ['noarch'], 'sparc64' : ['sparcv9'], 'sparc64v' : ['sparc64'], 'sparcv8' : ['sparc'], 'sparcv9' : ['sparcv8'], 'sparcv9v' : ['sparcv9'], 'sun4c' : ['sparc'], 'sun4d' : ['sparc'], 'sun4m' : ['sparc'], 'sun4u' : ['sparc64'], 'x86_64' : ['amd64','athlon','noarch'], } buildarchtranslate = { 'alphaev5' : ['alpha'], 'alphaev56' : ['alpha'], 'alphaev6' : ['alpha'], 'alphaev67' : ['alpha'], 'alphapca56' : ['alpha'], 'amd64' : ['x86_64'], 'armv3l' : ['armv3l'], 'armv4b' : ['armv4b'], 'armv4l' : ['armv4l'], 'armv4tl' : ['armv4tl'], 'armv5tejl' : ['armv5tejl'], 'armv5tel' : ['armv5tel'], 'armv6l' : ['armv6l'], 'armv7l' : ['armv7l'], 'atariclone' : ['m68kmint'], 'atarist' : ['m68kmint'], 'atariste' : ['m68kmint'], 'ataritt' : ['m68kmint'], 'athlon' : ['i386'], 'falcon' : ['m68kmint'], 'geode' : ['i386'], 'hades' : ['m68kmint'], 'i386' : ['i386'], 'i486' : ['i386'], 'i586' : ['i386'], 'i686' : ['i386'], 'ia32e' : ['x86_64'], 'ia64' : ['ia64'], 'milan' : ['m68kmint'], 'osfmach3_i386' : ['i386'], 'osfmach3_i486' : ['i386'], 'osfmach3_i586' : ['i386'], 'osfmach3_i686' : ['i386'], 'osfmach3_ppc' : ['ppc'], 'pentium3' : ['i386'], 'pentium4' : ['i386'], 'powerpc' : ['ppc'], 'powerppc' : ['ppc'], 'ppc32dy4' : ['ppc'], 'ppc64iseries' : ['ppc64'], 'ppc64pseries' : ['ppc64'], 'ppc8260' : ['ppc'], 'ppc8560' : ['ppc'], 'ppciseries' : ['ppc'], 'ppcpseries' : ['ppc'], 's390' : ['s390'], 's390x' : ['s390x'], 'sh3' : ['sh3'], 'sh4' : ['sh4'], 'sh4a' : ['sh4'], 'sparc64v' : ['sparc64'], 'sparcv8' : ['sparc'], 'sparcv9' : ['sparc'], 'sparcv9v' : ['sparc'], 'sun4c' : ['sparc'], 'sun4d' : ['sparc'], 'sun4m' : ['sparc'], 'sun4u' : ['sparc64'], 'x86_64' : ['x86_64'], } optflags = { 'alpha' : ['-O2','-g','-mieee'], 'alphaev5' : ['-O2','-g','-mieee','-mtune=ev5'], 'alphaev56' : ['-O2','-g','-mieee','-mtune=ev56'], 'alphaev6' : ['-O2','-g','-mieee','-mtune=ev6'], 'alphaev67' : ['-O2','-g','-mieee','-mtune=ev67'], 'alphapca56' : ['-O2','-g','-mieee','-mtune=pca56'], 'amd64' : ['-O2','-g'], 'armv3l' : ['-O2','-g','-march=armv3'], 'armv4b' : ['-O2','-g','-march=armv4'], 'armv4l' : ['-O2','-g','-march=armv4'], 'armv4tl' : ['-O2','-g','-march=armv4t'], 'armv5tejl' : ['-O2','-g','-march=armv5te'], 'armv5tel' : ['-O2','-g','-march=armv5te'], 'armv6l' : ['-O2','-g','-march=armv6'], 'armv7l' : ['-O2','-g','-march=armv7'], 'atariclone' : ['-O2','-g','-fomit-frame-pointer'], 'atarist' : ['-O2','-g','-fomit-frame-pointer'], 'atariste' : ['-O2','-g','-fomit-frame-pointer'], 'ataritt' : ['-O2','-g','-fomit-frame-pointer'], 'athlon' : ['-O2','-g','-march=athlon'], 'falcon' : ['-O2','-g','-fomit-frame-pointer'], 'fat' : ['-O2','-g','-arch','i386','-arch','ppc'], 'geode' : ['-Os','-g','-m32','-march=geode'], 'hades' : ['-O2','-g','-fomit-frame-pointer'], 'hppa1.0' : ['-O2','-g','-mpa-risc-1-0'], 'hppa1.1' : ['-O2','-g','-mpa-risc-1-0'], 'hppa1.2' : ['-O2','-g','-mpa-risc-1-0'], 'hppa2.0' : ['-O2','-g','-mpa-risc-1-0'], 'i386' : ['-O2','-g','-march=i386','-mtune=i686'], 'i486' : ['-O2','-g','-march=i486'], 'i586' : ['-O2','-g','-march=i586'], 'i686' : ['-O2','-g','-march=i686'], 'ia32e' : ['-O2','-g'], 'ia64' : ['-O2','-g'], 'm68k' : ['-O2','-g','-fomit-frame-pointer'], 'milan' : ['-O2','-g','-fomit-frame-pointer'], 'mips' : ['-O2','-g'], 'mipsel' : ['-O2','-g'], 'parisc' : ['-O2','-g','-mpa-risc-1-0'], 'pentium3' : ['-O2','-g','-march=pentium3'], 'pentium4' : ['-O2','-g','-march=pentium4'], 'ppc' : ['-O2','-g','-fsigned-char'], 'ppc32dy4' : ['-O2','-g','-fsigned-char'], 'ppc64' : ['-O2','-g','-fsigned-char'], 'ppc8260' : ['-O2','-g','-fsigned-char'], 'ppc8560' : ['-O2','-g','-fsigned-char'], 'ppciseries' : ['-O2','-g','-fsigned-char'], 'ppcpseries' : ['-O2','-g','-fsigned-char'], 's390' : ['-O2','-g'], 's390x' : ['-O2','-g'], 'sh3' : ['-O2','-g'], 'sh4' : ['-O2','-g','-mieee'], 'sh4a' : ['-O2','-g','-mieee'], 'sparc' : ['-O2','-g','-m32','-mtune=ultrasparc'], 'sparc64' : ['-O2','-g','-m64','-mtune=ultrasparc'], 'sparc64v' : ['-O2','-g','-m64','-mtune=niagara'], 'sparcv8' : ['-O2','-g','-m32','-mtune=ultrasparc','-mv8'], 'sparcv9' : ['-O2','-g','-m32','-mtune=ultrasparc'], 'sparcv9v' : ['-O2','-g','-m32','-mtune=niagara'], 'x86_64' : ['-O2','-g'], } arch_canon = { 'IP' : ['sgi','7'], 'alpha' : ['alpha','2'], 'alphaev5' : ['alphaev5','2'], 'alphaev56' : ['alphaev56','2'], 'alphaev6' : ['alphaev6','2'], 'alphaev67' : ['alphaev67','2'], 'alphapca56' : ['alphapca56','2'], 'amd64' : ['amd64','1'], 'armv3l' : ['armv3l','12'], 'armv4b' : ['armv4b','12'], 'armv4l' : ['armv4l','12'], 'armv5tejl' : ['armv5tejl','12'], 'armv5tel' : ['armv5tel','12'], 'armv6l' : ['armv6l','12'], 'armv7l' : ['armv7l','12'], 'atariclone' : ['m68kmint','13'], 'atarist' : ['m68kmint','13'], 'atariste' : ['m68kmint','13'], 'ataritt' : ['m68kmint','13'], 'athlon' : ['athlon','1'], 'falcon' : ['m68kmint','13'], 'geode' : ['geode','1'], 'hades' : ['m68kmint','13'], 'i370' : ['i370','14'], 'i386' : ['i386','1'], 'i486' : ['i486','1'], 'i586' : ['i586','1'], 'i686' : ['i686','1'], 'ia32e' : ['ia32e','1'], 'ia64' : ['ia64','9'], 'm68k' : ['m68k','6'], 'm68kmint' : ['m68kmint','13'], 'milan' : ['m68kmint','13'], 'mips' : ['mips','4'], 'mipsel' : ['mipsel','11'], 'pentium3' : ['pentium3','1'], 'pentium4' : ['pentium4','1'], 'ppc' : ['ppc','5'], 'ppc32dy4' : ['ppc32dy4','5'], 'ppc64' : ['ppc64','16'], 'ppc64iseries' : ['ppc64iseries','16'], 'ppc64pseries' : ['ppc64pseries','16'], 'ppc8260' : ['ppc8260','5'], 'ppc8560' : ['ppc8560','5'], 'ppciseries' : ['ppciseries','5'], 'ppcpseries' : ['ppcpseries','5'], 'rs6000' : ['rs6000','8'], 's390' : ['s390','14'], 's390x' : ['s390x','15'], 'sh' : ['sh','17'], 'sh3' : ['sh3','17'], 'sh4' : ['sh4','17'], 'sh4a' : ['sh4a','17'], 'sparc' : ['sparc','3'], 'sparc64' : ['sparc64','2'], 'sparc64v' : ['sparc64v','2'], 'sparcv8' : ['sparcv8','3'], 'sparcv9' : ['sparcv9','3'], 'sparcv9v' : ['sparcv9v','3'], 'sun4' : ['sparc','3'], 'sun4c' : ['sparc','3'], 'sun4d' : ['sparc','3'], 'sun4m' : ['sparc','3'], 'sun4u' : ['sparc64','2'], 'x86_64' : ['x86_64','1'], 'xtensa' : ['xtensa','18'], } # End of rpmrc dictionaries (Marker, don't change or remove!) def defaultMachine(): """ Return the canonicalized machine name. """ rmachine = platform.machine() # Try to lookup the string in the canon table if rmachine in arch_canon: rmachine = arch_canon[rmachine][0] return rmachine def defaultSystem(): """ Return the canonicalized system name. """ rsystem = platform.system() # Try to lookup the string in the canon tables if rsystem in os_canon: rsystem = os_canon[rsystem][0] return rsystem def defaultNames(): """ Return the canonicalized machine and system name. """ return defaultMachine(), defaultSystem() def updateRpmDicts(rpmrc, pyfile): """ Read the given rpmrc file with RPM definitions and update the info dictionaries in the file pyfile with it. The arguments will usually be 'rpmrc.in' from a recent RPM source tree, and 'rpmutils.py' referring to this script itself. See also usage() below. """ try: # Read old rpmutils.py file oldpy = open(pyfile,"r").readlines() # Read current rpmrc.in file rpm = open(rpmrc,"r").readlines() # Parse for data data = {} # Allowed section names that get parsed sections = ['optflags', 'arch_canon', 'os_canon', 'buildarchtranslate', 'arch_compat', 'os_compat', 'buildarch_compat'] for l in rpm: l = l.rstrip('\n').replace(':',' ') # Skip comments if l.lstrip().startswith('#'): continue tokens = l.strip().split() if len(tokens): key = tokens[0] if key in sections: # Have we met this section before? if not data.has_key(tokens[0]): # No, so insert it data[key] = {} # Insert data data[key][tokens[1]] = tokens[2:] # Write new rpmutils.py file out = open(pyfile,"w") pm = 0 for l in oldpy: if pm: if l.startswith('# End of rpmrc dictionaries'): pm = 0 out.write(l) else: out.write(l) if l.startswith('# Start of rpmrc dictionaries'): pm = 1 # Write data sections to single dictionaries for key, entries in data.iteritems(): out.write("%s = {\n" % key) for arch in sorted(entries.keys()): out.write(" '%s' : ['%s'],\n" % (arch, "','".join(entries[arch]))) out.write("}\n\n") out.close() except: pass def usage(): print "rpmutils.py rpmrc.in rpmutils.py" def main(): import sys if len(sys.argv) < 3: usage() sys.exit(0) updateRpmDicts(sys.argv[1], sys.argv[2]) if __name__ == "__main__": main() scons-doc-2.3.0/src/engine/SCons/Tool/cvf.py0000644000175000017500000000474012114661560021347 0ustar dktrkranzdktrkranz"""engine.SCons.Tool.cvf Tool-specific initialization for the Compaq Visual Fortran compiler. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/cvf.py 2013/03/03 09:48:35 garyo" import fortran compilers = ['f90'] def generate(env): """Add Builders and construction variables for compaq visual fortran to an Environment.""" fortran.generate(env) env['FORTRAN'] = 'f90' env['FORTRANCOM'] = '$FORTRAN $FORTRANFLAGS $_FORTRANMODFLAG $_FORTRANINCFLAGS /compile_only ${SOURCES.windows} /object:${TARGET.windows}' env['FORTRANPPCOM'] = '$FORTRAN $FORTRANFLAGS $CPPFLAGS $_CPPDEFFLAGS $_FORTRANMODFLAG $_FORTRANINCFLAGS /compile_only ${SOURCES.windows} /object:${TARGET.windows}' env['SHFORTRANCOM'] = '$SHFORTRAN $SHFORTRANFLAGS $_FORTRANMODFLAG $_FORTRANINCFLAGS /compile_only ${SOURCES.windows} /object:${TARGET.windows}' env['SHFORTRANPPCOM'] = '$SHFORTRAN $SHFORTRANFLAGS $CPPFLAGS $_CPPDEFFLAGS $_FORTRANMODFLAG $_FORTRANINCFLAGS /compile_only ${SOURCES.windows} /object:${TARGET.windows}' env['OBJSUFFIX'] = '.obj' env['FORTRANMODDIR'] = '${TARGET.dir}' env['FORTRANMODDIRPREFIX'] = '/module:' env['FORTRANMODDIRSUFFIX'] = '' def exists(env): return env.Detect(compilers) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/mssdk.py0000644000175000017500000000351512114661560021711 0ustar dktrkranzdktrkranz# # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/mssdk.py 2013/03/03 09:48:35 garyo" """engine.SCons.Tool.mssdk Tool-specific initialization for Microsoft SDKs, both Platform SDKs and Windows SDKs. There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Tool.Tool() selection method. """ from MSCommon import mssdk_exists, \ mssdk_setup_env def generate(env): """Add construction variables for an MS SDK to an Environment.""" mssdk_setup_env(env) def exists(env): return mssdk_exists() # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/install.py0000644000175000017500000004202112114661560022231 0ustar dktrkranzdktrkranz"""SCons.Tool.install Tool-specific initialization for the install tool. There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Tool.Tool() selection method. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/install.py 2013/03/03 09:48:35 garyo" import os import re import shutil import stat import SCons.Action from SCons.Util import make_path_relative # # We keep track of *all* installed files. _INSTALLED_FILES = [] _UNIQUE_INSTALLED_FILES = None class CopytreeError(EnvironmentError): pass # This is a patched version of shutil.copytree from python 2.5. It # doesn't fail if the dir exists, which regular copytree does # (annoyingly). Note the XXX comment in the docstring. def scons_copytree(src, dst, symlinks=False): """Recursively copy a directory tree using copy2(). The destination directory must not already exist. If exception(s) occur, an CopytreeError is raised with a list of reasons. If the optional symlinks flag is true, symbolic links in the source tree result in symbolic links in the destination tree; if it is false, the contents of the files pointed to by symbolic links are copied. XXX Consider this example code rather than the ultimate tool. """ names = os.listdir(src) # garyo@genarts.com fix: check for dir before making dirs. if not os.path.exists(dst): os.makedirs(dst) errors = [] for name in names: srcname = os.path.join(src, name) dstname = os.path.join(dst, name) try: if symlinks and os.path.islink(srcname): linkto = os.readlink(srcname) os.symlink(linkto, dstname) elif os.path.isdir(srcname): scons_copytree(srcname, dstname, symlinks) else: shutil.copy2(srcname, dstname) # XXX What about devices, sockets etc.? except (IOError, os.error), why: errors.append((srcname, dstname, str(why))) # catch the CopytreeError from the recursive copytree so that we can # continue with other files except CopytreeError, err: errors.extend(err.args[0]) try: shutil.copystat(src, dst) except WindowsError: # can't copy file access times on Windows pass except OSError, why: errors.extend((src, dst, str(why))) if errors: raise CopytreeError, errors # # Functions doing the actual work of the Install Builder. # def copyFunc(dest, source, env): """Install a source file or directory into a destination by copying, (including copying permission/mode bits).""" if os.path.isdir(source): if os.path.exists(dest): if not os.path.isdir(dest): raise SCons.Errors.UserError("cannot overwrite non-directory `%s' with a directory `%s'" % (str(dest), str(source))) else: parent = os.path.split(dest)[0] if not os.path.exists(parent): os.makedirs(parent) scons_copytree(source, dest) else: shutil.copy2(source, dest) st = os.stat(source) os.chmod(dest, stat.S_IMODE(st[stat.ST_MODE]) | stat.S_IWRITE) return 0 # # Functions doing the actual work of the InstallVersionedLib Builder. # def copyFuncVersionedLib(dest, source, env): """Install a versioned library into a destination by copying, (including copying permission/mode bits) and then creating required symlinks.""" if os.path.isdir(source): raise SCons.Errors.UserError("cannot install directory `%s' as a version library" % str(source) ) else: shutil.copy2(source, dest) st = os.stat(source) os.chmod(dest, stat.S_IMODE(st[stat.ST_MODE]) | stat.S_IWRITE) versionedLibLinks(dest, source, env) return 0 def versionedLibVersion(dest, env): """Check if dest is a version shared library name. Return version, libname, & install_dir if it is.""" Verbose = False platform = env.subst('$PLATFORM') if not (platform == 'posix' or platform == 'darwin'): return (None, None, None) libname = os.path.basename(dest) install_dir = os.path.dirname(dest) shlib_suffix = env.subst('$SHLIBSUFFIX') # See if the source name is a versioned shared library, get the version number result = False version_re = re.compile("[0-9]+\\.[0-9]+\\.[0-9a-zA-Z]+") version_File = None if platform == 'posix': # handle unix names versioned_re = re.compile(re.escape(shlib_suffix + '.') + "[0-9]+\\.[0-9]+\\.[0-9a-zA-Z]+") result = versioned_re.findall(libname) if result: version_File = version_re.findall(versioned_re.findall(libname)[-1])[-1] elif platform == 'darwin': # handle OSX names versioned_re = re.compile("\\.[0-9]+\\.[0-9]+\\.[0-9a-zA-Z]+" + re.escape(shlib_suffix) ) result = versioned_re.findall(libname) if result: version_File = version_re.findall(versioned_re.findall(libname)[-1])[-1] if Verbose: print "install: version_File ", version_File # result is False if we did not find a versioned shared library name, so return and empty list if not result: return (None, libname, install_dir) version = None # get version number from the environment try: version = env.subst('$SHLIBVERSION') except KeyError: version = None if version != version_File: #raise SCons.Errors.UserError("SHLIBVERSION '%s' does not match the version # '%s' in the filename" % (version, version_File) ) print "SHLIBVERSION '%s' does not match the version # '%s' in the filename, proceeding based on file name" % (version, version_File) version = version_File return (version, libname, install_dir) def versionedLibLinks(dest, source, env): """If we are installing a versioned shared library create the required links.""" Verbose = False linknames = [] version, libname, install_dir = versionedLibVersion(dest, env) if version != None: # libname includes the version number if one was given linknames = SCons.Tool.VersionShLibLinkNames(version,libname,env) for linkname in linknames: if Verbose: print "make link of %s to %s" %(libname, os.path.join(install_dir, linkname)) fulllinkname = os.path.join(install_dir, linkname) os.symlink(libname,fulllinkname) return def installFunc(target, source, env): """Install a source file into a target using the function specified as the INSTALL construction variable.""" try: install = env['INSTALL'] except KeyError: raise SCons.Errors.UserError('Missing INSTALL construction variable.') assert len(target)==len(source), \ "Installing source %s into target %s: target and source lists must have same length."%(list(map(str, source)), list(map(str, target))) for t,s in zip(target,source): if install(t.get_path(),s.get_path(),env): return 1 return 0 def installFuncVersionedLib(target, source, env): """Install a versioned library into a target using the function specified as the INSTALLVERSIONEDLIB construction variable.""" try: install = env['INSTALLVERSIONEDLIB'] except KeyError: raise SCons.Errors.UserError('Missing INSTALLVERSIONEDLIB construction variable.') assert len(target)==len(source), \ "Installing source %s into target %s: target and source lists must have same length."%(list(map(str, source)), list(map(str, target))) for t,s in zip(target,source): if install(t.get_path(),s.get_path(),env): return 1 return 0 def stringFunc(target, source, env): installstr = env.get('INSTALLSTR') if installstr: return env.subst_target_source(installstr, 0, target, source) target = str(target[0]) source = str(source[0]) if os.path.isdir(source): type = 'directory' else: type = 'file' return 'Install %s: "%s" as "%s"' % (type, source, target) # # Emitter functions # def add_targets_to_INSTALLED_FILES(target, source, env): """ an emitter that adds all target files to the list stored in the _INSTALLED_FILES global variable. This way all installed files of one scons call will be collected. """ global _INSTALLED_FILES, _UNIQUE_INSTALLED_FILES _INSTALLED_FILES.extend(target) _UNIQUE_INSTALLED_FILES = None return (target, source) def add_versioned_targets_to_INSTALLED_FILES(target, source, env): """ an emitter that adds all target files to the list stored in the _INSTALLED_FILES global variable. This way all installed files of one scons call will be collected. """ global _INSTALLED_FILES, _UNIQUE_INSTALLED_FILES Verbose = False _INSTALLED_FILES.extend(target) # see if we have a versioned shared library, if so generate side effects version, libname, install_dir = versionedLibVersion(target[0].path, env) if version != None: # generate list of link names linknames = SCons.Tool.VersionShLibLinkNames(version,libname,env) for linkname in linknames: if Verbose: print "make side effect of %s" % os.path.join(install_dir, linkname) fulllinkname = os.path.join(install_dir, linkname) env.SideEffect(fulllinkname,target[0]) env.Clean(target[0],fulllinkname) _UNIQUE_INSTALLED_FILES = None return (target, source) class DESTDIR_factory(object): """ a node factory, where all files will be relative to the dir supplied in the constructor. """ def __init__(self, env, dir): self.env = env self.dir = env.arg2nodes( dir, env.fs.Dir )[0] def Entry(self, name): name = make_path_relative(name) return self.dir.Entry(name) def Dir(self, name): name = make_path_relative(name) return self.dir.Dir(name) # # The Builder Definition # install_action = SCons.Action.Action(installFunc, stringFunc) installas_action = SCons.Action.Action(installFunc, stringFunc) installVerLib_action = SCons.Action.Action(installFuncVersionedLib, stringFunc) BaseInstallBuilder = None def InstallBuilderWrapper(env, target=None, source=None, dir=None, **kw): if target and dir: import SCons.Errors raise SCons.Errors.UserError("Both target and dir defined for Install(), only one may be defined.") if not dir: dir=target import SCons.Script install_sandbox = SCons.Script.GetOption('install_sandbox') if install_sandbox: target_factory = DESTDIR_factory(env, install_sandbox) else: target_factory = env.fs try: dnodes = env.arg2nodes(dir, target_factory.Dir) except TypeError: raise SCons.Errors.UserError("Target `%s' of Install() is a file, but should be a directory. Perhaps you have the Install() arguments backwards?" % str(dir)) sources = env.arg2nodes(source, env.fs.Entry) tgt = [] for dnode in dnodes: for src in sources: # Prepend './' so the lookup doesn't interpret an initial # '#' on the file name portion as meaning the Node should # be relative to the top-level SConstruct directory. target = env.fs.Entry('.'+os.sep+src.name, dnode) #tgt.extend(BaseInstallBuilder(env, target, src, **kw)) tgt.extend(BaseInstallBuilder(env, target, src, **kw)) return tgt def InstallAsBuilderWrapper(env, target=None, source=None, **kw): result = [] for src, tgt in map(lambda x, y: (x, y), source, target): #result.extend(BaseInstallBuilder(env, tgt, src, **kw)) result.extend(BaseInstallBuilder(env, tgt, src, **kw)) return result BaseVersionedInstallBuilder = None def InstallVersionedBuilderWrapper(env, target=None, source=None, dir=None, **kw): if target and dir: import SCons.Errors raise SCons.Errors.UserError("Both target and dir defined for Install(), only one may be defined.") if not dir: dir=target import SCons.Script install_sandbox = SCons.Script.GetOption('install_sandbox') if install_sandbox: target_factory = DESTDIR_factory(env, install_sandbox) else: target_factory = env.fs try: dnodes = env.arg2nodes(dir, target_factory.Dir) except TypeError: raise SCons.Errors.UserError("Target `%s' of Install() is a file, but should be a directory. Perhaps you have the Install() arguments backwards?" % str(dir)) sources = env.arg2nodes(source, env.fs.Entry) tgt = [] for dnode in dnodes: for src in sources: # Prepend './' so the lookup doesn't interpret an initial # '#' on the file name portion as meaning the Node should # be relative to the top-level SConstruct directory. target = env.fs.Entry('.'+os.sep+src.name, dnode) tgt.extend(BaseVersionedInstallBuilder(env, target, src, **kw)) return tgt added = None def generate(env): from SCons.Script import AddOption, GetOption global added if not added: added = 1 AddOption('--install-sandbox', dest='install_sandbox', type="string", action="store", help='A directory under which all installed files will be placed.') global BaseInstallBuilder if BaseInstallBuilder is None: install_sandbox = GetOption('install_sandbox') if install_sandbox: target_factory = DESTDIR_factory(env, install_sandbox) else: target_factory = env.fs BaseInstallBuilder = SCons.Builder.Builder( action = install_action, target_factory = target_factory.Entry, source_factory = env.fs.Entry, multi = 1, emitter = [ add_targets_to_INSTALLED_FILES, ], name = 'InstallBuilder') global BaseVersionedInstallBuilder if BaseVersionedInstallBuilder is None: install_sandbox = GetOption('install_sandbox') if install_sandbox: target_factory = DESTDIR_factory(env, install_sandbox) else: target_factory = env.fs BaseVersionedInstallBuilder = SCons.Builder.Builder( action = installVerLib_action, target_factory = target_factory.Entry, source_factory = env.fs.Entry, multi = 1, emitter = [ add_versioned_targets_to_INSTALLED_FILES, ], name = 'InstallVersionedBuilder') env['BUILDERS']['_InternalInstall'] = InstallBuilderWrapper env['BUILDERS']['_InternalInstallAs'] = InstallAsBuilderWrapper env['BUILDERS']['_InternalInstallVersionedLib'] = InstallVersionedBuilderWrapper # We'd like to initialize this doing something like the following, # but there isn't yet support for a ${SOURCE.type} expansion that # will print "file" or "directory" depending on what's being # installed. For now we punt by not initializing it, and letting # the stringFunc() that we put in the action fall back to the # hand-crafted default string if it's not set. # #try: # env['INSTALLSTR'] #except KeyError: # env['INSTALLSTR'] = 'Install ${SOURCE.type}: "$SOURCES" as "$TARGETS"' try: env['INSTALL'] except KeyError: env['INSTALL'] = copyFunc try: env['INSTALLVERSIONEDLIB'] except KeyError: env['INSTALLVERSIONEDLIB'] = copyFuncVersionedLib def exists(env): return 1 # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/aixcc.py0000644000175000017500000000453712114661560021664 0ustar dktrkranzdktrkranz"""SCons.Tool.aixcc Tool-specific initialization for IBM xlc / Visual Age C compiler. There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Tool.Tool() selection method. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/aixcc.py 2013/03/03 09:48:35 garyo" import os.path import SCons.Platform.aix import cc packages = ['vac.C', 'ibmcxx.cmp'] def get_xlc(env): xlc = env.get('CC', 'xlc') xlc_r = env.get('SHCC', 'xlc_r') return SCons.Platform.aix.get_xlc(env, xlc, xlc_r, packages) def generate(env): """Add Builders and construction variables for xlc / Visual Age suite to an Environment.""" path, _cc, _shcc, version = get_xlc(env) if path: _cc = os.path.join(path, _cc) _shcc = os.path.join(path, _shcc) cc.generate(env) env['CC'] = _cc env['SHCC'] = _shcc env['CCVERSION'] = version def exists(env): path, _cc, _shcc, version = get_xlc(env) if path and _cc: xlc = os.path.join(path, _cc) if os.path.exists(xlc): return xlc return None # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/MSCommon/0000755000175000017500000000000012114661557021710 5ustar dktrkranzdktrkranzscons-doc-2.3.0/src/engine/SCons/Tool/MSCommon/sdk.py0000644000175000017500000003336312114661557023053 0ustar dktrkranzdktrkranz# # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/MSCommon/sdk.py 2013/03/03 09:48:35 garyo" __doc__ = """Module to detect the Platform/Windows SDK PSDK 2003 R1 is the earliest version detected. """ import os import SCons.Errors import SCons.Util import common debug = common.debug # SDK Checks. This is of course a mess as everything else on MS platforms. Here # is what we do to detect the SDK: # # For Windows SDK >= 6.0: just look into the registry entries: # HKLM\Software\Microsoft\Microsoft SDKs\Windows # All the keys in there are the available versions. # # For Platform SDK before 6.0 (2003 server R1 and R2, etc...), there does not # seem to be any sane registry key, so the precise location is hardcoded. # # For versions below 2003R1, it seems the PSDK is included with Visual Studio? # # Also, per the following: # http://benjamin.smedbergs.us/blog/tag/atl/ # VC++ Professional comes with the SDK, VC++ Express does not. # Location of the SDK (checked for 6.1 only) _CURINSTALLED_SDK_HKEY_ROOT = \ r"Software\Microsoft\Microsoft SDKs\Windows\CurrentInstallFolder" class SDKDefinition(object): """ An abstract base class for trying to find installed SDK directories. """ def __init__(self, version, **kw): self.version = version self.__dict__.update(kw) def find_sdk_dir(self): """Try to find the MS SDK from the registry. Return None if failed or the directory does not exist. """ if not SCons.Util.can_read_reg: debug('find_sdk_dir(): can not read registry') return None hkey = self.HKEY_FMT % self.hkey_data debug('find_sdk_dir(): checking registry:%s'%hkey) try: sdk_dir = common.read_reg(hkey) except WindowsError, e: debug('find_sdk_dir(): no SDK registry key %s' % repr(hkey)) return None debug('find_sdk_dir(): Trying SDK Dir: %s'%sdk_dir) if not os.path.exists(sdk_dir): debug('find_sdk_dir(): %s not on file system' % sdk_dir) return None ftc = os.path.join(sdk_dir, self.sanity_check_file) if not os.path.exists(ftc): debug("find_sdk_dir(): sanity check %s not found" % ftc) return None return sdk_dir def get_sdk_dir(self): """Return the MSSSDK given the version string.""" try: return self._sdk_dir except AttributeError: sdk_dir = self.find_sdk_dir() self._sdk_dir = sdk_dir return sdk_dir def get_sdk_vc_script(self,host_arch, target_arch): """ Return the script to initialize the VC compiler installed by SDK """ if (host_arch == 'amd64' and target_arch == 'x86'): # No cross tools needed compiling 32 bits on 64 bit machine host_arch=target_arch arch_string=target_arch if (host_arch != target_arch): arch_string='%s_%s'%(host_arch,target_arch) debug("sdk.py: get_sdk_vc_script():arch_string:%s host_arch:%s target_arch:%s"%(arch_string, host_arch, target_arch)) file=self.vc_setup_scripts.get(arch_string,None) debug("sdk.py: get_sdk_vc_script():file:%s"%file) return file class WindowsSDK(SDKDefinition): """ A subclass for trying to find installed Windows SDK directories. """ HKEY_FMT = r'Software\Microsoft\Microsoft SDKs\Windows\v%s\InstallationFolder' def __init__(self, *args, **kw): SDKDefinition.__init__(self, *args, **kw) self.hkey_data = self.version class PlatformSDK(SDKDefinition): """ A subclass for trying to find installed Platform SDK directories. """ HKEY_FMT = r'Software\Microsoft\MicrosoftSDK\InstalledSDKS\%s\Install Dir' def __init__(self, *args, **kw): SDKDefinition.__init__(self, *args, **kw) self.hkey_data = self.uuid # # The list of VC initialization scripts installed by the SDK # These should be tried if the vcvarsall.bat TARGET_ARCH fails preSDK61VCSetupScripts = { 'x86' : r'bin\vcvars32.bat', 'amd64' : r'bin\vcvarsamd64.bat', 'x86_amd64': r'bin\vcvarsx86_amd64.bat', 'x86_ia64' : r'bin\vcvarsx86_ia64.bat', 'ia64' : r'bin\vcvarsia64.bat'} SDK61VCSetupScripts = {'x86' : r'bin\vcvars32.bat', 'amd64' : r'bin\amd64\vcvarsamd64.bat', 'x86_amd64': r'bin\x86_amd64\vcvarsx86_amd64.bat', 'x86_ia64' : r'bin\x86_ia64\vcvarsx86_ia64.bat', 'ia64' : r'bin\ia64\vcvarsia64.bat'} SDK70VCSetupScripts = { 'x86' : r'bin\vcvars32.bat', 'amd64' : r'bin\vcvars64.bat', 'x86_amd64': r'bin\vcvarsx86_amd64.bat', 'x86_ia64' : r'bin\vcvarsx86_ia64.bat', 'ia64' : r'bin\vcvarsia64.bat'} # The list of support SDKs which we know how to detect. # # The first SDK found in the list is the one used by default if there # are multiple SDKs installed. Barring good reasons to the contrary, # this means we should list SDKs with from most recent to oldest. # # If you update this list, update the documentation in Tool/mssdk.xml. SupportedSDKList = [ WindowsSDK('7.0', sanity_check_file=r'bin\SetEnv.Cmd', include_subdir='include', lib_subdir={ 'x86' : ['lib'], 'x86_64' : [r'lib\x64'], 'ia64' : [r'lib\ia64'], }, vc_setup_scripts = SDK70VCSetupScripts, ), WindowsSDK('6.1', sanity_check_file=r'bin\SetEnv.Cmd', include_subdir='include', lib_subdir={ 'x86' : ['lib'], 'x86_64' : [r'lib\x64'], 'ia64' : [r'lib\ia64'], }, vc_setup_scripts = SDK61VCSetupScripts, ), WindowsSDK('6.0A', sanity_check_file=r'include\windows.h', include_subdir='include', lib_subdir={ 'x86' : ['lib'], 'x86_64' : [r'lib\x64'], 'ia64' : [r'lib\ia64'], }, vc_setup_scripts = preSDK61VCSetupScripts, ), WindowsSDK('6.0', sanity_check_file=r'bin\gacutil.exe', include_subdir='include', lib_subdir='lib', vc_setup_scripts = preSDK61VCSetupScripts, ), PlatformSDK('2003R2', sanity_check_file=r'SetEnv.Cmd', uuid="D2FF9F89-8AA2-4373-8A31-C838BF4DBBE1", vc_setup_scripts = preSDK61VCSetupScripts, ), PlatformSDK('2003R1', sanity_check_file=r'SetEnv.Cmd', uuid="8F9E5EF3-A9A5-491B-A889-C58EFFECE8B3", vc_setup_scripts = preSDK61VCSetupScripts, ), ] SupportedSDKMap = {} for sdk in SupportedSDKList: SupportedSDKMap[sdk.version] = sdk # Finding installed SDKs isn't cheap, because it goes not only to the # registry but also to the disk to sanity-check that there is, in fact, # an SDK installed there and that the registry entry isn't just stale. # Find this information once, when requested, and cache it. InstalledSDKList = None InstalledSDKMap = None def get_installed_sdks(): global InstalledSDKList global InstalledSDKMap debug('sdk.py:get_installed_sdks()') if InstalledSDKList is None: InstalledSDKList = [] InstalledSDKMap = {} for sdk in SupportedSDKList: debug('MSCommon/sdk.py: trying to find SDK %s' % sdk.version) if sdk.get_sdk_dir(): debug('MSCommon/sdk.py:found SDK %s' % sdk.version) InstalledSDKList.append(sdk) InstalledSDKMap[sdk.version] = sdk return InstalledSDKList # We may be asked to update multiple construction environments with # SDK information. When doing this, we check on-disk for whether # the SDK has 'mfc' and 'atl' subdirectories. Since going to disk # is expensive, cache results by directory. SDKEnvironmentUpdates = {} def set_sdk_by_directory(env, sdk_dir): global SDKEnvironmentUpdates debug('set_sdk_by_directory: Using dir:%s'%sdk_dir) try: env_tuple_list = SDKEnvironmentUpdates[sdk_dir] except KeyError: env_tuple_list = [] SDKEnvironmentUpdates[sdk_dir] = env_tuple_list include_path = os.path.join(sdk_dir, 'include') mfc_path = os.path.join(include_path, 'mfc') atl_path = os.path.join(include_path, 'atl') if os.path.exists(mfc_path): env_tuple_list.append(('INCLUDE', mfc_path)) if os.path.exists(atl_path): env_tuple_list.append(('INCLUDE', atl_path)) env_tuple_list.append(('INCLUDE', include_path)) env_tuple_list.append(('LIB', os.path.join(sdk_dir, 'lib'))) env_tuple_list.append(('LIBPATH', os.path.join(sdk_dir, 'lib'))) env_tuple_list.append(('PATH', os.path.join(sdk_dir, 'bin'))) for variable, directory in env_tuple_list: env.PrependENVPath(variable, directory) # TODO(sgk): currently unused; remove? def get_cur_sdk_dir_from_reg(): """Try to find the platform sdk directory from the registry. Return None if failed or the directory does not exist""" if not SCons.Util.can_read_reg: debug('SCons cannot read registry') return None try: val = common.read_reg(_CURINSTALLED_SDK_HKEY_ROOT) debug("Found current sdk dir in registry: %s" % val) except WindowsError, e: debug("Did not find current sdk in registry") return None if not os.path.exists(val): debug("Current sdk dir %s not on fs" % val) return None return val def get_sdk_by_version(mssdk): if mssdk not in SupportedSDKMap: msg = "SDK version %s is not supported" % repr(mssdk) raise SCons.Errors.UserError(msg) get_installed_sdks() return InstalledSDKMap.get(mssdk) def get_default_sdk(): """Set up the default Platform/Windows SDK.""" get_installed_sdks() if not InstalledSDKList: return None return InstalledSDKList[0] def mssdk_setup_env(env): debug('sdk.py:mssdk_setup_env()') if 'MSSDK_DIR' in env: sdk_dir = env['MSSDK_DIR'] if sdk_dir is None: return sdk_dir = env.subst(sdk_dir) debug('sdk.py:mssdk_setup_env: Using MSSDK_DIR:%s'%sdk_dir) elif 'MSSDK_VERSION' in env: sdk_version = env['MSSDK_VERSION'] if sdk_version is None: msg = "SDK version %s is not installed" % repr(mssdk) raise SCons.Errors.UserError(msg) sdk_version = env.subst(sdk_version) mssdk = get_sdk_by_version(sdk_version) sdk_dir = mssdk.get_sdk_dir() debug('sdk.py:mssdk_setup_env: Using MSSDK_VERSION:%s'%sdk_dir) elif 'MSVS_VERSION' in env: msvs_version = env['MSVS_VERSION'] debug('sdk.py:mssdk_setup_env:Getting MSVS_VERSION from env:%s'%msvs_version) if msvs_version is None: debug('sdk.py:mssdk_setup_env thinks msvs_version is None') return msvs_version = env.subst(msvs_version) import vs msvs = vs.get_vs_by_version(msvs_version) debug('sdk.py:mssdk_setup_env:msvs is :%s'%msvs) if not msvs: debug('sdk.py:mssdk_setup_env: no VS version detected, bailingout:%s'%msvs) return sdk_version = msvs.sdk_version debug('sdk.py:msvs.sdk_version is %s'%sdk_version) if not sdk_version: return mssdk = get_sdk_by_version(sdk_version) if not mssdk: mssdk = get_default_sdk() if not mssdk: return sdk_dir = mssdk.get_sdk_dir() debug('sdk.py:mssdk_setup_env: Using MSVS_VERSION:%s'%sdk_dir) else: mssdk = get_default_sdk() if not mssdk: return sdk_dir = mssdk.get_sdk_dir() debug('sdk.py:mssdk_setup_env: not using any env values. sdk_dir:%s'%sdk_dir) set_sdk_by_directory(env, sdk_dir) #print "No MSVS_VERSION: this is likely to be a bug" def mssdk_exists(version=None): sdks = get_installed_sdks() if version is None: return len(sdks) > 0 return version in sdks # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/MSCommon/common.py0000644000175000017500000002041112114661557023550 0ustar dktrkranzdktrkranz# # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/MSCommon/common.py 2013/03/03 09:48:35 garyo" __doc__ = """ Common helper functions for working with the Microsoft tool chain. """ import copy import os import subprocess import re import SCons.Util logfile = os.environ.get('SCONS_MSCOMMON_DEBUG') if logfile == '-': def debug(x): print x elif logfile: try: import logging except ImportError: debug = lambda x: open(logfile, 'a').write(x + '\n') else: logging.basicConfig(filename=logfile, level=logging.DEBUG) debug = logging.debug else: debug = lambda x: None _is_win64 = None def is_win64(): """Return true if running on windows 64 bits. Works whether python itself runs in 64 bits or 32 bits.""" # Unfortunately, python does not provide a useful way to determine # if the underlying Windows OS is 32-bit or 64-bit. Worse, whether # the Python itself is 32-bit or 64-bit affects what it returns, # so nothing in sys.* or os.* help. # Apparently the best solution is to use env vars that Windows # sets. If PROCESSOR_ARCHITECTURE is not x86, then the python # process is running in 64 bit mode (on a 64-bit OS, 64-bit # hardware, obviously). # If this python is 32-bit but the OS is 64, Windows will set # ProgramW6432 and PROCESSOR_ARCHITEW6432 to non-null. # (Checking for HKLM\Software\Wow6432Node in the registry doesn't # work, because some 32-bit installers create it.) global _is_win64 if _is_win64 is None: # I structured these tests to make it easy to add new ones or # add exceptions in the future, because this is a bit fragile. _is_win64 = False if os.environ.get('PROCESSOR_ARCHITECTURE','x86') != 'x86': _is_win64 = True if os.environ.get('PROCESSOR_ARCHITEW6432'): _is_win64 = True if os.environ.get('ProgramW6432'): _is_win64 = True return _is_win64 def read_reg(value): return SCons.Util.RegGetValue(SCons.Util.HKEY_LOCAL_MACHINE, value)[0] def has_reg(value): """Return True if the given key exists in HKEY_LOCAL_MACHINE, False otherwise.""" try: SCons.Util.RegOpenKeyEx(SCons.Util.HKEY_LOCAL_MACHINE, value) ret = True except WindowsError: ret = False return ret # Functions for fetching environment variable settings from batch files. def normalize_env(env, keys, force=False): """Given a dictionary representing a shell environment, add the variables from os.environ needed for the processing of .bat files; the keys are controlled by the keys argument. It also makes sure the environment values are correctly encoded. If force=True, then all of the key values that exist are copied into the returned dictionary. If force=false, values are only copied if the key does not already exist in the copied dictionary. Note: the environment is copied.""" normenv = {} if env: for k in env.keys(): normenv[k] = copy.deepcopy(env[k]).encode('mbcs') for k in keys: if k in os.environ and (force or not k in normenv): normenv[k] = os.environ[k].encode('mbcs') return normenv def get_output(vcbat, args = None, env = None): """Parse the output of given bat file, with given args.""" if env is None: # Create a blank environment, for use in launching the tools env = SCons.Environment.Environment(tools=[]) # TODO: This is a hard-coded list of the variables that (may) need # to be imported from os.environ[] for v[sc]*vars*.bat file # execution to work. This list should really be either directly # controlled by vc.py, or else derived from the common_tools_var # settings in vs.py. vars = [ 'COMSPEC', 'VS110COMNTOOLS', 'VS100COMNTOOLS', 'VS90COMNTOOLS', 'VS80COMNTOOLS', 'VS71COMNTOOLS', 'VS70COMNTOOLS', 'VS60COMNTOOLS', ] env['ENV'] = normalize_env(env['ENV'], vars, force=False) if args: debug("Calling '%s %s'" % (vcbat, args)) popen = SCons.Action._subproc(env, '"%s" %s & set' % (vcbat, args), stdin = 'devnull', stdout=subprocess.PIPE, stderr=subprocess.PIPE) else: debug("Calling '%s'" % vcbat) popen = SCons.Action._subproc(env, '"%s" & set' % vcbat, stdin = 'devnull', stdout=subprocess.PIPE, stderr=subprocess.PIPE) # Use the .stdout and .stderr attributes directly because the # .communicate() method uses the threading module on Windows # and won't work under Pythons not built with threading. stdout = popen.stdout.read() stderr = popen.stderr.read() if stderr: # TODO: find something better to do with stderr; # this at least prevents errors from getting swallowed. import sys sys.stderr.write(stderr) if popen.wait() != 0: raise IOError(stderr.decode("mbcs")) output = stdout.decode("mbcs") return output def parse_output(output, keep = ("INCLUDE", "LIB", "LIBPATH", "PATH")): # dkeep is a dict associating key: path_list, where key is one item from # keep, and pat_list the associated list of paths dkeep = dict([(i, []) for i in keep]) # rdk will keep the regex to match the .bat file output line starts rdk = {} for i in keep: rdk[i] = re.compile('%s=(.*)' % i, re.I) def add_env(rmatch, key, dkeep=dkeep): plist = rmatch.group(1).split(os.pathsep) for p in plist: # Do not add empty paths (when a var ends with ;) if p: p = p.encode('mbcs') # XXX: For some reason, VC98 .bat file adds "" around the PATH # values, and it screws up the environment later, so we strip # it. p = p.strip('"') dkeep[key].append(p) for line in output.splitlines(): for k,v in rdk.items(): m = v.match(line) if m: add_env(m, k) return dkeep # TODO(sgk): unused def output_to_dict(output): """Given an output string, parse it to find env variables. Return a dict where keys are variables names, and values their content""" envlinem = re.compile(r'^([a-zA-z0-9]+)=([\S\s]*)$') parsedenv = {} for line in output.splitlines(): m = envlinem.match(line) if m: parsedenv[m.group(1)] = m.group(2) return parsedenv # TODO(sgk): unused def get_new(l1, l2): """Given two list l1 and l2, return the items in l2 which are not in l1. Order is maintained.""" # We don't try to be smart: lists are small, and this is not the bottleneck # is any case new = [] for i in l2: if i not in l1: new.append(i) return new # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/MSCommon/netframework.py0000644000175000017500000000540312114661557024770 0ustar dktrkranzdktrkranz# # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/MSCommon/netframework.py 2013/03/03 09:48:35 garyo" __doc__ = """ """ import os import re from common import read_reg, debug # Original value recorded by dcournapeau _FRAMEWORKDIR_HKEY_ROOT = r'Software\Microsoft\.NETFramework\InstallRoot' # On SGK's system _FRAMEWORKDIR_HKEY_ROOT = r'Software\Microsoft\Microsoft SDKs\.NETFramework\v2.0\InstallationFolder' def find_framework_root(): # XXX: find it from environment (FrameworkDir) try: froot = read_reg(_FRAMEWORKDIR_HKEY_ROOT) debug("Found framework install root in registry: %s" % froot) except WindowsError, e: debug("Could not read reg key %s" % _FRAMEWORKDIR_HKEY_ROOT) return None if not os.path.exists(froot): debug("%s not found on fs" % froot) return None return froot def query_versions(): froot = find_framework_root() if froot: contents = os.listdir(froot) l = re.compile('v[0-9]+.*') versions = [e for e in contents if l.match(e)] def versrt(a,b): # since version numbers aren't really floats... aa = a[1:] bb = b[1:] aal = aa.split('.') bbl = bb.split('.') # sequence comparison in python is lexicographical # which is exactly what we want. # Note we sort backwards so the highest version is first. return cmp(bbl,aal) versions.sort(versrt) else: versions = [] return versions # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/MSCommon/vs.py0000644000175000017500000005023412114661557022716 0ustar dktrkranzdktrkranz# # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/MSCommon/vs.py 2013/03/03 09:48:35 garyo" __doc__ = """Module to detect Visual Studio and/or Visual C/C++ """ import os import SCons.Errors import SCons.Util from common import debug, \ get_output, \ is_win64, \ normalize_env, \ parse_output, \ read_reg import SCons.Tool.MSCommon.vc class VisualStudio(object): """ An abstract base class for trying to find installed versions of Visual Studio. """ def __init__(self, version, **kw): self.version = version kw['vc_version'] = kw.get('vc_version', version) kw['sdk_version'] = kw.get('sdk_version', version) self.__dict__.update(kw) self._cache = {} # def find_batch_file(self): vs_dir = self.get_vs_dir() if not vs_dir: debug('find_executable(): no vs_dir') return None batch_file = os.path.join(vs_dir, self.batch_file_path) batch_file = os.path.normpath(batch_file) if not os.path.isfile(batch_file): debug('find_batch_file(): %s not on file system' % batch_file) return None return batch_file def find_vs_dir_by_vc(self): SCons.Tool.MSCommon.vc.get_installed_vcs() dir = SCons.Tool.MSCommon.vc.find_vc_pdir(self.vc_version) if not dir: debug('find_vs_dir(): no installed VC %s' % self.vc_version) return None return dir def find_vs_dir_by_reg(self): root = 'Software\\' if is_win64(): root = root + 'Wow6432Node\\' for key in self.hkeys: if key=='use_dir': return self.find_vs_dir_by_vc() key = root + key try: comps = read_reg(key) except WindowsError, e: debug('find_vs_dir_by_reg(): no VS registry key %s' % repr(key)) else: debug('find_vs_dir_by_reg(): found VS in registry: %s' % comps) return comps return None def find_vs_dir(self): """ Can use registry or location of VC to find vs dir First try to find by registry, and if that fails find via VC dir """ if True: vs_dir=self.find_vs_dir_by_reg() return vs_dir else: return self.find_vs_dir_by_vc() def find_executable(self): vs_dir = self.get_vs_dir() if not vs_dir: debug('find_executable(): no vs_dir (%s)'%vs_dir) return None executable = os.path.join(vs_dir, self.executable_path) executable = os.path.normpath(executable) if not os.path.isfile(executable): debug('find_executable(): %s not on file system' % executable) return None return executable # def get_batch_file(self): try: return self._cache['batch_file'] except KeyError: batch_file = self.find_batch_file() self._cache['batch_file'] = batch_file return batch_file def get_executable(self): try: debug('get_executable using cache:%s'%self._cache['executable']) return self._cache['executable'] except KeyError: executable = self.find_executable() self._cache['executable'] = executable debug('get_executable not in cache:%s'%executable) return executable def get_vs_dir(self): try: return self._cache['vs_dir'] except KeyError: vs_dir = self.find_vs_dir() self._cache['vs_dir'] = vs_dir return vs_dir def get_supported_arch(self): try: return self._cache['supported_arch'] except KeyError: # RDEVE: for the time being use hardcoded lists # supported_arch = self.find_supported_arch() self._cache['supported_arch'] = self.supported_arch return self.supported_arch def reset(self): self._cache = {} # The list of supported Visual Studio versions we know how to detect. # # How to look for .bat file ? # - VS 2008 Express (x86): # * from registry key productdir, gives the full path to vsvarsall.bat. In # HKEY_LOCAL_MACHINE): # Software\Microsoft\VCEpress\9.0\Setup\VC\productdir # * from environmnent variable VS90COMNTOOLS: the path is then ..\..\VC # relatively to the path given by the variable. # # - VS 2008 Express (WoW6432: 32 bits on windows x64): # Software\Wow6432Node\Microsoft\VCEpress\9.0\Setup\VC\productdir # # - VS 2005 Express (x86): # * from registry key productdir, gives the full path to vsvarsall.bat. In # HKEY_LOCAL_MACHINE): # Software\Microsoft\VCEpress\8.0\Setup\VC\productdir # * from environmnent variable VS80COMNTOOLS: the path is then ..\..\VC # relatively to the path given by the variable. # # - VS 2005 Express (WoW6432: 32 bits on windows x64): does not seem to have a # productdir ? # # - VS 2003 .Net (pro edition ? x86): # * from registry key productdir. The path is then ..\Common7\Tools\ # relatively to the key. The key is in HKEY_LOCAL_MACHINE): # Software\Microsoft\VisualStudio\7.1\Setup\VC\productdir # * from environmnent variable VS71COMNTOOLS: the path is the full path to # vsvars32.bat # # - VS 98 (VS 6): # * from registry key productdir. The path is then Bin # relatively to the key. The key is in HKEY_LOCAL_MACHINE): # Software\Microsoft\VisualStudio\6.0\Setup\VC98\productdir # # The first version found in the list is the one used by default if # there are multiple versions installed. Barring good reasons to # the contrary, this means we should list versions from most recent # to oldest. Pro versions get listed before Express versions on the # assumption that, by default, you'd rather use the version you paid # good money for in preference to whatever Microsoft makes available # for free. # # If you update this list, update the documentation in Tool/msvs.xml. SupportedVSList = [ # Visual Studio 2010 # TODO: find the settings, perhaps from someone with a CTP copy? #VisualStudio('TBD', # hkey_root=r'TBD', # common_tools_var='TBD', # executable_path=r'TBD', # default_dirname='TBD', #), # Visual Studio 11 # The batch file we look for is in the VC directory, # so the devenv.com executable is up in ..\..\Common7\IDE. VisualStudio('11.0', sdk_version='6.1', hkeys=[r'Microsoft\VisualStudio\11.0\Setup\VS\ProductDir'], common_tools_var='VS110COMNTOOLS', executable_path=r'Common7\IDE\devenv.com', batch_file_path=r'Common7\Tools\vsvars32.bat', default_dirname='Microsoft Visual Studio 11', supported_arch=['x86', 'amd64'], ), # Visual C++ 11 Express Edition # The batch file we look for is in the VC directory, # so the VCExpress.exe executable is up in ..\..\Common7\IDE. VisualStudio('11.0Exp', vc_version='11.0', sdk_version='6.1', hkeys=[r'Microsoft\VCExpress\11.0\Setup\VS\ProductDir'], common_tools_var='VS110COMNTOOLS', executable_path=r'Common7\IDE\VCExpress.exe', batch_file_path=r'Common7\Tools\vsvars32.bat', default_dirname='Microsoft Visual Studio 11', supported_arch=['x86'], ), # Visual Studio 2010 # The batch file we look for is in the VC directory, # so the devenv.com executable is up in ..\..\Common7\IDE. VisualStudio('10.0', sdk_version='6.1', hkeys=[r'Microsoft\VisualStudio\10.0\Setup\VS\ProductDir'], common_tools_var='VS100COMNTOOLS', executable_path=r'Common7\IDE\devenv.com', batch_file_path=r'Common7\Tools\vsvars32.bat', default_dirname='Microsoft Visual Studio 10', supported_arch=['x86', 'amd64'], ), # Visual C++ 2010 Express Edition # The batch file we look for is in the VC directory, # so the VCExpress.exe executable is up in ..\..\Common7\IDE. VisualStudio('10.0Exp', vc_version='10.0', sdk_version='6.1', hkeys=[r'Microsoft\VCExpress\10.0\Setup\VS\ProductDir'], common_tools_var='VS100COMNTOOLS', executable_path=r'Common7\IDE\VCExpress.exe', batch_file_path=r'Common7\Tools\vsvars32.bat', default_dirname='Microsoft Visual Studio 10', supported_arch=['x86'], ), # Visual Studio 2008 # The batch file we look for is in the VC directory, # so the devenv.com executable is up in ..\..\Common7\IDE. VisualStudio('9.0', sdk_version='6.1', hkeys=[r'Microsoft\VisualStudio\9.0\Setup\VS\ProductDir'], common_tools_var='VS90COMNTOOLS', executable_path=r'Common7\IDE\devenv.com', batch_file_path=r'Common7\Tools\vsvars32.bat', default_dirname='Microsoft Visual Studio 9', supported_arch=['x86', 'amd64'], ), # Visual C++ 2008 Express Edition # The batch file we look for is in the VC directory, # so the VCExpress.exe executable is up in ..\..\Common7\IDE. VisualStudio('9.0Exp', vc_version='9.0', sdk_version='6.1', hkeys=[r'Microsoft\VCExpress\9.0\Setup\VS\ProductDir'], common_tools_var='VS90COMNTOOLS', executable_path=r'Common7\IDE\VCExpress.exe', batch_file_path=r'Common7\Tools\vsvars32.bat', default_dirname='Microsoft Visual Studio 9', supported_arch=['x86'], ), # Visual Studio 2005 # The batch file we look for is in the VC directory, # so the devenv.com executable is up in ..\..\Common7\IDE. VisualStudio('8.0', sdk_version='6.0A', hkeys=[r'Microsoft\VisualStudio\8.0\Setup\VS\ProductDir'], common_tools_var='VS80COMNTOOLS', executable_path=r'Common7\IDE\devenv.com', batch_file_path=r'Common7\Tools\vsvars32.bat', default_dirname='Microsoft Visual Studio 8', supported_arch=['x86', 'amd64'], ), # Visual C++ 2005 Express Edition # The batch file we look for is in the VC directory, # so the VCExpress.exe executable is up in ..\..\Common7\IDE. VisualStudio('8.0Exp', vc_version='8.0Exp', sdk_version='6.0A', hkeys=[r'Microsoft\VCExpress\8.0\Setup\VS\ProductDir'], common_tools_var='VS80COMNTOOLS', executable_path=r'Common7\IDE\VCExpress.exe', batch_file_path=r'Common7\Tools\vsvars32.bat', default_dirname='Microsoft Visual Studio 8', supported_arch=['x86'], ), # Visual Studio .NET 2003 # The batch file we look for is in the Common7\Tools directory, # so the devenv.com executable is next door in ..\IDE. VisualStudio('7.1', sdk_version='6.0', hkeys=[r'Microsoft\VisualStudio\7.1\Setup\VS\ProductDir'], common_tools_var='VS71COMNTOOLS', executable_path=r'Common7\IDE\devenv.com', batch_file_path=r'Common7\Tools\vsvars32.bat', default_dirname='Microsoft Visual Studio .NET 2003', supported_arch=['x86'], ), # Visual Studio .NET # The batch file we look for is in the Common7\Tools directory, # so the devenv.com executable is next door in ..\IDE. VisualStudio('7.0', sdk_version='2003R2', hkeys=[r'Microsoft\VisualStudio\7.0\Setup\VS\ProductDir'], common_tools_var='VS70COMNTOOLS', executable_path=r'IDE\devenv.com', batch_file_path=r'Common7\Tools\vsvars32.bat', default_dirname='Microsoft Visual Studio .NET', supported_arch=['x86'], ), # Visual Studio 6.0 VisualStudio('6.0', sdk_version='2003R1', hkeys=[r'Microsoft\VisualStudio\6.0\Setup\Microsoft Visual Studio\ProductDir', 'use_dir'], common_tools_var='VS60COMNTOOLS', executable_path=r'Common\MSDev98\Bin\MSDEV.COM', batch_file_path=r'Common7\Tools\vsvars32.bat', default_dirname='Microsoft Visual Studio', supported_arch=['x86'], ), ] SupportedVSMap = {} for vs in SupportedVSList: SupportedVSMap[vs.version] = vs # Finding installed versions of Visual Studio isn't cheap, because it # goes not only to the registry but also to the disk to sanity-check # that there is, in fact, a Visual Studio directory there and that the # registry entry isn't just stale. Find this information once, when # requested, and cache it. InstalledVSList = None InstalledVSMap = None def get_installed_visual_studios(): global InstalledVSList global InstalledVSMap if InstalledVSList is None: InstalledVSList = [] InstalledVSMap = {} for vs in SupportedVSList: debug('trying to find VS %s' % vs.version) if vs.get_executable(): debug('found VS %s' % vs.version) InstalledVSList.append(vs) InstalledVSMap[vs.version] = vs return InstalledVSList def reset_installed_visual_studios(): global InstalledVSList global InstalledVSMap InstalledVSList = None InstalledVSMap = None for vs in SupportedVSList: vs.reset() # Need to clear installed VC's as well as they are used in finding # installed VS's SCons.Tool.MSCommon.vc.reset_installed_vcs() # We may be asked to update multiple construction environments with # SDK information. When doing this, we check on-disk for whether # the SDK has 'mfc' and 'atl' subdirectories. Since going to disk # is expensive, cache results by directory. #SDKEnvironmentUpdates = {} # #def set_sdk_by_directory(env, sdk_dir): # global SDKEnvironmentUpdates # try: # env_tuple_list = SDKEnvironmentUpdates[sdk_dir] # except KeyError: # env_tuple_list = [] # SDKEnvironmentUpdates[sdk_dir] = env_tuple_list # # include_path = os.path.join(sdk_dir, 'include') # mfc_path = os.path.join(include_path, 'mfc') # atl_path = os.path.join(include_path, 'atl') # # if os.path.exists(mfc_path): # env_tuple_list.append(('INCLUDE', mfc_path)) # if os.path.exists(atl_path): # env_tuple_list.append(('INCLUDE', atl_path)) # env_tuple_list.append(('INCLUDE', include_path)) # # env_tuple_list.append(('LIB', os.path.join(sdk_dir, 'lib'))) # env_tuple_list.append(('LIBPATH', os.path.join(sdk_dir, 'lib'))) # env_tuple_list.append(('PATH', os.path.join(sdk_dir, 'bin'))) # # for variable, directory in env_tuple_list: # env.PrependENVPath(variable, directory) def msvs_exists(): return (len(get_installed_visual_studios()) > 0) def get_vs_by_version(msvs): global InstalledVSMap global SupportedVSMap debug('vs.py:get_vs_by_version()') if msvs not in SupportedVSMap: msg = "Visual Studio version %s is not supported" % repr(msvs) raise SCons.Errors.UserError(msg) get_installed_visual_studios() vs = InstalledVSMap.get(msvs) debug('InstalledVSMap:%s'%InstalledVSMap) debug('vs.py:get_vs_by_version: found vs:%s'%vs) # Some check like this would let us provide a useful error message # if they try to set a Visual Studio version that's not installed. # However, we also want to be able to run tests (like the unit # tests) on systems that don't, or won't ever, have it installed. # It might be worth resurrecting this, with some configurable # setting that the tests can use to bypass the check. #if not vs: # msg = "Visual Studio version %s is not installed" % repr(msvs) # raise SCons.Errors.UserError, msg return vs def get_default_version(env): """Returns the default version string to use for MSVS. If no version was requested by the user through the MSVS environment variable, query all the available the visual studios through query_versions, and take the highest one. Return ------ version: str the default version. """ if 'MSVS' not in env or not SCons.Util.is_Dict(env['MSVS']): versions = [vs.version for vs in get_installed_visual_studios()] env['MSVS'] = {'VERSIONS' : versions} else: versions = env['MSVS'].get('VERSIONS', []) if 'MSVS_VERSION' not in env: if versions: env['MSVS_VERSION'] = versions[0] #use highest version by default else: env['MSVS_VERSION'] = SupportedVSList[0].version env['MSVS']['VERSION'] = env['MSVS_VERSION'] return env['MSVS_VERSION'] def get_default_arch(env): """Return the default arch to use for MSVS if no version was requested by the user through the MSVS_ARCH environment variable, select x86 Return ------ arch: str """ arch = env.get('MSVS_ARCH', 'x86') msvs = InstalledVSMap.get(env['MSVS_VERSION']) if not msvs: arch = 'x86' elif not arch in msvs.get_supported_arch(): fmt = "Visual Studio version %s does not support architecture %s" raise SCons.Errors.UserError(fmt % (env['MSVS_VERSION'], arch)) return arch def merge_default_version(env): version = get_default_version(env) arch = get_default_arch(env) def msvs_setup_env(env): batfilename = msvs.get_batch_file() msvs = get_vs_by_version(version) if msvs is None: return # XXX: I think this is broken. This will silently set a bogus tool instead # of failing, but there is no other way with the current scons tool # framework if batfilename is not None: vars = ('LIB', 'LIBPATH', 'PATH', 'INCLUDE') msvs_list = get_installed_visual_studios() vscommonvarnames = [vs.common_tools_var for vs in msvs_list] save_ENV = env['ENV'] nenv = normalize_env(env['ENV'], ['COMSPEC'] + vscommonvarnames, force=True) try: output = get_output(batfilename, arch, env=nenv) finally: env['ENV'] = save_ENV vars = parse_output(output, vars) for k, v in vars.items(): env.PrependENVPath(k, v, delete_existing=1) def query_versions(): """Query the system to get available versions of VS. A version is considered when a batfile is found.""" msvs_list = get_installed_visual_studios() versions = [msvs.version for msvs in msvs_list] return versions # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/MSCommon/vc.py0000644000175000017500000004130612114661557022676 0ustar dktrkranzdktrkranz# # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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. # # TODO: # * supported arch for versions: for old versions of batch file without # argument, giving bogus argument cannot be detected, so we have to hardcode # this here # * print warning when msvc version specified but not found # * find out why warning do not print # * test on 64 bits XP + VS 2005 (and VS 6 if possible) # * SDK # * Assembly __revision__ = "src/engine/SCons/Tool/MSCommon/vc.py 2013/03/03 09:48:35 garyo" __doc__ = """Module for Visual C/C++ detection and configuration. """ import SCons.compat import os import platform from string import digits as string_digits import SCons.Warnings import common debug = common.debug import sdk get_installed_sdks = sdk.get_installed_sdks class VisualCException(Exception): pass class UnsupportedVersion(VisualCException): pass class UnsupportedArch(VisualCException): pass class MissingConfiguration(VisualCException): pass class NoVersionFound(VisualCException): pass class BatchFileExecutionError(VisualCException): pass # Dict to 'canonalize' the arch _ARCH_TO_CANONICAL = { "amd64" : "amd64", "emt64" : "amd64", "i386" : "x86", "i486" : "x86", "i586" : "x86", "i686" : "x86", "ia64" : "ia64", "itanium" : "ia64", "x86" : "x86", "x86_64" : "amd64", } # Given a (host, target) tuple, return the argument for the bat file. Both host # and targets should be canonalized. _HOST_TARGET_ARCH_TO_BAT_ARCH = { ("x86", "x86"): "x86", ("x86", "amd64"): "x86_amd64", ("amd64", "amd64"): "amd64", ("amd64", "x86"): "x86", ("x86", "ia64"): "x86_ia64" } def get_host_target(env): debug('vc.py:get_host_target()') host_platform = env.get('HOST_ARCH') if not host_platform: host_platform = platform.machine() # TODO(2.5): the native Python platform.machine() function returns # '' on all Python versions before 2.6, after which it also uses # PROCESSOR_ARCHITECTURE. if not host_platform: host_platform = os.environ.get('PROCESSOR_ARCHITECTURE', '') # Retain user requested TARGET_ARCH req_target_platform = env.get('TARGET_ARCH') debug('vc.py:get_host_target() req_target_platform:%s'%req_target_platform) if req_target_platform: # If user requested a specific platform then only try that one. target_platform = req_target_platform else: target_platform = host_platform try: host = _ARCH_TO_CANONICAL[host_platform.lower()] except KeyError, e: msg = "Unrecognized host architecture %s" raise ValueError(msg % repr(host_platform)) try: target = _ARCH_TO_CANONICAL[target_platform.lower()] except KeyError, e: all_archs = str(_ARCH_TO_CANONICAL.keys()) raise ValueError("Unrecognized target architecture %s\n\tValid architectures: %s" % (target_platform, all_archs)) return (host, target,req_target_platform) _VCVER = ["11.0", "11.0Exp", "10.0", "10.0Exp", "9.0", "9.0Exp","8.0", "8.0Exp","7.1", "7.0", "6.0"] _VCVER_TO_PRODUCT_DIR = { '11.0': [ r'Microsoft\VisualStudio\11.0\Setup\VC\ProductDir'], '11.0Exp' : [ r'Microsoft\VCExpress\11.0\Setup\VC\ProductDir'], '10.0': [ r'Microsoft\VisualStudio\10.0\Setup\VC\ProductDir'], '10.0Exp' : [ r'Microsoft\VCExpress\10.0\Setup\VC\ProductDir'], '9.0': [ r'Microsoft\VisualStudio\9.0\Setup\VC\ProductDir'], '9.0Exp' : [ r'Microsoft\VCExpress\9.0\Setup\VC\ProductDir'], '8.0': [ r'Microsoft\VisualStudio\8.0\Setup\VC\ProductDir'], '8.0Exp': [ r'Microsoft\VCExpress\8.0\Setup\VC\ProductDir'], '7.1': [ r'Microsoft\VisualStudio\7.1\Setup\VC\ProductDir'], '7.0': [ r'Microsoft\VisualStudio\7.0\Setup\VC\ProductDir'], '6.0': [ r'Microsoft\VisualStudio\6.0\Setup\Microsoft Visual C++\ProductDir'] } def msvc_version_to_maj_min(msvc_version): msvc_version_numeric = ''.join([x for x in msvc_version if x in string_digits + '.']) t = msvc_version_numeric.split(".") if not len(t) == 2: raise ValueError("Unrecognized version %s (%s)" % (msvc_version,msvc_version_numeric)) try: maj = int(t[0]) min = int(t[1]) return maj, min except ValueError, e: raise ValueError("Unrecognized version %s (%s)" % (msvc_version,msvc_version_numeric)) def is_host_target_supported(host_target, msvc_version): """Return True if the given (host, target) tuple is supported given the msvc version. Parameters ---------- host_target: tuple tuple of (canonalized) host-target, e.g. ("x86", "amd64") for cross compilation from 32 bits windows to 64 bits. msvc_version: str msvc version (major.minor, e.g. 10.0) Note ---- This only check whether a given version *may* support the given (host, target), not that the toolchain is actually present on the machine. """ # We assume that any Visual Studio version supports x86 as a target if host_target[1] != "x86": maj, min = msvc_version_to_maj_min(msvc_version) if maj < 8: return False return True def find_vc_pdir(msvc_version): """Try to find the product directory for the given version. Note ---- If for some reason the requested version could not be found, an exception which inherits from VisualCException will be raised.""" root = 'Software\\' if common.is_win64(): root = root + 'Wow6432Node\\' try: hkeys = _VCVER_TO_PRODUCT_DIR[msvc_version] except KeyError: debug("Unknown version of MSVC: %s" % msvc_version) raise UnsupportedVersion("Unknown version %s" % msvc_version) for key in hkeys: key = root + key try: comps = common.read_reg(key) except WindowsError, e: debug('find_vc_dir(): no VC registry key %s' % repr(key)) else: debug('find_vc_dir(): found VC in registry: %s' % comps) if os.path.exists(comps): return comps else: debug('find_vc_dir(): reg says dir is %s, but it does not exist. (ignoring)'\ % comps) raise MissingConfiguration("registry dir %s not found on the filesystem" % comps) return None def find_batch_file(env,msvc_version,host_arch,target_arch): """ Find the location of the batch script which should set up the compiler for any TARGET_ARCH whose compilers were installed by Visual Studio/VCExpress """ pdir = find_vc_pdir(msvc_version) if pdir is None: raise NoVersionFound("No version of Visual Studio found") debug('vc.py: find_batch_file() pdir:%s'%pdir) # filter out e.g. "Exp" from the version name msvc_ver_numeric = ''.join([x for x in msvc_version if x in string_digits + "."]) vernum = float(msvc_ver_numeric) if 7 <= vernum < 8: pdir = os.path.join(pdir, os.pardir, "Common7", "Tools") batfilename = os.path.join(pdir, "vsvars32.bat") elif vernum < 7: pdir = os.path.join(pdir, "Bin") batfilename = os.path.join(pdir, "vcvars32.bat") else: # >= 8 batfilename = os.path.join(pdir, "vcvarsall.bat") if not os.path.exists(batfilename): debug("Not found: %s" % batfilename) batfilename = None installed_sdks=get_installed_sdks() for _sdk in installed_sdks: sdk_bat_file=_sdk.get_sdk_vc_script(host_arch,target_arch) sdk_bat_file_path=os.path.join(pdir,sdk_bat_file) debug('vc.py:find_batch_file() sdk_bat_file_path:%s'%sdk_bat_file_path) if os.path.exists(sdk_bat_file_path): return (batfilename,sdk_bat_file_path) else: debug("vc.py:find_batch_file() not found:%s"%sdk_bat_file_path) else: return (batfilename,None) __INSTALLED_VCS_RUN = None def cached_get_installed_vcs(): global __INSTALLED_VCS_RUN if __INSTALLED_VCS_RUN is None: ret = get_installed_vcs() __INSTALLED_VCS_RUN = ret return __INSTALLED_VCS_RUN def get_installed_vcs(): installed_versions = [] for ver in _VCVER: debug('trying to find VC %s' % ver) try: if find_vc_pdir(ver): debug('found VC %s' % ver) installed_versions.append(ver) else: debug('find_vc_pdir return None for ver %s' % ver) except VisualCException, e: debug('did not find VC %s: caught exception %s' % (ver, str(e))) return installed_versions def reset_installed_vcs(): """Make it try again to find VC. This is just for the tests.""" __INSTALLED_VCS_RUN = None def script_env(script, args=None): stdout = common.get_output(script, args) # Stupid batch files do not set return code: we take a look at the # beginning of the output for an error message instead olines = stdout.splitlines() if olines[0].startswith("The specified configuration type is missing"): raise BatchFileExecutionError("\n".join(olines[:2])) return common.parse_output(stdout) def get_default_version(env): debug('get_default_version()') msvc_version = env.get('MSVC_VERSION') msvs_version = env.get('MSVS_VERSION') debug('get_default_version(): msvc_version:%s msvs_version:%s'%(msvc_version,msvs_version)) if msvs_version and not msvc_version: SCons.Warnings.warn( SCons.Warnings.DeprecatedWarning, "MSVS_VERSION is deprecated: please use MSVC_VERSION instead ") return msvs_version elif msvc_version and msvs_version: if not msvc_version == msvs_version: SCons.Warnings.warn( SCons.Warnings.VisualVersionMismatch, "Requested msvc version (%s) and msvs version (%s) do " \ "not match: please use MSVC_VERSION only to request a " \ "visual studio version, MSVS_VERSION is deprecated" \ % (msvc_version, msvs_version)) return msvs_version if not msvc_version: installed_vcs = cached_get_installed_vcs() debug('installed_vcs:%s' % installed_vcs) if not installed_vcs: #msg = 'No installed VCs' #debug('msv %s\n' % repr(msg)) #SCons.Warnings.warn(SCons.Warnings.VisualCMissingWarning, msg) debug('msvc_setup_env: No installed VCs') return None msvc_version = installed_vcs[0] debug('msvc_setup_env: using default installed MSVC version %s\n' % repr(msvc_version)) return msvc_version def msvc_setup_env_once(env): try: has_run = env["MSVC_SETUP_RUN"] except KeyError: has_run = False if not has_run: msvc_setup_env(env) env["MSVC_SETUP_RUN"] = True def msvc_find_valid_batch_script(env,version): debug('vc.py:msvc_find_valid_batch_script()') # Find the host platform, target platform, and if present the requested # target platform (host_platform, target_platform,req_target_platform) = get_host_target(env) # If the user hasn't specifically requested a TARGET_ARCH, and # The TARGET_ARCH is amd64 then also try 32 bits if there are no viable # 64 bit tools installed try_target_archs = [target_platform] if not req_target_platform and target_platform in ('amd64','x86_64'): try_target_archs.append('x86') d = None for tp in try_target_archs: # Set to current arch. env['TARGET_ARCH']=tp debug("vc.py:msvc_find_valid_batch_script() trying target_platform:%s"%tp) host_target = (host_platform, tp) if not is_host_target_supported(host_target, version): warn_msg = "host, target = %s not supported for MSVC version %s" % \ (host_target, version) SCons.Warnings.warn(SCons.Warnings.VisualCMissingWarning, warn_msg) arg = _HOST_TARGET_ARCH_TO_BAT_ARCH[host_target] # Try to locate a batch file for this host/target platform combo try: (vc_script,sdk_script) = find_batch_file(env,version,host_platform,tp) debug('vc.py:msvc_find_valid_batch_script() vc_script:%s sdk_script:%s'%(vc_script,sdk_script)) except VisualCException, e: msg = str(e) debug('Caught exception while looking for batch file (%s)' % msg) warn_msg = "VC version %s not installed. " + \ "C/C++ compilers are most likely not set correctly.\n" + \ " Installed versions are: %s" warn_msg = warn_msg % (version, cached_get_installed_vcs()) SCons.Warnings.warn(SCons.Warnings.VisualCMissingWarning, warn_msg) continue # Try to use the located batch file for this host/target platform combo debug('vc.py:msvc_find_valid_batch_script() use_script 2 %s, args:%s\n' % (repr(vc_script), arg)) if vc_script: try: d = script_env(vc_script, args=arg) except BatchFileExecutionError, e: debug('vc.py:msvc_find_valid_batch_script() use_script 3: failed running VC script %s: %s: Error:%s'%(repr(vc_script),arg,e)) vc_script=None if not vc_script and sdk_script: debug('vc.py:msvc_find_valid_batch_script() use_script 4: trying sdk script: %s'%(sdk_script)) try: d = script_env(sdk_script,args=[]) except BatchFileExecutionError,e: debug('vc.py:msvc_find_valid_batch_script() use_script 5: failed running SDK script %s: Error:%s'%(repr(sdk_script),e)) continue elif not vc_script and not sdk_script: debug('vc.py:msvc_find_valid_batch_script() use_script 6: Neither VC script nor SDK script found') continue # If we cannot find a viable installed compiler, reset the TARGET_ARCH # To it's initial value if not d: env['TARGET_ARCH']=req_target_platform return d def msvc_setup_env(env): debug('msvc_setup_env()') version = get_default_version(env) if version is None: warn_msg = "No version of Visual Studio compiler found - C/C++ " \ "compilers most likely not set correctly" SCons.Warnings.warn(SCons.Warnings.VisualCMissingWarning, warn_msg) return None debug('msvc_setup_env: using specified MSVC version %s\n' % repr(version)) # XXX: we set-up both MSVS version for backward # compatibility with the msvs tool env['MSVC_VERSION'] = version env['MSVS_VERSION'] = version env['MSVS'] = {} use_script = env.get('MSVC_USE_SCRIPT', True) if SCons.Util.is_String(use_script): debug('vc.py:msvc_setup_env() use_script 1 %s\n' % repr(use_script)) d = script_env(use_script) elif use_script: d = msvc_find_valid_batch_script(env,version) debug('vc.py:msvc_setup_env() use_script 2 %s\n' % d) if not d: return d else: debug('MSVC_USE_SCRIPT set to False') warn_msg = "MSVC_USE_SCRIPT set to False, assuming environment " \ "set correctly." SCons.Warnings.warn(SCons.Warnings.VisualCMissingWarning, warn_msg) return None for k, v in d.items(): debug('vc.py:msvc_setup_env() env:%s -> %s'%(k,v)) env.PrependENVPath(k, v, delete_existing=True) def msvc_exists(version=None): vcs = cached_get_installed_vcs() if version is None: return len(vcs) > 0 return version in vcs scons-doc-2.3.0/src/engine/SCons/Tool/MSCommon/__init__.py0000644000175000017500000000410112114661557024015 0ustar dktrkranzdktrkranz# # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/MSCommon/__init__.py 2013/03/03 09:48:35 garyo" __doc__ = """ Common functions for Microsoft Visual Studio and Visual C/C++. """ import copy import os import re import subprocess import SCons.Errors import SCons.Platform.win32 import SCons.Util from SCons.Tool.MSCommon.sdk import mssdk_exists, \ mssdk_setup_env from SCons.Tool.MSCommon.vc import msvc_exists, \ msvc_setup_env, \ msvc_setup_env_once from SCons.Tool.MSCommon.vs import get_default_version, \ get_vs_by_version, \ merge_default_version, \ msvs_exists, \ query_versions # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/MSCommon/arch.py0000644000175000017500000000376712114661557023214 0ustar dktrkranzdktrkranz# # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/MSCommon/arch.py 2013/03/03 09:48:35 garyo" __doc__ = """Module to define supported Windows chip architectures. """ import os class ArchDefinition(object): """ A class for defining architecture-specific settings and logic. """ def __init__(self, arch, synonyms=[]): self.arch = arch self.synonyms = synonyms SupportedArchitectureList = [ ArchitectureDefinition( 'x86', ['i386', 'i486', 'i586', 'i686'], ), ArchitectureDefinition( 'x86_64', ['AMD64', 'amd64', 'em64t', 'EM64T', 'x86_64'], ), ArchitectureDefinition( 'ia64', ['IA64'], ), ] SupportedArchitectureMap = {} for a in SupportedArchitectureList: SupportedArchitectureMap[a.arch] = a for s in a.synonyms: SupportedArchitectureMap[s] = a scons-doc-2.3.0/src/engine/SCons/Tool/aixf77.xml0000644000175000017500000000061212114661560022040 0ustar dktrkranzdktrkranz Sets construction variables for the IBM Visual Age f77 Fortran compiler. F77 SHF77 scons-doc-2.3.0/src/engine/SCons/Tool/latex.xml0000644000175000017500000000314112114661560022050 0ustar dktrkranzdktrkranz Sets construction variables for the &latex; utility. LATEX LATEXFLAGS LATEXCOM LATEXCOMSTR The LaTeX structured formatter and typesetter. The command line used to call the LaTeX structured formatter and typesetter. The string displayed when calling the LaTeX structured formatter and typesetter. If this is not set, then &cv-link-LATEXCOM; (the command line) is displayed. env = Environment(LATEXCOMSTR = "Building $TARGET from LaTeX input $SOURCES") General options passed to the LaTeX structured formatter and typesetter. The maximum number of times that LaTeX will be re-run if the .log generated by the &cv-link-LATEXCOM; command indicates that there are undefined references. The default is to try to resolve undefined references by re-running LaTeX up to three times. List of directories that the LaTeX program will search for include directories. The LaTeX implicit dependency scanner will search these directories for \include and \import files. scons-doc-2.3.0/src/engine/SCons/Tool/hplink.xml0000644000175000017500000000060712114661560022224 0ustar dktrkranzdktrkranz Sets construction variables for the linker on HP/UX systems. LINKFLAGS SHLINKFLAGS SHLIBSUFFIX scons-doc-2.3.0/src/engine/SCons/Tool/pdftex.py0000644000175000017500000000730012114661560022056 0ustar dktrkranzdktrkranz"""SCons.Tool.pdftex Tool-specific initialization for pdftex. Generates .pdf files from .tex files There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Tool.Tool() selection method. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/pdftex.py 2013/03/03 09:48:35 garyo" import os import SCons.Action import SCons.Util import SCons.Tool.tex PDFTeXAction = None # This action might be needed more than once if we are dealing with # labels and bibtex. PDFLaTeXAction = None def PDFLaTeXAuxAction(target = None, source= None, env=None): result = SCons.Tool.tex.InternalLaTeXAuxAction( PDFLaTeXAction, target, source, env ) return result def PDFTeXLaTeXFunction(target = None, source= None, env=None): """A builder for TeX and LaTeX that scans the source file to decide the "flavor" of the source and then executes the appropriate program.""" basedir = os.path.split(str(source[0]))[0] abspath = os.path.abspath(basedir) if SCons.Tool.tex.is_LaTeX(source,env,abspath): result = PDFLaTeXAuxAction(target,source,env) if result != 0: SCons.Tool.tex.check_file_error_message(env['PDFLATEX']) else: result = PDFTeXAction(target,source,env) if result != 0: SCons.Tool.tex.check_file_error_message(env['PDFTEX']) return result PDFTeXLaTeXAction = None def generate(env): """Add Builders and construction variables for pdftex to an Environment.""" global PDFTeXAction if PDFTeXAction is None: PDFTeXAction = SCons.Action.Action('$PDFTEXCOM', '$PDFTEXCOMSTR') global PDFLaTeXAction if PDFLaTeXAction is None: PDFLaTeXAction = SCons.Action.Action("$PDFLATEXCOM", "$PDFLATEXCOMSTR") global PDFTeXLaTeXAction if PDFTeXLaTeXAction is None: PDFTeXLaTeXAction = SCons.Action.Action(PDFTeXLaTeXFunction, strfunction=SCons.Tool.tex.TeXLaTeXStrFunction) env.AppendUnique(LATEXSUFFIXES=SCons.Tool.LaTeXSuffixes) import pdf pdf.generate(env) bld = env['BUILDERS']['PDF'] bld.add_action('.tex', PDFTeXLaTeXAction) bld.add_emitter('.tex', SCons.Tool.tex.tex_pdf_emitter) # Add the epstopdf builder after the pdftex builder # so pdftex is the default for no source suffix pdf.generate2(env) SCons.Tool.tex.generate_common(env) def exists(env): SCons.Tool.tex.generate_darwin(env) return env.Detect('pdftex') # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/ilink32.xml0000644000175000017500000000073412114661560022213 0ustar dktrkranzdktrkranz Sets construction variables for the Borland ilink32 linker. LINK LINKFLAGS LINKCOM LIBDIRPREFIX LIBDIRSUFFIX LIBLINKPREFIX LIBLINKSUFFIX scons-doc-2.3.0/src/engine/SCons/Tool/intelc.xml0000644000175000017500000000132012114661560022206 0ustar dktrkranzdktrkranz Sets construction variables for the Intel C/C++ compiler (Linux and Windows, version 7 and later). Calls the &t-gcc; or &t-msvc; (on Linux and Windows, respectively) to set underlying variables. CC CXX LINK AR INTEL_C_COMPILER_VERSION Set by the "intelc" Tool to the major version number of the Intel C compiler selected for use. scons-doc-2.3.0/src/engine/SCons/Tool/rpcgen.xml0000644000175000017500000000615512114661560022221 0ustar dktrkranzdktrkranz Sets construction variables for building with RPCGEN. RPCGEN RPCGENFLAGS RPCGENCLIENTFLAGS RPCGENHEADERFLAGS RPCGENSERVICEFLAGS RPCGENXDRFLAGS Generates an RPC client stub (_clnt.c) file from a specified RPC (.x) source file. Because rpcgen only builds output files in the local directory, the command will be executed in the source file's directory by default. # Builds src/rpcif_clnt.c env.RPCGenClient('src/rpcif.x') Generates an RPC header (.h) file from a specified RPC (.x) source file. Because rpcgen only builds output files in the local directory, the command will be executed in the source file's directory by default. # Builds src/rpcif.h env.RPCGenHeader('src/rpcif.x') Generates an RPC server-skeleton (_svc.c) file from a specified RPC (.x) source file. Because rpcgen only builds output files in the local directory, the command will be executed in the source file's directory by default. # Builds src/rpcif_svc.c env.RPCGenClient('src/rpcif.x') Generates an RPC XDR routine (_xdr.c) file from a specified RPC (.x) source file. Because rpcgen only builds output files in the local directory, the command will be executed in the source file's directory by default. # Builds src/rpcif_xdr.c env.RPCGenClient('src/rpcif.x') The RPC protocol compiler. Options passed to the RPC protocol compiler when generating client side stubs. These are in addition to any flags specified in the &cv-link-RPCGENFLAGS; construction variable. General options passed to the RPC protocol compiler. Options passed to the RPC protocol compiler when generating a header file. These are in addition to any flags specified in the &cv-link-RPCGENFLAGS; construction variable. Options passed to the RPC protocol compiler when generating server side stubs. These are in addition to any flags specified in the &cv-link-RPCGENFLAGS; construction variable. Options passed to the RPC protocol compiler when generating XDR routines. These are in addition to any flags specified in the &cv-link-RPCGENFLAGS; construction variable. scons-doc-2.3.0/src/engine/SCons/Tool/rmic.xml0000644000175000017500000000423712114661560021674 0ustar dktrkranzdktrkranz Sets construction variables for the &rmic; utility. RMIC RMICFLAGS RMICCOM JAVACLASSSUFFIX RMICCOMSTR Builds stub and skeleton class files for remote objects from Java .class files. The target is a directory relative to which the stub and skeleton class files will be written. The source can be the names of .class files, or the objects return from the &b-Java; builder method. If the construction variable &cv-link-JAVACLASSDIR; is set, either in the environment or in the call to the &b-RMIC; builder method itself, then the value of the variable will be stripped from the beginning of any .class file names. classes = env.Java(target = 'classdir', source = 'src') env.RMIC(target = 'outdir1', source = classes) env.RMIC(target = 'outdir2', source = ['package/foo.class', 'package/bar.class']) env.RMIC(target = 'outdir3', source = ['classes/foo.class', 'classes/bar.class'], JAVACLASSDIR = 'classes') The Java RMI stub compiler. The command line used to compile stub and skeleton class files from Java classes that contain RMI implementations. Any options specified in the &cv-link-RMICFLAGS; construction variable are included on this command line. The string displayed when compiling stub and skeleton class files from Java classes that contain RMI implementations. If this is not set, then &cv-link-RMICCOM; (the command line) is displayed. env = Environment(RMICCOMSTR = "Generating stub/skeleton class files $TARGETS from $SOURCES") General options passed to the Java RMI stub compiler. scons-doc-2.3.0/src/engine/SCons/Tool/sunf90.py0000644000175000017500000000422012114661560021706 0ustar dktrkranzdktrkranz"""SCons.Tool.sunf90 Tool-specific initialization for sunf90, the Sun Studio F90 compiler. There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Tool.Tool() selection method. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/sunf90.py 2013/03/03 09:48:35 garyo" import SCons.Util from FortranCommon import add_all_to_env compilers = ['sunf90', 'f90'] def generate(env): """Add Builders and construction variables for sun f90 compiler to an Environment.""" add_all_to_env(env) fcomp = env.Detect(compilers) or 'f90' env['FORTRAN'] = fcomp env['F90'] = fcomp env['SHFORTRAN'] = '$FORTRAN' env['SHF90'] = '$F90' env['SHFORTRANFLAGS'] = SCons.Util.CLVar('$FORTRANFLAGS -KPIC') env['SHF90FLAGS'] = SCons.Util.CLVar('$F90FLAGS -KPIC') def exists(env): return env.Detect(compilers) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/f77.xml0000644000175000017500000001722012114661560021341 0ustar dktrkranzdktrkranz Set construction variables for generic POSIX Fortran 77 compilers. F77 F77FLAGS F77COM F77PPCOM F77FILESUFFIXES F77PPFILESUFFIXES FORTRAN FORTRANFLAGS FORTRANCOM SHF77 SHF77FLAGS SHF77COM SHF77PPCOM SHFORTRAN SHFORTRANFLAGS SHFORTRANCOM SHFORTRANPPCOM _F77INCFLAGS F77COMSTR F77PPCOMSTR FORTRANCOMSTR FORTRANPPCOMSTR SHF77COMSTR SHF77PPCOMSTR SHFORTRANCOMSTR SHFORTRANPPCOMSTR The Fortran 77 compiler. You should normally set the &cv-link-FORTRAN; variable, which specifies the default Fortran compiler for all Fortran versions. You only need to set &cv-link-F77; if you need to use a specific compiler or compiler version for Fortran 77 files. The command line used to compile a Fortran 77 source file to an object file. You only need to set &cv-link-F77COM; if you need to use a specific command line for Fortran 77 files. You should normally set the &cv-link-FORTRANCOM; variable, which specifies the default command line for all Fortran versions. The list of file extensions for which the F77 dialect will be used. By default, this is ['.f77'] The list of file extensions for which the compilation + preprocessor pass for F77 dialect will be used. By default, this is empty The string displayed when a Fortran 77 source file is compiled to an object file. If this is not set, then &cv-link-F77COM; or &cv-link-FORTRANCOM; (the command line) is displayed. General user-specified options that are passed to the Fortran 77 compiler. Note that this variable does not contain (or similar) include search path options that scons generates automatically from &cv-link-F77PATH;. See &cv-link-_F77INCFLAGS; below, for the variable that expands to those options. You only need to set &cv-link-F77FLAGS; if you need to define specific user options for Fortran 77 files. You should normally set the &cv-link-FORTRANFLAGS; variable, which specifies the user-specified options passed to the default Fortran compiler for all Fortran versions. An automatically-generated construction variable containing the Fortran 77 compiler command-line options for specifying directories to be searched for include files. The value of &cv-link-_F77INCFLAGS; is created by appending &cv-link-INCPREFIX; and &cv-link-INCSUFFIX; to the beginning and end of each directory in &cv-link-F77PATH;. The list of directories that the Fortran 77 compiler will search for include directories. The implicit dependency scanner will search these directories for include files. Don't explicitly put include directory arguments in &cv-link-F77FLAGS; because the result will be non-portable and the directories will not be searched by the dependency scanner. Note: directory names in &cv-link-F77PATH; will be looked-up relative to the SConscript directory when they are used in a command. To force &scons; to look-up a directory relative to the root of the source tree use #: You only need to set &cv-link-F77PATH; if you need to define a specific include path for Fortran 77 files. You should normally set the &cv-link-FORTRANPATH; variable, which specifies the include path for the default Fortran compiler for all Fortran versions. env = Environment(F77PATH='#/include') The directory look-up can also be forced using the &Dir;() function: include = Dir('include') env = Environment(F77PATH=include) The directory list will be added to command lines through the automatically-generated &cv-link-_F77INCFLAGS; construction variable, which is constructed by appending the values of the &cv-link-INCPREFIX; and &cv-link-INCSUFFIX; construction variables to the beginning and end of each directory in &cv-link-F77PATH;. Any command lines you define that need the F77PATH directory list should include &cv-link-_F77INCFLAGS;: env = Environment(F77COM="my_compiler $_F77INCFLAGS -c -o $TARGET $SOURCE") The command line used to compile a Fortran 77 source file to an object file after first running the file through the C preprocessor. Any options specified in the &cv-link-F77FLAGS; and &cv-link-CPPFLAGS; construction variables are included on this command line. You only need to set &cv-link-F77PPCOM; if you need to use a specific C-preprocessor command line for Fortran 77 files. You should normally set the &cv-link-FORTRANPPCOM; variable, which specifies the default C-preprocessor command line for all Fortran versions. The string displayed when a Fortran 77 source file is compiled to an object file after first running the file through the C preprocessor. If this is not set, then &cv-link-F77PPCOM; or &cv-link-FORTRANPPCOM; (the command line) is displayed. The Fortran 77 compiler used for generating shared-library objects. You should normally set the &cv-link-SHFORTRAN; variable, which specifies the default Fortran compiler for all Fortran versions. You only need to set &cv-link-SHF77; if you need to use a specific compiler or compiler version for Fortran 77 files. The command line used to compile a Fortran 77 source file to a shared-library object file. You only need to set &cv-link-SHF77COM; if you need to use a specific command line for Fortran 77 files. You should normally set the &cv-link-SHFORTRANCOM; variable, which specifies the default command line for all Fortran versions. The string displayed when a Fortran 77 source file is compiled to a shared-library object file. If this is not set, then &cv-link-SHF77COM; or &cv-link-SHFORTRANCOM; (the command line) is displayed. Options that are passed to the Fortran 77 compiler to generated shared-library objects. You only need to set &cv-link-SHF77FLAGS; if you need to define specific user options for Fortran 77 files. You should normally set the &cv-link-SHFORTRANFLAGS; variable, which specifies the user-specified options passed to the default Fortran compiler for all Fortran versions. The command line used to compile a Fortran 77 source file to a shared-library object file after first running the file through the C preprocessor. Any options specified in the &cv-link-SHF77FLAGS; and &cv-link-CPPFLAGS; construction variables are included on this command line. You only need to set &cv-link-SHF77PPCOM; if you need to use a specific C-preprocessor command line for Fortran 77 files. You should normally set the &cv-link-SHFORTRANPPCOM; variable, which specifies the default C-preprocessor command line for all Fortran versions. The string displayed when a Fortran 77 source file is compiled to a shared-library object file after first running the file through the C preprocessor. If this is not set, then &cv-link-SHF77PPCOM; or &cv-link-SHFORTRANPPCOM; (the command line) is displayed. scons-doc-2.3.0/src/engine/SCons/Tool/c++.xml0000644000175000017500000000440712114661560021311 0ustar dktrkranzdktrkranz Sets construction variables for generic POSIX C++ compilers. CXX CXXFLAGS CXXCOM SHCXX SHCXXFLAGS SHCXXCOM CPPDEFPREFIX CPPDEFSUFFIX INCPREFIX INCSUFFIX SHOBJSUFFIX OBJSUFFIX CXXFILESUFFIX CXXCOMSTR The C++ compiler. The command line used to compile a C++ source file to an object file. Any options specified in the &cv-link-CXXFLAGS; and &cv-link-CPPFLAGS; construction variables are included on this command line. The string displayed when a C++ source file is compiled to a (static) object file. If this is not set, then &cv-link-CXXCOM; (the command line) is displayed. env = Environment(CXXCOMSTR = "Compiling static object $TARGET") General options that are passed to the C++ compiler. By default, this includes the value of &cv-link-CCFLAGS;, so that setting &cv-CCFLAGS; affects both C and C++ compilation. If you want to add C++-specific flags, you must set or override the value of &cv-link-CXXFLAGS;. The C++ compiler used for generating shared-library objects. The command line used to compile a C++ source file to a shared-library object file. Any options specified in the &cv-link-SHCXXFLAGS; and &cv-link-CPPFLAGS; construction variables are included on this command line. The string displayed when a C++ source file is compiled to a shared object file. If this is not set, then &cv-link-SHCXXCOM; (the command line) is displayed. env = Environment(SHCXXCOMSTR = "Compiling shared object $TARGET") Options that are passed to the C++ compiler to generate shared-library objects. scons-doc-2.3.0/src/engine/SCons/Tool/SCCS.xml0000644000175000017500000000343312114661557021500 0ustar dktrkranzdktrkranz Sets construction variables for interacting with the Source Code Control System. SCCS SCCSFLAGS SCCSGETFLAGS SCCSCOM SCCSCOMSTR The SCCS executable. The command line used to fetch source files from SCCS. The string displayed when fetching a source file from a CVS repository. If this is not set, then &cv-link-SCCSCOM; (the command line) is displayed. General options that are passed to SCCS. Options that are passed specifically to the SCCS "get" subcommand. This can be set, for example, to to check out editable files from SCCS. () A factory function that returns a Builder object to be used to fetch source files from SCCS. The returned Builder is intended to be passed to the &f-link-SourceCode; function. Example: env.SourceCode('.', env.SCCS()) Note that &scons; will fetch source files from SCCS subdirectories automatically, so configuring SCCS as demonstrated in the above example should only be necessary if you are fetching from s.SCCS files in the same directory as the source files, or if you need to explicitly specify SCCS for a specific subdirectory. scons-doc-2.3.0/src/engine/SCons/Tool/c++.py0000644000175000017500000000650212114661560021137 0ustar dktrkranzdktrkranz"""SCons.Tool.c++ Tool-specific initialization for generic Posix C++ compilers. There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Tool.Tool() selection method. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/c++.py 2013/03/03 09:48:35 garyo" import os.path import SCons.Tool import SCons.Defaults import SCons.Util compilers = ['CC', 'c++'] CXXSuffixes = ['.cpp', '.cc', '.cxx', '.c++', '.C++', '.mm'] if SCons.Util.case_sensitive_suffixes('.c', '.C'): CXXSuffixes.append('.C') def iscplusplus(source): if not source: # Source might be None for unusual cases like SConf. return 0 for s in source: if s.sources: ext = os.path.splitext(str(s.sources[0]))[1] if ext in CXXSuffixes: return 1 return 0 def generate(env): """ Add Builders and construction variables for Visual Age C++ compilers to an Environment. """ import SCons.Tool import SCons.Tool.cc static_obj, shared_obj = SCons.Tool.createObjBuilders(env) for suffix in CXXSuffixes: static_obj.add_action(suffix, SCons.Defaults.CXXAction) shared_obj.add_action(suffix, SCons.Defaults.ShCXXAction) static_obj.add_emitter(suffix, SCons.Defaults.StaticObjectEmitter) shared_obj.add_emitter(suffix, SCons.Defaults.SharedObjectEmitter) SCons.Tool.cc.add_common_cc_variables(env) env['CXX'] = 'c++' env['CXXFLAGS'] = SCons.Util.CLVar('') env['CXXCOM'] = '$CXX -o $TARGET -c $CXXFLAGS $CCFLAGS $_CCCOMCOM $SOURCES' env['SHCXX'] = '$CXX' env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS') env['SHCXXCOM'] = '$SHCXX -o $TARGET -c $SHCXXFLAGS $SHCCFLAGS $_CCCOMCOM $SOURCES' env['CPPDEFPREFIX'] = '-D' env['CPPDEFSUFFIX'] = '' env['INCPREFIX'] = '-I' env['INCSUFFIX'] = '' env['SHOBJSUFFIX'] = '.os' env['OBJSUFFIX'] = '.o' env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME'] = 0 env['CXXFILESUFFIX'] = '.cc' def exists(env): return env.Detect(compilers) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/CVS.py0000644000175000017500000000555412114661557021236 0ustar dktrkranzdktrkranz"""SCons.Tool.CVS.py Tool-specific initialization for CVS. There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Tool.Tool() selection method. """ # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/CVS.py 2013/03/03 09:48:35 garyo" import SCons.Action import SCons.Builder import SCons.Util def generate(env): """Add a Builder factory function and construction variables for CVS to an Environment.""" def CVSFactory(repos, module='', env=env): """ """ import SCons.Warnings as W W.warn(W.DeprecatedSourceCodeWarning, """The CVS() factory is deprecated and there is no replacement.""") # fail if repos is not an absolute path name? if module != '': # Don't use os.path.join() because the name we fetch might # be across a network and must use POSIX slashes as separators. module = module + '/' env['CVSCOM'] = '$CVS $CVSFLAGS co $CVSCOFLAGS -d ${TARGET.dir} $CVSMODULE${TARGET.posix}' act = SCons.Action.Action('$CVSCOM', '$CVSCOMSTR') return SCons.Builder.Builder(action = act, env = env, CVSREPOSITORY = repos, CVSMODULE = module) #setattr(env, 'CVS', CVSFactory) env.CVS = CVSFactory env['CVS'] = 'cvs' env['CVSFLAGS'] = SCons.Util.CLVar('-d $CVSREPOSITORY') env['CVSCOFLAGS'] = SCons.Util.CLVar('') env['CVSCOM'] = '$CVS $CVSFLAGS co $CVSCOFLAGS ${TARGET.posix}' def exists(env): return env.Detect('cvs') # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/qt.xml0000644000175000017500000002032112114661560021356 0ustar dktrkranzdktrkranz Sets construction variables for building Qt applications. QTDIR QT_BINPATH QT_CPPPATH QT_LIBPATH QT_MOC QT_UIC QT_LIB QT_AUTOSCAN QT_UICIMPLFLAGS QT_UICDECLFLAGS QT_MOCFROMHFLAGS QT_MOCFROMCXXFLAGS QT_UICDECLPREFIX QT_UICDECLSUFFIX QT_UICIMPLPREFIX QT_UICIMPLSUFFIX QT_MOCHPREFIX QT_MOCHSUFFIX QT_MOCCXXPREFIX QT_MOCCXXSUFFIX QT_UISUFFIX QT_UICCOM QT_MOCFROMHCOM QT_MOCFROMCXXCOM Builds an output file from a moc input file. Moc input files are either header files or cxx files. This builder is only available after using the tool 'qt'. See the &cv-link-QTDIR; variable for more information. Example: env.Moc('foo.h') # generates moc_foo.cc env.Moc('foo.cpp') # generates foo.moc Builds a header file, an implementation file and a moc file from an ui file. and returns the corresponding nodes in the above order. This builder is only available after using the tool 'qt'. Note: you can specify .ui files directly as source files to the &b-Program;, &b-Library; and &b-SharedLibrary; builders without using this builder. Using this builder lets you override the standard naming conventions (be careful: prefixes are always prepended to names of built files; if you don't want prefixes, you may set them to ``). See the &cv-link-QTDIR; variable for more information. Example: env.Uic('foo.ui') # -> ['foo.h', 'uic_foo.cc', 'moc_foo.cc'] env.Uic(target = Split('include/foo.h gen/uicfoo.cc gen/mocfoo.cc'), source = 'foo.ui') # -> ['include/foo.h', 'gen/uicfoo.cc', 'gen/mocfoo.cc'] The qt tool tries to take this from os.environ. It also initializes all QT_* construction variables listed below. (Note that all paths are constructed with python's os.path.join() method, but are listed here with the '/' separator for easier reading.) In addition, the construction environment variables &cv-link-CPPPATH;, &cv-link-LIBPATH; and &cv-link-LIBS; may be modified and the variables &cv-link-PROGEMITTER;, &cv-link-SHLIBEMITTER; and &cv-link-LIBEMITTER; are modified. Because the build-performance is affected when using this tool, you have to explicitly specify it at Environment creation: Environment(tools=['default','qt']) The qt tool supports the following operations: Automatic moc file generation from header files. You do not have to specify moc files explicitly, the tool does it for you. However, there are a few preconditions to do so: Your header file must have the same filebase as your implementation file and must stay in the same directory. It must have one of the suffixes .h, .hpp, .H, .hxx, .hh. You can turn off automatic moc file generation by setting QT_AUTOSCAN to 0. See also the corresponding &b-Moc(); builder method. Automatic moc file generation from cxx files. As stated in the qt documentation, include the moc file at the end of the cxx file. Note that you have to include the file, which is generated by the transformation ${QT_MOCCXXPREFIX}<basename>${QT_MOCCXXSUFFIX}, by default <basename>.moc. A warning is generated after building the moc file, if you do not include the correct file. If you are using VariantDir, you may need to specify duplicate=1. You can turn off automatic moc file generation by setting QT_AUTOSCAN to 0. See also the corresponding &b-Moc; builder method. Automatic handling of .ui files. The implementation files generated from .ui files are handled much the same as yacc or lex files. Each .ui file given as a source of Program, Library or SharedLibrary will generate three files, the declaration file, the implementation file and a moc file. Because there are also generated headers, you may need to specify duplicate=1 in calls to VariantDir. See also the corresponding &b-Uic; builder method. Turn off scanning for mocable files. Use the Moc Builder to explicitly specify files to run moc on. The path where the qt binaries are installed. The default value is '&cv-link-QTDIR;/bin'. The path where the qt header files are installed. The default value is '&cv-link-QTDIR;/include'. Note: If you set this variable to None, the tool won't change the &cv-link-CPPPATH; construction variable. Prints lots of debugging information while scanning for moc files. Default value is 'qt'. You may want to set this to 'qt-mt'. Note: If you set this variable to None, the tool won't change the &cv-link-LIBS; variable. The path where the qt libraries are installed. The default value is '&cv-link-QTDIR;/lib'. Note: If you set this variable to None, the tool won't change the &cv-link-LIBPATH; construction variable. Default value is '&cv-link-QT_BINPATH;/moc'. Default value is ''. Prefix for moc output files, when source is a cxx file. Default value is '.moc'. Suffix for moc output files, when source is a cxx file. Default value is '-i'. These flags are passed to moc, when moccing a C++ file. Command to generate a moc file from a cpp file. The string displayed when generating a moc file from a cpp file. If this is not set, then &cv-link-QT_MOCFROMCXXCOM; (the command line) is displayed. Command to generate a moc file from a header. The string displayed when generating a moc file from a cpp file. If this is not set, then &cv-link-QT_MOCFROMHCOM; (the command line) is displayed. Default value is ''. These flags are passed to moc, when moccing a header file. Default value is 'moc_'. Prefix for moc output files, when source is a header. Default value is '&cv-link-CXXFILESUFFIX;'. Suffix for moc output files, when source is a header. Default value is '&cv-link-QT_BINPATH;/uic'. Command to generate header files from .ui files. The string displayed when generating header files from .ui files. If this is not set, then &cv-link-QT_UICCOM; (the command line) is displayed. Default value is ''. These flags are passed to uic, when creating a a h file from a .ui file. Default value is ''. Prefix for uic generated header files. Default value is '.h'. Suffix for uic generated header files. Default value is ''. These flags are passed to uic, when creating a cxx file from a .ui file. Default value is 'uic_'. Prefix for uic generated implementation files. Default value is '&cv-link-CXXFILESUFFIX;'. Suffix for uic generated implementation files. Default value is '.ui'. Suffix of designer input files. scons-doc-2.3.0/src/engine/SCons/Tool/xgettext.xml0000644000175000017500000002431512114661560022615 0ustar dktrkranzdktrkranz This scons tool is a part of scons &t-link-gettext; toolset. It provides scons interface to xgettext(1) program, which extracts internationalized messages from source code. The tool provides &b-POTUpdate; builder to make PO Template files. POTSUFFIX POTUPDATE_ALIAS XGETTEXTCOM XGETTEXTCOMSTR XGETTEXTFLAGS XGETTEXTFROM XGETTEXTFROMPREFIX XGETTEXTFROMSUFFIX XGETTEXTPATH XGETTEXTPATHPREFIX XGETTEXTPATHSUFFIX _XGETTEXTDOMAIN _XGETTEXTFROMFLAGS _XGETTEXTPATHFLAGS POTDOMAIN The builder belongs to &t-link-xgettext; tool. The builder updates target POT file if exists or creates one if it doesn't. The node is not built by default (i.e. it is Ignored from '.'), but only on demand (i.e. when given POT file is required or when special alias is invoked). This builder adds its targe node (messages.pot, say) to a special alias (pot-update by default, see &cv-link-POTUPDATE_ALIAS;) so you can update/create them easily with scons pot-update. The file is not written until there is no real change in internationalized messages (or in comments that enter POT file). You may see xgettext(1) being invoked by the &t-link-xgettext; tool even if there is no real change in internationalized messages (so the POT file is not being updated). This happens every time a source file has changed. In such case we invoke xgettext(1) and compare its output with the content of POT file to decide whether the file should be updated or not. Example 1. Let's create po/ directory and place following SConstruct script there: # SConstruct in 'po/' subdir env = Environment( tools = ['default', 'xgettext'] ) env.POTUpdate(['foo'], ['../a.cpp', '../b.cpp']) env.POTUpdate(['bar'], ['../c.cpp', '../d.cpp']) Then invoke scons few times: user@host:$ scons # Does not create foo.pot nor bar.pot user@host:$ scons foo.pot # Updates or creates foo.pot user@host:$ scons pot-update # Updates or creates foo.pot and bar.pot user@host:$ scons -c # Does not clean foo.pot nor bar.pot. the results shall be as the comments above say. Example 2. The &b-POTUpdate; builder may be used with no target specified, in which case default target messages.pot will be used. The default target may also be overriden by setting &cv-link-POTDOMAIN; construction variable or providing it as an override to &b-POTUpdate; builder: # SConstruct script env = Environment( tools = ['default', 'xgettext'] ) env['POTDOMAIN'] = "foo" env.POTUpdate(source = ["a.cpp", "b.cpp"]) # Creates foo.pot ... env.POTUpdate(POTDOMAIN = "bar", source = ["c.cpp", "d.cpp"]) # and bar.pot Example 3. The sources may be specified within separate file, for example POTFILES.in: # POTFILES.in in 'po/' subdirectory ../a.cpp ../b.cpp # end of file The name of the file (POTFILES.in) containing the list of sources is provided via &cv-link-XGETTEXTFROM;: # SConstruct file in 'po/' subdirectory env = Environment( tools = ['default', 'xgettext'] ) env.POTUpdate(XGETTEXTFROM = 'POTFILES.in') Example 4. You may use &cv-link-XGETTEXTPATH; to define source search path. Assume, for example, that you have files a.cpp, b.cpp, po/SConstruct, po/POTFILES.in. Then your POT-related files could look as below: # POTFILES.in in 'po/' subdirectory a.cpp b.cpp # end of file # SConstruct file in 'po/' subdirectory env = Environment( tools = ['default', 'xgettext'] ) env.POTUpdate(XGETTEXTFROM = 'POTFILES.in', XGETTEXTPATH='../') Example 5. Multiple search directories may be defined within a list, i.e. XGETTEXTPATH = ['dir1', 'dir2', ...]. The order in the list determines the search order of source files. The path to the first file found is used. Let's create 0/1/po/SConstruct script: # SConstruct file in '0/1/po/' subdirectory env = Environment( tools = ['default', 'xgettext'] ) env.POTUpdate(XGETTEXTFROM = 'POTFILES.in', XGETTEXTPATH=['../', '../../']) and 0/1/po/POTFILES.in: # POTFILES.in in '0/1/po/' subdirectory a.cpp # end of file Write two *.cpp files, the first one is 0/a.cpp: /* 0/a.cpp */ gettext("Hello from ../../a.cpp") and the second is 0/1/a.cpp: /* 0/1/a.cpp */ gettext("Hello from ../a.cpp") then run scons. You'll obtain 0/1/po/messages.pot with the message "Hello from ../a.cpp". When you reverse order in $XGETTEXTFOM, i.e. when you write SConscript as # SConstruct file in '0/1/po/' subdirectory env = Environment( tools = ['default', 'xgettext'] ) env.POTUpdate(XGETTEXTFROM = 'POTFILES.in', XGETTEXTPATH=['../../', '../']) then the messages.pot will contain msgid "Hello from ../../a.cpp" line and not msgid "Hello from ../a.cpp". Suffix used for PO Template files (default: '.pot'). See &t-link-xgettext; tool and &b-link-POTUpdate; builder. Name of the common phony target for all PO Templates created with &b-link-POUpdate; (default: 'pot-update'). See &t-link-xgettext; tool and &b-link-POTUpdate; builder. Path to xgettext(1) program (found via Detect()). See &t-link-xgettext; tool and &b-link-POTUpdate; builder. Complete xgettext command line. See &t-link-xgettext; tool and &b-link-POTUpdate; builder. A string that is shown when xgettext(1) command is invoked (default: '', which means "print &cv-link-XGETTEXTCOM;"). See &t-link-xgettext; tool and &b-link-POTUpdate; builder. Additional flags to xgettext(1). See &t-link-xgettext; tool and &b-link-POTUpdate; builder. Name of file containing list of xgettext(1)'s source files. Autotools' users know this as POTFILES.in so they will in most cases set XGETTEXTFROM="POTFILES.in" here. The &cv-XGETTEXTFROM; files have same syntax and semantics as the well known GNU POTFILES.in. See &t-link-xgettext; tool and &b-link-POTUpdate; builder. List of directories, there xgettext(1) will look for source files (default: []). This variable works only together with &cv-link-XGETTEXTFROM; See also &t-link-xgettext; tool and &b-link-POTUpdate; builder. This flag is used to add single search path to xgettext(1)'s commandline (default: '-D'). (default: '') This flag is used to add single &cv-link-XGETTEXTFROM; file to xgettext(1)'s commandline (default: '-f'). (default: '') Internal "macro". Generates xgettext domain name form source and target (default: '${TARGET.filebase}'). Internal "macro". Genrates list of -D<dir> flags from the &cv-link-XGETTEXTPATH; list. Internal "macro". Generates list of -f<file> flags from &cv-link-XGETTEXTFROM;. scons-doc-2.3.0/src/engine/SCons/Tool/dmd.xml0000644000175000017500000000140312114661560021476 0ustar dktrkranzdktrkranz Sets construction variables for D language compilers (the Digital Mars D compiler, or GDC). scons-doc-2.3.0/src/engine/SCons/Tool/applelink.py0000644000175000017500000000545712114661560022556 0ustar dktrkranzdktrkranz"""SCons.Tool.applelink Tool-specific initialization for the Apple gnu-like linker. There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Tool.Tool() selection method. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/applelink.py 2013/03/03 09:48:35 garyo" import SCons.Util # Even though the Mac is based on the GNU toolchain, it doesn't understand # the -rpath option, so we use the "link" tool instead of "gnulink". import link def generate(env): """Add Builders and construction variables for applelink to an Environment.""" link.generate(env) env['FRAMEWORKPATHPREFIX'] = '-F' env['_FRAMEWORKPATH'] = '${_concat(FRAMEWORKPATHPREFIX, FRAMEWORKPATH, "", __env__)}' env['_FRAMEWORKS'] = '${_concat("-framework ", FRAMEWORKS, "", __env__)}' env['LINKCOM'] = env['LINKCOM'] + ' $_FRAMEWORKPATH $_FRAMEWORKS $FRAMEWORKSFLAGS' env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -dynamiclib') env['SHLINKCOM'] = env['SHLINKCOM'] + ' $_FRAMEWORKPATH $_FRAMEWORKS $FRAMEWORKSFLAGS' # override the default for loadable modules, which are different # on OS X than dynamic shared libs. echoing what XCode does for # pre/suffixes: env['LDMODULEPREFIX'] = '' env['LDMODULESUFFIX'] = '' env['LDMODULEFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -bundle') env['LDMODULECOM'] = '$LDMODULE -o ${TARGET} $LDMODULEFLAGS $SOURCES $_LIBDIRFLAGS $_LIBFLAGS $_FRAMEWORKPATH $_FRAMEWORKS $FRAMEWORKSFLAGS' def exists(env): return env['PLATFORM'] == 'darwin' # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/sgiar.py0000644000175000017500000000503612114661560021675 0ustar dktrkranzdktrkranz"""SCons.Tool.sgiar Tool-specific initialization for SGI ar (library archive). If CC exists, static libraries should be built with it, so the prelinker has a chance to resolve C++ template instantiations. There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Tool.Tool() selection method. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/sgiar.py 2013/03/03 09:48:35 garyo" import SCons.Defaults import SCons.Tool import SCons.Util def generate(env): """Add Builders and construction variables for ar to an Environment.""" SCons.Tool.createStaticLibBuilder(env) if env.Detect('CC'): env['AR'] = 'CC' env['ARFLAGS'] = SCons.Util.CLVar('-ar') env['ARCOM'] = '$AR $ARFLAGS -o $TARGET $SOURCES' else: env['AR'] = 'ar' env['ARFLAGS'] = SCons.Util.CLVar('r') env['ARCOM'] = '$AR $ARFLAGS $TARGET $SOURCES' env['SHLINK'] = '$LINK' env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -shared') env['SHLINKCOM'] = '$SHLINK $SHLINKFLAGS -o $TARGET $SOURCES $_LIBDIRFLAGS $_LIBFLAGS' env['LIBPREFIX'] = 'lib' env['LIBSUFFIX'] = '.a' def exists(env): return env.Detect('CC') or env.Detect('ar') # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/sgic++.py0000644000175000017500000000401412114661560021636 0ustar dktrkranzdktrkranz"""SCons.Tool.sgic++ Tool-specific initialization for MIPSpro C++ on SGI. There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Tool.Tool() selection method. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/sgic++.py 2013/03/03 09:48:35 garyo" import SCons.Util cplusplus = __import__('c++', globals(), locals(), []) def generate(env): """Add Builders and construction variables for SGI MIPS C++ to an Environment.""" cplusplus.generate(env) env['CXX'] = 'CC' env['CXXFLAGS'] = SCons.Util.CLVar('-LANG:std') env['SHCXX'] = '$CXX' env['SHOBJSUFFIX'] = '.o' env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME'] = 1 def exists(env): return env.Detect('CC') # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/msgmerge.py0000644000175000017500000001030512114661560022371 0ustar dktrkranzdktrkranz""" msgmerget tool Tool specific initialization for `msgmerge` tool. """ # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/msgmerge.py 2013/03/03 09:48:35 garyo" ############################################################################# def _update_or_init_po_files(target, source, env): """ Action function for `POUpdate` builder """ import SCons.Action from SCons.Tool.GettextCommon import _init_po_files for tgt in target: if tgt.rexists(): action = SCons.Action.Action('$MSGMERGECOM', '$MSGMERGECOMSTR') else: action = _init_po_files status = action([tgt], source, env) if status : return status return 0 ############################################################################# ############################################################################# def _POUpdateBuilder(env, **kw): """ Create an object of `POUpdate` builder """ import SCons.Action from SCons.Tool.GettextCommon import _POFileBuilder action = SCons.Action.Action(_update_or_init_po_files, None) return _POFileBuilder(env, action=action, target_alias='$POUPDATE_ALIAS') ############################################################################# ############################################################################# from SCons.Environment import _null ############################################################################# def _POUpdateBuilderWrapper(env, target=None, source=_null, **kw): """ Wrapper for `POUpdate` builder - make user's life easier """ if source is _null: if 'POTDOMAIN' in kw: domain = kw['POTDOMAIN'] elif env.has_key('POTDOMAIN') and env['POTDOMAIN']: domain = env['POTDOMAIN'] else: domain = 'messages' source = [ domain ] # NOTE: Suffix shall be appended automatically return env._POUpdateBuilder(target, source, **kw) ############################################################################# ############################################################################# def generate(env,**kw): """ Generate the `xgettext` tool """ from SCons.Tool.GettextCommon import _detect_msgmerge try: env['MSGMERGE'] = _detect_msgmerge(env) except: env['MSGMERGE'] = 'msgmerge' env.SetDefault( POTSUFFIX = ['.pot'], POSUFFIX = ['.po'], MSGMERGECOM = '$MSGMERGE $MSGMERGEFLAGS --update $TARGET $SOURCE', MSGMERGECOMSTR = '', MSGMERGEFLAGS = [ ], POUPDATE_ALIAS = 'po-update' ) env.Append(BUILDERS = { '_POUpdateBuilder':_POUpdateBuilder(env) }) env.AddMethod(_POUpdateBuilderWrapper, 'POUpdate') env.AlwaysBuild(env.Alias('$POUPDATE_ALIAS')) ############################################################################# ############################################################################# def exists(env): """ Check if the tool exists """ from SCons.Tool.GettextCommon import _msgmerge_exists try: return _msgmerge_exists(env) except: return False ############################################################################# # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/packaging.xml0000644000175000017500000000263012114661560022661 0ustar dktrkranzdktrkranz A framework for building binary and source packages. Builds a Binary Package of the given source files. env.Package(source = FindInstalledFiles()) The Java archive tool. The directory to which the Java archive tool should change (using the option). The command line used to call the Java archive tool. The string displayed when the Java archive tool is called If this is not set, then &cv-JARCOM; (the command line) is displayed. env = Environment(JARCOMSTR = "JARchiving $SOURCES into $TARGET") General options passed to the Java archive tool. By default this is set to to create the necessary jar file. The suffix for Java archives: .jar by default. scons-doc-2.3.0/src/engine/SCons/Tool/sunar.py0000644000175000017500000000501312114661560021713 0ustar dktrkranzdktrkranz"""engine.SCons.Tool.sunar Tool-specific initialization for Solaris (Forte) ar (library archive). If CC exists, static libraries should be built with it, so that template instantians can be resolved. There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Tool.Tool() selection method. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/sunar.py 2013/03/03 09:48:35 garyo" import SCons.Defaults import SCons.Tool import SCons.Util def generate(env): """Add Builders and construction variables for ar to an Environment.""" SCons.Tool.createStaticLibBuilder(env) if env.Detect('CC'): env['AR'] = 'CC' env['ARFLAGS'] = SCons.Util.CLVar('-xar') env['ARCOM'] = '$AR $ARFLAGS -o $TARGET $SOURCES' else: env['AR'] = 'ar' env['ARFLAGS'] = SCons.Util.CLVar('r') env['ARCOM'] = '$AR $ARFLAGS $TARGET $SOURCES' env['SHLINK'] = '$LINK' env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -G') env['SHLINKCOM'] = '$SHLINK $SHLINKFLAGS -o $TARGET $SOURCES $_LIBDIRFLAGS $_LIBFLAGS' env['LIBPREFIX'] = 'lib' env['LIBSUFFIX'] = '.a' def exists(env): return env.Detect('CC') or env.Detect('ar') # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/sunf90.xml0000644000175000017500000000063512114661560022064 0ustar dktrkranzdktrkranz Set construction variables for the Sun &f90; Fortran compiler. FORTRAN F90 SHFORTRAN SHF90 SHFORTRANFLAGS SHF90FLAGS scons-doc-2.3.0/src/engine/SCons/Tool/dvi.xml0000644000175000017500000000333712114661560021524 0ustar dktrkranzdktrkranz Attaches the &b-DVI; builder to the construction environment. Builds a .dvi file from a .tex, .ltx or .latex input file. If the source file suffix is .tex, &scons; will examine the contents of the file; if the string \documentclass or \documentstyle is found, the file is assumed to be a LaTeX file and the target is built by invoking the &cv-link-LATEXCOM; command line; otherwise, the &cv-link-TEXCOM; command line is used. If the file is a LaTeX file, the &b-DVI; builder method will also examine the contents of the .aux file and invoke the &cv-link-BIBTEX; command line if the string bibdata is found, start &cv-link-MAKEINDEX; to generate an index if a .ind file is found and will examine the contents .log file and re-run the &cv-link-LATEXCOM; command if the log file says it is necessary. The suffix .dvi (hard-coded within TeX itself) is automatically added to the target if it is not already present. Examples: # builds from aaa.tex env.DVI(target = 'aaa.dvi', source = 'aaa.tex') # builds bbb.dvi env.DVI(target = 'bbb', source = 'bbb.ltx') # builds from ccc.latex env.DVI(target = 'ccc.dvi', source = 'ccc.latex') scons-doc-2.3.0/src/engine/SCons/Tool/ilink.xml0000644000175000017500000000074012114661560022043 0ustar dktrkranzdktrkranz Sets construction variables for the ilink linker on OS/2 systems. LINK LINKFLAGS LINKCOM LIBDIRPREFIX LIBDIRSUFFIX LIBLINKPREFIX LIBLINKSUFFIX scons-doc-2.3.0/src/engine/SCons/Tool/Perforce.py0000644000175000017500000000736412114661557022351 0ustar dktrkranzdktrkranz"""SCons.Tool.Perforce.py Tool-specific initialization for Perforce Source Code Management system. There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Tool.Tool() selection method. """ # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/Perforce.py 2013/03/03 09:48:35 garyo" import os import SCons.Action import SCons.Builder import SCons.Node.FS import SCons.Util # This function should maybe be moved to SCons.Util? from SCons.Tool.PharLapCommon import addPathIfNotExists # Variables that we want to import from the base OS environment. _import_env = [ 'P4PORT', 'P4CLIENT', 'P4USER', 'USER', 'USERNAME', 'P4PASSWD', 'P4CHARSET', 'P4LANGUAGE', 'SystemRoot' ] PerforceAction = SCons.Action.Action('$P4COM', '$P4COMSTR') def generate(env): """Add a Builder factory function and construction variables for Perforce to an Environment.""" def PerforceFactory(env=env): """ """ import SCons.Warnings as W W.warn(W.DeprecatedSourceCodeWarning, """The Perforce() factory is deprecated and there is no replacement.""") return SCons.Builder.Builder(action = PerforceAction, env = env) #setattr(env, 'Perforce', PerforceFactory) env.Perforce = PerforceFactory env['P4'] = 'p4' env['P4FLAGS'] = SCons.Util.CLVar('') env['P4COM'] = '$P4 $P4FLAGS sync $TARGET' try: environ = env['ENV'] except KeyError: environ = {} env['ENV'] = environ # Perforce seems to use the PWD environment variable rather than # calling getcwd() for itself, which is odd. If no PWD variable # is present, p4 WILL call getcwd, but this seems to cause problems # with good ol' Windows's tilde-mangling for long file names. environ['PWD'] = env.Dir('#').get_abspath() for var in _import_env: v = os.environ.get(var) if v: environ[var] = v if SCons.Util.can_read_reg: # If we can read the registry, add the path to Perforce to our environment. try: k=SCons.Util.RegOpenKeyEx(SCons.Util.hkey_mod.HKEY_LOCAL_MACHINE, 'Software\\Perforce\\environment') val, tok = SCons.Util.RegQueryValueEx(k, 'P4INSTROOT') addPathIfNotExists(environ, 'PATH', val) except SCons.Util.RegError: # Can't detect where Perforce is, hope the user has it set in the # PATH. pass def exists(env): return env.Detect('p4') # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/f77.py0000644000175000017500000000402412114661560021167 0ustar dktrkranzdktrkranz"""engine.SCons.Tool.f77 Tool-specific initialization for the generic Posix f77 Fortran compiler. There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Tool.Tool() selection method. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/f77.py 2013/03/03 09:48:35 garyo" import SCons.Defaults import SCons.Scanner.Fortran import SCons.Tool import SCons.Util from SCons.Tool.FortranCommon import add_all_to_env, add_f77_to_env compilers = ['f77'] def generate(env): add_all_to_env(env) add_f77_to_env(env) fcomp = env.Detect(compilers) or 'f77' env['F77'] = fcomp env['SHF77'] = fcomp env['FORTRAN'] = fcomp env['SHFORTRAN'] = fcomp def exists(env): return env.Detect(compilers) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/JavaCommonTests.py0000644000175000017500000003515012114661557023653 0ustar dktrkranzdktrkranz# # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/JavaCommonTests.py 2013/03/03 09:48:35 garyo" import os.path import sys import unittest import SCons.Tool.JavaCommon # Adding trace=trace to any of the parse_jave() calls below will cause # the parser to spit out trace messages of the tokens it sees and the # attendant transitions. def trace(token, newstate): from SCons.Debug import Trace statename = newstate.__class__.__name__ Trace('token = %s, state = %s\n' % (repr(token), statename)) class parse_javaTestCase(unittest.TestCase): def test_bare_bones(self): """Test a bare-bones class""" input = """\ package com.sub.bar; public class Foo { public static void main(String[] args) { /* This tests a former bug where strings would eat later code. */ String hello1 = new String("Hello, world!"); } } """ pkg_dir, classes = SCons.Tool.JavaCommon.parse_java(input) assert pkg_dir == os.path.join('com', 'sub', 'bar'), pkg_dir assert classes == ['Foo'], classes def test_dollar_sign(self): """Test class names with $ in them""" input = """\ public class BadDep { public void new$rand () {} } """ pkg_dir, classes = SCons.Tool.JavaCommon.parse_java(input) assert pkg_dir is None, pkg_dir assert classes == ['BadDep'], classes def test_inner_classes(self): """Test parsing various forms of inner classes""" input = """\ class Empty { } interface Listener { public void execute(); } public class Test implements Listener { class Inner { void go() { use(new Listener() { public void execute() { System.out.println("In Inner"); } }); } String s1 = "class A"; String s2 = "new Listener() { }"; /* class B */ /* new Listener() { } */ } class Inner2 { Inner2() { Listener l = new Listener(); } } /* Make sure this class doesn't get interpreted as an inner class of the previous one, when "new" is used in the previous class. */ class Inner3 { } public static void main(String[] args) { new Test().run(); } void run() { use(new Listener() { public void execute() { use(new Listener( ) { public void execute() { System.out.println("Inside execute()"); } }); } }); new Inner().go(); } void use(Listener l) { l.execute(); } } class Private { void run() { new Listener() { public void execute() { } }; } } """ pkg_dir, classes = SCons.Tool.JavaCommon.parse_java(input, '1.4') assert pkg_dir is None, pkg_dir expect = [ 'Empty', 'Listener', 'Test$1', 'Test$Inner', 'Test$Inner2', 'Test$Inner3', 'Test$2', 'Test$3', 'Test', 'Private$1', 'Private', ] assert classes == expect, classes pkg_dir, classes = SCons.Tool.JavaCommon.parse_java(input, '1.5') assert pkg_dir is None, pkg_dir expect = [ 'Empty', 'Listener', 'Test$Inner$1', 'Test$Inner', 'Test$Inner2', 'Test$Inner3', 'Test$1', 'Test$1$1', 'Test', 'Private$1', 'Private', ] assert classes == expect, (expect, classes) pkg_dir, classes = SCons.Tool.JavaCommon.parse_java(input, '5') assert pkg_dir is None, pkg_dir expect = [ 'Empty', 'Listener', 'Test$Inner$1', 'Test$Inner', 'Test$Inner2', 'Test$Inner3', 'Test$1', 'Test$1$1', 'Test', 'Private$1', 'Private', ] assert classes == expect, (expect, classes) def test_comments(self): """Test a class with comments""" input = """\ package com.sub.foo; import java.rmi.Naming; import java.rmi.RemoteException; import java.rmi.RMISecurityManager; import java.rmi.server.UnicastRemoteObject; public class Example1 extends UnicastRemoteObject implements Hello { public Example1() throws RemoteException { super(); } public String sayHello() { return "Hello World!"; } public static void main(String args[]) { if (System.getSecurityManager() == null) { System.setSecurityManager(new RMISecurityManager()); } // a comment try { Example1 obj = new Example1(); Naming.rebind("//myhost/HelloServer", obj); System.out.println("HelloServer bound in registry"); } catch (Exception e) { System.out.println("Example1 err: " + e.getMessage()); e.printStackTrace(); } } } """ pkg_dir, classes = SCons.Tool.JavaCommon.parse_java(input) assert pkg_dir == os.path.join('com', 'sub', 'foo'), pkg_dir assert classes == ['Example1'], classes def test_arrays(self): """Test arrays of class instances""" input = """\ public class Test { MyClass abc = new MyClass(); MyClass xyz = new MyClass(); MyClass _array[] = new MyClass[] { abc, xyz } } """ pkg_dir, classes = SCons.Tool.JavaCommon.parse_java(input) assert pkg_dir is None, pkg_dir assert classes == ['Test'], classes def test_backslash(self): """Test backslash handling""" input = """\ public class MyTabs { private class MyInternal { } private final static String PATH = "images\\\\"; } """ pkg_dir, classes = SCons.Tool.JavaCommon.parse_java(input) assert pkg_dir is None, pkg_dir assert classes == ['MyTabs$MyInternal', 'MyTabs'], classes def test_enum(self): """Test the Java 1.5 enum keyword""" input = """\ package p; public enum a {} """ pkg_dir, classes = SCons.Tool.JavaCommon.parse_java(input) assert pkg_dir == 'p', pkg_dir assert classes == ['a'], classes def test_anon_classes(self): """Test anonymous classes""" input = """\ public abstract class TestClass { public void completed() { new Thread() { }.start(); new Thread() { }.start(); } } """ pkg_dir, classes = SCons.Tool.JavaCommon.parse_java(input) assert pkg_dir is None, pkg_dir assert classes == ['TestClass$1', 'TestClass$2', 'TestClass'], classes def test_closing_bracket(self): """Test finding a closing bracket instead of an anonymous class""" input = """\ class TestSCons { public static void main(String[] args) { Foo[] fooArray = new Foo[] { new Foo() }; } } class Foo { } """ pkg_dir, classes = SCons.Tool.JavaCommon.parse_java(input) assert pkg_dir is None, pkg_dir assert classes == ['TestSCons', 'Foo'], classes def test_dot_class_attributes(self): """Test handling ".class" attributes""" input = """\ public class Test extends Object { static { Class c = Object[].class; Object[] s = new Object[] {}; } } """ pkg_dir, classes = SCons.Tool.JavaCommon.parse_java(input) assert classes == ['Test'], classes input = """\ public class A { public class B { public void F(Object[] o) { F(new Object[] {Object[].class}); } public void G(Object[] o) { F(new Object[] {}); } } } """ pkg_dir, classes = SCons.Tool.JavaCommon.parse_java(input) assert pkg_dir is None, pkg_dir assert classes == ['A$B', 'A'], classes def test_anonymous_classes_with_parentheses(self): """Test finding anonymous classes marked by parentheses""" input = """\ 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"; } }; } } """ pkg_dir, classes = SCons.Tool.JavaCommon.parse_java(input, '1.4') assert classes == ['Foo$1', 'Foo$2', 'Foo'], classes pkg_dir, classes = SCons.Tool.JavaCommon.parse_java(input, '1.5') assert classes == ['Foo$1', 'Foo$1$1', 'Foo'], classes pkg_dir, classes = SCons.Tool.JavaCommon.parse_java(input, '6') assert classes == ['Foo$1', 'Foo$1$1', 'Foo'], classes def test_nested_anonymous_inner_classes(self): """Test finding nested anonymous inner classes""" input = """\ // import java.util.*; public class NestedExample { public NestedExample() { Thread t = new Thread() { public void start() { Thread t = new Thread() { public void start() { try {Thread.sleep(200);} catch (Exception e) {} } }; while (true) { try {Thread.sleep(200);} catch (Exception e) {} } } }; } public static void main(String argv[]) { NestedExample e = new NestedExample(); } } """ pkg_dir, classes = SCons.Tool.JavaCommon.parse_java(input, '1.4') expect = [ 'NestedExample$1', 'NestedExample$2', 'NestedExample' ] assert expect == classes, (expect, classes) pkg_dir, classes = SCons.Tool.JavaCommon.parse_java(input, '1.5') expect = [ 'NestedExample$1', 'NestedExample$1$1', 'NestedExample' ] assert expect == classes, (expect, classes) pkg_dir, classes = SCons.Tool.JavaCommon.parse_java(input, '6') expect = [ 'NestedExample$1', 'NestedExample$1$1', 'NestedExample' ] assert expect == classes, (expect, classes) def test_private_inner_class_instantiation(self): """Test anonymous inner class generated by private instantiation""" input = """\ class test { test() { super(); new inner(); } static class inner { private inner() {} } } """ # This is what we *should* generate, apparently due to the # private instantiation of the inner class, but don't today. #expect = [ 'test$1', 'test$inner', 'test' ] # What our parser currently generates, which doesn't match # what the Java compiler actually generates. expect = [ 'test$inner', 'test' ] pkg_dir, classes = SCons.Tool.JavaCommon.parse_java(input, '1.4') assert expect == classes, (expect, classes) pkg_dir, classes = SCons.Tool.JavaCommon.parse_java(input, '1.5') assert expect == classes, (expect, classes) def test_floating_point_numbers(self): """Test floating-point numbers in the input stream""" input = """ // Broken.java class Broken { /** * Detected. */ Object anonymousInnerOK = new Runnable() { public void run () {} }; /** * Detected. */ class InnerOK { InnerOK () { } } { System.out.println("a number: " + 1000.0 + ""); } /** * Not detected. */ Object anonymousInnerBAD = new Runnable() { public void run () {} }; /** * Not detected. */ class InnerBAD { InnerBAD () { } } } """ expect = ['Broken$1', 'Broken$InnerOK', 'Broken$2', 'Broken$InnerBAD', 'Broken'] pkg_dir, classes = SCons.Tool.JavaCommon.parse_java(input, '1.4') assert expect == classes, (expect, classes) pkg_dir, classes = SCons.Tool.JavaCommon.parse_java(input, '1.5') assert expect == classes, (expect, classes) def test_genercis(self): """Test that generics don't interfere with detecting anonymous classes""" input = """\ import java.util.Date; import java.util.Comparator; public class Foo { public void foo() { Comparator comp = new Comparator() { static final long serialVersionUID = 1L; public int compare(Date lhs, Date rhs) { return 0; } }; } } """ expect = [ 'Foo$1', 'Foo' ] pkg_dir, classes = SCons.Tool.JavaCommon.parse_java(input, '1.6') assert expect == classes, (expect, classes) pkg_dir, classes = SCons.Tool.JavaCommon.parse_java(input, '6') assert expect == classes, (expect, classes) if __name__ == "__main__": suite = unittest.TestSuite() tclasses = [ parse_javaTestCase ] for tclass in tclasses: names = unittest.getTestCaseNames(tclass, 'test_') suite.addTests(list(map(tclass, names))) if not unittest.TextTestRunner().run(suite).wasSuccessful(): sys.exit(1) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/latex.py0000644000175000017500000000531712114661560021707 0ustar dktrkranzdktrkranz"""SCons.Tool.latex Tool-specific initialization for LaTeX. Generates .dvi files from .latex or .ltx files There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Tool.Tool() selection method. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/latex.py 2013/03/03 09:48:35 garyo" import SCons.Action import SCons.Defaults import SCons.Scanner.LaTeX import SCons.Util import SCons.Tool import SCons.Tool.tex def LaTeXAuxFunction(target = None, source= None, env=None): result = SCons.Tool.tex.InternalLaTeXAuxAction( SCons.Tool.tex.LaTeXAction, target, source, env ) if result != 0: SCons.Tool.tex.check_file_error_message(env['LATEX']) return result LaTeXAuxAction = SCons.Action.Action(LaTeXAuxFunction, strfunction=SCons.Tool.tex.TeXLaTeXStrFunction) def generate(env): """Add Builders and construction variables for LaTeX to an Environment.""" env.AppendUnique(LATEXSUFFIXES=SCons.Tool.LaTeXSuffixes) import dvi dvi.generate(env) import pdf pdf.generate(env) bld = env['BUILDERS']['DVI'] bld.add_action('.ltx', LaTeXAuxAction) bld.add_action('.latex', LaTeXAuxAction) bld.add_emitter('.ltx', SCons.Tool.tex.tex_eps_emitter) bld.add_emitter('.latex', SCons.Tool.tex.tex_eps_emitter) SCons.Tool.tex.generate_common(env) def exists(env): SCons.Tool.tex.generate_darwin(env) return env.Detect('latex') # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/qt.py0000644000175000017500000003172012114661560021213 0ustar dktrkranzdktrkranz """SCons.Tool.qt Tool-specific initialization for Qt. There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Tool.Tool() selection method. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/qt.py 2013/03/03 09:48:35 garyo" import os.path import re import SCons.Action import SCons.Builder import SCons.Defaults import SCons.Scanner import SCons.Tool import SCons.Util class ToolQtWarning(SCons.Warnings.Warning): pass class GeneratedMocFileNotIncluded(ToolQtWarning): pass class QtdirNotFound(ToolQtWarning): pass SCons.Warnings.enableWarningClass(ToolQtWarning) header_extensions = [".h", ".hxx", ".hpp", ".hh"] if SCons.Util.case_sensitive_suffixes('.h', '.H'): header_extensions.append('.H') cplusplus = __import__('c++', globals(), locals(), []) cxx_suffixes = cplusplus.CXXSuffixes def checkMocIncluded(target, source, env): moc = target[0] cpp = source[0] # looks like cpp.includes is cleared before the build stage :-( # not really sure about the path transformations (moc.cwd? cpp.cwd?) :-/ path = SCons.Defaults.CScan.path(env, moc.cwd) includes = SCons.Defaults.CScan(cpp, env, path) if not moc in includes: SCons.Warnings.warn( GeneratedMocFileNotIncluded, "Generated moc file '%s' is not included by '%s'" % (str(moc), str(cpp))) def find_file(filename, paths, node_factory): for dir in paths: node = node_factory(filename, dir) if node.rexists(): return node return None class _Automoc(object): """ Callable class, which works as an emitter for Programs, SharedLibraries and StaticLibraries. """ def __init__(self, objBuilderName): self.objBuilderName = objBuilderName def __call__(self, target, source, env): """ Smart autoscan function. Gets the list of objects for the Program or Lib. Adds objects and builders for the special qt files. """ try: if int(env.subst('$QT_AUTOSCAN')) == 0: return target, source except ValueError: pass try: debug = int(env.subst('$QT_DEBUG')) except ValueError: debug = 0 # some shortcuts used in the scanner splitext = SCons.Util.splitext objBuilder = getattr(env, self.objBuilderName) # some regular expressions: # Q_OBJECT detection q_object_search = re.compile(r'[^A-Za-z0-9]Q_OBJECT[^A-Za-z0-9]') # cxx and c comment 'eater' #comment = re.compile(r'(//.*)|(/\*(([^*])|(\*[^/]))*\*/)') # CW: something must be wrong with the regexp. See also bug #998222 # CURRENTLY THERE IS NO TEST CASE FOR THAT # The following is kind of hacky to get builders working properly (FIXME) objBuilderEnv = objBuilder.env objBuilder.env = env mocBuilderEnv = env.Moc.env env.Moc.env = env # make a deep copy for the result; MocH objects will be appended out_sources = source[:] for obj in source: if not obj.has_builder(): # binary obj file provided if debug: print "scons: qt: '%s' seems to be a binary. Discarded." % str(obj) continue cpp = obj.sources[0] if not splitext(str(cpp))[1] in cxx_suffixes: if debug: print "scons: qt: '%s' is no cxx file. Discarded." % str(cpp) # c or fortran source continue #cpp_contents = comment.sub('', cpp.get_text_contents()) cpp_contents = cpp.get_text_contents() h=None for h_ext in header_extensions: # try to find the header file in the corresponding source # directory hname = splitext(cpp.name)[0] + h_ext h = find_file(hname, (cpp.get_dir(),), env.File) if h: if debug: print "scons: qt: Scanning '%s' (header of '%s')" % (str(h), str(cpp)) #h_contents = comment.sub('', h.get_text_contents()) h_contents = h.get_text_contents() break if not h and debug: print "scons: qt: no header for '%s'." % (str(cpp)) if h and q_object_search.search(h_contents): # h file with the Q_OBJECT macro found -> add moc_cpp moc_cpp = env.Moc(h) moc_o = objBuilder(moc_cpp) out_sources.append(moc_o) #moc_cpp.target_scanner = SCons.Defaults.CScan if debug: print "scons: qt: found Q_OBJECT macro in '%s', moc'ing to '%s'" % (str(h), str(moc_cpp)) if cpp and q_object_search.search(cpp_contents): # cpp file with Q_OBJECT macro found -> add moc # (to be included in cpp) moc = env.Moc(cpp) env.Ignore(moc, moc) if debug: print "scons: qt: found Q_OBJECT macro in '%s', moc'ing to '%s'" % (str(cpp), str(moc)) #moc.source_scanner = SCons.Defaults.CScan # restore the original env attributes (FIXME) objBuilder.env = objBuilderEnv env.Moc.env = mocBuilderEnv return (target, out_sources) AutomocShared = _Automoc('SharedObject') AutomocStatic = _Automoc('StaticObject') def _detect(env): """Not really safe, but fast method to detect the QT library""" QTDIR = None if not QTDIR: QTDIR = env.get('QTDIR',None) if not QTDIR: QTDIR = os.environ.get('QTDIR',None) if not QTDIR: moc = env.WhereIs('moc') if moc: QTDIR = os.path.dirname(os.path.dirname(moc)) SCons.Warnings.warn( QtdirNotFound, "Could not detect qt, using moc executable as a hint (QTDIR=%s)" % QTDIR) else: QTDIR = None SCons.Warnings.warn( QtdirNotFound, "Could not detect qt, using empty QTDIR") return QTDIR def uicEmitter(target, source, env): adjustixes = SCons.Util.adjustixes bs = SCons.Util.splitext(str(source[0].name))[0] bs = os.path.join(str(target[0].get_dir()),bs) # first target (header) is automatically added by builder if len(target) < 2: # second target is implementation target.append(adjustixes(bs, env.subst('$QT_UICIMPLPREFIX'), env.subst('$QT_UICIMPLSUFFIX'))) if len(target) < 3: # third target is moc file target.append(adjustixes(bs, env.subst('$QT_MOCHPREFIX'), env.subst('$QT_MOCHSUFFIX'))) return target, source def uicScannerFunc(node, env, path): lookout = [] lookout.extend(env['CPPPATH']) lookout.append(str(node.rfile().dir)) includes = re.findall("(.*?)
", node.get_text_contents()) result = [] for incFile in includes: dep = env.FindFile(incFile,lookout) if dep: result.append(dep) return result uicScanner = SCons.Scanner.Base(uicScannerFunc, name = "UicScanner", node_class = SCons.Node.FS.File, node_factory = SCons.Node.FS.File, recursive = 0) def generate(env): """Add Builders and construction variables for qt to an Environment.""" CLVar = SCons.Util.CLVar Action = SCons.Action.Action Builder = SCons.Builder.Builder env.SetDefault(QTDIR = _detect(env), QT_BINPATH = os.path.join('$QTDIR', 'bin'), QT_CPPPATH = os.path.join('$QTDIR', 'include'), QT_LIBPATH = os.path.join('$QTDIR', 'lib'), QT_MOC = os.path.join('$QT_BINPATH','moc'), QT_UIC = os.path.join('$QT_BINPATH','uic'), QT_LIB = 'qt', # may be set to qt-mt QT_AUTOSCAN = 1, # scan for moc'able sources # Some QT specific flags. I don't expect someone wants to # manipulate those ... QT_UICIMPLFLAGS = CLVar(''), QT_UICDECLFLAGS = CLVar(''), QT_MOCFROMHFLAGS = CLVar(''), QT_MOCFROMCXXFLAGS = CLVar('-i'), # suffixes/prefixes for the headers / sources to generate QT_UICDECLPREFIX = '', QT_UICDECLSUFFIX = '.h', QT_UICIMPLPREFIX = 'uic_', QT_UICIMPLSUFFIX = '$CXXFILESUFFIX', QT_MOCHPREFIX = 'moc_', QT_MOCHSUFFIX = '$CXXFILESUFFIX', QT_MOCCXXPREFIX = '', QT_MOCCXXSUFFIX = '.moc', QT_UISUFFIX = '.ui', # Commands for the qt support ... # command to generate header, implementation and moc-file # from a .ui file QT_UICCOM = [ CLVar('$QT_UIC $QT_UICDECLFLAGS -o ${TARGETS[0]} $SOURCE'), CLVar('$QT_UIC $QT_UICIMPLFLAGS -impl ${TARGETS[0].file} ' '-o ${TARGETS[1]} $SOURCE'), CLVar('$QT_MOC $QT_MOCFROMHFLAGS -o ${TARGETS[2]} ${TARGETS[0]}')], # command to generate meta object information for a class # declarated in a header QT_MOCFROMHCOM = ( '$QT_MOC $QT_MOCFROMHFLAGS -o ${TARGETS[0]} $SOURCE'), # command to generate meta object information for a class # declarated in a cpp file QT_MOCFROMCXXCOM = [ CLVar('$QT_MOC $QT_MOCFROMCXXFLAGS -o ${TARGETS[0]} $SOURCE'), Action(checkMocIncluded,None)]) # ... and the corresponding builders uicBld = Builder(action=SCons.Action.Action('$QT_UICCOM', '$QT_UICCOMSTR'), emitter=uicEmitter, src_suffix='$QT_UISUFFIX', suffix='$QT_UICDECLSUFFIX', prefix='$QT_UICDECLPREFIX', source_scanner=uicScanner) mocBld = Builder(action={}, prefix={}, suffix={}) for h in header_extensions: act = SCons.Action.Action('$QT_MOCFROMHCOM', '$QT_MOCFROMHCOMSTR') mocBld.add_action(h, act) mocBld.prefix[h] = '$QT_MOCHPREFIX' mocBld.suffix[h] = '$QT_MOCHSUFFIX' for cxx in cxx_suffixes: act = SCons.Action.Action('$QT_MOCFROMCXXCOM', '$QT_MOCFROMCXXCOMSTR') mocBld.add_action(cxx, act) mocBld.prefix[cxx] = '$QT_MOCCXXPREFIX' mocBld.suffix[cxx] = '$QT_MOCCXXSUFFIX' # register the builders env['BUILDERS']['Uic'] = uicBld env['BUILDERS']['Moc'] = mocBld static_obj, shared_obj = SCons.Tool.createObjBuilders(env) static_obj.add_src_builder('Uic') shared_obj.add_src_builder('Uic') # We use the emitters of Program / StaticLibrary / SharedLibrary # to scan for moc'able files # We can't refer to the builders directly, we have to fetch them # as Environment attributes because that sets them up to be called # correctly later by our emitter. env.AppendUnique(PROGEMITTER =[AutomocStatic], SHLIBEMITTER=[AutomocShared], LIBEMITTER =[AutomocStatic], # Of course, we need to link against the qt libraries CPPPATH=["$QT_CPPPATH"], LIBPATH=["$QT_LIBPATH"], LIBS=['$QT_LIB']) def exists(env): return _detect(env) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/tar.xml0000644000175000017500000000360612114661560021527 0ustar dktrkranzdktrkranz Sets construction variables for the &tar; archiver. TAR TARFLAGS TARCOM TARSUFFIX TARCOMSTR Builds a tar archive of the specified files and/or directories. Unlike most builder methods, the &b-Tar; builder method may be called multiple times for a given target; each additional call adds to the list of entries that will be built into the archive. Any source directories will be scanned for changes to any on-disk files, regardless of whether or not &scons; knows about them from other Builder or function calls. env.Tar('src.tar', 'src') # Create the stuff.tar file. env.Tar('stuff', ['subdir1', 'subdir2']) # Also add "another" to the stuff.tar file. env.Tar('stuff', 'another') # Set TARFLAGS to create a gzip-filtered archive. env = Environment(TARFLAGS = '-c -z') env.Tar('foo.tar.gz', 'foo') # Also set the suffix to .tgz. env = Environment(TARFLAGS = '-c -z', TARSUFFIX = '.tgz') env.Tar('foo') The tar archiver. The command line used to call the tar archiver. The string displayed when archiving files using the tar archiver. If this is not set, then &cv-link-TARCOM; (the command line) is displayed. env = Environment(TARCOMSTR = "Archiving $TARGET") General options passed to the tar archiver. The suffix used for tar file names. scons-doc-2.3.0/src/engine/SCons/Tool/g++.xml0000644000175000017500000000066012114661560021312 0ustar dktrkranzdktrkranz Set construction variables for the &gXX; C++ compiler. CXX SHCXXFLAGS SHOBJSUFFIX CXXVERSION scons-doc-2.3.0/src/engine/SCons/Tool/gcc.xml0000644000175000017500000000056112114661560021472 0ustar dktrkranzdktrkranz Set construction variables for the &gcc; C compiler. CC SHCCFLAGS CCVERSION scons-doc-2.3.0/src/engine/SCons/Tool/dvi.py0000644000175000017500000000454012114661560021351 0ustar dktrkranzdktrkranz"""SCons.Tool.dvi Common DVI Builder definition for various other Tool modules that use it. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/dvi.py 2013/03/03 09:48:35 garyo" import SCons.Builder import SCons.Tool DVIBuilder = None def generate(env): try: env['BUILDERS']['DVI'] except KeyError: global DVIBuilder if DVIBuilder is None: # The suffix is hard-coded to '.dvi', not configurable via a # construction variable like $DVISUFFIX, because the output # file name is hard-coded within TeX. DVIBuilder = SCons.Builder.Builder(action = {}, source_scanner = SCons.Tool.LaTeXScanner, suffix = '.dvi', emitter = {}, source_ext_match = None) env['BUILDERS']['DVI'] = DVIBuilder def exists(env): # This only puts a skeleton Builder in place, so if someone # references this Tool directly, it's always "available." return 1 # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/Subversion.xml0000644000175000017500000000372512114661557023110 0ustar dktrkranzdktrkranz scons-doc-2.3.0/src/engine/SCons/Tool/icl.xml0000644000175000017500000000060712114661560021506 0ustar dktrkranzdktrkranz Sets construction variables for the Intel C/C++ compiler. Calls the &t-intelc; Tool module to set its variables. scons-doc-2.3.0/src/engine/SCons/Tool/dvipdf.xml0000644000175000017500000000201712114661560022210 0ustar dktrkranzdktrkranz Sets construction variables for the dvipdf utility. DVIPDF DVIPDFFLAGS DVIPDFCOM DVIPDFCOMSTR The TeX DVI file to PDF file converter. General options passed to the TeX DVI file to PDF file converter. The command line used to convert TeX DVI files into a PDF file. The string displayed when a TeX DVI file is converted into a PDF file. If this is not set, then &cv-link-DVIPDFCOM; (the command line) is displayed. A deprecated synonym for &cv-link-DVIPDFCOM;. scons-doc-2.3.0/src/engine/SCons/Tool/javac.py0000644000175000017500000002071312114661560021653 0ustar dktrkranzdktrkranz"""SCons.Tool.javac Tool-specific initialization for javac. There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Tool.Tool() selection method. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/javac.py 2013/03/03 09:48:35 garyo" import os import os.path import SCons.Action import SCons.Builder from SCons.Node.FS import _my_normcase from SCons.Tool.JavaCommon import parse_java_file import SCons.Util def classname(path): """Turn a string (path name) into a Java class name.""" return os.path.normpath(path).replace(os.sep, '.') def emit_java_classes(target, source, env): """Create and return lists of source java files and their corresponding target class files. """ java_suffix = env.get('JAVASUFFIX', '.java') class_suffix = env.get('JAVACLASSSUFFIX', '.class') target[0].must_be_same(SCons.Node.FS.Dir) classdir = target[0] s = source[0].rentry().disambiguate() if isinstance(s, SCons.Node.FS.File): sourcedir = s.dir.rdir() elif isinstance(s, SCons.Node.FS.Dir): sourcedir = s.rdir() else: raise SCons.Errors.UserError("Java source must be File or Dir, not '%s'" % s.__class__) slist = [] js = _my_normcase(java_suffix) for entry in source: entry = entry.rentry().disambiguate() if isinstance(entry, SCons.Node.FS.File): slist.append(entry) elif isinstance(entry, SCons.Node.FS.Dir): result = SCons.Util.OrderedDict() dirnode = entry.rdir() def find_java_files(arg, dirpath, filenames): java_files = sorted([n for n in filenames if _my_normcase(n).endswith(js)]) mydir = dirnode.Dir(dirpath) java_paths = [mydir.File(f) for f in java_files] for jp in java_paths: arg[jp] = True for dirpath, dirnames, filenames in os.walk(dirnode.get_abspath()): find_java_files(result, dirpath, filenames) entry.walk(find_java_files, result) slist.extend(list(result.keys())) else: raise SCons.Errors.UserError("Java source must be File or Dir, not '%s'" % entry.__class__) version = env.get('JAVAVERSION', '1.4') full_tlist = [] for f in slist: tlist = [] source_file_based = True pkg_dir = None if not f.is_derived(): pkg_dir, classes = parse_java_file(f.rfile().get_abspath(), version) if classes: source_file_based = False if pkg_dir: d = target[0].Dir(pkg_dir) p = pkg_dir + os.sep else: d = target[0] p = '' for c in classes: t = d.File(c + class_suffix) t.attributes.java_classdir = classdir t.attributes.java_sourcedir = sourcedir t.attributes.java_classname = classname(p + c) tlist.append(t) if source_file_based: base = f.name[:-len(java_suffix)] if pkg_dir: t = target[0].Dir(pkg_dir).File(base + class_suffix) else: t = target[0].File(base + class_suffix) t.attributes.java_classdir = classdir t.attributes.java_sourcedir = f.dir t.attributes.java_classname = classname(base) tlist.append(t) for t in tlist: t.set_specific_source([f]) full_tlist.extend(tlist) return full_tlist, slist JavaAction = SCons.Action.Action('$JAVACCOM', '$JAVACCOMSTR') JavaBuilder = SCons.Builder.Builder(action = JavaAction, emitter = emit_java_classes, target_factory = SCons.Node.FS.Entry, source_factory = SCons.Node.FS.Entry) class pathopt(object): """ Callable object for generating javac-style path options from a construction variable (e.g. -classpath, -sourcepath). """ def __init__(self, opt, var, default=None): self.opt = opt self.var = var self.default = default def __call__(self, target, source, env, for_signature): path = env[self.var] if path and not SCons.Util.is_List(path): path = [path] if self.default: default = env[self.default] if default: if not SCons.Util.is_List(default): default = [default] path = path + default if path: return [self.opt, os.pathsep.join(map(str, path))] else: return [] def Java(env, target, source, *args, **kw): """ A pseudo-Builder wrapper around the separate JavaClass{File,Dir} Builders. """ if not SCons.Util.is_List(target): target = [target] if not SCons.Util.is_List(source): source = [source] # Pad the target list with repetitions of the last element in the # list so we have a target for every source element. target = target + ([target[-1]] * (len(source) - len(target))) java_suffix = env.subst('$JAVASUFFIX') result = [] for t, s in zip(target, source): if isinstance(s, SCons.Node.FS.Base): if isinstance(s, SCons.Node.FS.File): b = env.JavaClassFile else: b = env.JavaClassDir else: if os.path.isfile(s): b = env.JavaClassFile elif os.path.isdir(s): b = env.JavaClassDir elif s[-len(java_suffix):] == java_suffix: b = env.JavaClassFile else: b = env.JavaClassDir result.extend(b(t, s, *args, **kw)) return result def generate(env): """Add Builders and construction variables for javac to an Environment.""" java_file = SCons.Tool.CreateJavaFileBuilder(env) java_class = SCons.Tool.CreateJavaClassFileBuilder(env) java_class_dir = SCons.Tool.CreateJavaClassDirBuilder(env) java_class.add_emitter(None, emit_java_classes) java_class.add_emitter(env.subst('$JAVASUFFIX'), emit_java_classes) java_class_dir.emitter = emit_java_classes env.AddMethod(Java) env['JAVAC'] = 'javac' env['JAVACFLAGS'] = SCons.Util.CLVar('') env['JAVABOOTCLASSPATH'] = [] env['JAVACLASSPATH'] = [] env['JAVASOURCEPATH'] = [] env['_javapathopt'] = pathopt env['_JAVABOOTCLASSPATH'] = '${_javapathopt("-bootclasspath", "JAVABOOTCLASSPATH")} ' env['_JAVACLASSPATH'] = '${_javapathopt("-classpath", "JAVACLASSPATH")} ' env['_JAVASOURCEPATH'] = '${_javapathopt("-sourcepath", "JAVASOURCEPATH", "_JAVASOURCEPATHDEFAULT")} ' env['_JAVASOURCEPATHDEFAULT'] = '${TARGET.attributes.java_sourcedir}' env['_JAVACCOM'] = '$JAVAC $JAVACFLAGS $_JAVABOOTCLASSPATH $_JAVACLASSPATH -d ${TARGET.attributes.java_classdir} $_JAVASOURCEPATH $SOURCES' env['JAVACCOM'] = "${TEMPFILE('$_JAVACCOM')}" env['JAVACLASSSUFFIX'] = '.class' env['JAVASUFFIX'] = '.java' def exists(env): return 1 # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/linkloc.xml0000644000175000017500000000114312114661560022366 0ustar dktrkranzdktrkranz Sets construction variables for the LinkLoc linker for the Phar Lap ETS embedded operating system. SHLINK SHLINKFLAGS SHLINKCOM LINK LINKFLAGS LINKCOM LIBDIRPREFIX LIBDIRSUFFIX LIBLINKPREFIX LIBLINKSUFFIX SHLINKCOMSTR LINKCOMSTR scons-doc-2.3.0/src/engine/SCons/Tool/gas.py0000644000175000017500000000356512114661560021347 0ustar dktrkranzdktrkranz"""SCons.Tool.gas Tool-specific initialization for as, the Gnu assembler. There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Tool.Tool() selection method. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/gas.py 2013/03/03 09:48:35 garyo" as_module = __import__('as', globals(), locals(), []) assemblers = ['as', 'gas'] def generate(env): """Add Builders and construction variables for as to an Environment.""" as_module.generate(env) env['AS'] = env.Detect(assemblers) or 'as' def exists(env): return env.Detect(assemblers) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/msgfmt.py0000644000175000017500000001054612114661560022067 0ustar dktrkranzdktrkranz""" msgfmt tool """ # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/msgfmt.py 2013/03/03 09:48:35 garyo" from SCons.Builder import BuilderBase ############################################################################# class _MOFileBuilder(BuilderBase): """ The builder class for `MO` files. The reason for this builder to exists and its purpose is quite simillar as for `_POFileBuilder`. This time, we extend list of sources, not targets, and call `BuilderBase._execute()` only once (as we assume single-target here). """ def _execute(self, env, target, source, *args, **kw): # Here we add support for 'LINGUAS_FILE' keyword. Emitter is not suitable # in this case, as it is called too late (after multiple sources # are handled single_source builder. import SCons.Util from SCons.Tool.GettextCommon import _read_linguas_from_files linguas_files = None if env.has_key('LINGUAS_FILE') and env['LINGUAS_FILE'] is not None: linguas_files = env['LINGUAS_FILE'] # This should prevent from endless recursion. env['LINGUAS_FILE'] = None # We read only languages. Suffixes shall be added automatically. linguas = _read_linguas_from_files(env, linguas_files) if SCons.Util.is_List(source): source.extend(linguas) elif source is not None: source = [source] + linguas else: source = linguas result = BuilderBase._execute(self,env,target,source,*args, **kw) if linguas_files is not None: env['LINGUAS_FILE'] = linguas_files return result ############################################################################# ############################################################################# def _create_mo_file_builder(env, **kw): """ Create builder object for `MOFiles` builder """ import SCons.Action # FIXME: What factory use for source? Ours or their? kw['action'] = SCons.Action.Action('$MSGFMTCOM','$MSGFMTCOMSTR') kw['suffix'] = '$MOSUFFIX' kw['src_suffix'] = '$POSUFFIX' kw['src_builder'] = '_POUpdateBuilder' kw['single_source'] = True return _MOFileBuilder(**kw) ############################################################################# ############################################################################# def generate(env,**kw): """ Generate `msgfmt` tool """ import SCons.Util from SCons.Tool.GettextCommon import _detect_msgfmt try: env['MSGFMT'] = _detect_msgfmt(env) except: env['MSGFMT'] = 'msgfmt' env.SetDefault( MSGFMTFLAGS = [ SCons.Util.CLVar('-c') ], MSGFMTCOM = '$MSGFMT $MSGFMTFLAGS -o $TARGET $SOURCE', MSGFMTCOMSTR = '', MOSUFFIX = ['.mo'], POSUFFIX = ['.po'] ) env.Append( BUILDERS = { 'MOFiles' : _create_mo_file_builder(env) } ) ############################################################################# ############################################################################# def exists(env): """ Check if the tool exists """ from SCons.Tool.GettextCommon import _msgfmt_exists try: return _msgfmt_exists(env) except: return False ############################################################################# # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/ifort.py0000644000175000017500000000644212114661560021715 0ustar dktrkranzdktrkranz"""SCons.Tool.ifort Tool-specific initialization for newer versions of the Intel Fortran Compiler for Linux/Windows (and possibly Mac OS X). There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Tool.Tool() selection method. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/ifort.py 2013/03/03 09:48:35 garyo" import SCons.Defaults from SCons.Scanner.Fortran import FortranScan from FortranCommon import add_all_to_env def generate(env): """Add Builders and construction variables for ifort to an Environment.""" # ifort supports Fortran 90 and Fortran 95 # Additionally, ifort recognizes more file extensions. fscan = FortranScan("FORTRANPATH") SCons.Tool.SourceFileScanner.add_scanner('.i', fscan) SCons.Tool.SourceFileScanner.add_scanner('.i90', fscan) if 'FORTRANFILESUFFIXES' not in env: env['FORTRANFILESUFFIXES'] = ['.i'] else: env['FORTRANFILESUFFIXES'].append('.i') if 'F90FILESUFFIXES' not in env: env['F90FILESUFFIXES'] = ['.i90'] else: env['F90FILESUFFIXES'].append('.i90') add_all_to_env(env) fc = 'ifort' for dialect in ['F77', 'F90', 'FORTRAN', 'F95']: env['%s' % dialect] = fc env['SH%s' % dialect] = '$%s' % dialect if env['PLATFORM'] == 'posix': env['SH%sFLAGS' % dialect] = SCons.Util.CLVar('$%sFLAGS -fPIC' % dialect) if env['PLATFORM'] == 'win32': # On Windows, the ifort compiler specifies the object on the # command line with -object:, not -o. Massage the necessary # command-line construction variables. for dialect in ['F77', 'F90', 'FORTRAN', 'F95']: for var in ['%sCOM' % dialect, '%sPPCOM' % dialect, 'SH%sCOM' % dialect, 'SH%sPPCOM' % dialect]: env[var] = env[var].replace('-o $TARGET', '-object:$TARGET') env['FORTRANMODDIRPREFIX'] = "/module:" else: env['FORTRANMODDIRPREFIX'] = "-module " def exists(env): return env.Detect('ifort') # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/swig.py0000644000175000017500000001606412114661560021544 0ustar dktrkranzdktrkranz"""SCons.Tool.swig Tool-specific initialization for swig. There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Tool.Tool() selection method. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/swig.py 2013/03/03 09:48:35 garyo" import os.path import re import subprocess import SCons.Action import SCons.Defaults import SCons.Scanner import SCons.Tool import SCons.Util SwigAction = SCons.Action.Action('$SWIGCOM', '$SWIGCOMSTR') def swigSuffixEmitter(env, source): if '-c++' in SCons.Util.CLVar(env.subst("$SWIGFLAGS", source=source)): return '$SWIGCXXFILESUFFIX' else: return '$SWIGCFILESUFFIX' # Match '%module test', as well as '%module(directors="1") test' # Also allow for test to be quoted (SWIG permits double quotes, but not single) # Also allow for the line to have spaces after test if not quoted _reModule = re.compile(r'%module(\s*\(.*\))?\s+("?)(\S+)\2') def _find_modules(src): """Find all modules referenced by %module lines in `src`, a SWIG .i file. Returns a list of all modules, and a flag set if SWIG directors have been requested (SWIG will generate an additional header file in this case.)""" directors = 0 mnames = [] try: matches = _reModule.findall(open(src).read()) except IOError: # If the file's not yet generated, guess the module name from the file stem matches = [] mnames.append(os.path.splitext(os.path.basename(src))[0]) for m in matches: mnames.append(m[2]) directors = directors or m[0].find('directors') >= 0 return mnames, directors def _add_director_header_targets(target, env): # Directors only work with C++ code, not C suffix = env.subst(env['SWIGCXXFILESUFFIX']) # For each file ending in SWIGCXXFILESUFFIX, add a new target director # header by replacing the ending with SWIGDIRECTORSUFFIX. for x in target[:]: n = x.name d = x.dir if n[-len(suffix):] == suffix: target.append(d.File(n[:-len(suffix)] + env['SWIGDIRECTORSUFFIX'])) def _swigEmitter(target, source, env): swigflags = env.subst("$SWIGFLAGS", target=target, source=source) flags = SCons.Util.CLVar(swigflags) for src in source: src = str(src.rfile()) mnames = None if "-python" in flags and "-noproxy" not in flags: if mnames is None: mnames, directors = _find_modules(src) if directors: _add_director_header_targets(target, env) python_files = [m + ".py" for m in mnames] outdir = env.subst('$SWIGOUTDIR', target=target, source=source) # .py files should be generated in SWIGOUTDIR if specified, # otherwise in the same directory as the target if outdir: python_files = [env.fs.File(os.path.join(outdir, j)) for j in python_files] else: python_files = [target[0].dir.File(m) for m in python_files] target.extend(python_files) if "-java" in flags: if mnames is None: mnames, directors = _find_modules(src) if directors: _add_director_header_targets(target, env) java_files = [[m + ".java", m + "JNI.java"] for m in mnames] java_files = SCons.Util.flatten(java_files) outdir = env.subst('$SWIGOUTDIR', target=target, source=source) if outdir: java_files = [os.path.join(outdir, j) for j in java_files] java_files = list(map(env.fs.File, java_files)) for jf in java_files: t_from_s = lambda t, p, s, x: t.dir SCons.Util.AddMethod(jf, t_from_s, 'target_from_source') target.extend(java_files) return (target, source) def _get_swig_version(env): """Run the SWIG command line tool to get and return the version number""" pipe = SCons.Action._subproc(env, [env['SWIG'], '-version'], stdin = 'devnull', stderr = 'devnull', stdout = subprocess.PIPE) if pipe.wait() != 0: return out = pipe.stdout.read() match = re.search(r'SWIG Version\s+(\S+)$', out, re.MULTILINE) if match: return match.group(1) def generate(env): """Add Builders and construction variables for swig to an Environment.""" c_file, cxx_file = SCons.Tool.createCFileBuilders(env) c_file.suffix['.i'] = swigSuffixEmitter cxx_file.suffix['.i'] = swigSuffixEmitter c_file.add_action('.i', SwigAction) c_file.add_emitter('.i', _swigEmitter) cxx_file.add_action('.i', SwigAction) cxx_file.add_emitter('.i', _swigEmitter) java_file = SCons.Tool.CreateJavaFileBuilder(env) java_file.suffix['.i'] = swigSuffixEmitter java_file.add_action('.i', SwigAction) java_file.add_emitter('.i', _swigEmitter) env['SWIG'] = 'swig' env['SWIGVERSION'] = _get_swig_version(env) env['SWIGFLAGS'] = SCons.Util.CLVar('') env['SWIGDIRECTORSUFFIX'] = '_wrap.h' env['SWIGCFILESUFFIX'] = '_wrap$CFILESUFFIX' env['SWIGCXXFILESUFFIX'] = '_wrap$CXXFILESUFFIX' env['_SWIGOUTDIR'] = r'${"-outdir \"%s\"" % SWIGOUTDIR}' env['SWIGPATH'] = [] env['SWIGINCPREFIX'] = '-I' env['SWIGINCSUFFIX'] = '' env['_SWIGINCFLAGS'] = '$( ${_concat(SWIGINCPREFIX, SWIGPATH, SWIGINCSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)' env['SWIGCOM'] = '$SWIG -o $TARGET ${_SWIGOUTDIR} ${_SWIGINCFLAGS} $SWIGFLAGS $SOURCES' expr = '^[ \t]*%[ \t]*(?:include|import|extern)[ \t]*(<|"?)([^>\s"]+)(?:>|"?)' scanner = SCons.Scanner.ClassicCPP("SWIGScan", ".i", "SWIGPATH", expr) env.Append(SCANNERS = scanner) def exists(env): return env.Detect(['swig']) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/gettext.py0000644000175000017500000000405512114661560022254 0ustar dktrkranzdktrkranz"""gettext tool """ # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/gettext.py 2013/03/03 09:48:35 garyo" ############################################################################# def generate(env,**kw): import SCons.Tool from SCons.Tool.GettextCommon \ import _translate, tool_list for t in tool_list(env['PLATFORM'], env): env.Tool(t) env.AddMethod(_translate, 'Translate') ############################################################################# ############################################################################# def exists(env): from SCons.Tool.GettextCommon \ import _xgettext_exists, _msginit_exists, \ _msgmerge_exists, _msgfmt_exists try: return _xgettext_exists(env) and _msginit_exists(env) \ and _msgmerge_exists(env) and _msgfmt_exists(env) except: return False ############################################################################# scons-doc-2.3.0/src/engine/SCons/Tool/swig.xml0000644000175000017500000001256212114661560021713 0ustar dktrkranzdktrkranz Sets construction variables for the SWIG interface generator. SWIG SWIGFLAGS SWIGDIRECTORSUFFIX SWIGCFILESUFFIX SWIGCXXFILESUFFIX _SWIGINCFLAGS SWIGINCPREFIX SWIGINCSUFFIX SWIGCOM SWIGPATH SWIGVERSION SWIGCOMSTR The scripting language wrapper and interface generator. The suffix that will be used for intermediate C source files generated by the scripting language wrapper and interface generator. The default value is _wrap&cv-link-CFILESUFFIX;. By default, this value is used whenever the option is not specified as part of the &cv-link-SWIGFLAGS; construction variable. The suffix that will be used for intermediate C++ header files generated by the scripting language wrapper and interface generator. These are only generated for C++ code when the SWIG 'directors' feature is turned on. The default value is _wrap.h. The command line used to call the scripting language wrapper and interface generator. The string displayed when calling the scripting language wrapper and interface generator. If this is not set, then &cv-link-SWIGCOM; (the command line) is displayed. The suffix that will be used for intermediate C++ source files generated by the scripting language wrapper and interface generator. The default value is _wrap&cv-link-CFILESUFFIX;. By default, this value is used whenever the -c++ option is specified as part of the &cv-link-SWIGFLAGS; construction variable. General options passed to the scripting language wrapper and interface generator. This is where you should set , , , or whatever other options you want to specify to SWIG. If you set the option in this variable, &scons; will, by default, generate a C++ intermediate source file with the extension that is specified as the &cv-link-CXXFILESUFFIX; variable. An automatically-generated construction variable containing the SWIG command-line options for specifying directories to be searched for included files. The value of &cv-_SWIGINCFLAGS; is created by appending &cv-SWIGINCPREFIX; and &cv-SWIGINCSUFFIX; to the beginning and end of each directory in &cv-SWIGPATH;. The prefix used to specify an include directory on the SWIG command line. This will be appended to the beginning of each directory in the &cv-SWIGPATH; construction variable when the &cv-_SWIGINCFLAGS; variable is automatically generated. The suffix used to specify an include directory on the SWIG command line. This will be appended to the end of each directory in the &cv-SWIGPATH; construction variable when the &cv-_SWIGINCFLAGS; variable is automatically generated. Specifies the output directory in which the scripting language wrapper and interface generator should place generated language-specific files. This will be used by SCons to identify the files that will be generated by the &swig; call, and translated into the swig -outdir option on the command line. The list of directories that the scripting language wrapper and interface generate will search for included files. The SWIG implicit dependency scanner will search these directories for include files. The default is to use the same path specified as &cv-CPPPATH;. Don't explicitly put include directory arguments in SWIGFLAGS; the result will be non-portable and the directories will not be searched by the dependency scanner. Note: directory names in SWIGPATH will be looked-up relative to the SConscript directory when they are used in a command. To force &scons; to look-up a directory relative to the root of the source tree use #: env = Environment(SWIGPATH='#/include') The directory look-up can also be forced using the &Dir;() function: include = Dir('include') env = Environment(SWIGPATH=include) The directory list will be added to command lines through the automatically-generated &cv-_SWIGINCFLAGS; construction variable, which is constructed by appending the values of the &cv-SWIGINCPREFIX; and &cv-SWIGINCSUFFIX; construction variables to the beginning and end of each directory in &cv-SWIGPATH;. Any command lines you define that need the SWIGPATH directory list should include &cv-_SWIGINCFLAGS;: env = Environment(SWIGCOM="my_swig -o $TARGET $_SWIGINCFLAGS $SORUCES") The version number of the SWIG tool. scons-doc-2.3.0/src/engine/SCons/Tool/icc.xml0000644000175000017500000000103312114661560021467 0ustar dktrkranzdktrkranz Sets construction variables for the icc compiler on OS/2 systems. CC CCCOM CXXCOM CPPDEFPREFIX CPPDEFSUFFIX INCPREFIX INCSUFFIX CFILESUFFIX CXXFILESUFFIX CFLAGS CCFLAGS CPPFLAGS _CPPDEFFLAGS _CPPINCFLAGS scons-doc-2.3.0/src/engine/SCons/Tool/wixTests.py0000644000175000017500000000443712114661560022426 0ustar dktrkranzdktrkranz# # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/wixTests.py 2013/03/03 09:48:35 garyo" import unittest import os.path import os import sys import SCons.Errors from SCons.Tool.wix import * from SCons.Environment import Environment import TestCmd # create fake candle and light, so the tool's exists() method will succeed test = TestCmd.TestCmd(workdir = '') test.write('candle.exe', 'rem this is candle') test.write('light.exe', 'rem this is light') os.environ['PATH'] += os.pathsep + test.workdir class WixTestCase(unittest.TestCase): def test_vars(self): """Test that WiX tool adds vars""" env = Environment(tools=['wix']) assert env['WIXCANDLE'] is not None assert env['WIXCANDLEFLAGS'] is not None assert env['WIXLIGHTFLAGS'] is not None assert env.subst('$WIXOBJSUF') == '.wixobj' assert env.subst('$WIXSRCSUF') == '.wxs' if __name__ == "__main__": suite = unittest.makeSuite(WixTestCase, 'test_') if not unittest.TextTestRunner().run(suite).wasSuccessful(): sys.exit(1) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/lex.py0000644000175000017500000000650112114661560021356 0ustar dktrkranzdktrkranz"""SCons.Tool.lex Tool-specific initialization for lex. There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Tool.Tool() selection method. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/lex.py 2013/03/03 09:48:35 garyo" import os.path import SCons.Action import SCons.Tool import SCons.Util LexAction = SCons.Action.Action("$LEXCOM", "$LEXCOMSTR") def lexEmitter(target, source, env): sourceBase, sourceExt = os.path.splitext(SCons.Util.to_String(source[0])) if sourceExt == ".lm": # If using Objective-C target = [sourceBase + ".m"] # the extension is ".m". # This emitter essentially tries to add to the target all extra # files generated by flex. # Different options that are used to trigger the creation of extra files. fileGenOptions = ["--header-file=", "--tables-file="] lexflags = env.subst("$LEXFLAGS", target=target, source=source) for option in SCons.Util.CLVar(lexflags): for fileGenOption in fileGenOptions: l = len(fileGenOption) if option[:l] == fileGenOption: # A file generating option is present, so add the # file name to the target list. fileName = option[l:].strip() target.append(fileName) return (target, source) def generate(env): """Add Builders and construction variables for lex to an Environment.""" c_file, cxx_file = SCons.Tool.createCFileBuilders(env) # C c_file.add_action(".l", LexAction) c_file.add_emitter(".l", lexEmitter) c_file.add_action(".lex", LexAction) c_file.add_emitter(".lex", lexEmitter) # Objective-C cxx_file.add_action(".lm", LexAction) cxx_file.add_emitter(".lm", lexEmitter) # C++ cxx_file.add_action(".ll", LexAction) cxx_file.add_emitter(".ll", lexEmitter) env["LEX"] = env.Detect("flex") or "lex" env["LEXFLAGS"] = SCons.Util.CLVar("") env["LEXCOM"] = "$LEX $LEXFLAGS -t $SOURCES > $TARGET" def exists(env): return env.Detect(["flex", "lex"]) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/mslink.py0000644000175000017500000003320612114661560022065 0ustar dktrkranzdktrkranz"""SCons.Tool.mslink Tool-specific initialization for the Microsoft linker. There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Tool.Tool() selection method. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/mslink.py 2013/03/03 09:48:35 garyo" import os.path import SCons.Action import SCons.Defaults import SCons.Errors import SCons.Platform.win32 import SCons.Tool import SCons.Tool.msvc import SCons.Tool.msvs import SCons.Util from MSCommon import msvc_setup_env_once, msvc_exists def pdbGenerator(env, target, source, for_signature): try: return ['/PDB:%s' % target[0].attributes.pdb, '/DEBUG'] except (AttributeError, IndexError): return None def _dllTargets(target, source, env, for_signature, paramtp): listCmd = [] dll = env.FindIxes(target, '%sPREFIX' % paramtp, '%sSUFFIX' % paramtp) if dll: listCmd.append("/out:%s"%dll.get_string(for_signature)) implib = env.FindIxes(target, 'LIBPREFIX', 'LIBSUFFIX') if implib: listCmd.append("/implib:%s"%implib.get_string(for_signature)) return listCmd def _dllSources(target, source, env, for_signature, paramtp): listCmd = [] deffile = env.FindIxes(source, "WINDOWSDEFPREFIX", "WINDOWSDEFSUFFIX") for src in source: # Check explicitly for a non-None deffile so that the __cmp__ # method of the base SCons.Util.Proxy class used for some Node # proxies doesn't try to use a non-existent __dict__ attribute. if deffile and src == deffile: # Treat this source as a .def file. listCmd.append("/def:%s" % src.get_string(for_signature)) else: # Just treat it as a generic source file. listCmd.append(src) return listCmd def windowsShlinkTargets(target, source, env, for_signature): return _dllTargets(target, source, env, for_signature, 'SHLIB') def windowsShlinkSources(target, source, env, for_signature): return _dllSources(target, source, env, for_signature, 'SHLIB') def _windowsLdmodTargets(target, source, env, for_signature): """Get targets for loadable modules.""" return _dllTargets(target, source, env, for_signature, 'LDMODULE') def _windowsLdmodSources(target, source, env, for_signature): """Get sources for loadable modules.""" return _dllSources(target, source, env, for_signature, 'LDMODULE') def _dllEmitter(target, source, env, paramtp): """Common implementation of dll emitter.""" SCons.Tool.msvc.validate_vars(env) extratargets = [] extrasources = [] dll = env.FindIxes(target, '%sPREFIX' % paramtp, '%sSUFFIX' % paramtp) no_import_lib = env.get('no_import_lib', 0) if not dll: raise SCons.Errors.UserError('A shared library should have exactly one target with the suffix: %s' % env.subst('$%sSUFFIX' % paramtp)) insert_def = env.subst("$WINDOWS_INSERT_DEF") if not insert_def in ['', '0', 0] and \ not env.FindIxes(source, "WINDOWSDEFPREFIX", "WINDOWSDEFSUFFIX"): # append a def file to the list of sources extrasources.append( env.ReplaceIxes(dll, '%sPREFIX' % paramtp, '%sSUFFIX' % paramtp, "WINDOWSDEFPREFIX", "WINDOWSDEFSUFFIX")) version_num, suite = SCons.Tool.msvs.msvs_parse_version(env.get('MSVS_VERSION', '6.0')) if version_num >= 8.0 and \ (env.get('WINDOWS_INSERT_MANIFEST', 0) or env.get('WINDOWS_EMBED_MANIFEST', 0)): # MSVC 8 and above automatically generate .manifest files that must be installed extratargets.append( env.ReplaceIxes(dll, '%sPREFIX' % paramtp, '%sSUFFIX' % paramtp, "WINDOWSSHLIBMANIFESTPREFIX", "WINDOWSSHLIBMANIFESTSUFFIX")) if 'PDB' in env and env['PDB']: pdb = env.arg2nodes('$PDB', target=target, source=source)[0] extratargets.append(pdb) target[0].attributes.pdb = pdb if not no_import_lib and \ not env.FindIxes(target, "LIBPREFIX", "LIBSUFFIX"): # Append an import library to the list of targets. extratargets.append( env.ReplaceIxes(dll, '%sPREFIX' % paramtp, '%sSUFFIX' % paramtp, "LIBPREFIX", "LIBSUFFIX")) # and .exp file is created if there are exports from a DLL extratargets.append( env.ReplaceIxes(dll, '%sPREFIX' % paramtp, '%sSUFFIX' % paramtp, "WINDOWSEXPPREFIX", "WINDOWSEXPSUFFIX")) return (target+extratargets, source+extrasources) def windowsLibEmitter(target, source, env): return _dllEmitter(target, source, env, 'SHLIB') def ldmodEmitter(target, source, env): """Emitter for loadable modules. Loadable modules are identical to shared libraries on Windows, but building them is subject to different parameters (LDMODULE*). """ return _dllEmitter(target, source, env, 'LDMODULE') def prog_emitter(target, source, env): SCons.Tool.msvc.validate_vars(env) extratargets = [] extrasources = [] exe = env.FindIxes(target, "PROGPREFIX", "PROGSUFFIX") if not exe: raise SCons.Errors.UserError("An executable should have exactly one target with the suffix: %s" % env.subst("$PROGSUFFIX")) version_num, suite = SCons.Tool.msvs.msvs_parse_version(env.get('MSVS_VERSION', '6.0')) if version_num >= 8.0 and \ (env.get('WINDOWS_INSERT_MANIFEST', 0) or env.get('WINDOWS_EMBED_MANIFEST', 0)): # MSVC 8 and above automatically generate .manifest files that have to be installed extratargets.append( env.ReplaceIxes(exe, "PROGPREFIX", "PROGSUFFIX", "WINDOWSPROGMANIFESTPREFIX", "WINDOWSPROGMANIFESTSUFFIX")) if 'PDB' in env and env['PDB']: pdb = env.arg2nodes('$PDB', target=target, source=source)[0] extratargets.append(pdb) target[0].attributes.pdb = pdb if version_num >= 11.0 and env.get('PCH', 0): # MSVC 11 and above need the PCH object file to be added to the link line, # otherwise you get link error LNK2011. pchobj = SCons.Util.splitext(str(env['PCH']))[0] + '.obj' # print "prog_emitter, version %s, appending pchobj %s"%(version_num, pchobj) if pchobj not in extrasources: extrasources.append(pchobj) return (target+extratargets,source+extrasources) def RegServerFunc(target, source, env): if 'register' in env and env['register']: ret = regServerAction([target[0]], [source[0]], env) if ret: raise SCons.Errors.UserError("Unable to register %s" % target[0]) else: print "Registered %s sucessfully" % target[0] return ret return 0 # These are the actual actions run to embed the manifest. # They are only called from the Check versions below. embedManifestExeAction = SCons.Action.Action('$MTEXECOM') embedManifestDllAction = SCons.Action.Action('$MTSHLIBCOM') def embedManifestDllCheck(target, source, env): """Function run by embedManifestDllCheckAction to check for existence of manifest and other conditions, and embed the manifest by calling embedManifestDllAction if so.""" if env.get('WINDOWS_EMBED_MANIFEST', 0): manifestSrc = target[0].abspath + '.manifest' if os.path.exists(manifestSrc): ret = (embedManifestDllAction) ([target[0]],None,env) if ret: raise SCons.Errors.UserError, "Unable to embed manifest into %s" % (target[0]) return ret else: print '(embed: no %s.manifest found; not embedding.)'%str(target[0]) return 0 def embedManifestExeCheck(target, source, env): """Function run by embedManifestExeCheckAction to check for existence of manifest and other conditions, and embed the manifest by calling embedManifestExeAction if so.""" if env.get('WINDOWS_EMBED_MANIFEST', 0): manifestSrc = target[0].abspath + '.manifest' if os.path.exists(manifestSrc): ret = (embedManifestExeAction) ([target[0]],None,env) if ret: raise SCons.Errors.UserError, "Unable to embed manifest into %s" % (target[0]) return ret else: print '(embed: no %s.manifest found; not embedding.)'%str(target[0]) return 0 embedManifestDllCheckAction = SCons.Action.Action(embedManifestDllCheck, None) embedManifestExeCheckAction = SCons.Action.Action(embedManifestExeCheck, None) regServerAction = SCons.Action.Action("$REGSVRCOM", "$REGSVRCOMSTR") regServerCheck = SCons.Action.Action(RegServerFunc, None) shlibLinkAction = SCons.Action.Action('${TEMPFILE("$SHLINK $SHLINKFLAGS $_SHLINK_TARGETS $_LIBDIRFLAGS $_LIBFLAGS $_PDB $_SHLINK_SOURCES")}', '$SHLINKCOMSTR') compositeShLinkAction = shlibLinkAction + regServerCheck + embedManifestDllCheckAction ldmodLinkAction = SCons.Action.Action('${TEMPFILE("$LDMODULE $LDMODULEFLAGS $_LDMODULE_TARGETS $_LIBDIRFLAGS $_LIBFLAGS $_PDB $_LDMODULE_SOURCES")}', '$LDMODULECOMSTR') compositeLdmodAction = ldmodLinkAction + regServerCheck + embedManifestDllCheckAction exeLinkAction = SCons.Action.Action('${TEMPFILE("$LINK $LINKFLAGS /OUT:$TARGET.windows $_LIBDIRFLAGS $_LIBFLAGS $_PDB $SOURCES.windows")}', '$LINKCOMSTR') compositeLinkAction = exeLinkAction + embedManifestExeCheckAction def generate(env): """Add Builders and construction variables for ar to an Environment.""" SCons.Tool.createSharedLibBuilder(env) SCons.Tool.createProgBuilder(env) env['SHLINK'] = '$LINK' env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS /dll') env['_SHLINK_TARGETS'] = windowsShlinkTargets env['_SHLINK_SOURCES'] = windowsShlinkSources env['SHLINKCOM'] = compositeShLinkAction env.Append(SHLIBEMITTER = [windowsLibEmitter]) env['LINK'] = 'link' env['LINKFLAGS'] = SCons.Util.CLVar('/nologo') env['_PDB'] = pdbGenerator env['LINKCOM'] = compositeLinkAction env.Append(PROGEMITTER = [prog_emitter]) env['LIBDIRPREFIX']='/LIBPATH:' env['LIBDIRSUFFIX']='' env['LIBLINKPREFIX']='' env['LIBLINKSUFFIX']='$LIBSUFFIX' env['WIN32DEFPREFIX'] = '' env['WIN32DEFSUFFIX'] = '.def' env['WIN32_INSERT_DEF'] = 0 env['WINDOWSDEFPREFIX'] = '${WIN32DEFPREFIX}' env['WINDOWSDEFSUFFIX'] = '${WIN32DEFSUFFIX}' env['WINDOWS_INSERT_DEF'] = '${WIN32_INSERT_DEF}' env['WIN32EXPPREFIX'] = '' env['WIN32EXPSUFFIX'] = '.exp' env['WINDOWSEXPPREFIX'] = '${WIN32EXPPREFIX}' env['WINDOWSEXPSUFFIX'] = '${WIN32EXPSUFFIX}' env['WINDOWSSHLIBMANIFESTPREFIX'] = '' env['WINDOWSSHLIBMANIFESTSUFFIX'] = '${SHLIBSUFFIX}.manifest' env['WINDOWSPROGMANIFESTPREFIX'] = '' env['WINDOWSPROGMANIFESTSUFFIX'] = '${PROGSUFFIX}.manifest' env['REGSVRACTION'] = regServerCheck env['REGSVR'] = os.path.join(SCons.Platform.win32.get_system_root(),'System32','regsvr32') env['REGSVRFLAGS'] = '/s ' env['REGSVRCOM'] = '$REGSVR $REGSVRFLAGS ${TARGET.windows}' env['WINDOWS_EMBED_MANIFEST'] = 0 env['MT'] = 'mt' #env['MTFLAGS'] = ['-hashupdate'] env['MTFLAGS'] = SCons.Util.CLVar('/nologo') # Note: use - here to prevent build failure if no manifest produced. # This seems much simpler than a fancy system using a function action to see # if the manifest actually exists before trying to run mt with it. env['MTEXECOM'] = '-$MT $MTFLAGS -manifest ${TARGET}.manifest $_MANIFEST_SOURCES -outputresource:$TARGET;1' env['MTSHLIBCOM'] = '-$MT $MTFLAGS -manifest ${TARGET}.manifest $_MANIFEST_SOURCES -outputresource:$TARGET;2' # Future work garyo 27-Feb-11 env['_MANIFEST_SOURCES'] = None # _windowsManifestSources # Set-up ms tools paths msvc_setup_env_once(env) # Loadable modules are on Windows the same as shared libraries, but they # are subject to different build parameters (LDMODULE* variables). # Therefore LDMODULE* variables correspond as much as possible to # SHLINK*/SHLIB* ones. SCons.Tool.createLoadableModuleBuilder(env) env['LDMODULE'] = '$SHLINK' env['LDMODULEPREFIX'] = '$SHLIBPREFIX' env['LDMODULESUFFIX'] = '$SHLIBSUFFIX' env['LDMODULEFLAGS'] = '$SHLINKFLAGS' env['_LDMODULE_TARGETS'] = _windowsLdmodTargets env['_LDMODULE_SOURCES'] = _windowsLdmodSources env['LDMODULEEMITTER'] = [ldmodEmitter] env['LDMODULECOM'] = compositeLdmodAction def exists(env): return msvc_exists() # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/wix.py0000644000175000017500000000732412114661560021401 0ustar dktrkranzdktrkranz"""SCons.Tool.wix Tool-specific initialization for wix, the Windows Installer XML Tool. There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Tool.Tool() selection method. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/wix.py 2013/03/03 09:48:35 garyo" import SCons.Builder import SCons.Action import os def generate(env): """Add Builders and construction variables for WiX to an Environment.""" if not exists(env): return env['WIXCANDLEFLAGS'] = ['-nologo'] env['WIXCANDLEINCLUDE'] = [] env['WIXCANDLECOM'] = '$WIXCANDLE $WIXCANDLEFLAGS -I $WIXCANDLEINCLUDE -o ${TARGET} ${SOURCE}' env['WIXLIGHTFLAGS'].append( '-nologo' ) env['WIXLIGHTCOM'] = "$WIXLIGHT $WIXLIGHTFLAGS -out ${TARGET} ${SOURCES}" env['WIXSRCSUF'] = '.wxs' env['WIXOBJSUF'] = '.wixobj' object_builder = SCons.Builder.Builder( action = '$WIXCANDLECOM', suffix = '$WIXOBJSUF', src_suffix = '$WIXSRCSUF') linker_builder = SCons.Builder.Builder( action = '$WIXLIGHTCOM', src_suffix = '$WIXOBJSUF', src_builder = object_builder) env['BUILDERS']['WiX'] = linker_builder def exists(env): env['WIXCANDLE'] = 'candle.exe' env['WIXLIGHT'] = 'light.exe' # try to find the candle.exe and light.exe tools and # add the install directory to light libpath. for path in os.environ['PATH'].split(os.pathsep): if not path: continue # workaround for some weird python win32 bug. if path[0] == '"' and path[-1:]=='"': path = path[1:-1] # normalize the path path = os.path.normpath(path) # search for the tools in the PATH environment variable try: files = os.listdir(path) if env['WIXCANDLE'] in files and env['WIXLIGHT'] in files: env.PrependENVPath('PATH', path) # include appropriate flags if running WiX 2.0 if 'wixui.wixlib' in files and 'WixUI_en-us.wxl' in files: env['WIXLIGHTFLAGS'] = [ os.path.join( path, 'wixui.wixlib' ), '-loc', os.path.join( path, 'WixUI_en-us.wxl' ) ] else: env['WIXLIGHTFLAGS'] = [] return 1 except OSError: pass # ignore this, could be a stale PATH entry. return None # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/sgic++.xml0000644000175000017500000000065312114661560022013 0ustar dktrkranzdktrkranz Sets construction variables for the SGI C++ compiler. CXX CXXFLAGS SHCXX SHOBJSUFFIX scons-doc-2.3.0/src/engine/SCons/Tool/f03.py0000644000175000017500000000400712114661560021155 0ustar dktrkranzdktrkranz"""engine.SCons.Tool.f03 Tool-specific initialization for the generic Posix f03 Fortran compiler. There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Tool.Tool() selection method. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/f03.py 2013/03/03 09:48:35 garyo" import SCons.Defaults import SCons.Tool import SCons.Util import fortran from SCons.Tool.FortranCommon import add_all_to_env, add_f03_to_env compilers = ['f03'] def generate(env): add_all_to_env(env) add_f03_to_env(env) fcomp = env.Detect(compilers) or 'f03' env['F03'] = fcomp env['SHF03'] = fcomp env['FORTRAN'] = fcomp env['SHFORTRAN'] = fcomp def exists(env): return env.Detect(compilers) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/msgmerge.xml0000644000175000017500000001232312114661560022543 0ustar dktrkranzdktrkranz This scons tool is a part of scons &t-link-gettext; toolset. It provides scons interface to msgmerge(1) command, which merges two Uniform style .po files together. MSGMERGE MSGMERGECOM MSGMERGECOMSTR MSGMERGEFLAGS POSUFFIX POTSUFFIX POUPDATE_ALIAS POTDOMAIN LINGUAS_FILE POAUTOINIT The builder belongs to &t-link-msgmerge; tool. The builder updates PO files with msgmerge(1), or initializes missing PO files as described in documentation of &t-link-msginit; tool and &b-link-POInit; builder (see also &cv-link-POAUTOINIT;). Note, that &b-POUpdate; does not add its targets to po-create alias as &b-link-POInit; does. Target nodes defined through &b-POUpdate; are not built by default (they're Ignored from '.' node). Instead, they are added automatically to special Alias ('po-update' by default). The alias name may be changed through the &cv-link-POUPDATE_ALIAS; construction variable. You can easilly update PO files in your project by scons po-update. Example 1. Update en.po and pl.po from messages.pot template (see also &cv-link-POTDOMAIN;), assuming that the later one exists or there is rule to build it (see &b-link-POTUpdate;): # ... env.POUpdate(['en','pl']) # messages.pot --> [en.po, pl.po] Example 2. Update en.po and pl.po from foo.pot template: # ... env.POUpdate(['en', 'pl'], ['foo']) # foo.pot --> [en.po, pl.pl] Example 3. Update en.po and pl.po from foo.pot (another version): # ... env.POUpdate(['en', 'pl'], POTDOMAIN='foo') # foo.pot -- > [en.po, pl.pl] Example 4. Update files for languages defined in LINGUAS file. The files are updated from messages.pot template: # ... env.POUpdate(LINGUAS_FILE = 1) # needs 'LINGUAS' file Example 5. Same as above, but update from foo.pot template: # ... env.POUpdate(LINGUAS_FILE = 1, source = ['foo']) Example 6. Update en.po and pl.po plus files for languages defined in LINGUAS file. The files are updated from messages.pot template: # produce 'en.po', 'pl.po' + files defined in 'LINGUAS': env.POUpdate(['en', 'pl' ], LINGUAS_FILE = 1) Example 7. Use &cv-link-POAUTOINIT; to automatically initialize PO file if it doesn't exist: # ... env.POUpdate(LINGUAS_FILE = 1, POAUTOINIT = 1) Example 8. Update PO files for languages defined in LINGUAS file. The files are updated from foo.pot template. All necessary settings are pre-configured via environment. # ... env['POAUTOINIT'] = 1 env['LINGUAS_FILE'] = 1 env['POTDOMAIN'] = 'foo' env.POUpdate() Common alias for all PO files being defined with &b-link-POUpdate; builder (default: 'po-update'). See &t-link-msgmerge; tool and &b-link-POUpdate; builder. Absolute path to msgmerge(1) binary as found by Detect(). See &t-link-msgmerge; tool and &b-link-POUpdate; builder. Complete command line to run msgmerge(1) command. See &t-link-msgmerge; tool and &b-link-POUpdate; builder. String to be displayed when msgmerge(1) is invoked (default: '', which means ``print &cv-link-MSGMERGECOM;''). See &t-link-msgmerge; tool and &b-link-POUpdate; builder. Additional flags to msgmerge(1) command. See &t-link-msgmerge; tool and &b-link-POUpdate; builder. scons-doc-2.3.0/src/engine/SCons/Tool/aixf77.py0000644000175000017500000000527212114661560021677 0ustar dktrkranzdktrkranz"""engine.SCons.Tool.aixf77 Tool-specific initialization for IBM Visual Age f77 Fortran compiler. There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Tool.Tool() selection method. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/aixf77.py 2013/03/03 09:48:35 garyo" import os.path #import SCons.Platform.aix import f77 # It would be good to look for the AIX F77 package the same way we're now # looking for the C and C++ packages. This should be as easy as supplying # the correct package names in the following list and uncommenting the # SCons.Platform.aix_get_xlc() call the in the function below. packages = [] def get_xlf77(env): xlf77 = env.get('F77', 'xlf77') xlf77_r = env.get('SHF77', 'xlf77_r') #return SCons.Platform.aix.get_xlc(env, xlf77, xlf77_r, packages) return (None, xlf77, xlf77_r, None) def generate(env): """ Add Builders and construction variables for the Visual Age FORTRAN compiler to an Environment. """ path, _f77, _shf77, version = get_xlf77(env) if path: _f77 = os.path.join(path, _f77) _shf77 = os.path.join(path, _shf77) f77.generate(env) env['F77'] = _f77 env['SHF77'] = _shf77 def exists(env): path, _f77, _shf77, version = get_xlf77(env) if path and _f77: xlf77 = os.path.join(path, _f77) if os.path.exists(xlf77): return xlf77 return None # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/pdflatex.py0000644000175000017500000000562212114661560022400 0ustar dktrkranzdktrkranz"""SCons.Tool.pdflatex Tool-specific initialization for pdflatex. Generates .pdf files from .latex or .ltx files There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Tool.Tool() selection method. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/pdflatex.py 2013/03/03 09:48:35 garyo" import SCons.Action import SCons.Util import SCons.Tool.pdf import SCons.Tool.tex PDFLaTeXAction = None def PDFLaTeXAuxFunction(target = None, source= None, env=None): result = SCons.Tool.tex.InternalLaTeXAuxAction( PDFLaTeXAction, target, source, env ) if result != 0: SCons.Tool.tex.check_file_error_message(env['PDFLATEX']) return result PDFLaTeXAuxAction = None def generate(env): """Add Builders and construction variables for pdflatex to an Environment.""" global PDFLaTeXAction if PDFLaTeXAction is None: PDFLaTeXAction = SCons.Action.Action('$PDFLATEXCOM', '$PDFLATEXCOMSTR') global PDFLaTeXAuxAction if PDFLaTeXAuxAction is None: PDFLaTeXAuxAction = SCons.Action.Action(PDFLaTeXAuxFunction, strfunction=SCons.Tool.tex.TeXLaTeXStrFunction) env.AppendUnique(LATEXSUFFIXES=SCons.Tool.LaTeXSuffixes) import pdf pdf.generate(env) bld = env['BUILDERS']['PDF'] bld.add_action('.ltx', PDFLaTeXAuxAction) bld.add_action('.latex', PDFLaTeXAuxAction) bld.add_emitter('.ltx', SCons.Tool.tex.tex_pdf_emitter) bld.add_emitter('.latex', SCons.Tool.tex.tex_pdf_emitter) SCons.Tool.tex.generate_common(env) def exists(env): SCons.Tool.tex.generate_darwin(env) return env.Detect('pdflatex') # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/sunc++.py0000644000175000017500000001123412114661560021663 0ustar dktrkranzdktrkranz"""SCons.Tool.sunc++ Tool-specific initialization for C++ on SunOS / Solaris. There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Tool.Tool() selection method. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/sunc++.py 2013/03/03 09:48:35 garyo" import SCons import os import re import subprocess cplusplus = __import__('c++', globals(), locals(), []) package_info = {} def get_package_info(package_name, pkginfo, pkgchk): try: return package_info[package_name] except KeyError: version = None pathname = None try: sadm_contents = open('/var/sadm/install/contents', 'r').read() except EnvironmentError: pass else: sadm_re = re.compile('^(\S*/bin/CC)(=\S*)? %s$' % package_name, re.M) sadm_match = sadm_re.search(sadm_contents) if sadm_match: pathname = os.path.dirname(sadm_match.group(1)) try: p = subprocess.Popen([pkginfo, '-l', package_name], stdout=subprocess.PIPE, stderr=open('/dev/null', 'w')) except EnvironmentError: pass else: pkginfo_contents = p.communicate()[0] version_re = re.compile('^ *VERSION:\s*(.*)$', re.M) version_match = version_re.search(pkginfo_contents) if version_match: version = version_match.group(1) if pathname is None: try: p = subprocess.Popen([pkgchk, '-l', package_name], stdout=subprocess.PIPE, stderr=open('/dev/null', 'w')) except EnvironmentError: pass else: pkgchk_contents = p.communicate()[0] pathname_re = re.compile(r'^Pathname:\s*(.*/bin/CC)$', re.M) pathname_match = pathname_re.search(pkgchk_contents) if pathname_match: pathname = os.path.dirname(pathname_match.group(1)) package_info[package_name] = (pathname, version) return package_info[package_name] # use the package installer tool lslpp to figure out where cppc and what # version of it is installed def get_cppc(env): cxx = env.subst('$CXX') if cxx: cppcPath = os.path.dirname(cxx) else: cppcPath = None cppcVersion = None pkginfo = env.subst('$PKGINFO') pkgchk = env.subst('$PKGCHK') for package in ['SPROcpl']: path, version = get_package_info(package, pkginfo, pkgchk) if path and version: cppcPath, cppcVersion = path, version break return (cppcPath, 'CC', 'CC', cppcVersion) def generate(env): """Add Builders and construction variables for SunPRO C++.""" path, cxx, shcxx, version = get_cppc(env) if path: cxx = os.path.join(path, cxx) shcxx = os.path.join(path, shcxx) cplusplus.generate(env) env['CXX'] = cxx env['SHCXX'] = shcxx env['CXXVERSION'] = version env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS -KPIC') env['SHOBJPREFIX'] = 'so_' env['SHOBJSUFFIX'] = '.o' def exists(env): path, cxx, shcxx, version = get_cppc(env) if path and cxx: cppc = os.path.join(path, cxx) if os.path.exists(cppc): return cppc return None # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/sunf77.py0000644000175000017500000000420212114661560021713 0ustar dktrkranzdktrkranz"""SCons.Tool.sunf77 Tool-specific initialization for sunf77, the Sun Studio F77 compiler. There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Tool.Tool() selection method. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/sunf77.py 2013/03/03 09:48:35 garyo" import SCons.Util from FortranCommon import add_all_to_env compilers = ['sunf77', 'f77'] def generate(env): """Add Builders and construction variables for sunf77 to an Environment.""" add_all_to_env(env) fcomp = env.Detect(compilers) or 'f77' env['FORTRAN'] = fcomp env['F77'] = fcomp env['SHFORTRAN'] = '$FORTRAN' env['SHF77'] = '$F77' env['SHFORTRANFLAGS'] = SCons.Util.CLVar('$FORTRANFLAGS -KPIC') env['SHF77FLAGS'] = SCons.Util.CLVar('$F77FLAGS -KPIC') def exists(env): return env.Detect(compilers) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/link.xml0000644000175000017500000000736512114661560021704 0ustar dktrkranzdktrkranz Sets construction variables for generic POSIX linkers. SHLINK SHLINKFLAGS SHLINKCOM LINK LINKFLAGS LINKCOM LIBDIRPREFIX LIBDIRSUFFIX LIBLINKPREFIX LIBLINKSUFFIX SHLIBSUFFIX LDMODULE LDMODULEPREFIX LDMODULESUFFIX LDMODULEFLAGS LDMODULECOM SHLINKCOMSTR LINKCOMSTR LDMODULECOMSTR The linker for building loadable modules. By default, this is the same as &cv-link-SHLINK;. The command line for building loadable modules. On Mac OS X, this uses the &cv-link-LDMODULE;, &cv-link-LDMODULEFLAGS; and &cv-link-FRAMEWORKSFLAGS; variables. On other systems, this is the same as &cv-link-SHLINK;. The string displayed when building loadable modules. If this is not set, then &cv-link-LDMODULECOM; (the command line) is displayed. General user options passed to the linker for building loadable modules. The prefix used for loadable module file names. On Mac OS X, this is null; on other systems, this is the same as &cv-link-SHLIBPREFIX;. The suffix used for loadable module file names. On Mac OS X, this is null; on other systems, this is the same as $SHLIBSUFFIX. The linker. The command line used to link object files into an executable. The string displayed when object files are linked into an executable. If this is not set, then &cv-link-LINKCOM; (the command line) is displayed. env = Environment(LINKCOMSTR = "Linking $TARGET") General user options passed to the linker. Note that this variable should not contain (or similar) options for linking with the libraries listed in &cv-link-LIBS;, nor (or similar) library search path options that scons generates automatically from &cv-link-LIBPATH;. See &cv-link-_LIBFLAGS; above, for the variable that expands to library-link options, and &cv-link-_LIBDIRFLAGS; above, for the variable that expands to library search path options. The linker for programs that use shared libraries. The command line used to link programs using shared libraries. The string displayed when programs using shared libraries are linked. If this is not set, then &cv-link-SHLINKCOM; (the command line) is displayed. env = Environment(SHLINKCOMSTR = "Linking shared $TARGET") General user options passed to the linker for programs using shared libraries. Note that this variable should not contain (or similar) options for linking with the libraries listed in &cv-link-LIBS;, nor (or similar) include search path options that scons generates automatically from &cv-link-LIBPATH;. See &cv-link-_LIBFLAGS; above, for the variable that expands to library-link options, and &cv-link-_LIBDIRFLAGS; above, for the variable that expands to library search path options. scons-doc-2.3.0/src/engine/SCons/Tool/PharLapCommonTests.py0000644000175000017500000000621012114661557024314 0ustar dktrkranzdktrkranz# # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/PharLapCommonTests.py 2013/03/03 09:48:35 garyo" import unittest import os.path import os import sys import SCons.Errors from SCons.Tool.PharLapCommon import * class PharLapCommonTestCase(unittest.TestCase): def test_addPathIfNotExists(self): """Test the addPathIfNotExists() function""" env_dict = { 'FOO' : os.path.normpath('/foo/bar') + os.pathsep + \ os.path.normpath('/baz/blat'), 'BAR' : os.path.normpath('/foo/bar') + os.pathsep + \ os.path.normpath('/baz/blat'), 'BLAT' : [ os.path.normpath('/foo/bar'), os.path.normpath('/baz/blat') ] } addPathIfNotExists(env_dict, 'FOO', os.path.normpath('/foo/bar')) addPathIfNotExists(env_dict, 'BAR', os.path.normpath('/bar/foo')) addPathIfNotExists(env_dict, 'BAZ', os.path.normpath('/foo/baz')) addPathIfNotExists(env_dict, 'BLAT', os.path.normpath('/baz/blat')) addPathIfNotExists(env_dict, 'BLAT', os.path.normpath('/baz/foo')) assert env_dict['FOO'] == os.path.normpath('/foo/bar') + os.pathsep + \ os.path.normpath('/baz/blat'), env_dict['FOO'] assert env_dict['BAR'] == os.path.normpath('/bar/foo') + os.pathsep + \ os.path.normpath('/foo/bar') + os.pathsep + \ os.path.normpath('/baz/blat'), env_dict['BAR'] assert env_dict['BAZ'] == os.path.normpath('/foo/baz'), env_dict['BAZ'] assert env_dict['BLAT'] == [ os.path.normpath('/baz/foo'), os.path.normpath('/foo/bar'), os.path.normpath('/baz/blat') ], env_dict['BLAT' ] if __name__ == "__main__": suite = unittest.makeSuite(PharLapCommonTestCase, 'test_') if not unittest.TextTestRunner().run(suite).wasSuccessful(): sys.exit(1) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/zip.py0000644000175000017500000000640112114661560021367 0ustar dktrkranzdktrkranz"""SCons.Tool.zip Tool-specific initialization for zip. There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Tool.Tool() selection method. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/zip.py 2013/03/03 09:48:35 garyo" import os.path import SCons.Builder import SCons.Defaults import SCons.Node.FS import SCons.Util try: import zipfile internal_zip = 1 except ImportError: internal_zip = 0 if internal_zip: zipcompression = zipfile.ZIP_DEFLATED def zip(target, source, env): compression = env.get('ZIPCOMPRESSION', 0) zf = zipfile.ZipFile(str(target[0]), 'w', compression) for s in source: if s.isdir(): for dirpath, dirnames, filenames in os.walk(str(s)): for fname in filenames: path = os.path.join(dirpath, fname) if os.path.isfile(path): zf.write(path) else: zf.write(str(s)) zf.close() else: zipcompression = 0 zip = "$ZIP $ZIPFLAGS ${TARGET.abspath} $SOURCES" zipAction = SCons.Action.Action(zip, varlist=['ZIPCOMPRESSION']) ZipBuilder = SCons.Builder.Builder(action = SCons.Action.Action('$ZIPCOM', '$ZIPCOMSTR'), source_factory = SCons.Node.FS.Entry, source_scanner = SCons.Defaults.DirScanner, suffix = '$ZIPSUFFIX', multi = 1) def generate(env): """Add Builders and construction variables for zip to an Environment.""" try: bld = env['BUILDERS']['Zip'] except KeyError: bld = ZipBuilder env['BUILDERS']['Zip'] = bld env['ZIP'] = 'zip' env['ZIPFLAGS'] = SCons.Util.CLVar('') env['ZIPCOM'] = zipAction env['ZIPCOMPRESSION'] = zipcompression env['ZIPSUFFIX'] = '.zip' def exists(env): return internal_zip or env.Detect('zip') # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/xgettext.py0000644000175000017500000003145012114661560022443 0ustar dktrkranzdktrkranz""" xgettext tool Tool specific initialization of `xgettext` tool. """ # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/xgettext.py 2013/03/03 09:48:35 garyo" ############################################################################# class _CmdRunner(object): """ Callabe object, which runs shell command storing its stdout and stderr to variables. It also provides `strfunction()` method, which shall be used by scons Action objects to print command string. """ def __init__( self, command, commandstr = None): self.out = None self.err = None self.status = None self.command = command self.commandstr = commandstr def __call__(self, target, source, env): import SCons.Action import subprocess import os import sys kw = { 'stdin' : 'devnull', 'stdout' : subprocess.PIPE, 'stderr' : subprocess.PIPE, 'universal_newlines' : True, 'shell' : True } command = env.subst(self.command, target = target, source = source) proc = SCons.Action._subproc(env, command, **kw) self.out, self.err = proc.communicate() self.status = proc.wait() if self.err: sys.stderr.write(unicode(self.err)) return self.status def strfunction(self, target, source, env): import os comstr = self.commandstr if env.subst(comstr, target = target, source = source) == "": comstr = self.command s = env.subst(comstr, target = target, source = source) return s ############################################################################# ############################################################################# def _update_pot_file(target, source, env): """ Action function for `POTUpdate` builder """ import re import os import SCons.Action nop = lambda target, source, env : 0 # Save scons cwd and os cwd (NOTE: they may be different. After the job, we # revert ech one to its original state). save_cwd = env.fs.getcwd() save_os_cwd = os.getcwd() chdir = target[0].dir chdir_str = repr(chdir.get_abspath()) # Print chdir message (employ SCons.Action.Action for that. It knows better # than me how to to this correctly). env.Execute(SCons.Action.Action(nop, "Entering " + chdir_str)) # Go to target's directory and do our job env.fs.chdir(chdir, 1) # Go into target's directory try: cmd = _CmdRunner('$XGETTEXTCOM', '$XGETTEXTCOMSTR') action = SCons.Action.Action(cmd, strfunction=cmd.strfunction) status = action([ target[0] ], source, env) except: # Something went wrong. env.Execute(SCons.Action.Action(nop, "Leaving " + chdir_str)) # Revert working dirs to previous state and re-throw exception. env.fs.chdir(save_cwd, 0) os.chdir(save_os_cwd) raise # Print chdir message. env.Execute(SCons.Action.Action(nop, "Leaving " + chdir_str)) # Revert working dirs to previous state. env.fs.chdir(save_cwd, 0) os.chdir(save_os_cwd) # If the command was not successfull, return error code. if status: return status new_content = cmd.out if not new_content: # When xgettext finds no internationalized messages, no *.pot is created # (because we don't want to bother translators with empty POT files). needs_update = False explain = "no internationalized messages encountered" else: if target[0].exists(): # If the file already exists, it's left unaltered unless its messages # are outdated (w.r.t. to these recovered by xgettext from sources). old_content = target[0].get_text_contents() re_cdate = re.compile(r'^"POT-Creation-Date: .*"$[\r\n]?', re.M) old_content_nocdate = re.sub(re_cdate,"",old_content) new_content_nocdate = re.sub(re_cdate,"",new_content) if(old_content_nocdate == new_content_nocdate): # Messages are up-to-date needs_update = False explain = "messages in file found to be up-to-date" else: # Messages are outdated needs_update = True explain = "messages in file were outdated" else: # No POT file found, create new one needs_update = True explain = "new file" if needs_update: # Print message employing SCons.Action.Action for that. msg = "Writting " + repr(str(target[0])) + " (" + explain + ")" env.Execute(SCons.Action.Action(nop, msg)) f = open(str(target[0]),"w") f.write(new_content) f.close() return 0 else: # Print message employing SCons.Action.Action for that. msg = "Not writting " + repr(str(target[0])) + " (" + explain + ")" env.Execute(SCons.Action.Action(nop, msg)) return 0 ############################################################################# ############################################################################# from SCons.Builder import BuilderBase ############################################################################# class _POTBuilder(BuilderBase): def _execute(self, env, target, source, *args): if not target: if env.has_key('POTDOMAIN') and env['POTDOMAIN']: domain = env['POTDOMAIN'] else: domain = 'messages' target = [ domain ] return BuilderBase._execute(self, env, target, source, *args) ############################################################################# ############################################################################# def _scan_xgettext_from_files(target, source, env, files = None, path = None): """ Parses `POTFILES.in`-like file and returns list of extracted file names. """ import re import SCons.Util import SCons.Node.FS if files is None: return 0 if not SCons.Util.is_List(files): files = [ files ] if path is None: if env.has_key('XGETTEXTPATH'): path = env['XGETTEXTPATH'] else: path = [] if not SCons.Util.is_List(path): path = [ path ] path = SCons.Util.flatten(path) dirs = () for p in path: if not isinstance(p, SCons.Node.FS.Base): if SCons.Util.is_String(p): p = env.subst(p, source = source, target = target) p = env.arg2nodes(p, env.fs.Dir) dirs += tuple(p) # cwd is the default search path (when no path is defined by user) if not dirs: dirs = (env.fs.getcwd(),) # Parse 'POTFILE.in' files. re_comment = re.compile(r'^#[^\n\r]*$\r?\n?', re.M) re_emptyln = re.compile(r'^[ \t\r]*$\r?\n?', re.M) re_trailws = re.compile(r'[ \t\r]+$') for f in files: # Find files in search path $XGETTEXTPATH if isinstance(f, SCons.Node.FS.Base) and f.rexists(): contents = f.get_text_contents() contents = re_comment.sub("", contents) contents = re_emptyln.sub("", contents) contents = re_trailws.sub("", contents) depnames = contents.splitlines() for depname in depnames: depfile = SCons.Node.FS.find_file(depname, dirs) if not depfile: depfile = env.arg2nodes(depname, dirs[0].File) env.Depends(target, depfile) return 0 ############################################################################# ############################################################################# def _pot_update_emitter(target, source, env): """ Emitter function for `POTUpdate` builder """ from SCons.Tool.GettextCommon import _POTargetFactory import SCons.Util import SCons.Node.FS if env.has_key('XGETTEXTFROM'): xfrom = env['XGETTEXTFROM'] else: return target, source if not SCons.Util.is_List(xfrom): xfrom = [ xfrom ] xfrom = SCons.Util.flatten(xfrom) # Prepare list of 'POTFILE.in' files. files = [] for xf in xfrom: if not isinstance(xf, SCons.Node.FS.Base): if SCons.Util.is_String(xf): # Interpolate variables in strings xf = env.subst(xf, source = source, target = target) xf = env.arg2nodes(xf) files.extend(xf) if files: env.Depends(target, files) _scan_xgettext_from_files(target, source, env, files) return target, source ############################################################################# ############################################################################# from SCons.Environment import _null ############################################################################# def _POTUpdateBuilderWrapper(env, target=None, source=_null, **kw): return env._POTUpdateBuilder(target, source, **kw) ############################################################################# ############################################################################# def _POTUpdateBuilder(env, **kw): """ Creates `POTUpdate` builder object """ import SCons.Action from SCons.Tool.GettextCommon import _POTargetFactory kw['action'] = SCons.Action.Action(_update_pot_file, None) kw['suffix'] = '$POTSUFFIX' kw['target_factory'] = _POTargetFactory(env, alias='$POTUPDATE_ALIAS').File kw['emitter'] = _pot_update_emitter return _POTBuilder(**kw) ############################################################################# ############################################################################# def generate(env,**kw): """ Generate `xgettext` tool """ import SCons.Util from SCons.Tool.GettextCommon import RPaths, _detect_xgettext try: env['XGETTEXT'] = _detect_xgettext(env) except: env['XGETTEXT'] = 'xgettext' # NOTE: sources="$SOURCES" would work as well. However, we use following # construction to convert absolute paths provided by scons onto paths # relative to current working dir. Note, that scons expands $SOURCE(S) to # absolute paths for sources $SOURCE(s) outside of current subtree (e.g. in # "../"). With source=$SOURCE these absolute paths would be written to the # resultant *.pot file (and its derived *.po files) as references to lines in # source code (e.g. referring lines in *.c files). Such references would be # correct (e.g. in poedit) only on machine on which *.pot was generated and # would be of no use on other hosts (having a copy of source code located # in different place in filesystem). sources = '$( ${_concat( "", SOURCES, "", __env__, XgettextRPaths, TARGET' \ + ', SOURCES)} $)' # NOTE: the output from $XGETTEXTCOM command must go to stdout, not to a file. # This is required by the POTUpdate builder's action. xgettextcom = '$XGETTEXT $XGETTEXTFLAGS $_XGETTEXTPATHFLAGS' \ + ' $_XGETTEXTFROMFLAGS -o - ' + sources xgettextpathflags = '$( ${_concat( XGETTEXTPATHPREFIX, XGETTEXTPATH' \ + ', XGETTEXTPATHSUFFIX, __env__, RDirs, TARGET, SOURCES)} $)' xgettextfromflags = '$( ${_concat( XGETTEXTFROMPREFIX, XGETTEXTFROM' \ + ', XGETTEXTFROMSUFFIX, __env__, target=TARGET, source=SOURCES)} $)' env.SetDefault( _XGETTEXTDOMAIN = '${TARGET.filebase}', XGETTEXTFLAGS = [ ], XGETTEXTCOM = xgettextcom, XGETTEXTCOMSTR = '', XGETTEXTPATH = [ ], XGETTEXTPATHPREFIX = '-D', XGETTEXTPATHSUFFIX = '', XGETTEXTFROM = None, XGETTEXTFROMPREFIX = '-f', XGETTEXTFROMSUFFIX = '', _XGETTEXTPATHFLAGS = xgettextpathflags, _XGETTEXTFROMFLAGS = xgettextfromflags, POTSUFFIX = ['.pot'], POTUPDATE_ALIAS = 'pot-update', XgettextRPaths = RPaths(env) ) env.Append( BUILDERS = { '_POTUpdateBuilder' : _POTUpdateBuilder(env) } ) env.AddMethod(_POTUpdateBuilderWrapper, 'POTUpdate') env.AlwaysBuild(env.Alias('$POTUPDATE_ALIAS')) ############################################################################# ############################################################################# def exists(env): """ Check, whether the tool exists """ from SCons.Tool.GettextCommon import _xgettext_exists try: return _xgettext_exists(env) except: return False ############################################################################# # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/hpcc.xml0000644000175000017500000000070512114661560021653 0ustar dktrkranzdktrkranz Set construction variables for the aCC on HP/UX systems. Calls the &t-cXX; tool for additional variables. CXX SHCXXFLAGS CXXVERSION scons-doc-2.3.0/src/engine/SCons/Tool/fortran.py0000644000175000017500000000401612114661560022240 0ustar dktrkranzdktrkranz"""SCons.Tool.fortran Tool-specific initialization for a generic Posix f77/f90 Fortran compiler. There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Tool.Tool() selection method. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/fortran.py 2013/03/03 09:48:35 garyo" import re import SCons.Action import SCons.Defaults import SCons.Scanner.Fortran import SCons.Tool import SCons.Util from SCons.Tool.FortranCommon import add_all_to_env, add_fortran_to_env compilers = ['f95', 'f90', 'f77'] def generate(env): add_all_to_env(env) add_fortran_to_env(env) fc = env.Detect(compilers) or 'f77' env['SHFORTRAN'] = fc env['FORTRAN'] = fc def exists(env): return env.Detect(compilers) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/msginit.xml0000644000175000017500000001327112114661560022412 0ustar dktrkranzdktrkranz This scons tool is a part of scons &t-link-gettext; toolset. It provides scons interface to msginit(1) program, which creates new PO file, initializing the meta information with values from user's environment (or options). MSGINIT MSGINITCOM MSGINITCOMSTR MSGINITFLAGS POAUTOINIT POCREATE_ALIAS POSUFFIX POTSUFFIX _MSGINITLOCALE POTDOMAIN LINGUAS_FILE POAUTOINIT This builder belongs to &t-link-msginit; tool. The builder initializes missing PO file(s) if &cv-link-POAUTOINIT; is set. If &cv-link-POAUTOINIT; is not set (default), &b-POInit; prints instruction for user (that is supposed to be a translator), telling how the PO file should be initialized. In normal projects you should not use &b-POInit; and use &b-link-POUpdate; instead. &b-link-POUpdate; chooses intelligently between msgmerge(1) and msginit(1). &b-POInit; always uses msginit(1) and should be regarded as builder for special purposes or for temporary use (e.g. for quick, one time initialization of a bunch of PO files) or for tests. Target nodes defined through &b-POInit; are not built by default (they're Ignored from '.' node) but are added to special Alias ('po-create' by default). The alias name may be changed through the &cv-link-POCREATE_ALIAS; construction variable. All PO files defined through &b-POInit; may be easily initialized by scons po-create. Example 1. Initialize en.po and pl.po from messages.pot: # ... env.POInit(['en', 'pl']) # messages.pot --> [en.po, pl.po] Example 2. Initialize en.po and pl.po from foo.pot: # ... env.POInit(['en', 'pl'], ['foo']) # foo.pot --> [en.po, pl.po] Example 3. Initialize en.po and pl.po from foo.pot but using &cv-link-POTDOMAIN; construction variable: # ... env.POInit(['en', 'pl'], POTDOMAIN='foo') # foo.pot --> [en.po, pl.po] Example 4. Initialize PO files for languages defined in LINGUAS file. The files will be initialized from template messages.pot: # ... env.POInit(LINGUAS_FILE = 1) # needs 'LINGUAS' file Example 5. Initialize en.po and pl.pl PO files plus files for languages defined in LINGUAS file. The files will be initialized from template messages.pot: # ... env.POInit(['en', 'pl'], LINGUAS_FILE = 1) Example 6. You may preconfigure your environment first, and then initialize PO files: # ... env['POAUTOINIT'] = 1 env['LINGUAS_FILE'] = 1 env['POTDOMAIN'] = 'foo' env.POInit() which has same efect as: # ... env.POInit(POAUTOINIT = 1, LINGUAS_FILE = 1, POTDOMAIN = 'foo') Common alias for all PO files created with &b-POInit; builder (default: 'po-create'). See &t-link-msginit; tool and &b-link-POInit; builder. Suffix used for PO files (default: '.po') See &t-link-msginit; tool and &b-link-POInit; builder. Path to msginit(1) program (found via Detect()). See &t-link-msginit; tool and &b-link-POInit; builder. Complete command line to run msginit(1) program. See &t-link-msginit; tool and &b-link-POInit; builder. String to display when msginit(1) is invoked (default: '', which means ``print &cv-link-MSGINITCOM;''). See &t-link-msginit; tool and &b-link-POInit; builder. List of additional flags to msginit(1) (default: []). See &t-link-msginit; tool and &b-link-POInit; builder. Internal ``macro''. Computes locale (language) name based on target filename (default: '${TARGET.filebase}' ). See &t-link-msginit; tool and &b-link-POInit; builder. scons-doc-2.3.0/src/engine/SCons/Tool/gfortran.py0000644000175000017500000000442112114661560022407 0ustar dktrkranzdktrkranz"""SCons.Tool.gfortran Tool-specific initialization for gfortran, the GNU Fortran 95/Fortran 2003 compiler. There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Tool.Tool() selection method. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/gfortran.py 2013/03/03 09:48:35 garyo" import SCons.Util import fortran def generate(env): """Add Builders and construction variables for gfortran to an Environment.""" fortran.generate(env) for dialect in ['F77', 'F90', 'FORTRAN', 'F95', 'F03']: env['%s' % dialect] = 'gfortran' env['SH%s' % dialect] = '$%s' % dialect if env['PLATFORM'] in ['cygwin', 'win32']: env['SH%sFLAGS' % dialect] = SCons.Util.CLVar('$%sFLAGS' % dialect) else: env['SH%sFLAGS' % dialect] = SCons.Util.CLVar('$%sFLAGS -fPIC' % dialect) env['INC%sPREFIX' % dialect] = "-I" env['INC%sSUFFIX' % dialect] = "" def exists(env): return env.Detect('gfortran') # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/intelc.py0000644000175000017500000005504712114661560022055 0ustar dktrkranzdktrkranz"""SCons.Tool.icl Tool-specific initialization for the Intel C/C++ compiler. Supports Linux and Windows compilers, v7 and up. There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Tool.Tool() selection method. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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. from __future__ import division __revision__ = "src/engine/SCons/Tool/intelc.py 2013/03/03 09:48:35 garyo" import math, sys, os.path, glob, string, re is_windows = sys.platform == 'win32' is_win64 = is_windows and (os.environ['PROCESSOR_ARCHITECTURE'] == 'AMD64' or ('PROCESSOR_ARCHITEW6432' in os.environ and os.environ['PROCESSOR_ARCHITEW6432'] == 'AMD64')) is_linux = sys.platform.startswith('linux') is_mac = sys.platform == 'darwin' if is_windows: import SCons.Tool.msvc elif is_linux: import SCons.Tool.gcc elif is_mac: import SCons.Tool.gcc import SCons.Util import SCons.Warnings # Exceptions for this tool class IntelCError(SCons.Errors.InternalError): pass class MissingRegistryError(IntelCError): # missing registry entry pass class MissingDirError(IntelCError): # dir not found pass class NoRegistryModuleError(IntelCError): # can't read registry at all pass def uniquify(s): """Return a sequence containing only one copy of each unique element from input sequence s. Does not preserve order. Input sequence must be hashable (i.e. must be usable as a dictionary key).""" u = {} for x in s: u[x] = 1 return list(u.keys()) def linux_ver_normalize(vstr): """Normalize a Linux compiler version number. Intel changed from "80" to "9.0" in 2005, so we assume if the number is greater than 60 it's an old-style number and otherwise new-style. Always returns an old-style float like 80 or 90 for compatibility with Windows. Shades of Y2K!""" # Check for version number like 9.1.026: return 91.026 # XXX needs to be updated for 2011+ versions (like 2011.11.344 which is compiler v12.1.5) m = re.match(r'([0-9]+)\.([0-9]+)\.([0-9]+)', vstr) if m: vmaj,vmin,build = m.groups() return float(vmaj) * 10. + float(vmin) + float(build) / 1000.; else: f = float(vstr) if is_windows: return f else: if f < 60: return f * 10.0 else: return f def check_abi(abi): """Check for valid ABI (application binary interface) name, and map into canonical one""" if not abi: return None abi = abi.lower() # valid_abis maps input name to canonical name if is_windows: valid_abis = {'ia32' : 'ia32', 'x86' : 'ia32', 'ia64' : 'ia64', 'em64t' : 'em64t', 'amd64' : 'em64t'} if is_linux: valid_abis = {'ia32' : 'ia32', 'x86' : 'ia32', 'x86_64' : 'x86_64', 'em64t' : 'x86_64', 'amd64' : 'x86_64'} if is_mac: valid_abis = {'ia32' : 'ia32', 'x86' : 'ia32', 'x86_64' : 'x86_64', 'em64t' : 'x86_64'} try: abi = valid_abis[abi] except KeyError: raise SCons.Errors.UserError("Intel compiler: Invalid ABI %s, valid values are %s"% \ (abi, list(valid_abis.keys()))) return abi def vercmp(a, b): """Compare strings as floats, but Intel changed Linux naming convention at 9.0""" return cmp(linux_ver_normalize(b), linux_ver_normalize(a)) def get_version_from_list(v, vlist): """See if we can match v (string) in vlist (list of strings) Linux has to match in a fuzzy way.""" if is_windows: # Simple case, just find it in the list if v in vlist: return v else: return None else: # Fuzzy match: normalize version number first, but still return # original non-normalized form. fuzz = 0.001 for vi in vlist: if math.fabs(linux_ver_normalize(vi) - linux_ver_normalize(v)) < fuzz: return vi # Not found return None def get_intel_registry_value(valuename, version=None, abi=None): """ Return a value from the Intel compiler registry tree. (Windows only) """ # Open the key: if is_win64: K = 'Software\\Wow6432Node\\Intel\\Compilers\\C++\\' + version + '\\'+abi.upper() else: K = 'Software\\Intel\\Compilers\\C++\\' + version + '\\'+abi.upper() try: k = SCons.Util.RegOpenKeyEx(SCons.Util.HKEY_LOCAL_MACHINE, K) except SCons.Util.RegError: raise MissingRegistryError("%s was not found in the registry, for Intel compiler version %s, abi='%s'"%(K, version,abi)) # Get the value: try: v = SCons.Util.RegQueryValueEx(k, valuename)[0] return v # or v.encode('iso-8859-1', 'replace') to remove unicode? except SCons.Util.RegError: raise MissingRegistryError("%s\\%s was not found in the registry."%(K, valuename)) def get_all_compiler_versions(): """Returns a sorted list of strings, like "70" or "80" or "9.0" with most recent compiler version first. """ versions=[] if is_windows: if is_win64: keyname = 'Software\\WoW6432Node\\Intel\\Compilers\\C++' else: keyname = 'Software\\Intel\\Compilers\\C++' try: k = SCons.Util.RegOpenKeyEx(SCons.Util.HKEY_LOCAL_MACHINE, keyname) except WindowsError: return [] i = 0 versions = [] try: while i < 100: subkey = SCons.Util.RegEnumKey(k, i) # raises EnvironmentError # Check that this refers to an existing dir. # This is not 100% perfect but should catch common # installation issues like when the compiler was installed # and then the install directory deleted or moved (rather # than uninstalling properly), so the registry values # are still there. ok = False for try_abi in ('IA32', 'IA32e', 'IA64', 'EM64T'): try: d = get_intel_registry_value('ProductDir', subkey, try_abi) except MissingRegistryError: continue # not found in reg, keep going if os.path.exists(d): ok = True if ok: versions.append(subkey) else: try: # Registry points to nonexistent dir. Ignore this # version. value = get_intel_registry_value('ProductDir', subkey, 'IA32') except MissingRegistryError, e: # Registry key is left dangling (potentially # after uninstalling). print \ "scons: *** Ignoring the registry key for the Intel compiler version %s.\n" \ "scons: *** It seems that the compiler was uninstalled and that the registry\n" \ "scons: *** was not cleaned up properly.\n" % subkey else: print "scons: *** Ignoring "+str(value) i = i + 1 except EnvironmentError: # no more subkeys pass elif is_linux or is_mac: for d in glob.glob('/opt/intel_cc_*'): # Typical dir here is /opt/intel_cc_80. m = re.search(r'cc_(.*)$', d) if m: versions.append(m.group(1)) for d in glob.glob('/opt/intel/cc*/*'): # Typical dir here is /opt/intel/cc/9.0 for IA32, # /opt/intel/cce/9.0 for EMT64 (AMD64) m = re.search(r'([0-9][0-9.]*)$', d) if m: versions.append(m.group(1)) for d in glob.glob('/opt/intel/Compiler/*'): # Typical dir here is /opt/intel/Compiler/11.1 m = re.search(r'([0-9][0-9.]*)$', d) if m: versions.append(m.group(1)) for d in glob.glob('/opt/intel/composerxe-*'): # Typical dir here is /opt/intel/composerxe-2011.4.184 m = re.search(r'([0-9][0-9.]*)$', d) if m: versions.append(m.group(1)) for d in glob.glob('/opt/intel/composer_xe_*'): # Typical dir here is /opt/intel/composer_xe_2011_sp1.11.344 # The _sp1 is useless, the installers are named 2011.9.x, 2011.10.x, 2011.11.x m = re.search(r'([0-9]{0,4})(?:_sp\d*)?\.([0-9][0-9.]*)$', d) if m: versions.append("%s.%s"%(m.group(1), m.group(2))) def keyfunc(str): """Given a dot-separated version string, return a tuple of ints representing it.""" return [int(x) for x in str.split('.')] # split into ints, sort, then remove dups return sorted(uniquify(versions), key=keyfunc, reverse=True) def get_intel_compiler_top(version, abi): """ Return the main path to the top-level dir of the Intel compiler, using the given version. The compiler will be in /bin/icl.exe (icc on linux), the include dir is /include, etc. """ if is_windows: if not SCons.Util.can_read_reg: raise NoRegistryModuleError("No Windows registry module was found") top = get_intel_registry_value('ProductDir', version, abi) # pre-11, icl was in Bin. 11 and later, it's in Bin/ apparently. if not os.path.exists(os.path.join(top, "Bin", "icl.exe")) \ and not os.path.exists(os.path.join(top, "Bin", abi, "icl.exe")): raise MissingDirError("Can't find Intel compiler in %s"%(top)) elif is_mac or is_linux: def find_in_2008style_dir(version): # first dir is new (>=9.0) style, second is old (8.0) style. dirs=('/opt/intel/cc/%s', '/opt/intel_cc_%s') if abi == 'x86_64': dirs=('/opt/intel/cce/%s',) # 'e' stands for 'em64t', aka x86_64 aka amd64 top=None for d in dirs: if os.path.exists(os.path.join(d%version, "bin", "icc")): top = d%version break return top def find_in_2010style_dir(version): dirs=('/opt/intel/Compiler/%s/*'%version) # typically /opt/intel/Compiler/11.1/064 (then bin/intel64/icc) dirs=glob.glob(dirs) # find highest sub-version number by reverse sorting and picking first existing one. dirs.sort() dirs.reverse() top=None for d in dirs: if (os.path.exists(os.path.join(d, "bin", "ia32", "icc")) or os.path.exists(os.path.join(d, "bin", "intel64", "icc"))): top = d break return top def find_in_2011style_dir(version): # The 2011 (compiler v12) dirs are inconsistent, so just redo the search from # get_all_compiler_versions and look for a match (search the newest form first) top=None for d in glob.glob('/opt/intel/composer_xe_*'): # Typical dir here is /opt/intel/composer_xe_2011_sp1.11.344 # The _sp1 is useless, the installers are named 2011.9.x, 2011.10.x, 2011.11.x m = re.search(r'([0-9]{0,4})(?:_sp\d*)?\.([0-9][0-9.]*)$', d) if m: cur_ver = "%s.%s"%(m.group(1), m.group(2)) if cur_ver == version and \ (os.path.exists(os.path.join(d, "bin", "ia32", "icc")) or os.path.exists(os.path.join(d, "bin", "intel64", "icc"))): top = d break if not top: for d in glob.glob('/opt/intel/composerxe-*'): # Typical dir here is /opt/intel/composerxe-2011.4.184 m = re.search(r'([0-9][0-9.]*)$', d) if m and m.group(1) == verison and \ (os.path.exists(os.path.join(d, "bin", "ia32", "icc")) or os.path.exists(os.path.join(d, "bin", "intel64", "icc"))): top = d break return top top = find_in_2011style_dir(version) or find_in_2010style_dir(version) or find_in_2008style_dir(version) # print "INTELC: top=",top if not top: raise MissingDirError("Can't find version %s Intel compiler in %s (abi='%s')"%(version,top, abi)) return top def generate(env, version=None, abi=None, topdir=None, verbose=0): """Add Builders and construction variables for Intel C/C++ compiler to an Environment. args: version: (string) compiler version to use, like "80" abi: (string) 'win32' or whatever Itanium version wants topdir: (string) compiler top dir, like "c:\Program Files\Intel\Compiler70" If topdir is used, version and abi are ignored. verbose: (int) if >0, prints compiler version used. """ if not (is_mac or is_linux or is_windows): # can't handle this platform return if is_windows: SCons.Tool.msvc.generate(env) elif is_linux: SCons.Tool.gcc.generate(env) elif is_mac: SCons.Tool.gcc.generate(env) # if version is unspecified, use latest vlist = get_all_compiler_versions() if not version: if vlist: version = vlist[0] else: # User may have specified '90' but we need to get actual dirname '9.0'. # get_version_from_list does that mapping. v = get_version_from_list(version, vlist) if not v: raise SCons.Errors.UserError("Invalid Intel compiler version %s: "%version + \ "installed versions are %s"%(', '.join(vlist))) version = v # if abi is unspecified, use ia32 # alternatives are ia64 for Itanium, or amd64 or em64t or x86_64 (all synonyms here) abi = check_abi(abi) if abi is None: if is_mac or is_linux: # Check if we are on 64-bit linux, default to 64 then. uname_m = os.uname()[4] if uname_m == 'x86_64': abi = 'x86_64' else: abi = 'ia32' else: if is_win64: abi = 'em64t' else: abi = 'ia32' if version and not topdir: try: topdir = get_intel_compiler_top(version, abi) except (SCons.Util.RegError, IntelCError): topdir = None if not topdir: # Normally this is an error, but it might not be if the compiler is # on $PATH and the user is importing their env. class ICLTopDirWarning(SCons.Warnings.Warning): pass if (is_mac or is_linux) and not env.Detect('icc') or \ is_windows and not env.Detect('icl'): SCons.Warnings.enableWarningClass(ICLTopDirWarning) SCons.Warnings.warn(ICLTopDirWarning, "Failed to find Intel compiler for version='%s', abi='%s'"% (str(version), str(abi))) else: # should be cleaned up to say what this other version is # since in this case we have some other Intel compiler installed SCons.Warnings.enableWarningClass(ICLTopDirWarning) SCons.Warnings.warn(ICLTopDirWarning, "Can't find Intel compiler top dir for version='%s', abi='%s'"% (str(version), str(abi))) if topdir: archdir={'x86_64': 'intel64', 'amd64' : 'intel64', 'em64t' : 'intel64', 'x86' : 'ia32', 'i386' : 'ia32', 'ia32' : 'ia32' }[abi] # for v11 and greater if os.path.exists(os.path.join(topdir, 'bin', archdir)): bindir="bin/%s"%archdir libdir="lib/%s"%archdir else: bindir="bin" libdir="lib" if verbose: print "Intel C compiler: using version %s (%g), abi %s, in '%s/%s'"%\ (repr(version), linux_ver_normalize(version),abi,topdir,bindir) if is_linux: # Show the actual compiler version by running the compiler. os.system('%s/%s/icc --version'%(topdir,bindir)) if is_mac: # Show the actual compiler version by running the compiler. os.system('%s/%s/icc --version'%(topdir,bindir)) env['INTEL_C_COMPILER_TOP'] = topdir if is_linux: paths={'INCLUDE' : 'include', 'LIB' : libdir, 'PATH' : bindir, 'LD_LIBRARY_PATH' : libdir} for p in paths.keys(): env.PrependENVPath(p, os.path.join(topdir, paths[p])) if is_mac: paths={'INCLUDE' : 'include', 'LIB' : libdir, 'PATH' : bindir, 'LD_LIBRARY_PATH' : libdir} for p in paths.keys(): env.PrependENVPath(p, os.path.join(topdir, paths[p])) if is_windows: # env key reg valname default subdir of top paths=(('INCLUDE', 'IncludeDir', 'Include'), ('LIB' , 'LibDir', 'Lib'), ('PATH' , 'BinDir', 'Bin')) # We are supposed to ignore version if topdir is set, so set # it to the emptry string if it's not already set. if version is None: version = '' # Each path has a registry entry, use that or default to subdir for p in paths: try: path=get_intel_registry_value(p[1], version, abi) # These paths may have $(ICInstallDir) # which needs to be substituted with the topdir. path=path.replace('$(ICInstallDir)', topdir + os.sep) except IntelCError: # Couldn't get it from registry: use default subdir of topdir env.PrependENVPath(p[0], os.path.join(topdir, p[2])) else: env.PrependENVPath(p[0], path.split(os.pathsep)) # print "ICL %s: %s, final=%s"%(p[0], path, str(env['ENV'][p[0]])) if is_windows: env['CC'] = 'icl' env['CXX'] = 'icl' env['LINK'] = 'xilink' else: env['CC'] = 'icc' env['CXX'] = 'icpc' # Don't reset LINK here; # use smart_link which should already be here from link.py. #env['LINK'] = '$CC' env['AR'] = 'xiar' env['LD'] = 'xild' # not used by default # This is not the exact (detailed) compiler version, # just the major version as determined above or specified # by the user. It is a float like 80 or 90, in normalized form for Linux # (i.e. even for Linux 9.0 compiler, still returns 90 rather than 9.0) if version: env['INTEL_C_COMPILER_VERSION']=linux_ver_normalize(version) if is_windows: # Look for license file dir # in system environment, registry, and default location. envlicdir = os.environ.get("INTEL_LICENSE_FILE", '') K = ('SOFTWARE\Intel\Licenses') try: k = SCons.Util.RegOpenKeyEx(SCons.Util.HKEY_LOCAL_MACHINE, K) reglicdir = SCons.Util.RegQueryValueEx(k, "w_cpp")[0] except (AttributeError, SCons.Util.RegError): reglicdir = "" defaultlicdir = r'C:\Program Files\Common Files\Intel\Licenses' licdir = None for ld in [envlicdir, reglicdir]: # If the string contains an '@', then assume it's a network # license (port@system) and good by definition. if ld and (ld.find('@') != -1 or os.path.exists(ld)): licdir = ld break if not licdir: licdir = defaultlicdir if not os.path.exists(licdir): class ICLLicenseDirWarning(SCons.Warnings.Warning): pass SCons.Warnings.enableWarningClass(ICLLicenseDirWarning) SCons.Warnings.warn(ICLLicenseDirWarning, "Intel license dir was not found." " Tried using the INTEL_LICENSE_FILE environment variable (%s), the registry (%s) and the default path (%s)." " Using the default path as a last resort." % (envlicdir, reglicdir, defaultlicdir)) env['ENV']['INTEL_LICENSE_FILE'] = licdir def exists(env): if not (is_mac or is_linux or is_windows): # can't handle this platform return 0 try: versions = get_all_compiler_versions() except (SCons.Util.RegError, IntelCError): versions = None detected = versions is not None and len(versions) > 0 if not detected: # try env.Detect, maybe that will work if is_windows: return env.Detect('icl') elif is_linux: return env.Detect('icc') elif is_mac: return env.Detect('icc') return detected # end of file # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/sunlink.xml0000644000175000017500000000057512114661560022426 0ustar dktrkranzdktrkranz Sets construction variables for the Sun linker. SHLINKFLAGS RPATHPREFIX RPATHSUFFIX scons-doc-2.3.0/src/engine/SCons/Tool/386asm.xml0000644000175000017500000000074512114661557021771 0ustar dktrkranzdktrkranz Sets construction variables for the 386ASM assembler for the Phar Lap ETS embedded operating system. AS ASFLAGS ASPPFLAGS ASCOM ASPPCOM CC CPPFLAGS _CPPDEFFLAGS _CPPINCFLAGS scons-doc-2.3.0/src/engine/SCons/Tool/aixc++.xml0000644000175000017500000000063712114661560022014 0ustar dktrkranzdktrkranz Sets construction variables for the IMB xlc / Visual Age C++ compiler. CXX SHCXX CXXVERSION SHOBJSUFFIX scons-doc-2.3.0/src/engine/SCons/Tool/g77.py0000644000175000017500000000470212114661560021173 0ustar dktrkranzdktrkranz"""engine.SCons.Tool.g77 Tool-specific initialization for g77. There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Tool.Tool() selection method. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/g77.py 2013/03/03 09:48:35 garyo" import SCons.Util from SCons.Tool.FortranCommon import add_all_to_env, add_f77_to_env compilers = ['g77', 'f77'] def generate(env): """Add Builders and construction variables for g77 to an Environment.""" add_all_to_env(env) add_f77_to_env(env) fcomp = env.Detect(compilers) or 'g77' if env['PLATFORM'] in ['cygwin', 'win32']: env['SHFORTRANFLAGS'] = SCons.Util.CLVar('$FORTRANFLAGS') env['SHF77FLAGS'] = SCons.Util.CLVar('$F77FLAGS') else: env['SHFORTRANFLAGS'] = SCons.Util.CLVar('$FORTRANFLAGS -fPIC') env['SHF77FLAGS'] = SCons.Util.CLVar('$F77FLAGS -fPIC') env['FORTRAN'] = fcomp env['SHFORTRAN'] = '$FORTRAN' env['F77'] = fcomp env['SHF77'] = '$F77' env['INCFORTRANPREFIX'] = "-I" env['INCFORTRANSUFFIX'] = "" env['INCF77PREFIX'] = "-I" env['INCF77SUFFIX'] = "" def exists(env): return env.Detect(compilers) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/ifl.xml0000644000175000017500000000074012114661560021507 0ustar dktrkranzdktrkranz Sets construction variables for the Intel Fortran compiler. FORTRAN FORTRANCOM FORTRANPPCOM SHFORTRANCOM SHFORTRANPPCOM FORTRANFLAGS _FORTRANINCFLAGS CPPFLAGS _CPPDEFFLAGS scons-doc-2.3.0/src/engine/SCons/Tool/gas.xml0000644000175000017500000000056612114661560021515 0ustar dktrkranzdktrkranz Sets construction variables for the &gas; assembler. Calls the &t-as; module. AS scons-doc-2.3.0/src/engine/SCons/Tool/gfortran.xml0000644000175000017500000000071212114661560022556 0ustar dktrkranzdktrkranz Sets construction variables for the GNU F95/F2003 GNU compiler. FORTRAN F77 F90 F95 SHFORTRAN SHF77 SHF90 SHF95 SHFORTRANFLAGS SHF77FLAGS SHF90FLAGS SHF95FLAGS scons-doc-2.3.0/src/engine/SCons/Tool/jar.xml0000644000175000017500000000450712114661560021516 0ustar dktrkranzdktrkranz Sets construction variables for the &jar; utility. JAR JARFLAGS JARCOM JARSUFFIX JARCOMSTR Builds a Java archive (.jar) file from the specified list of sources. Any directories in the source list will be searched for .class files). Any .java files in the source list will be compiled to .class files by calling the &b-link-Java; Builder. If the &cv-link-JARCHDIR; value is set, the &jar; command will change to the specified directory using the option. If &cv-JARCHDIR; is not set explicitly, &SCons; will use the top of any subdirectory tree in which Java .class were built by the &b-link-Java; Builder. If the contents any of the source files begin with the string Manifest-Version, the file is assumed to be a manifest and is passed to the &jar; command with the option set. env.Jar(target = 'foo.jar', source = 'classes') env.Jar(target = 'bar.jar', source = ['bar1.java', 'bar2.java']) The Java archive tool. The directory to which the Java archive tool should change (using the option). The command line used to call the Java archive tool. The string displayed when the Java archive tool is called If this is not set, then &cv-link-JARCOM; (the command line) is displayed. env = Environment(JARCOMSTR = "JARchiving $SOURCES into $TARGET") General options passed to the Java archive tool. By default this is set to to create the necessary jar file. The suffix for Java archives: .jar by default. scons-doc-2.3.0/src/engine/SCons/Tool/yacc.py0000644000175000017500000001171512114661560021510 0ustar dktrkranzdktrkranz"""SCons.Tool.yacc Tool-specific initialization for yacc. There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Tool.Tool() selection method. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/yacc.py 2013/03/03 09:48:35 garyo" import os.path import SCons.Defaults import SCons.Tool import SCons.Util YaccAction = SCons.Action.Action("$YACCCOM", "$YACCCOMSTR") def _yaccEmitter(target, source, env, ysuf, hsuf): yaccflags = env.subst("$YACCFLAGS", target=target, source=source) flags = SCons.Util.CLVar(yaccflags) targetBase, targetExt = os.path.splitext(SCons.Util.to_String(target[0])) if '.ym' in ysuf: # If using Objective-C target = [targetBase + ".m"] # the extension is ".m". # If -d is specified on the command line, yacc will emit a .h # or .hpp file with the same name as the .c or .cpp output file. if '-d' in flags: target.append(targetBase + env.subst(hsuf, target=target, source=source)) # If -g is specified on the command line, yacc will emit a .vcg # file with the same base name as the .y, .yacc, .ym or .yy file. if "-g" in flags: base, ext = os.path.splitext(SCons.Util.to_String(source[0])) target.append(base + env.subst("$YACCVCGFILESUFFIX")) # If -v is specirfied yacc will create the output debug file # which is not really source for any process, but should # be noted and also be cleaned # Bug #2558 if "-v" in flags: env.SideEffect(targetBase+'.output',target[0]) env.Clean(target[0],targetBase+'.output') # With --defines and --graph, the name of the file is totally defined # in the options. fileGenOptions = ["--defines=", "--graph="] for option in flags: for fileGenOption in fileGenOptions: l = len(fileGenOption) if option[:l] == fileGenOption: # A file generating option is present, so add the file # name to the list of targets. fileName = option[l:].strip() target.append(fileName) return (target, source) def yEmitter(target, source, env): return _yaccEmitter(target, source, env, ['.y', '.yacc'], '$YACCHFILESUFFIX') def ymEmitter(target, source, env): return _yaccEmitter(target, source, env, ['.ym'], '$YACCHFILESUFFIX') def yyEmitter(target, source, env): return _yaccEmitter(target, source, env, ['.yy'], '$YACCHXXFILESUFFIX') def generate(env): """Add Builders and construction variables for yacc to an Environment.""" c_file, cxx_file = SCons.Tool.createCFileBuilders(env) # C c_file.add_action('.y', YaccAction) c_file.add_emitter('.y', yEmitter) c_file.add_action('.yacc', YaccAction) c_file.add_emitter('.yacc', yEmitter) # Objective-C c_file.add_action('.ym', YaccAction) c_file.add_emitter('.ym', ymEmitter) # C++ cxx_file.add_action('.yy', YaccAction) cxx_file.add_emitter('.yy', yyEmitter) env['YACC'] = env.Detect('bison') or 'yacc' env['YACCFLAGS'] = SCons.Util.CLVar('') env['YACCCOM'] = '$YACC $YACCFLAGS -o $TARGET $SOURCES' env['YACCHFILESUFFIX'] = '.h' # Apparently, OS X now creates file.hpp like everybody else # I have no idea when it changed; it was fixed in 10.4 #if env['PLATFORM'] == 'darwin': # # Bison on Mac OS X just appends ".h" to the generated target .cc # # or .cpp file name. Hooray for delayed expansion of variables. # env['YACCHXXFILESUFFIX'] = '${TARGET.suffix}.h' #else: # env['YACCHXXFILESUFFIX'] = '.hpp' env['YACCHXXFILESUFFIX'] = '.hpp' env['YACCVCGFILESUFFIX'] = '.vcg' def exists(env): return env.Detect(['bison', 'yacc']) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/gs.py0000644000175000017500000000500512114661560021175 0ustar dktrkranzdktrkranz"""SCons.Tool.gs Tool-specific initialization for Ghostscript. There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Tool.Tool() selection method. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/gs.py 2013/03/03 09:48:35 garyo" import SCons.Action import SCons.Platform import SCons.Util # Ghostscript goes by different names on different platforms... platform = SCons.Platform.platform_default() if platform == 'os2': gs = 'gsos2' elif platform == 'win32': gs = 'gswin32c' else: gs = 'gs' GhostscriptAction = None def generate(env): """Add Builders and construction variables for Ghostscript to an Environment.""" global GhostscriptAction if GhostscriptAction is None: GhostscriptAction = SCons.Action.Action('$GSCOM', '$GSCOMSTR') import pdf pdf.generate(env) bld = env['BUILDERS']['PDF'] bld.add_action('.ps', GhostscriptAction) env['GS'] = gs env['GSFLAGS'] = SCons.Util.CLVar('-dNOPAUSE -dBATCH -sDEVICE=pdfwrite') env['GSCOM'] = '$GS $GSFLAGS -sOutputFile=$TARGET $SOURCES' def exists(env): if 'PS2PDF' in env: return env.Detect(env['PS2PDF']) else: return env.Detect(gs) or SCons.Util.WhereIs(gs) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/f95.xml0000644000175000017500000001673112114661560021347 0ustar dktrkranzdktrkranz Set construction variables for generic POSIX Fortran 95 compilers. F95 F95FLAGS F95COM F95PPCOM SHF95 SHF95FLAGS SHF95COM SHF95PPCOM _F95INCFLAGS F95COMSTR F95PPCOMSTR SHF95COMSTR SHF95PPCOMSTR The Fortran 95 compiler. You should normally set the &cv-link-FORTRAN; variable, which specifies the default Fortran compiler for all Fortran versions. You only need to set &cv-link-F95; if you need to use a specific compiler or compiler version for Fortran 95 files. The command line used to compile a Fortran 95 source file to an object file. You only need to set &cv-link-F95COM; if you need to use a specific command line for Fortran 95 files. You should normally set the &cv-link-FORTRANCOM; variable, which specifies the default command line for all Fortran versions. The string displayed when a Fortran 95 source file is compiled to an object file. If this is not set, then &cv-link-F95COM; or &cv-link-FORTRANCOM; (the command line) is displayed. The list of file extensions for which the F95 dialect will be used. By default, this is ['.f95'] The list of file extensions for which the compilation + preprocessor pass for F95 dialect will be used. By default, this is empty General user-specified options that are passed to the Fortran 95 compiler. Note that this variable does not contain (or similar) include search path options that scons generates automatically from &cv-link-F95PATH;. See &cv-link-_F95INCFLAGS; below, for the variable that expands to those options. You only need to set &cv-link-F95FLAGS; if you need to define specific user options for Fortran 95 files. You should normally set the &cv-link-FORTRANFLAGS; variable, which specifies the user-specified options passed to the default Fortran compiler for all Fortran versions. An automatically-generated construction variable containing the Fortran 95 compiler command-line options for specifying directories to be searched for include files. The value of &cv-link-_F95INCFLAGS; is created by appending &cv-link-INCPREFIX; and &cv-link-INCSUFFIX; to the beginning and end of each directory in &cv-link-F95PATH;. The list of directories that the Fortran 95 compiler will search for include directories. The implicit dependency scanner will search these directories for include files. Don't explicitly put include directory arguments in &cv-link-F95FLAGS; because the result will be non-portable and the directories will not be searched by the dependency scanner. Note: directory names in &cv-link-F95PATH; will be looked-up relative to the SConscript directory when they are used in a command. To force &scons; to look-up a directory relative to the root of the source tree use #: You only need to set &cv-link-F95PATH; if you need to define a specific include path for Fortran 95 files. You should normally set the &cv-link-FORTRANPATH; variable, which specifies the include path for the default Fortran compiler for all Fortran versions. env = Environment(F95PATH='#/include') The directory look-up can also be forced using the &Dir;() function: include = Dir('include') env = Environment(F95PATH=include) The directory list will be added to command lines through the automatically-generated &cv-link-_F95INCFLAGS; construction variable, which is constructed by appending the values of the &cv-link-INCPREFIX; and &cv-link-INCSUFFIX; construction variables to the beginning and end of each directory in &cv-link-F95PATH;. Any command lines you define that need the F95PATH directory list should include &cv-link-_F95INCFLAGS;: env = Environment(F95COM="my_compiler $_F95INCFLAGS -c -o $TARGET $SOURCE") The command line used to compile a Fortran 95 source file to an object file after first running the file through the C preprocessor. Any options specified in the &cv-link-F95FLAGS; and &cv-link-CPPFLAGS; construction variables are included on this command line. You only need to set &cv-link-F95PPCOM; if you need to use a specific C-preprocessor command line for Fortran 95 files. You should normally set the &cv-link-FORTRANPPCOM; variable, which specifies the default C-preprocessor command line for all Fortran versions. The string displayed when a Fortran 95 source file is compiled to an object file after first running the file through the C preprocessor. If this is not set, then &cv-link-F95PPCOM; or &cv-link-FORTRANPPCOM; (the command line) is displayed. The Fortran 95 compiler used for generating shared-library objects. You should normally set the &cv-link-SHFORTRAN; variable, which specifies the default Fortran compiler for all Fortran versions. You only need to set &cv-link-SHF95; if you need to use a specific compiler or compiler version for Fortran 95 files. The command line used to compile a Fortran 95 source file to a shared-library object file. You only need to set &cv-link-SHF95COM; if you need to use a specific command line for Fortran 95 files. You should normally set the &cv-link-SHFORTRANCOM; variable, which specifies the default command line for all Fortran versions. The string displayed when a Fortran 95 source file is compiled to a shared-library object file. If this is not set, then &cv-link-SHF95COM; or &cv-link-SHFORTRANCOM; (the command line) is displayed. Options that are passed to the Fortran 95 compiler to generated shared-library objects. You only need to set &cv-link-SHF95FLAGS; if you need to define specific user options for Fortran 95 files. You should normally set the &cv-link-SHFORTRANFLAGS; variable, which specifies the user-specified options passed to the default Fortran compiler for all Fortran versions. The command line used to compile a Fortran 95 source file to a shared-library object file after first running the file through the C preprocessor. Any options specified in the &cv-link-SHF95FLAGS; and &cv-link-CPPFLAGS; construction variables are included on this command line. You only need to set &cv-link-SHF95PPCOM; if you need to use a specific C-preprocessor command line for Fortran 95 files. You should normally set the &cv-link-SHFORTRANPPCOM; variable, which specifies the default C-preprocessor command line for all Fortran versions. The string displayed when a Fortran 95 source file is compiled to a shared-library object file after first running the file through the C preprocessor. If this is not set, then &cv-link-SHF95PPCOM; or &cv-link-SHFORTRANPPCOM; (the command line) is displayed. scons-doc-2.3.0/src/engine/SCons/Tool/pdf.xml0000644000175000017500000000222412114661560021505 0ustar dktrkranzdktrkranz Sets construction variables for the Portable Document Format builder. PDFPREFIX PDFSUFFIX Builds a .pdf file from a .dvi input file (or, by extension, a .tex, .ltx, or .latex input file). The suffix specified by the &cv-link-PDFSUFFIX; construction variable (.pdf by default) is added automatically to the target if it is not already present. Example: # builds from aaa.tex env.PDF(target = 'aaa.pdf', source = 'aaa.tex') # builds bbb.pdf from bbb.dvi env.PDF(target = 'bbb', source = 'bbb.dvi') The prefix used for PDF file names. The suffix used for PDF file names. scons-doc-2.3.0/src/engine/SCons/Tool/RCS.py0000644000175000017500000000446312114661557021230 0ustar dktrkranzdktrkranz"""SCons.Tool.RCS.py Tool-specific initialization for RCS. There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Tool.Tool() selection method. """ # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/RCS.py 2013/03/03 09:48:35 garyo" import SCons.Action import SCons.Builder import SCons.Util def generate(env): """Add a Builder factory function and construction variables for RCS to an Environment.""" def RCSFactory(env=env): """ """ import SCons.Warnings as W W.warn(W.DeprecatedSourceCodeWarning, """The RCS() factory is deprecated and there is no replacement.""") act = SCons.Action.Action('$RCS_COCOM', '$RCS_COCOMSTR') return SCons.Builder.Builder(action = act, env = env) #setattr(env, 'RCS', RCSFactory) env.RCS = RCSFactory env['RCS'] = 'rcs' env['RCS_CO'] = 'co' env['RCS_COFLAGS'] = SCons.Util.CLVar('') env['RCS_COCOM'] = '$RCS_CO $RCS_COFLAGS $TARGET' def exists(env): return env.Detect('rcs') # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/PharLapCommon.py0000644000175000017500000001217212114661557023275 0ustar dktrkranzdktrkranz"""SCons.Tool.PharLapCommon This module contains common code used by all Tools for the Phar Lap ETS tool chain. Right now, this is linkloc and 386asm. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/PharLapCommon.py 2013/03/03 09:48:35 garyo" import os import os.path import SCons.Errors import SCons.Util import re def getPharLapPath(): """Reads the registry to find the installed path of the Phar Lap ETS development kit. Raises UserError if no installed version of Phar Lap can be found.""" if not SCons.Util.can_read_reg: raise SCons.Errors.InternalError("No Windows registry module was found") try: k=SCons.Util.RegOpenKeyEx(SCons.Util.HKEY_LOCAL_MACHINE, 'SOFTWARE\\Pharlap\\ETS') val, type = SCons.Util.RegQueryValueEx(k, 'BaseDir') # The following is a hack...there is (not surprisingly) # an odd issue in the Phar Lap plug in that inserts # a bunch of junk data after the phar lap path in the # registry. We must trim it. idx=val.find('\0') if idx >= 0: val = val[:idx] return os.path.normpath(val) except SCons.Util.RegError: raise SCons.Errors.UserError("Cannot find Phar Lap ETS path in the registry. Is it installed properly?") REGEX_ETS_VER = re.compile(r'#define\s+ETS_VER\s+([0-9]+)') def getPharLapVersion(): """Returns the version of the installed ETS Tool Suite as a decimal number. This version comes from the ETS_VER #define in the embkern.h header. For example, '#define ETS_VER 1010' (which is what Phar Lap 10.1 defines) would cause this method to return 1010. Phar Lap 9.1 does not have such a #define, but this method will return 910 as a default. Raises UserError if no installed version of Phar Lap can be found.""" include_path = os.path.join(getPharLapPath(), os.path.normpath("include/embkern.h")) if not os.path.exists(include_path): raise SCons.Errors.UserError("Cannot find embkern.h in ETS include directory.\nIs Phar Lap ETS installed properly?") mo = REGEX_ETS_VER.search(open(include_path, 'r').read()) if mo: return int(mo.group(1)) # Default return for Phar Lap 9.1 return 910 def addPathIfNotExists(env_dict, key, path, sep=os.pathsep): """This function will take 'key' out of the dictionary 'env_dict', then add the path 'path' to that key if it is not already there. This treats the value of env_dict[key] as if it has a similar format to the PATH variable...a list of paths separated by tokens. The 'path' will get added to the list if it is not already there.""" try: is_list = 1 paths = env_dict[key] if not SCons.Util.is_List(env_dict[key]): paths = paths.split(sep) is_list = 0 if os.path.normcase(path) not in list(map(os.path.normcase, paths)): paths = [ path ] + paths if is_list: env_dict[key] = paths else: env_dict[key] = sep.join(paths) except KeyError: env_dict[key] = path def addPharLapPaths(env): """This function adds the path to the Phar Lap binaries, includes, and libraries, if they are not already there.""" ph_path = getPharLapPath() try: env_dict = env['ENV'] except KeyError: env_dict = {} env['ENV'] = env_dict addPathIfNotExists(env_dict, 'PATH', os.path.join(ph_path, 'bin')) addPathIfNotExists(env_dict, 'INCLUDE', os.path.join(ph_path, 'include')) addPathIfNotExists(env_dict, 'LIB', os.path.join(ph_path, 'lib')) addPathIfNotExists(env_dict, 'LIB', os.path.join(ph_path, os.path.normpath('lib/vclib'))) env['PHARLAP_PATH'] = getPharLapPath() env['PHARLAP_VERSION'] = str(getPharLapVersion()) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/sunc++.xml0000644000175000017500000000062612114661560022036 0ustar dktrkranzdktrkranz Sets construction variables for the Sun C++ compiler. CXX SHCXX CXXVERSION SHCXXFLAGS SHOBJPREFIX SHOBJSUFFIX scons-doc-2.3.0/src/engine/SCons/Tool/mwcc.py0000644000175000017500000001537212114661560021525 0ustar dktrkranzdktrkranz"""SCons.Tool.mwcc Tool-specific initialization for the Metrowerks CodeWarrior compiler. There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Tool.Tool() selection method. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/mwcc.py 2013/03/03 09:48:35 garyo" import os import os.path import SCons.Util def set_vars(env): """Set MWCW_VERSION, MWCW_VERSIONS, and some codewarrior environment vars MWCW_VERSIONS is set to a list of objects representing installed versions MWCW_VERSION is set to the version object that will be used for building. MWCW_VERSION can be set to a string during Environment construction to influence which version is chosen, otherwise the latest one from MWCW_VERSIONS is used. Returns true if at least one version is found, false otherwise """ desired = env.get('MWCW_VERSION', '') # return right away if the variables are already set if isinstance(desired, MWVersion): return 1 elif desired is None: return 0 versions = find_versions() version = None if desired: for v in versions: if str(v) == desired: version = v elif versions: version = versions[-1] env['MWCW_VERSIONS'] = versions env['MWCW_VERSION'] = version if version is None: return 0 env.PrependENVPath('PATH', version.clpath) env.PrependENVPath('PATH', version.dllpath) ENV = env['ENV'] ENV['CWFolder'] = version.path ENV['LM_LICENSE_FILE'] = version.license plus = lambda x: '+%s' % x ENV['MWCIncludes'] = os.pathsep.join(map(plus, version.includes)) ENV['MWLibraries'] = os.pathsep.join(map(plus, version.libs)) return 1 def find_versions(): """Return a list of MWVersion objects representing installed versions""" versions = [] ### This function finds CodeWarrior by reading from the registry on ### Windows. Some other method needs to be implemented for other ### platforms, maybe something that calls env.WhereIs('mwcc') if SCons.Util.can_read_reg: try: HLM = SCons.Util.HKEY_LOCAL_MACHINE product = 'SOFTWARE\\Metrowerks\\CodeWarrior\\Product Versions' product_key = SCons.Util.RegOpenKeyEx(HLM, product) i = 0 while True: name = product + '\\' + SCons.Util.RegEnumKey(product_key, i) name_key = SCons.Util.RegOpenKeyEx(HLM, name) try: version = SCons.Util.RegQueryValueEx(name_key, 'VERSION') path = SCons.Util.RegQueryValueEx(name_key, 'PATH') mwv = MWVersion(version[0], path[0], 'Win32-X86') versions.append(mwv) except SCons.Util.RegError: pass i = i + 1 except SCons.Util.RegError: pass return versions class MWVersion(object): def __init__(self, version, path, platform): self.version = version self.path = path self.platform = platform self.clpath = os.path.join(path, 'Other Metrowerks Tools', 'Command Line Tools') self.dllpath = os.path.join(path, 'Bin') # The Metrowerks tools don't store any configuration data so they # are totally dumb when it comes to locating standard headers, # libraries, and other files, expecting all the information # to be handed to them in environment variables. The members set # below control what information scons injects into the environment ### The paths below give a normal build environment in CodeWarrior for ### Windows, other versions of CodeWarrior might need different paths. msl = os.path.join(path, 'MSL') support = os.path.join(path, '%s Support' % platform) self.license = os.path.join(path, 'license.dat') self.includes = [msl, support] self.libs = [msl, support] def __str__(self): return self.version CSuffixes = ['.c', '.C'] CXXSuffixes = ['.cc', '.cpp', '.cxx', '.c++', '.C++'] def generate(env): """Add Builders and construction variables for the mwcc to an Environment.""" import SCons.Defaults import SCons.Tool set_vars(env) static_obj, shared_obj = SCons.Tool.createObjBuilders(env) for suffix in CSuffixes: static_obj.add_action(suffix, SCons.Defaults.CAction) shared_obj.add_action(suffix, SCons.Defaults.ShCAction) for suffix in CXXSuffixes: static_obj.add_action(suffix, SCons.Defaults.CXXAction) shared_obj.add_action(suffix, SCons.Defaults.ShCXXAction) env['CCCOMFLAGS'] = '$CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS -nolink -o $TARGET $SOURCES' env['CC'] = 'mwcc' env['CCCOM'] = '$CC $CFLAGS $CCFLAGS $CCCOMFLAGS' env['CXX'] = 'mwcc' env['CXXCOM'] = '$CXX $CXXFLAGS $CCCOMFLAGS' env['SHCC'] = '$CC' env['SHCCFLAGS'] = '$CCFLAGS' env['SHCFLAGS'] = '$CFLAGS' env['SHCCCOM'] = '$SHCC $SHCFLAGS $SHCCFLAGS $CCCOMFLAGS' env['SHCXX'] = '$CXX' env['SHCXXFLAGS'] = '$CXXFLAGS' env['SHCXXCOM'] = '$SHCXX $SHCXXFLAGS $CCCOMFLAGS' env['CFILESUFFIX'] = '.c' env['CXXFILESUFFIX'] = '.cpp' env['CPPDEFPREFIX'] = '-D' env['CPPDEFSUFFIX'] = '' env['INCPREFIX'] = '-I' env['INCSUFFIX'] = '' #env['PCH'] = ? #env['PCHSTOP'] = ? def exists(env): return set_vars(env) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/msginit.py0000644000175000017500000001117412114661560022242 0ustar dktrkranzdktrkranz""" msginit tool Tool specific initialization of msginit tool. """ # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/msginit.py 2013/03/03 09:48:35 garyo" import SCons.Warnings import SCons.Builder import re ############################################################################# def _optional_no_translator_flag(env): """ Return '--no-translator' flag if we run *msginit(1)* in non-interactive mode.""" import SCons.Util if env.has_key('POAUTOINIT'): autoinit = env['POAUTOINIT'] else: autoinit = False if autoinit: return [SCons.Util.CLVar('--no-translator')] else: return [SCons.Util.CLVar('')] ############################################################################# ############################################################################# def _POInitBuilder(env, **kw): """ Create builder object for `POInit` builder. """ import SCons.Action from SCons.Tool.GettextCommon import _init_po_files, _POFileBuilder action = SCons.Action.Action(_init_po_files, None) return _POFileBuilder(env, action=action, target_alias='$POCREATE_ALIAS') ############################################################################# ############################################################################# from SCons.Environment import _null ############################################################################# def _POInitBuilderWrapper(env, target=None, source=_null, **kw): """ Wrapper for _POFileBuilder. We use it to make user's life easier. This wrapper checks for `$POTDOMAIN` construction variable (or override in `**kw`) and treats it appropriatelly. """ if source is _null: if 'POTDOMAIN' in kw: domain = kw['POTDOMAIN'] elif env.has_key('POTDOMAIN'): domain = env['POTDOMAIN'] else: domain = 'messages' source = [ domain ] # NOTE: Suffix shall be appended automatically return env._POInitBuilder(target, source, **kw) ############################################################################# ############################################################################# def generate(env,**kw): """ Generate the `msginit` tool """ import SCons.Util from SCons.Tool.GettextCommon import _detect_msginit try: env['MSGINIT'] = _detect_msginit(env) except: env['MSGINIT'] = 'msginit' msginitcom = '$MSGINIT ${_MSGNoTranslator(__env__)} -l ${_MSGINITLOCALE}' \ + ' $MSGINITFLAGS -i $SOURCE -o $TARGET' # NOTE: We set POTSUFFIX here, in case the 'xgettext' is not loaded # (sometimes we really don't need it) env.SetDefault( POSUFFIX = ['.po'], POTSUFFIX = ['.pot'], _MSGINITLOCALE = '${TARGET.filebase}', _MSGNoTranslator = _optional_no_translator_flag, MSGINITCOM = msginitcom, MSGINITCOMSTR = '', MSGINITFLAGS = [ ], POAUTOINIT = False, POCREATE_ALIAS = 'po-create' ) env.Append( BUILDERS = { '_POInitBuilder' : _POInitBuilder(env) } ) env.AddMethod(_POInitBuilderWrapper, 'POInit') env.AlwaysBuild(env.Alias('$POCREATE_ALIAS')) ############################################################################# ############################################################################# def exists(env): """ Check if the tool exists """ from SCons.Tool.GettextCommon import _msginit_exists try: return _msginit_exists(env) except: return False ############################################################################# # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/hpc++.xml0000644000175000017500000000053312114661560021635 0ustar dktrkranzdktrkranz Set construction variables for the compilers aCC on HP/UX systems. scons-doc-2.3.0/src/engine/SCons/Tool/javah.py0000644000175000017500000001110612114661560021654 0ustar dktrkranzdktrkranz"""SCons.Tool.javah Tool-specific initialization for javah. There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Tool.Tool() selection method. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/javah.py 2013/03/03 09:48:35 garyo" import os.path import SCons.Action import SCons.Builder import SCons.Node.FS import SCons.Tool.javac import SCons.Util def emit_java_headers(target, source, env): """Create and return lists of Java stub header files that will be created from a set of class files. """ class_suffix = env.get('JAVACLASSSUFFIX', '.class') classdir = env.get('JAVACLASSDIR') if not classdir: try: s = source[0] except IndexError: classdir = '.' else: try: classdir = s.attributes.java_classdir except AttributeError: classdir = '.' classdir = env.Dir(classdir).rdir() if str(classdir) == '.': c_ = None else: c_ = str(classdir) + os.sep slist = [] for src in source: try: classname = src.attributes.java_classname except AttributeError: classname = str(src) if c_ and classname[:len(c_)] == c_: classname = classname[len(c_):] if class_suffix and classname[-len(class_suffix):] == class_suffix: classname = classname[:-len(class_suffix)] classname = SCons.Tool.javac.classname(classname) s = src.rfile() s.attributes.java_classname = classname slist.append(s) s = source[0].rfile() if not hasattr(s.attributes, 'java_classdir'): s.attributes.java_classdir = classdir if target[0].__class__ is SCons.Node.FS.File: tlist = target else: if not isinstance(target[0], SCons.Node.FS.Dir): target[0].__class__ = SCons.Node.FS.Dir target[0]._morph() tlist = [] for s in source: fname = s.attributes.java_classname.replace('.', '_') + '.h' t = target[0].File(fname) t.attributes.java_lookupdir = target[0] tlist.append(t) return tlist, source def JavaHOutFlagGenerator(target, source, env, for_signature): try: t = target[0] except (AttributeError, IndexError, TypeError): t = target try: return '-d ' + str(t.attributes.java_lookupdir) except AttributeError: return '-o ' + str(t) def getJavaHClassPath(env,target, source, for_signature): path = "${SOURCE.attributes.java_classdir}" if 'JAVACLASSPATH' in env and env['JAVACLASSPATH']: path = SCons.Util.AppendPath(path, env['JAVACLASSPATH']) return "-classpath %s" % (path) def generate(env): """Add Builders and construction variables for javah to an Environment.""" java_javah = SCons.Tool.CreateJavaHBuilder(env) java_javah.emitter = emit_java_headers env['_JAVAHOUTFLAG'] = JavaHOutFlagGenerator env['JAVAH'] = 'javah' env['JAVAHFLAGS'] = SCons.Util.CLVar('') env['_JAVAHCLASSPATH'] = getJavaHClassPath env['JAVAHCOM'] = '$JAVAH $JAVAHFLAGS $_JAVAHOUTFLAG $_JAVAHCLASSPATH ${SOURCES.attributes.java_classname}' env['JAVACLASSSUFFIX'] = '.class' def exists(env): return env.Detect('javah') # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/masm.xml0000644000175000017500000000076612114661560021702 0ustar dktrkranzdktrkranz Sets construction variables for the Microsoft assembler. AS ASFLAGS ASPPFLAGS ASCOM ASPPCOM ASCOMSTR ASPPCOMSTR CPPFLAGS _CPPDEFFLAGS _CPPINCFLAGS scons-doc-2.3.0/src/engine/SCons/Tool/as.xml0000644000175000017500000000363612114661560021347 0ustar dktrkranzdktrkranz Sets construction variables for the &as; assembler. AS ASFLAGS ASCOM ASPPFLAGS ASPPCOM CC CPPFLAGS _CPPDEFFLAGS _CPPINCFLAGS The assembler. The command line used to generate an object file from an assembly-language source file. The string displayed when an object file is generated from an assembly-language source file. If this is not set, then &cv-link-ASCOM; (the command line) is displayed. env = Environment(ASCOMSTR = "Assembling $TARGET") General options passed to the assembler. The command line used to assemble an assembly-language source file into an object file after first running the file through the C preprocessor. Any options specified in the &cv-link-ASFLAGS; and &cv-link-CPPFLAGS; construction variables are included on this command line. The string displayed when an object file is generated from an assembly-language source file after first running the file through the C preprocessor. If this is not set, then &cv-link-ASPPCOM; (the command line) is displayed. env = Environment(ASPPCOMSTR = "Assembling $TARGET") General options when an assembling an assembly-language source file into an object file after first running the file through the C preprocessor. The default is to use the value of &cv-link-ASFLAGS;. scons-doc-2.3.0/src/engine/SCons/Tool/tar.py0000644000175000017500000000475212114661560021362 0ustar dktrkranzdktrkranz"""SCons.Tool.tar Tool-specific initialization for tar. There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Tool.Tool() selection method. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/tar.py 2013/03/03 09:48:35 garyo" import SCons.Action import SCons.Builder import SCons.Defaults import SCons.Node.FS import SCons.Util tars = ['tar', 'gtar'] TarAction = SCons.Action.Action('$TARCOM', '$TARCOMSTR') TarBuilder = SCons.Builder.Builder(action = TarAction, source_factory = SCons.Node.FS.Entry, source_scanner = SCons.Defaults.DirScanner, suffix = '$TARSUFFIX', multi = 1) def generate(env): """Add Builders and construction variables for tar to an Environment.""" try: bld = env['BUILDERS']['Tar'] except KeyError: bld = TarBuilder env['BUILDERS']['Tar'] = bld env['TAR'] = env.Detect(tars) or 'gtar' env['TARFLAGS'] = SCons.Util.CLVar('-c') env['TARCOM'] = '$TAR $TARFLAGS -f $TARGET $SOURCES' env['TARSUFFIX'] = '.tar' def exists(env): return env.Detect(tars) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/cc.xml0000644000175000017500000000712312114661560021324 0ustar dktrkranzdktrkranz Sets construction variables for generic POSIX C copmilers. FRAMEWORKS FRAMEWORKPATH CC CFLAGS CCFLAGS CCCOM SHCC SHCFLAGS SHCCFLAGS SHCCCOM CPPDEFPREFIX CPPDEFSUFFIX INCPREFIX INCSUFFIX SHOBJSUFFIX CFILESUFFIX PLATFORM The C compiler. The command line used to compile a C source file to a (static) object file. Any options specified in the &cv-link-CFLAGS;, &cv-link-CCFLAGS; and &cv-link-CPPFLAGS; construction variables are included on this command line. The string displayed when a C source file is compiled to a (static) object file. If this is not set, then &cv-link-CCCOM; (the command line) is displayed. env = Environment(CCCOMSTR = "Compiling static object $TARGET") General options that are passed to the C and C++ compilers. General options that are passed to the C compiler (C only; not C++). User-specified C preprocessor options. These will be included in any command that uses the C preprocessor, including not just compilation of C and C++ source files via the &cv-link-CCCOM;, &cv-link-SHCCCOM;, &cv-link-CXXCOM; and &cv-link-SHCXXCOM; command lines, but also the &cv-link-FORTRANPPCOM;, &cv-link-SHFORTRANPPCOM;, &cv-link-F77PPCOM; and &cv-link-SHF77PPCOM; command lines used to compile a Fortran source file, and the &cv-link-ASPPCOM; command line used to assemble an assembly language source file, after first running each file through the C preprocessor. Note that this variable does not contain (or similar) include search path options that scons generates automatically from &cv-link-CPPPATH;. See &cv-link-_CPPINCFLAGS;, below, for the variable that expands to those options. The list of suffixes of files that will be scanned for C preprocessor implicit dependencies (#include lines). The default list is: [".c", ".C", ".cxx", ".cpp", ".c++", ".cc", ".h", ".H", ".hxx", ".hpp", ".hh", ".F", ".fpp", ".FPP", ".m", ".mm", ".S", ".spp", ".SPP"] The C compiler used for generating shared-library objects. The command line used to compile a C source file to a shared-library object file. Any options specified in the &cv-link-SHCFLAGS;, &cv-link-SHCCFLAGS; and &cv-link-CPPFLAGS; construction variables are included on this command line. The string displayed when a C source file is compiled to a shared object file. If this is not set, then &cv-link-SHCCCOM; (the command line) is displayed. env = Environment(SHCCCOMSTR = "Compiling shared object $TARGET") Options that are passed to the C and C++ compilers to generate shared-library objects. Options that are passed to the C compiler (only; not C++) to generate shared-library objects. scons-doc-2.3.0/src/engine/SCons/Tool/packaging/0000755000175000017500000000000012114661560022136 5ustar dktrkranzdktrkranzscons-doc-2.3.0/src/engine/SCons/Tool/packaging/src_tarbz2.py0000644000175000017500000000334312114661560024566 0ustar dktrkranzdktrkranz"""SCons.Tool.Packaging.tarbz2 The tarbz2 SRC packager. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/packaging/src_tarbz2.py 2013/03/03 09:48:35 garyo" from SCons.Tool.packaging import putintopackageroot def package(env, target, source, PACKAGEROOT, **kw): bld = env['BUILDERS']['Tar'] bld.set_suffix('.tar.bz2') target, source = putintopackageroot(target, source, env, PACKAGEROOT, honor_install_location=0) return bld(env, target, source, TARFLAGS='-jc') # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/packaging/src_targz.py0000644000175000017500000000333612114661560024513 0ustar dktrkranzdktrkranz"""SCons.Tool.Packaging.targz The targz SRC packager. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/packaging/src_targz.py 2013/03/03 09:48:35 garyo" from SCons.Tool.packaging import putintopackageroot def package(env, target, source, PACKAGEROOT, **kw): bld = env['BUILDERS']['Tar'] bld.set_suffix('.tar.gz') target, source = putintopackageroot(target, source, env, PACKAGEROOT, honor_install_location=0) return bld(env, target, source, TARFLAGS='-zc') # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/packaging/zip.py0000644000175000017500000000337312114661560023320 0ustar dktrkranzdktrkranz"""SCons.Tool.Packaging.zip The zip SRC packager. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/packaging/zip.py 2013/03/03 09:48:35 garyo" from SCons.Tool.packaging import stripinstallbuilder, putintopackageroot def package(env, target, source, PACKAGEROOT, **kw): bld = env['BUILDERS']['Zip'] bld.set_suffix('.zip') target, source = stripinstallbuilder(target, source, env) target, source = putintopackageroot(target, source, env, PACKAGEROOT) return bld(env, target, source) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/packaging/msi.py0000644000175000017500000004746112114661560023314 0ustar dktrkranzdktrkranz"""SCons.Tool.packaging.msi The msi packager. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/packaging/msi.py 2013/03/03 09:48:35 garyo" import os import SCons from SCons.Action import Action from SCons.Builder import Builder from xml.dom.minidom import * from xml.sax.saxutils import escape from SCons.Tool.packaging import stripinstallbuilder # # Utility functions # def convert_to_id(s, id_set): """ Some parts of .wxs need an Id attribute (for example: The File and Directory directives. The charset is limited to A-Z, a-z, digits, underscores, periods. Each Id must begin with a letter or with a underscore. Google for "CNDL0015" for information about this. Requirements: * the string created must only contain chars from the target charset. * the string created must have a minimal editing distance from the original string. * the string created must be unique for the whole .wxs file. Observation: * There are 62 chars in the charset. Idea: * filter out forbidden characters. Check for a collision with the help of the id_set. Add the number of the number of the collision at the end of the created string. Furthermore care for a correct start of the string. """ charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYabcdefghijklmnopqrstuvwxyz0123456789_.' if s[0] in '0123456789.': s += '_'+s id = [c for c in s if c in charset] # did we already generate an id for this file? try: return id_set[id][s] except KeyError: # no we did not so initialize with the id if id not in id_set: id_set[id] = { s : id } # there is a collision, generate an id which is unique by appending # the collision number else: id_set[id][s] = id + str(len(id_set[id])) return id_set[id][s] def is_dos_short_file_name(file): """ examine if the given file is in the 8.3 form. """ fname, ext = os.path.splitext(file) proper_ext = len(ext) == 0 or (2 <= len(ext) <= 4) # the ext contains the dot proper_fname = file.isupper() and len(fname) <= 8 return proper_ext and proper_fname def gen_dos_short_file_name(file, filename_set): """ see http://support.microsoft.com/default.aspx?scid=kb;en-us;Q142982 These are no complete 8.3 dos short names. The ~ char is missing and replaced with one character from the filename. WiX warns about such filenames, since a collision might occur. Google for "CNDL1014" for more information. """ # guard this to not confuse the generation if is_dos_short_file_name(file): return file fname, ext = os.path.splitext(file) # ext contains the dot # first try if it suffices to convert to upper file = file.upper() if is_dos_short_file_name(file): return file # strip forbidden characters. forbidden = '."/[]:;=, ' fname = [c for c in fname if c not in forbidden] # check if we already generated a filename with the same number: # thisis1.txt, thisis2.txt etc. duplicate, num = not None, 1 while duplicate: shortname = "%s%s" % (fname[:8-len(str(num))].upper(),\ str(num)) if len(ext) >= 2: shortname = "%s%s" % (shortname, ext[:4].upper()) duplicate, num = shortname in filename_set, num+1 assert( is_dos_short_file_name(shortname) ), 'shortname is %s, longname is %s' % (shortname, file) filename_set.append(shortname) return shortname def create_feature_dict(files): """ X_MSI_FEATURE and doc FileTag's can be used to collect files in a hierarchy. This function collects the files into this hierarchy. """ dict = {} def add_to_dict( feature, file ): if not SCons.Util.is_List( feature ): feature = [ feature ] for f in feature: if f not in dict: dict[ f ] = [ file ] else: dict[ f ].append( file ) for file in files: if hasattr( file, 'PACKAGING_X_MSI_FEATURE' ): add_to_dict(file.PACKAGING_X_MSI_FEATURE, file) elif hasattr( file, 'PACKAGING_DOC' ): add_to_dict( 'PACKAGING_DOC', file ) else: add_to_dict( 'default', file ) return dict def generate_guids(root): """ generates globally unique identifiers for parts of the xml which need them. Component tags have a special requirement. Their UUID is only allowed to change if the list of their contained resources has changed. This allows for clean removal and proper updates. To handle this requirement, the uuid is generated with an md5 hashing the whole subtree of a xml node. """ from hashlib import md5 # specify which tags need a guid and in which attribute this should be stored. needs_id = { 'Product' : 'Id', 'Package' : 'Id', 'Component' : 'Guid', } # find all XMl nodes matching the key, retrieve their attribute, hash their # subtree, convert hash to string and add as a attribute to the xml node. for (key,value) in needs_id.items(): node_list = root.getElementsByTagName(key) attribute = value for node in node_list: hash = md5(node.toxml()).hexdigest() hash_str = '%s-%s-%s-%s-%s' % ( hash[:8], hash[8:12], hash[12:16], hash[16:20], hash[20:] ) node.attributes[attribute] = hash_str def string_wxsfile(target, source, env): return "building WiX file %s"%( target[0].path ) def build_wxsfile(target, source, env): """ compiles a .wxs file from the keywords given in env['msi_spec'] and by analyzing the tree of source nodes and their tags. """ file = open(target[0].abspath, 'w') try: # Create a document with the Wix root tag doc = Document() root = doc.createElement( 'Wix' ) root.attributes['xmlns']='http://schemas.microsoft.com/wix/2003/01/wi' doc.appendChild( root ) filename_set = [] # this is to circumvent duplicates in the shortnames id_set = {} # this is to circumvent duplicates in the ids # Create the content build_wxsfile_header_section(root, env) build_wxsfile_file_section(root, source, env['NAME'], env['VERSION'], env['VENDOR'], filename_set, id_set) generate_guids(root) build_wxsfile_features_section(root, source, env['NAME'], env['VERSION'], env['SUMMARY'], id_set) build_wxsfile_default_gui(root) build_license_file(target[0].get_dir(), env) # write the xml to a file file.write( doc.toprettyxml() ) # call a user specified function if 'CHANGE_SPECFILE' in env: env['CHANGE_SPECFILE'](target, source) except KeyError, e: raise SCons.Errors.UserError( '"%s" package field for MSI is missing.' % e.args[0] ) # # setup function # def create_default_directory_layout(root, NAME, VERSION, VENDOR, filename_set): """ Create the wix default target directory layout and return the innermost directory. We assume that the XML tree delivered in the root argument already contains the Product tag. Everything is put under the PFiles directory property defined by WiX. After that a directory with the 'VENDOR' tag is placed and then a directory with the name of the project and its VERSION. This leads to the following TARGET Directory Layout: C:\\\\ Example: C:\Programme\Company\Product-1.2\ """ doc = Document() d1 = doc.createElement( 'Directory' ) d1.attributes['Id'] = 'TARGETDIR' d1.attributes['Name'] = 'SourceDir' d2 = doc.createElement( 'Directory' ) d2.attributes['Id'] = 'ProgramFilesFolder' d2.attributes['Name'] = 'PFiles' d3 = doc.createElement( 'Directory' ) d3.attributes['Id'] = 'VENDOR_folder' d3.attributes['Name'] = escape( gen_dos_short_file_name( VENDOR, filename_set ) ) d3.attributes['LongName'] = escape( VENDOR ) d4 = doc.createElement( 'Directory' ) project_folder = "%s-%s" % ( NAME, VERSION ) d4.attributes['Id'] = 'MY_DEFAULT_FOLDER' d4.attributes['Name'] = escape( gen_dos_short_file_name( project_folder, filename_set ) ) d4.attributes['LongName'] = escape( project_folder ) d1.childNodes.append( d2 ) d2.childNodes.append( d3 ) d3.childNodes.append( d4 ) root.getElementsByTagName('Product')[0].childNodes.append( d1 ) return d4 # # mandatory and optional file tags # def build_wxsfile_file_section(root, files, NAME, VERSION, VENDOR, filename_set, id_set): """ builds the Component sections of the wxs file with their included files. Files need to be specified in 8.3 format and in the long name format, long filenames will be converted automatically. Features are specficied with the 'X_MSI_FEATURE' or 'DOC' FileTag. """ root = create_default_directory_layout( root, NAME, VERSION, VENDOR, filename_set ) components = create_feature_dict( files ) factory = Document() def get_directory( node, dir ): """ returns the node under the given node representing the directory. Returns the component node if dir is None or empty. """ if dir == '' or not dir: return node Directory = node dir_parts = dir.split(os.path.sep) # to make sure that our directory ids are unique, the parent folders are # consecutively added to upper_dir upper_dir = '' # walk down the xml tree finding parts of the directory dir_parts = [d for d in dir_parts if d != ''] for d in dir_parts[:]: already_created = [c for c in Directory.childNodes if c.nodeName == 'Directory' and c.attributes['LongName'].value == escape(d)] if already_created != []: Directory = already_created[0] dir_parts.remove(d) upper_dir += d else: break for d in dir_parts: nDirectory = factory.createElement( 'Directory' ) nDirectory.attributes['LongName'] = escape( d ) nDirectory.attributes['Name'] = escape( gen_dos_short_file_name( d, filename_set ) ) upper_dir += d nDirectory.attributes['Id'] = convert_to_id( upper_dir, id_set ) Directory.childNodes.append( nDirectory ) Directory = nDirectory return Directory for file in files: drive, path = os.path.splitdrive( file.PACKAGING_INSTALL_LOCATION ) filename = os.path.basename( path ) dirname = os.path.dirname( path ) h = { # tagname : default value 'PACKAGING_X_MSI_VITAL' : 'yes', 'PACKAGING_X_MSI_FILEID' : convert_to_id(filename, id_set), 'PACKAGING_X_MSI_LONGNAME' : filename, 'PACKAGING_X_MSI_SHORTNAME' : gen_dos_short_file_name(filename, filename_set), 'PACKAGING_X_MSI_SOURCE' : file.get_path(), } # fill in the default tags given above. for k,v in [ (k, v) for (k,v) in h.items() if not hasattr(file, k) ]: setattr( file, k, v ) File = factory.createElement( 'File' ) File.attributes['LongName'] = escape( file.PACKAGING_X_MSI_LONGNAME ) File.attributes['Name'] = escape( file.PACKAGING_X_MSI_SHORTNAME ) File.attributes['Source'] = escape( file.PACKAGING_X_MSI_SOURCE ) File.attributes['Id'] = escape( file.PACKAGING_X_MSI_FILEID ) File.attributes['Vital'] = escape( file.PACKAGING_X_MSI_VITAL ) # create the Tag under which this file should appear Component = factory.createElement('Component') Component.attributes['DiskId'] = '1' Component.attributes['Id'] = convert_to_id( filename, id_set ) # hang the component node under the root node and the file node # under the component node. Directory = get_directory( root, dirname ) Directory.childNodes.append( Component ) Component.childNodes.append( File ) # # additional functions # def build_wxsfile_features_section(root, files, NAME, VERSION, SUMMARY, id_set): """ This function creates the tag based on the supplied xml tree. This is achieved by finding all s and adding them to a default target. It should be called after the tree has been built completly. We assume that a MY_DEFAULT_FOLDER Property is defined in the wxs file tree. Furthermore a top-level with the name and VERSION of the software will be created. An PACKAGING_X_MSI_FEATURE can either be a string, where the feature DESCRIPTION will be the same as its title or a Tuple, where the first part will be its title and the second its DESCRIPTION. """ factory = Document() Feature = factory.createElement('Feature') Feature.attributes['Id'] = 'complete' Feature.attributes['ConfigurableDirectory'] = 'MY_DEFAULT_FOLDER' Feature.attributes['Level'] = '1' Feature.attributes['Title'] = escape( '%s %s' % (NAME, VERSION) ) Feature.attributes['Description'] = escape( SUMMARY ) Feature.attributes['Display'] = 'expand' for (feature, files) in create_feature_dict(files).items(): SubFeature = factory.createElement('Feature') SubFeature.attributes['Level'] = '1' if SCons.Util.is_Tuple(feature): SubFeature.attributes['Id'] = convert_to_id( feature[0], id_set ) SubFeature.attributes['Title'] = escape(feature[0]) SubFeature.attributes['Description'] = escape(feature[1]) else: SubFeature.attributes['Id'] = convert_to_id( feature, id_set ) if feature=='default': SubFeature.attributes['Description'] = 'Main Part' SubFeature.attributes['Title'] = 'Main Part' elif feature=='PACKAGING_DOC': SubFeature.attributes['Description'] = 'Documentation' SubFeature.attributes['Title'] = 'Documentation' else: SubFeature.attributes['Description'] = escape(feature) SubFeature.attributes['Title'] = escape(feature) # build the componentrefs. As one of the design decision is that every # file is also a component we walk the list of files and create a # reference. for f in files: ComponentRef = factory.createElement('ComponentRef') ComponentRef.attributes['Id'] = convert_to_id( os.path.basename(f.get_path()), id_set ) SubFeature.childNodes.append(ComponentRef) Feature.childNodes.append(SubFeature) root.getElementsByTagName('Product')[0].childNodes.append(Feature) def build_wxsfile_default_gui(root): """ this function adds a default GUI to the wxs file """ factory = Document() Product = root.getElementsByTagName('Product')[0] UIRef = factory.createElement('UIRef') UIRef.attributes['Id'] = 'WixUI_Mondo' Product.childNodes.append(UIRef) UIRef = factory.createElement('UIRef') UIRef.attributes['Id'] = 'WixUI_ErrorProgressText' Product.childNodes.append(UIRef) def build_license_file(directory, spec): """ creates a License.rtf file with the content of "X_MSI_LICENSE_TEXT" in the given directory """ name, text = '', '' try: name = spec['LICENSE'] text = spec['X_MSI_LICENSE_TEXT'] except KeyError: pass # ignore this as X_MSI_LICENSE_TEXT is optional if name!='' or text!='': file = open( os.path.join(directory.get_path(), 'License.rtf'), 'w' ) file.write('{\\rtf') if text!='': file.write(text.replace('\n', '\\par ')) else: file.write(name+'\\par\\par') file.write('}') file.close() # # mandatory and optional package tags # def build_wxsfile_header_section(root, spec): """ Adds the xml file node which define the package meta-data. """ # Create the needed DOM nodes and add them at the correct position in the tree. factory = Document() Product = factory.createElement( 'Product' ) Package = factory.createElement( 'Package' ) root.childNodes.append( Product ) Product.childNodes.append( Package ) # set "mandatory" default values if 'X_MSI_LANGUAGE' not in spec: spec['X_MSI_LANGUAGE'] = '1033' # select english # mandatory sections, will throw a KeyError if the tag is not available Product.attributes['Name'] = escape( spec['NAME'] ) Product.attributes['Version'] = escape( spec['VERSION'] ) Product.attributes['Manufacturer'] = escape( spec['VENDOR'] ) Product.attributes['Language'] = escape( spec['X_MSI_LANGUAGE'] ) Package.attributes['Description'] = escape( spec['SUMMARY'] ) # now the optional tags, for which we avoid the KeyErrror exception if 'DESCRIPTION' in spec: Package.attributes['Comments'] = escape( spec['DESCRIPTION'] ) if 'X_MSI_UPGRADE_CODE' in spec: Package.attributes['X_MSI_UPGRADE_CODE'] = escape( spec['X_MSI_UPGRADE_CODE'] ) # We hardcode the media tag as our current model cannot handle it. Media = factory.createElement('Media') Media.attributes['Id'] = '1' Media.attributes['Cabinet'] = 'default.cab' Media.attributes['EmbedCab'] = 'yes' root.getElementsByTagName('Product')[0].childNodes.append(Media) # this builder is the entry-point for .wxs file compiler. wxs_builder = Builder( action = Action( build_wxsfile, string_wxsfile ), ensure_suffix = '.wxs' ) def package(env, target, source, PACKAGEROOT, NAME, VERSION, DESCRIPTION, SUMMARY, VENDOR, X_MSI_LANGUAGE, **kw): # make sure that the Wix Builder is in the environment SCons.Tool.Tool('wix').generate(env) # get put the keywords for the specfile compiler. These are the arguments # given to the package function and all optional ones stored in kw, minus # the the source, target and env one. loc = locals() del loc['kw'] kw.update(loc) del kw['source'], kw['target'], kw['env'] # strip the install builder from the source files target, source = stripinstallbuilder(target, source, env) # put the arguments into the env and call the specfile builder. env['msi_spec'] = kw specfile = wxs_builder(* [env, target, source], **kw) # now call the WiX Tool with the built specfile added as a source. msifile = env.WiX(target, specfile) # return the target and source tuple. return (msifile, source+[specfile]) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/packaging/src_zip.py0000644000175000017500000000330612114661560024163 0ustar dktrkranzdktrkranz"""SCons.Tool.Packaging.zip The zip SRC packager. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/packaging/src_zip.py 2013/03/03 09:48:35 garyo" from SCons.Tool.packaging import putintopackageroot def package(env, target, source, PACKAGEROOT, **kw): bld = env['BUILDERS']['Zip'] bld.set_suffix('.zip') target, source = putintopackageroot(target, source, env, PACKAGEROOT, honor_install_location=0) return bld(env, target, source) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/packaging/tarbz2.py0000644000175000017500000000342712114661560023722 0ustar dktrkranzdktrkranz"""SCons.Tool.Packaging.tarbz2 The tarbz2 SRC packager. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/packaging/tarbz2.py 2013/03/03 09:48:35 garyo" from SCons.Tool.packaging import stripinstallbuilder, putintopackageroot def package(env, target, source, PACKAGEROOT, **kw): bld = env['BUILDERS']['Tar'] bld.set_suffix('.tar.gz') target, source = putintopackageroot(target, source, env, PACKAGEROOT) target, source = stripinstallbuilder(target, source, env) return bld(env, target, source, TARFLAGS='-jc') # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/packaging/rpm.py0000644000175000017500000003164312114661560023315 0ustar dktrkranzdktrkranz"""SCons.Tool.Packaging.rpm The rpm packager. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/packaging/rpm.py 2013/03/03 09:48:35 garyo" import os import SCons.Builder import SCons.Tool.rpmutils from SCons.Environment import OverrideEnvironment from SCons.Tool.packaging import stripinstallbuilder, src_targz from SCons.Errors import UserError def package(env, target, source, PACKAGEROOT, NAME, VERSION, PACKAGEVERSION, DESCRIPTION, SUMMARY, X_RPM_GROUP, LICENSE, **kw): # initialize the rpm tool SCons.Tool.Tool('rpm').generate(env) bld = env['BUILDERS']['Rpm'] # Generate a UserError whenever the target name has been set explicitly, # since rpm does not allow for controlling it. This is detected by # checking if the target has been set to the default by the Package() # Environment function. if str(target[0])!="%s-%s"%(NAME, VERSION): raise UserError( "Setting target is not supported for rpm." ) else: # This should be overridable from the construction environment, # which it is by using ARCHITECTURE=. buildarchitecture = SCons.Tool.rpmutils.defaultMachine() if 'ARCHITECTURE' in kw: buildarchitecture = kw['ARCHITECTURE'] fmt = '%s-%s-%s.%s.rpm' srcrpm = fmt % (NAME, VERSION, PACKAGEVERSION, 'src') binrpm = fmt % (NAME, VERSION, PACKAGEVERSION, buildarchitecture) target = [ srcrpm, binrpm ] # get the correct arguments into the kw hash loc=locals() del loc['kw'] kw.update(loc) del kw['source'], kw['target'], kw['env'] # if no "SOURCE_URL" tag is given add a default one. if 'SOURCE_URL' not in kw: #kw['SOURCE_URL']=(str(target[0])+".tar.gz").replace('.rpm', '') kw['SOURCE_URL']=(str(target[0])+".tar.gz").replace('.rpm', '') # mangle the source and target list for the rpmbuild env = OverrideEnvironment(env, kw) target, source = stripinstallbuilder(target, source, env) target, source = addspecfile(target, source, env) target, source = collectintargz(target, source, env) # now call the rpm builder to actually build the packet. return bld(env, target, source, **kw) def collectintargz(target, source, env): """ Puts all source files into a tar.gz file. """ # the rpm tool depends on a source package, until this is chagned # this hack needs to be here that tries to pack all sources in. sources = env.FindSourceFiles() # filter out the target we are building the source list for. #sources = [s for s in sources if not (s in target)] sources = [s for s in sources if s not in target] # find the .spec file for rpm and add it since it is not necessarily found # by the FindSourceFiles function. #sources.extend( [s for s in source if str(s).rfind('.spec')!=-1] ) spec_file = lambda s: str(s).rfind('.spec') != -1 sources.extend( list(filter(spec_file, source)) ) # as the source contains the url of the source package this rpm package # is built from, we extract the target name #tarball = (str(target[0])+".tar.gz").replace('.rpm', '') tarball = (str(target[0])+".tar.gz").replace('.rpm', '') try: #tarball = env['SOURCE_URL'].split('/')[-1] tarball = env['SOURCE_URL'].split('/')[-1] except KeyError, e: raise SCons.Errors.UserError( "Missing PackageTag '%s' for RPM packager" % e.args[0] ) tarball = src_targz.package(env, source=sources, target=tarball, PACKAGEROOT=env['PACKAGEROOT'], ) return (target, tarball) def addspecfile(target, source, env): specfile = "%s-%s" % (env['NAME'], env['VERSION']) bld = SCons.Builder.Builder(action = build_specfile, suffix = '.spec', target_factory = SCons.Node.FS.File) source.extend(bld(env, specfile, source)) return (target,source) def build_specfile(target, source, env): """ Builds a RPM specfile from a dictionary with string metadata and by analyzing a tree of nodes. """ file = open(target[0].abspath, 'w') str = "" try: file.write( build_specfile_header(env) ) file.write( build_specfile_sections(env) ) file.write( build_specfile_filesection(env, source) ) file.close() # call a user specified function if 'CHANGE_SPECFILE' in env: env['CHANGE_SPECFILE'](target, source) except KeyError, e: raise SCons.Errors.UserError( '"%s" package field for RPM is missing.' % e.args[0] ) # # mandatory and optional package tag section # def build_specfile_sections(spec): """ Builds the sections of a rpm specfile. """ str = "" mandatory_sections = { 'DESCRIPTION' : '\n%%description\n%s\n\n', } str = str + SimpleTagCompiler(mandatory_sections).compile( spec ) optional_sections = { 'DESCRIPTION_' : '%%description -l %s\n%s\n\n', 'CHANGELOG' : '%%changelog\n%s\n\n', 'X_RPM_PREINSTALL' : '%%pre\n%s\n\n', 'X_RPM_POSTINSTALL' : '%%post\n%s\n\n', 'X_RPM_PREUNINSTALL' : '%%preun\n%s\n\n', 'X_RPM_POSTUNINSTALL' : '%%postun\n%s\n\n', 'X_RPM_VERIFY' : '%%verify\n%s\n\n', # These are for internal use but could possibly be overriden 'X_RPM_PREP' : '%%prep\n%s\n\n', 'X_RPM_BUILD' : '%%build\n%s\n\n', 'X_RPM_INSTALL' : '%%install\n%s\n\n', 'X_RPM_CLEAN' : '%%clean\n%s\n\n', } # Default prep, build, install and clean rules # TODO: optimize those build steps, to not compile the project a second time if 'X_RPM_PREP' not in spec: spec['X_RPM_PREP'] = '[ -n "$RPM_BUILD_ROOT" -a "$RPM_BUILD_ROOT" != / ] && rm -rf "$RPM_BUILD_ROOT"' + '\n%setup -q' if 'X_RPM_BUILD' not in spec: spec['X_RPM_BUILD'] = 'mkdir "$RPM_BUILD_ROOT"' if 'X_RPM_INSTALL' not in spec: spec['X_RPM_INSTALL'] = 'scons --install-sandbox="$RPM_BUILD_ROOT" "$RPM_BUILD_ROOT"' if 'X_RPM_CLEAN' not in spec: spec['X_RPM_CLEAN'] = '[ -n "$RPM_BUILD_ROOT" -a "$RPM_BUILD_ROOT" != / ] && rm -rf "$RPM_BUILD_ROOT"' str = str + SimpleTagCompiler(optional_sections, mandatory=0).compile( spec ) return str def build_specfile_header(spec): """ Builds all section but the %file of a rpm specfile """ str = "" # first the mandatory sections mandatory_header_fields = { 'NAME' : '%%define name %s\nName: %%{name}\n', 'VERSION' : '%%define version %s\nVersion: %%{version}\n', 'PACKAGEVERSION' : '%%define release %s\nRelease: %%{release}\n', 'X_RPM_GROUP' : 'Group: %s\n', 'SUMMARY' : 'Summary: %s\n', 'LICENSE' : 'License: %s\n', } str = str + SimpleTagCompiler(mandatory_header_fields).compile( spec ) # now the optional tags optional_header_fields = { 'VENDOR' : 'Vendor: %s\n', 'X_RPM_URL' : 'Url: %s\n', 'SOURCE_URL' : 'Source: %s\n', 'SUMMARY_' : 'Summary(%s): %s\n', 'X_RPM_DISTRIBUTION' : 'Distribution: %s\n', 'X_RPM_ICON' : 'Icon: %s\n', 'X_RPM_PACKAGER' : 'Packager: %s\n', 'X_RPM_GROUP_' : 'Group(%s): %s\n', 'X_RPM_REQUIRES' : 'Requires: %s\n', 'X_RPM_PROVIDES' : 'Provides: %s\n', 'X_RPM_CONFLICTS' : 'Conflicts: %s\n', 'X_RPM_BUILDREQUIRES' : 'BuildRequires: %s\n', 'X_RPM_SERIAL' : 'Serial: %s\n', 'X_RPM_EPOCH' : 'Epoch: %s\n', 'X_RPM_AUTOREQPROV' : 'AutoReqProv: %s\n', 'X_RPM_EXCLUDEARCH' : 'ExcludeArch: %s\n', 'X_RPM_EXCLUSIVEARCH' : 'ExclusiveArch: %s\n', 'X_RPM_PREFIX' : 'Prefix: %s\n', 'X_RPM_CONFLICTS' : 'Conflicts: %s\n', # internal use 'X_RPM_BUILDROOT' : 'BuildRoot: %s\n', } # fill in default values: # Adding a BuildRequires renders the .rpm unbuildable under System, which # are not managed by rpm, since the database to resolve this dependency is # missing (take Gentoo as an example) # if not s.has_key('x_rpm_BuildRequires'): # s['x_rpm_BuildRequires'] = 'scons' if 'X_RPM_BUILDROOT' not in spec: spec['X_RPM_BUILDROOT'] = '%{_tmppath}/%{name}-%{version}-%{release}' str = str + SimpleTagCompiler(optional_header_fields, mandatory=0).compile( spec ) return str # # mandatory and optional file tags # def build_specfile_filesection(spec, files): """ builds the %file section of the specfile """ str = '%files\n' if 'X_RPM_DEFATTR' not in spec: spec['X_RPM_DEFATTR'] = '(-,root,root)' str = str + '%%defattr %s\n' % spec['X_RPM_DEFATTR'] supported_tags = { 'PACKAGING_CONFIG' : '%%config %s', 'PACKAGING_CONFIG_NOREPLACE' : '%%config(noreplace) %s', 'PACKAGING_DOC' : '%%doc %s', 'PACKAGING_UNIX_ATTR' : '%%attr %s', 'PACKAGING_LANG_' : '%%lang(%s) %s', 'PACKAGING_X_RPM_VERIFY' : '%%verify %s', 'PACKAGING_X_RPM_DIR' : '%%dir %s', 'PACKAGING_X_RPM_DOCDIR' : '%%docdir %s', 'PACKAGING_X_RPM_GHOST' : '%%ghost %s', } for file in files: # build the tagset tags = {} for k in supported_tags.keys(): try: tags[k]=getattr(file, k) except AttributeError: pass # compile the tagset str = str + SimpleTagCompiler(supported_tags, mandatory=0).compile( tags ) str = str + ' ' str = str + file.PACKAGING_INSTALL_LOCATION str = str + '\n\n' return str class SimpleTagCompiler(object): """ This class is a simple string substition utility: the replacement specfication is stored in the tagset dictionary, something like: { "abc" : "cdef %s ", "abc_" : "cdef %s %s" } the compile function gets a value dictionary, which may look like: { "abc" : "ghij", "abc_gh" : "ij" } The resulting string will be: "cdef ghij cdef gh ij" """ def __init__(self, tagset, mandatory=1): self.tagset = tagset self.mandatory = mandatory def compile(self, values): """ compiles the tagset and returns a str containing the result """ def is_international(tag): #return tag.endswith('_') return tag[-1:] == '_' def get_country_code(tag): return tag[-2:] def strip_country_code(tag): return tag[:-2] replacements = list(self.tagset.items()) str = "" #domestic = [ (k,v) for k,v in replacements if not is_international(k) ] domestic = [t for t in replacements if not is_international(t[0])] for key, replacement in domestic: try: str = str + replacement % values[key] except KeyError, e: if self.mandatory: raise e #international = [ (k,v) for k,v in replacements if is_international(k) ] international = [t for t in replacements if is_international(t[0])] for key, replacement in international: try: #int_values_for_key = [ (get_country_code(k),v) for k,v in values.items() if strip_country_code(k) == key ] x = [t for t in values.items() if strip_country_code(t[0]) == key] int_values_for_key = [(get_country_code(t[0]),t[1]) for t in x] for v in int_values_for_key: str = str + replacement % v except KeyError, e: if self.mandatory: raise e return str # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/packaging/__init__.xml0000644000175000017500000003220512114661560024421 0ustar dktrkranzdktrkranz Sets construction variables for the &b-Package; Builder. Builds software distribution packages. Packages consist of files to install and packaging information. The former may be specified with the &source; parameter and may be left out, in which case the &FindInstalledFiles; function will collect all files that have an &b-Install; or &b-InstallAs; Builder attached. If the ⌖ is not specified it will be deduced from additional information given to this Builder. The packaging information is specified with the help of construction variables documented below. This information is called a tag to stress that some of them can also be attached to files with the &Tag; function. The mandatory ones will complain if they were not specified. They vary depending on chosen target packager. The target packager may be selected with the "PACKAGETYPE" command line option or with the &cv-PACKAGETYPE; construction variable. Currently the following packagers available: * msi - Microsoft Installer * rpm - Redhat Package Manger * ipkg - Itsy Package Management System * tarbz2 - compressed tar * targz - compressed tar * zip - zip file * src_tarbz2 - compressed tar source * src_targz - compressed tar source * src_zip - zip file source An updated list is always available under the "package_type" option when running "scons --help" on a project that has packaging activated. env = Environment(tools=['default', 'packaging']) env.Install('/bin/', 'my_program') env.Package( NAME = 'foo', VERSION = '1.2.3', PACKAGEVERSION = 0, PACKAGETYPE = 'rpm', LICENSE = 'gpl', SUMMARY = 'balalalalal', DESCRIPTION = 'this should be really really long', X_RPM_GROUP = 'Application/fu', SOURCE_URL = 'http://foo.org/foo-1.2.3.tar.gz' ) Specifies the system architecture for which the package is being built. The default is the system architecture of the machine on which SCons is running. This is used to fill in the Architecture: field in an Ipkg control file, and as part of the name of a generated RPM file. A hook for modifying the file that controls the packaging build (the .spec for RPM, the control for Ipkg, the .wxs for MSI). If set, the function will be called after the SCons template for the file has been written. XXX The name of a file containing the change log text to be included in the package. This is included as the %changelog section of the RPM .spec file. A long description of the project being packaged. This is included in the relevant section of the file that controls the packaging build. A language-specific long description for the specified lang. This is used to populate a %description -l section of an RPM .spec file. The abbreviated name of the license under which this project is released (gpl, lpgl, bsd etc.). See http://www.opensource.org/licenses/alphabetical for a list of license names. Specfies the name of the project to package. Specifies the directory where all files in resulting archive will be placed if applicable. The default value is "$NAME-$VERSION". Selects the package type to build. Currently these are available: * msi - Microsoft Installer * rpm - Redhat Package Manger * ipkg - Itsy Package Management System * tarbz2 - compressed tar * targz - compressed tar * zip - zip file * src_tarbz2 - compressed tar source * src_targz - compressed tar source * src_zip - zip file source This may be overridden with the "package_type" command line option. The version of the package (not the underlying project). This is currently only used by the rpm packager and should reflect changes in the packaging, not the underlying project code itself. The URL (web address) of the location from which the project was retrieved. This is used to fill in the Source: field in the controlling information for Ipkg and RPM packages. A short summary of what the project is about. This is used to fill in the Summary: field in the controlling information for Ipkg and RPM packages, and as the Description: field in MSI packages. The person or organization who supply the packaged software. This is used to fill in the Vendor: field in the controlling information for RPM packages, and the Manufacturer: field in the controlling information for MSI packages. The version of the project, specified as a string. This is used to fill in the Depends: field in the controlling information for Ipkg packages. This is used to fill in the Description: field in the controlling information for Ipkg packages. The default value is $SUMMARY\n$DESCRIPTION This is used to fill in the Maintainer: field in the controlling information for Ipkg packages. This is used to fill in the Priority: field in the controlling information for Ipkg packages. This is used to fill in the Section: field in the controlling information for Ipkg packages. This is used to fill in the Language: attribute in the controlling information for MSI packages. The text of the software license in RTF format. Carriage return characters will be replaced with the RTF equivalent \\par. TODO This is used to fill in the AutoReqProv: field in the RPM .spec file. internal, but overridable This is used to fill in the BuildRequires: field in the RPM .spec file. internal, but overridable internal, but overridable This is used to fill in the Conflicts: field in the RPM .spec file. This value is used as the default attributes for the files in the RPM package. The default value is (-,root,root). This is used to fill in the Distribution: field in the RPM .spec file. This is used to fill in the Epoch: field in the controlling information for RPM packages. This is used to fill in the ExcludeArch: field in the RPM .spec file. This is used to fill in the ExclusiveArch: field in the RPM .spec file. This is used to fill in the Group: field in the RPM .spec file. This is used to fill in the Group(lang): field in the RPM .spec file. Note that lang is not literal and should be replaced by the appropriate language code. This is used to fill in the Icon: field in the RPM .spec file. internal, but overridable This is used to fill in the Packager: field in the RPM .spec file. This is used to fill in the Provides: field in the RPM .spec file. This is used to fill in the %post: section in the RPM .spec file. This is used to fill in the %pre: section in the RPM .spec file. This is used to fill in the Prefix: field in the RPM .spec file. internal, but overridable This is used to fill in the %postun: section in the RPM .spec file. This is used to fill in the %preun: section in the RPM .spec file. This is used to fill in the Requires: field in the RPM .spec file. This is used to fill in the Serial: field in the RPM .spec file. This is used to fill in the Url: field in the RPM .spec file. (node, tags) Annotates file or directory Nodes with information about how the &b-link-Package; Builder should package those files or directories. All tags are optional. Examples: # makes sure the built library will be installed with 0644 file # access mode Tag( Library( 'lib.c' ), UNIX_ATTR="0644" ) # marks file2.txt to be a documentation file Tag( 'file2.txt', DOC ) scons-doc-2.3.0/src/engine/SCons/Tool/packaging/targz.py0000644000175000017500000000342412114661560023642 0ustar dktrkranzdktrkranz"""SCons.Tool.Packaging.targz The targz SRC packager. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/packaging/targz.py 2013/03/03 09:48:35 garyo" from SCons.Tool.packaging import stripinstallbuilder, putintopackageroot def package(env, target, source, PACKAGEROOT, **kw): bld = env['BUILDERS']['Tar'] bld.set_suffix('.tar.gz') target, source = stripinstallbuilder(target, source, env) target, source = putintopackageroot(target, source, env, PACKAGEROOT) return bld(env, target, source, TARFLAGS='-zc') # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/packaging/ipk.py0000644000175000017500000001426712114661560023305 0ustar dktrkranzdktrkranz"""SCons.Tool.Packaging.ipk """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/packaging/ipk.py 2013/03/03 09:48:35 garyo" import SCons.Builder import SCons.Node.FS import os from SCons.Tool.packaging import stripinstallbuilder, putintopackageroot def package(env, target, source, PACKAGEROOT, NAME, VERSION, DESCRIPTION, SUMMARY, X_IPK_PRIORITY, X_IPK_SECTION, SOURCE_URL, X_IPK_MAINTAINER, X_IPK_DEPENDS, **kw): """ this function prepares the packageroot directory for packaging with the ipkg builder. """ SCons.Tool.Tool('ipkg').generate(env) # setup the Ipkg builder bld = env['BUILDERS']['Ipkg'] target, source = stripinstallbuilder(target, source, env) target, source = putintopackageroot(target, source, env, PACKAGEROOT) # This should be overridable from the construction environment, # which it is by using ARCHITECTURE=. # Guessing based on what os.uname() returns at least allows it # to work for both i386 and x86_64 Linux systems. archmap = { 'i686' : 'i386', 'i586' : 'i386', 'i486' : 'i386', } buildarchitecture = os.uname()[4] buildarchitecture = archmap.get(buildarchitecture, buildarchitecture) if 'ARCHITECTURE' in kw: buildarchitecture = kw['ARCHITECTURE'] # setup the kw to contain the mandatory arguments to this fucntion. # do this before calling any builder or setup function loc=locals() del loc['kw'] kw.update(loc) del kw['source'], kw['target'], kw['env'] # generate the specfile specfile = gen_ipk_dir(PACKAGEROOT, source, env, kw) # override the default target. if str(target[0])=="%s-%s"%(NAME, VERSION): target=[ "%s_%s_%s.ipk"%(NAME, VERSION, buildarchitecture) ] # now apply the Ipkg builder return bld(env, target, specfile, **kw) def gen_ipk_dir(proot, source, env, kw): # make sure the packageroot is a Dir object. if SCons.Util.is_String(proot): proot=env.Dir(proot) # create the specfile builder s_bld=SCons.Builder.Builder( action = build_specfiles, ) # create the specfile targets spec_target=[] control=proot.Dir('CONTROL') spec_target.append(control.File('control')) spec_target.append(control.File('conffiles')) spec_target.append(control.File('postrm')) spec_target.append(control.File('prerm')) spec_target.append(control.File('postinst')) spec_target.append(control.File('preinst')) # apply the builder to the specfile targets s_bld(env, spec_target, source, **kw) # the packageroot directory does now contain the specfiles. return proot def build_specfiles(source, target, env): """ filter the targets for the needed files and use the variables in env to create the specfile. """ # # At first we care for the CONTROL/control file, which is the main file for ipk. # # For this we need to open multiple files in random order, so we store into # a dict so they can be easily accessed. # # opened_files={} def open_file(needle, haystack): try: return opened_files[needle] except KeyError: file=filter(lambda x: x.get_path().rfind(needle)!=-1, haystack)[0] opened_files[needle]=open(file.abspath, 'w') return opened_files[needle] control_file=open_file('control', target) if 'X_IPK_DESCRIPTION' not in env: env['X_IPK_DESCRIPTION']="%s\n %s"%(env['SUMMARY'], env['DESCRIPTION'].replace('\n', '\n ')) content = """ Package: $NAME Version: $VERSION Priority: $X_IPK_PRIORITY Section: $X_IPK_SECTION Source: $SOURCE_URL Architecture: $ARCHITECTURE Maintainer: $X_IPK_MAINTAINER Depends: $X_IPK_DEPENDS Description: $X_IPK_DESCRIPTION """ control_file.write(env.subst(content)) # # now handle the various other files, which purpose it is to set post-, # pre-scripts and mark files as config files. # # We do so by filtering the source files for files which are marked with # the "config" tag and afterwards we do the same for x_ipk_postrm, # x_ipk_prerm, x_ipk_postinst and x_ipk_preinst tags. # # The first one will write the name of the file into the file # CONTROL/configfiles, the latter add the content of the x_ipk_* variable # into the same named file. # for f in [x for x in source if 'PACKAGING_CONFIG' in dir(x)]: config=open_file('conffiles') config.write(f.PACKAGING_INSTALL_LOCATION) config.write('\n') for str in 'POSTRM PRERM POSTINST PREINST'.split(): name="PACKAGING_X_IPK_%s"%str for f in [x for x in source if name in dir(x)]: file=open_file(name) file.write(env[str]) # # close all opened files for f in opened_files.values(): f.close() # call a user specified function if 'CHANGE_SPECFILE' in env: content += env['CHANGE_SPECFILE'](target) return 0 # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/packaging/__init__.py0000644000175000017500000002502412114661560024252 0ustar dktrkranzdktrkranz"""SCons.Tool.Packaging SCons Packaging Tool. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/packaging/__init__.py 2013/03/03 09:48:35 garyo" import SCons.Environment from SCons.Variables import * from SCons.Errors import * from SCons.Util import is_List, make_path_relative from SCons.Warnings import warn, Warning import os, imp import SCons.Defaults __all__ = [ 'src_targz', 'src_tarbz2', 'src_zip', 'tarbz2', 'targz', 'zip', 'rpm', 'msi', 'ipk' ] # # Utility and Builder function # def Tag(env, target, source, *more_tags, **kw_tags): """ Tag a file with the given arguments, just sets the accordingly named attribute on the file object. TODO: FIXME """ if not target: target=source first_tag=None else: first_tag=source if first_tag: kw_tags[first_tag[0]] = '' if len(kw_tags) == 0 and len(more_tags) == 0: raise UserError("No tags given.") # XXX: sanity checks for x in more_tags: kw_tags[x] = '' if not SCons.Util.is_List(target): target=[target] else: # hmm, sometimes the target list, is a list of a list # make sure it is flattened prior to processing. # TODO: perhaps some bug ?!? target=env.Flatten(target) for t in target: for (k,v) in kw_tags.items(): # all file tags have to start with PACKAGING_, so we can later # differentiate between "normal" object attributes and the # packaging attributes. As the user should not be bothered with # that, the prefix will be added here if missing. #if not k.startswith('PACKAGING_'): if k[:10] != 'PACKAGING_': k='PACKAGING_'+k setattr(t, k, v) def Package(env, target=None, source=None, **kw): """ Entry point for the package tool. """ # check if we need to find the source files ourself if not source: source = env.FindInstalledFiles() if len(source)==0: raise UserError("No source for Package() given") # decide which types of packages shall be built. Can be defined through # four mechanisms: command line argument, keyword argument, # environment argument and default selection( zip or tar.gz ) in that # order. try: kw['PACKAGETYPE']=env['PACKAGETYPE'] except KeyError: pass if not kw.get('PACKAGETYPE'): from SCons.Script import GetOption kw['PACKAGETYPE'] = GetOption('package_type') if kw['PACKAGETYPE'] == None: if 'Tar' in env['BUILDERS']: kw['PACKAGETYPE']='targz' elif 'Zip' in env['BUILDERS']: kw['PACKAGETYPE']='zip' else: raise UserError("No type for Package() given") PACKAGETYPE=kw['PACKAGETYPE'] if not is_List(PACKAGETYPE): PACKAGETYPE=PACKAGETYPE.split(',') # load the needed packagers. def load_packager(type): try: file,path,desc=imp.find_module(type, __path__) return imp.load_module(type, file, path, desc) except ImportError, e: raise EnvironmentError("packager %s not available: %s"%(type,str(e))) packagers=list(map(load_packager, PACKAGETYPE)) # set up targets and the PACKAGEROOT try: # fill up the target list with a default target name until the PACKAGETYPE # list is of the same size as the target list. if not target: target = [] size_diff = len(PACKAGETYPE)-len(target) default_name = "%(NAME)s-%(VERSION)s" if size_diff>0: default_target = default_name%kw target.extend( [default_target]*size_diff ) if 'PACKAGEROOT' not in kw: kw['PACKAGEROOT'] = default_name%kw except KeyError, e: raise SCons.Errors.UserError( "Missing Packagetag '%s'"%e.args[0] ) # setup the source files source=env.arg2nodes(source, env.fs.Entry) # call the packager to setup the dependencies. targets=[] try: for packager in packagers: t=[target.pop(0)] t=packager.package(env,t,source, **kw) targets.extend(t) assert( len(target) == 0 ) except KeyError, e: raise SCons.Errors.UserError( "Missing Packagetag '%s' for %s packager"\ % (e.args[0],packager.__name__) ) except TypeError, e: # this exception means that a needed argument for the packager is # missing. As our packagers get their "tags" as named function # arguments we need to find out which one is missing. from inspect import getargspec args,varargs,varkw,defaults=getargspec(packager.package) if defaults!=None: args=args[:-len(defaults)] # throw away arguments with default values args.remove('env') args.remove('target') args.remove('source') # now remove any args for which we have a value in kw. args=[x for x in args if x not in kw] if len(args)==0: raise # must be a different error, so reraise elif len(args)==1: raise SCons.Errors.UserError( "Missing Packagetag '%s' for %s packager"\ % (args[0],packager.__name__) ) else: raise SCons.Errors.UserError( "Missing Packagetags '%s' for %s packager"\ % (", ".join(args),packager.__name__) ) target=env.arg2nodes(target, env.fs.Entry) targets.extend(env.Alias( 'package', targets )) return targets # # SCons tool initialization functions # added = None def generate(env): from SCons.Script import AddOption global added if not added: added = 1 AddOption('--package-type', dest='package_type', default=None, type="string", action="store", help='The type of package to create.') try: env['BUILDERS']['Package'] env['BUILDERS']['Tag'] except KeyError: env['BUILDERS']['Package'] = Package env['BUILDERS']['Tag'] = Tag def exists(env): return 1 # XXX def options(opts): opts.AddVariables( EnumVariable( 'PACKAGETYPE', 'the type of package to create.', None, allowed_values=list(map( str, __all__ )), ignorecase=2 ) ) # # Internal utility functions # def copy_attr(f1, f2): """ copies the special packaging file attributes from f1 to f2. """ #pattrs = [x for x in dir(f1) if not hasattr(f2, x) and\ # x.startswith('PACKAGING_')] copyit = lambda x: not hasattr(f2, x) and x[:10] == 'PACKAGING_' pattrs = list(filter(copyit, dir(f1))) for attr in pattrs: setattr(f2, attr, getattr(f1, attr)) def putintopackageroot(target, source, env, pkgroot, honor_install_location=1): """ Uses the CopyAs builder to copy all source files to the directory given in pkgroot. If honor_install_location is set and the copied source file has an PACKAGING_INSTALL_LOCATION attribute, the PACKAGING_INSTALL_LOCATION is used as the new name of the source file under pkgroot. The source file will not be copied if it is already under the the pkgroot directory. All attributes of the source file will be copied to the new file. """ # make sure the packageroot is a Dir object. if SCons.Util.is_String(pkgroot): pkgroot=env.Dir(pkgroot) if not SCons.Util.is_List(source): source=[source] new_source = [] for file in source: if SCons.Util.is_String(file): file = env.File(file) if file.is_under(pkgroot): new_source.append(file) else: if hasattr(file, 'PACKAGING_INSTALL_LOCATION') and\ honor_install_location: new_name=make_path_relative(file.PACKAGING_INSTALL_LOCATION) else: new_name=make_path_relative(file.get_path()) new_file=pkgroot.File(new_name) new_file=env.CopyAs(new_file, file)[0] copy_attr(file, new_file) new_source.append(new_file) return (target, new_source) def stripinstallbuilder(target, source, env): """ strips the install builder action from the source list and stores the final installation location as the "PACKAGING_INSTALL_LOCATION" of the source of the source file. This effectively removes the final installed files from the source list while remembering the installation location. It also warns about files which have no install builder attached. """ def has_no_install_location(file): return not (file.has_builder() and\ hasattr(file.builder, 'name') and\ (file.builder.name=="InstallBuilder" or\ file.builder.name=="InstallAsBuilder")) if len(list(filter(has_no_install_location, source))): warn(Warning, "there are files to package which have no\ InstallBuilder attached, this might lead to irreproducible packages") n_source=[] for s in source: if has_no_install_location(s): n_source.append(s) else: for ss in s.sources: n_source.append(ss) copy_attr(s, ss) setattr(ss, 'PACKAGING_INSTALL_LOCATION', s.get_path()) return (target, n_source) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/nasm.py0000644000175000017500000000517312114661560021530 0ustar dktrkranzdktrkranz"""SCons.Tool.nasm Tool-specific initialization for nasm, the famous Netwide Assembler. There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Tool.Tool() selection method. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/nasm.py 2013/03/03 09:48:35 garyo" import SCons.Defaults import SCons.Tool import SCons.Util ASSuffixes = ['.s', '.asm', '.ASM'] ASPPSuffixes = ['.spp', '.SPP', '.sx'] if SCons.Util.case_sensitive_suffixes('.s', '.S'): ASPPSuffixes.extend(['.S']) else: ASSuffixes.extend(['.S']) def generate(env): """Add Builders and construction variables for nasm to an Environment.""" static_obj, shared_obj = SCons.Tool.createObjBuilders(env) for suffix in ASSuffixes: static_obj.add_action(suffix, SCons.Defaults.ASAction) static_obj.add_emitter(suffix, SCons.Defaults.StaticObjectEmitter) for suffix in ASPPSuffixes: static_obj.add_action(suffix, SCons.Defaults.ASPPAction) static_obj.add_emitter(suffix, SCons.Defaults.StaticObjectEmitter) env['AS'] = 'nasm' env['ASFLAGS'] = SCons.Util.CLVar('') env['ASPPFLAGS'] = '$ASFLAGS' env['ASCOM'] = '$AS $ASFLAGS -o $TARGET $SOURCES' env['ASPPCOM'] = '$CC $ASPPFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS -c -o $TARGET $SOURCES' def exists(env): return env.Detect('nasm') # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/jar.py0000644000175000017500000001012512114661560021337 0ustar dktrkranzdktrkranz"""SCons.Tool.jar Tool-specific initialization for jar. There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Tool.Tool() selection method. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/jar.py 2013/03/03 09:48:35 garyo" import SCons.Subst import SCons.Util def jarSources(target, source, env, for_signature): """Only include sources that are not a manifest file.""" try: env['JARCHDIR'] except KeyError: jarchdir_set = False else: jarchdir_set = True jarchdir = env.subst('$JARCHDIR', target=target, source=source) if jarchdir: jarchdir = env.fs.Dir(jarchdir) result = [] for src in source: contents = src.get_text_contents() if contents[:16] != "Manifest-Version": if jarchdir_set: _chdir = jarchdir else: try: _chdir = src.attributes.java_classdir except AttributeError: _chdir = None if _chdir: # If we are changing the dir with -C, then sources should # be relative to that directory. src = SCons.Subst.Literal(src.get_path(_chdir)) result.append('-C') result.append(_chdir) result.append(src) return result def jarManifest(target, source, env, for_signature): """Look in sources for a manifest file, if any.""" for src in source: contents = src.get_text_contents() if contents[:16] == "Manifest-Version": return src return '' def jarFlags(target, source, env, for_signature): """If we have a manifest, make sure that the 'm' flag is specified.""" jarflags = env.subst('$JARFLAGS', target=target, source=source) for src in source: contents = src.get_text_contents() if contents[:16] == "Manifest-Version": if not 'm' in jarflags: return jarflags + 'm' break return jarflags def generate(env): """Add Builders and construction variables for jar to an Environment.""" SCons.Tool.CreateJarBuilder(env) env['JAR'] = 'jar' env['JARFLAGS'] = SCons.Util.CLVar('cf') env['_JARFLAGS'] = jarFlags env['_JARMANIFEST'] = jarManifest env['_JARSOURCES'] = jarSources env['_JARCOM'] = '$JAR $_JARFLAGS $TARGET $_JARMANIFEST $_JARSOURCES' env['JARCOM'] = "${TEMPFILE('$_JARCOM')}" env['JARSUFFIX'] = '.jar' def exists(env): # As reported by Jan Nijtmans in issue #2730, the simple # return env.Detect('jar') # doesn't always work during initialization. For now, we # stop trying to detect an executable (analogous to the # javac Builder). # TODO: Come up with a proper detect() routine...and enable it. return 1 # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/rmic.py0000644000175000017500000001053012114661560021515 0ustar dktrkranzdktrkranz"""SCons.Tool.rmic Tool-specific initialization for rmic. There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Tool.Tool() selection method. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/rmic.py 2013/03/03 09:48:35 garyo" import os.path import SCons.Action import SCons.Builder import SCons.Node.FS import SCons.Util def emit_rmic_classes(target, source, env): """Create and return lists of Java RMI stub and skeleton class files to be created from a set of class files. """ class_suffix = env.get('JAVACLASSSUFFIX', '.class') classdir = env.get('JAVACLASSDIR') if not classdir: try: s = source[0] except IndexError: classdir = '.' else: try: classdir = s.attributes.java_classdir except AttributeError: classdir = '.' classdir = env.Dir(classdir).rdir() if str(classdir) == '.': c_ = None else: c_ = str(classdir) + os.sep slist = [] for src in source: try: classname = src.attributes.java_classname except AttributeError: classname = str(src) if c_ and classname[:len(c_)] == c_: classname = classname[len(c_):] if class_suffix and classname[:-len(class_suffix)] == class_suffix: classname = classname[-len(class_suffix):] s = src.rfile() s.attributes.java_classdir = classdir s.attributes.java_classname = classname slist.append(s) stub_suffixes = ['_Stub'] if env.get('JAVAVERSION') == '1.4': stub_suffixes.append('_Skel') tlist = [] for s in source: for suff in stub_suffixes: fname = s.attributes.java_classname.replace('.', os.sep) + \ suff + class_suffix t = target[0].File(fname) t.attributes.java_lookupdir = target[0] tlist.append(t) return tlist, source RMICAction = SCons.Action.Action('$RMICCOM', '$RMICCOMSTR') RMICBuilder = SCons.Builder.Builder(action = RMICAction, emitter = emit_rmic_classes, src_suffix = '$JAVACLASSSUFFIX', target_factory = SCons.Node.FS.Dir, source_factory = SCons.Node.FS.File) def generate(env): """Add Builders and construction variables for rmic to an Environment.""" env['BUILDERS']['RMIC'] = RMICBuilder env['RMIC'] = 'rmic' env['RMICFLAGS'] = SCons.Util.CLVar('') env['RMICCOM'] = '$RMIC $RMICFLAGS -d ${TARGET.attributes.java_lookupdir} -classpath ${SOURCE.attributes.java_classdir} ${SOURCES.attributes.java_classname}' env['JAVACLASSSUFFIX'] = '.class' def exists(env): # As reported by Jan Nijtmans in issue #2730, the simple # return env.Detect('rmic') # doesn't always work during initialization. For now, we # stop trying to detect an executable (analogous to the # javac Builder). # TODO: Come up with a proper detect() routine...and enable it. return 1 # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/suncc.py0000644000175000017500000000371012114661560021700 0ustar dktrkranzdktrkranz"""SCons.Tool.suncc Tool-specific initialization for Sun Solaris (Forte) CC and cc. There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Tool.Tool() selection method. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/suncc.py 2013/03/03 09:48:35 garyo" import SCons.Util import cc def generate(env): """ Add Builders and construction variables for Forte C and C++ compilers to an Environment. """ cc.generate(env) env['CXX'] = 'CC' env['SHCCFLAGS'] = SCons.Util.CLVar('$CCFLAGS -KPIC') env['SHOBJPREFIX'] = 'so_' env['SHOBJSUFFIX'] = '.o' def exists(env): return env.Detect('CC') # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/sgicc.py0000644000175000017500000000353412114661560021661 0ustar dktrkranzdktrkranz"""SCons.Tool.sgicc Tool-specific initialization for MIPSPro cc on SGI. There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Tool.Tool() selection method. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/sgicc.py 2013/03/03 09:48:35 garyo" import cc def generate(env): """Add Builders and construction variables for gcc to an Environment.""" cc.generate(env) env['CXX'] = 'CC' env['SHOBJSUFFIX'] = '.o' env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME'] = 1 def exists(env): return env.Detect('cc') # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/hplink.py0000644000175000017500000000454212114661560022056 0ustar dktrkranzdktrkranz"""SCons.Tool.hplink Tool-specific initialization for the HP linker. There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Tool.Tool() selection method. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/hplink.py 2013/03/03 09:48:35 garyo" import os import os.path import SCons.Util import link ccLinker = None # search for the acc compiler and linker front end try: dirs = os.listdir('/opt') except (IOError, OSError): # Not being able to read the directory because it doesn't exist # (IOError) or isn't readable (OSError) is okay. dirs = [] for dir in dirs: linker = '/opt/' + dir + '/bin/aCC' if os.path.exists(linker): ccLinker = linker break def generate(env): """ Add Builders and construction variables for Visual Age linker to an Environment. """ link.generate(env) env['LINKFLAGS'] = SCons.Util.CLVar('-Wl,+s -Wl,+vnocompatwarnings') env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -b') env['SHLIBSUFFIX'] = '.sl' def exists(env): return ccLinker # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/rpcgen.py0000644000175000017500000000552012114661560022044 0ustar dktrkranzdktrkranz"""SCons.Tool.rpcgen Tool-specific initialization for RPCGEN tools. Three normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Tool.Tool() selection method. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/rpcgen.py 2013/03/03 09:48:35 garyo" from SCons.Builder import Builder import SCons.Util cmd = "cd ${SOURCE.dir} && $RPCGEN -%s $RPCGENFLAGS %s -o ${TARGET.abspath} ${SOURCE.file}" rpcgen_client = cmd % ('l', '$RPCGENCLIENTFLAGS') rpcgen_header = cmd % ('h', '$RPCGENHEADERFLAGS') rpcgen_service = cmd % ('m', '$RPCGENSERVICEFLAGS') rpcgen_xdr = cmd % ('c', '$RPCGENXDRFLAGS') def generate(env): "Add RPCGEN Builders and construction variables for an Environment." client = Builder(action=rpcgen_client, suffix='_clnt.c', src_suffix='.x') header = Builder(action=rpcgen_header, suffix='.h', src_suffix='.x') service = Builder(action=rpcgen_service, suffix='_svc.c', src_suffix='.x') xdr = Builder(action=rpcgen_xdr, suffix='_xdr.c', src_suffix='.x') env.Append(BUILDERS={'RPCGenClient' : client, 'RPCGenHeader' : header, 'RPCGenService' : service, 'RPCGenXDR' : xdr}) env['RPCGEN'] = 'rpcgen' env['RPCGENFLAGS'] = SCons.Util.CLVar('') env['RPCGENCLIENTFLAGS'] = SCons.Util.CLVar('') env['RPCGENHEADERFLAGS'] = SCons.Util.CLVar('') env['RPCGENSERVICEFLAGS'] = SCons.Util.CLVar('') env['RPCGENXDRFLAGS'] = SCons.Util.CLVar('') def exists(env): return env.Detect('rpcgen') # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/gettext.xml0000644000175000017500000001634712114661560022433 0ustar dktrkranzdktrkranz This is actually a toolset, which supports internationalization and localization of sofware being constructed with SCons. The toolset loads following tools: &t-link-xgettext; - to extract internationalized messages from source code to POT file(s), &t-link-msginit; - may be optionally used to initialize PO files, &t-link-msgmerge; - to update PO files, that already contain translated messages, &t-link-msgfmt; - to compile textual PO file to binary installable MO file. When you enable &t-gettext;, it internally loads all abovementioned tools, so you're encouraged to see their individual documentation. Each of the above tools provides its own builder(s) which may be used to perform particular activities related to software internationalization. You may be however interested in top-level builder &b-Translate; described few paragraphs later. To use &t-gettext; tools add 'gettext' tool to your environment: env = Environment( tools = ['default', 'gettext'] ) This pseudo-builder belongs to &t-link-gettext; toolset. The builder extracts internationalized messages from source files, updates POT template (if necessary) and then updates PO translations (if necessary). If &cv-link-POAUTOINIT; is set, missing PO files will be automatically created (i.e. without translator person intervention). The variables &cv-link-LINGUAS_FILE; and &cv-link-POTDOMAIN; are taken into acount too. All other construction variables used by &b-link-POTUpdate;, and &b-link-POUpdate; work here too. Example 1. The simplest way is to specify input files and output languages inline in a SCons script when invoking &b-Translate; # SConscript in 'po/' directory env = Environment( tools = ["default", "gettext"] ) env['POAUTOINIT'] = 1 env.Translate(['en','pl'], ['../a.cpp','../b.cpp']) Example 2. If you wish, you may also stick to conventional style known from autotools, i.e. using POTFILES.in and LINGUAS files # LINGUAS en pl #end # POTFILES.in a.cpp b.cpp # end # SConscript env = Environment( tools = ["default", "gettext"] ) env['POAUTOINIT'] = 1 env['XGETTEXTPATH'] = ['../'] env.Translate(LINGUAS_FILE = 1, XGETTEXTFROM = 'POTFILES.in') The last approach is perhaps the recommended one. It allows easily split internationalization/localization onto separate SCons scripts, where a script in source tree is responsible for translations (from sources to PO files) and script(s) under variant directories are responsible for compilation of PO to MO files to and for installation of MO files. The "gluing factor" synchronizing these two scripts is then the content of LINGUAS file. Note, that the updated POT and PO files are usually going to be committed back to the repository, so they must be updated within the source directory (and not in variant directories). Additionaly, the file listing of po/ directory contains LINGUAS file, so the source tree looks familiar to translators, and they may work with the project in their usual way. Example 3. Let's prepare a development tree as below project/ + SConstruct + build/ + src/ + po/ + SConscript + SConscript.i18n + POTFILES.in + LINGUAS with build being variant directory. Write the top-level SConstruct script as follows # SConstruct env = Environment( tools = ["default", "gettext"] ) VariantDir('build', 'src', duplicate = 0) env['POAUTOINIT'] = 1 SConscript('src/po/SConscript.i18n', exports = 'env') SConscript('build/po/SConscript', exports = 'env') the src/po/SConscript.i18n as # src/po/SConscript.i18n Import('env') env.Translate(LINGUAS_FILE=1, XGETTEXTFROM='POTFILES.in', XGETTEXTPATH=['../']) and the src/po/SConscript # src/po/SConscript Import('env') env.MOFiles(LINGUAS_FILE = 1) Such setup produces POT and PO files under source tree in src/po/ and binary MO files under variant tree in build/po/. This way the POT and PO files are separated from other output files, which must not be committed back to source repositories (e.g. MO files). In above example, the PO files are not updated, nor created automatically when you issue scons '.' command. The files must be updated (created) by hand via scons po-update and then MO files can be compiled by running scons '.'. The &cv-POTDOMAIN; defines default domain, used to generate POT filename as &cv-POTDOMAIN;.pot when no POT file name is provided by the user. This applies to &b-link-POTUpdate;, &b-link-POInit; and &b-link-POUpdate; builders (and builders, that use them, e.g. &b-Translate;). Normally (if &cv-POTDOMAIN; is not defined), the builders use messages.pot as default POT file name. The &cv-POAUTOINIT; variable, if set to True (on non-zero numeric value), let the &t-link-msginit; tool to automatically initialize missing PO files with msginit(1). This applies to both, &b-link-POInit; and &b-link-POUpdate; builders (and others that use any of them). The &cv-LINGUAS_FILE; defines file(s) containing list of additional linguas to be processed by &b-link-POInit;, &b-link-POUpdate; or &b-link-MOFiles; builders. It also affects &b-link-Translate; builder. If the variable contains a string, it defines name of the list file. The &cv-LINGUAS_FILE; may be a list of file names as well. If &cv-LINGUAS_FILE; is set to True (or non-zero numeric value), the list will be read from default file named LINGUAS. scons-doc-2.3.0/src/engine/SCons/Tool/mwld.py0000644000175000017500000000707412114661560021537 0ustar dktrkranzdktrkranz"""SCons.Tool.mwld Tool-specific initialization for the Metrowerks CodeWarrior linker. There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Tool.Tool() selection method. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/mwld.py 2013/03/03 09:48:35 garyo" import SCons.Tool def generate(env): """Add Builders and construction variables for lib to an Environment.""" SCons.Tool.createStaticLibBuilder(env) SCons.Tool.createSharedLibBuilder(env) SCons.Tool.createProgBuilder(env) env['AR'] = 'mwld' env['ARCOM'] = '$AR $ARFLAGS -library -o $TARGET $SOURCES' env['LIBDIRPREFIX'] = '-L' env['LIBDIRSUFFIX'] = '' env['LIBLINKPREFIX'] = '-l' env['LIBLINKSUFFIX'] = '.lib' env['LINK'] = 'mwld' env['LINKCOM'] = '$LINK $LINKFLAGS -o $TARGET $SOURCES $_LIBDIRFLAGS $_LIBFLAGS' env['SHLINK'] = '$LINK' env['SHLINKFLAGS'] = '$LINKFLAGS' env['SHLINKCOM'] = shlib_action env['SHLIBEMITTER']= shlib_emitter def exists(env): import SCons.Tool.mwcc return SCons.Tool.mwcc.set_vars(env) def shlib_generator(target, source, env, for_signature): cmd = ['$SHLINK', '$SHLINKFLAGS', '-shared'] no_import_lib = env.get('no_import_lib', 0) if no_import_lib: cmd.extend('-noimplib') dll = env.FindIxes(target, 'SHLIBPREFIX', 'SHLIBSUFFIX') if dll: cmd.extend(['-o', dll]) implib = env.FindIxes(target, 'LIBPREFIX', 'LIBSUFFIX') if implib: cmd.extend(['-implib', implib.get_string(for_signature)]) cmd.extend(['$SOURCES', '$_LIBDIRFLAGS', '$_LIBFLAGS']) return [cmd] def shlib_emitter(target, source, env): dll = env.FindIxes(target, 'SHLIBPREFIX', 'SHLIBSUFFIX') no_import_lib = env.get('no_import_lib', 0) if not dll: raise SCons.Errors.UserError("A shared library should have exactly one target with the suffix: %s" % env.subst("$SHLIBSUFFIX")) if not no_import_lib and \ not env.FindIxes(target, 'LIBPREFIX', 'LIBSUFFIX'): # Append an import library to the list of targets. target.append(env.ReplaceIxes(dll, 'SHLIBPREFIX', 'SHLIBSUFFIX', 'LIBPREFIX', 'LIBSUFFIX')) return target, source shlib_action = SCons.Action.Action(shlib_generator, generator=1) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/tex.xml0000644000175000017500000000543712114661560021545 0ustar dktrkranzdktrkranz Sets construction variables for the TeX formatter and typesetter. TEX TEXFLAGS TEXCOM LATEX LATEXFLAGS LATEXCOM BIBTEX BIBTEXFLAGS BIBTEXCOM MAKEINDEX MAKEINDEXFLAGS MAKEINDEXCOM TEXCOMSTR LATEXCOMSTR BIBTEXCOMSTR MAKEINDEXCOMSTR The bibliography generator for the TeX formatter and typesetter and the LaTeX structured formatter and typesetter. The command line used to call the bibliography generator for the TeX formatter and typesetter and the LaTeX structured formatter and typesetter. The string displayed when generating a bibliography for TeX or LaTeX. If this is not set, then &cv-link-BIBTEXCOM; (the command line) is displayed. env = Environment(BIBTEXCOMSTR = "Generating bibliography $TARGET") General options passed to the bibliography generator for the TeX formatter and typesetter and the LaTeX structured formatter and typesetter. The makeindex generator for the TeX formatter and typesetter and the LaTeX structured formatter and typesetter. The command line used to call the makeindex generator for the TeX formatter and typesetter and the LaTeX structured formatter and typesetter. The string displayed when calling the makeindex generator for the TeX formatter and typesetter and the LaTeX structured formatter and typesetter. If this is not set, then &cv-link-MAKEINDEXCOM; (the command line) is displayed. General options passed to the makeindex generator for the TeX formatter and typesetter and the LaTeX structured formatter and typesetter. The TeX formatter and typesetter. The command line used to call the TeX formatter and typesetter. The string displayed when calling the TeX formatter and typesetter. If this is not set, then &cv-link-TEXCOM; (the command line) is displayed. env = Environment(TEXCOMSTR = "Building $TARGET from TeX input $SOURCES") General options passed to the TeX formatter and typesetter. scons-doc-2.3.0/src/engine/SCons/Tool/tex.py0000644000175000017500000011600712114661560021371 0ustar dktrkranzdktrkranz"""SCons.Tool.tex Tool-specific initialization for TeX. Generates .dvi files from .tex files There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Tool.Tool() selection method. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/tex.py 2013/03/03 09:48:35 garyo" import os.path import re import shutil import sys import platform import glob import SCons.Action import SCons.Node import SCons.Node.FS import SCons.Util import SCons.Scanner.LaTeX Verbose = False must_rerun_latex = True # these are files that just need to be checked for changes and then rerun latex check_suffixes = ['.toc', '.lof', '.lot', '.out', '.nav', '.snm'] # these are files that require bibtex or makeindex to be run when they change all_suffixes = check_suffixes + ['.bbl', '.idx', '.nlo', '.glo', '.acn', '.bcf'] # # regular expressions used to search for Latex features # or outputs that require rerunning latex # # search for all .aux files opened by latex (recorded in the .fls file) openout_aux_re = re.compile(r"OUTPUT *(.*\.aux)") # search for all .bcf files opened by latex (recorded in the .fls file) # for use by biber openout_bcf_re = re.compile(r"OUTPUT *(.*\.bcf)") #printindex_re = re.compile(r"^[^%]*\\printindex", re.MULTILINE) #printnomenclature_re = re.compile(r"^[^%]*\\printnomenclature", re.MULTILINE) #printglossary_re = re.compile(r"^[^%]*\\printglossary", re.MULTILINE) # search to find rerun warnings warning_rerun_str = '(^LaTeX Warning:.*Rerun)|(^Package \w+ Warning:.*Rerun)' warning_rerun_re = re.compile(warning_rerun_str, re.MULTILINE) # search to find citation rerun warnings rerun_citations_str = "^LaTeX Warning:.*\n.*Rerun to get citations correct" rerun_citations_re = re.compile(rerun_citations_str, re.MULTILINE) # search to find undefined references or citations warnings undefined_references_str = '(^LaTeX Warning:.*undefined references)|(^Package \w+ Warning:.*undefined citations)' undefined_references_re = re.compile(undefined_references_str, re.MULTILINE) # used by the emitter auxfile_re = re.compile(r".", re.MULTILINE) tableofcontents_re = re.compile(r"^[^%\n]*\\tableofcontents", re.MULTILINE) makeindex_re = re.compile(r"^[^%\n]*\\makeindex", re.MULTILINE) bibliography_re = re.compile(r"^[^%\n]*\\bibliography", re.MULTILINE) bibunit_re = re.compile(r"^[^%\n]*\\begin\{bibunit\}", re.MULTILINE) multibib_re = re.compile(r"^[^%\n]*\\newcites\{([^\}]*)\}", re.MULTILINE) addbibresource_re = re.compile(r"^[^%\n]*\\(addbibresource|addglobalbib|addsectionbib)", re.MULTILINE) listoffigures_re = re.compile(r"^[^%\n]*\\listoffigures", re.MULTILINE) listoftables_re = re.compile(r"^[^%\n]*\\listoftables", re.MULTILINE) hyperref_re = re.compile(r"^[^%\n]*\\usepackage.*\{hyperref\}", re.MULTILINE) makenomenclature_re = re.compile(r"^[^%\n]*\\makenomenclature", re.MULTILINE) makeglossary_re = re.compile(r"^[^%\n]*\\makeglossary", re.MULTILINE) makeglossaries_re = re.compile(r"^[^%\n]*\\makeglossaries", re.MULTILINE) makeacronyms_re = re.compile(r"^[^%\n]*\\makeglossaries", re.MULTILINE) beamer_re = re.compile(r"^[^%\n]*\\documentclass\{beamer\}", re.MULTILINE) regex = r'^[^%\n]*\\newglossary\s*\[([^\]]+)\]?\s*\{([^}]*)\}\s*\{([^}]*)\}\s*\{([^}]*)\}\s*\{([^}]*)\}' newglossary_re = re.compile(regex, re.MULTILINE) newglossary_suffix = [] # search to find all files included by Latex include_re = re.compile(r'^[^%\n]*\\(?:include|input){([^}]*)}', re.MULTILINE) includeOnly_re = re.compile(r'^[^%\n]*\\(?:include){([^}]*)}', re.MULTILINE) # search to find all graphics files included by Latex includegraphics_re = re.compile(r'^[^%\n]*\\(?:includegraphics(?:\[[^\]]+\])?){([^}]*)}', re.MULTILINE) # search to find all files opened by Latex (recorded in .log file) openout_re = re.compile(r"OUTPUT *(.*)") # list of graphics file extensions for TeX and LaTeX TexGraphics = SCons.Scanner.LaTeX.TexGraphics LatexGraphics = SCons.Scanner.LaTeX.LatexGraphics # An Action sufficient to build any generic tex file. TeXAction = None # An action to build a latex file. This action might be needed more # than once if we are dealing with labels and bibtex. LaTeXAction = None # An action to run BibTeX on a file. BibTeXAction = None # An action to run Biber on a file. BiberAction = None # An action to run MakeIndex on a file. MakeIndexAction = None # An action to run MakeIndex (for nomencl) on a file. MakeNclAction = None # An action to run MakeIndex (for glossary) on a file. MakeGlossaryAction = None # An action to run MakeIndex (for acronyms) on a file. MakeAcronymsAction = None # An action to run MakeIndex (for newglossary commands) on a file. MakeNewGlossaryAction = None # Used as a return value of modify_env_var if the variable is not set. _null = SCons.Scanner.LaTeX._null modify_env_var = SCons.Scanner.LaTeX.modify_env_var def check_file_error_message(utility, filename='log'): msg = '%s returned an error, check the %s file\n' % (utility, filename) sys.stdout.write(msg) def FindFile(name,suffixes,paths,env,requireExt=False): if requireExt: name,ext = SCons.Util.splitext(name) # if the user gave an extension use it. if ext: name = name + ext if Verbose: print " searching for '%s' with extensions: " % name,suffixes for path in paths: testName = os.path.join(path,name) if Verbose: print " look for '%s'" % testName if os.path.isfile(testName): if Verbose: print " found '%s'" % testName return env.fs.File(testName) else: name_ext = SCons.Util.splitext(testName)[1] if name_ext: continue # if no suffix try adding those passed in for suffix in suffixes: testNameExt = testName + suffix if Verbose: print " look for '%s'" % testNameExt if os.path.isfile(testNameExt): if Verbose: print " found '%s'" % testNameExt return env.fs.File(testNameExt) if Verbose: print " did not find '%s'" % name return None def InternalLaTeXAuxAction(XXXLaTeXAction, target = None, source= None, env=None): """A builder for LaTeX files that checks the output in the aux file and decides how many times to use LaTeXAction, and BibTeXAction.""" global must_rerun_latex # This routine is called with two actions. In this file for DVI builds # with LaTeXAction and from the pdflatex.py with PDFLaTeXAction # set this up now for the case where the user requests a different extension # for the target filename if (XXXLaTeXAction == LaTeXAction): callerSuffix = ".dvi" else: callerSuffix = env['PDFSUFFIX'] basename = SCons.Util.splitext(str(source[0]))[0] basedir = os.path.split(str(source[0]))[0] basefile = os.path.split(str(basename))[1] abspath = os.path.abspath(basedir) targetext = os.path.splitext(str(target[0]))[1] targetdir = os.path.split(str(target[0]))[0] saved_env = {} for var in SCons.Scanner.LaTeX.LaTeX.env_variables: saved_env[var] = modify_env_var(env, var, abspath) # Create base file names with the target directory since the auxiliary files # will be made there. That's because the *COM variables have the cd # command in the prolog. We check # for the existence of files before opening them--even ones like the # aux file that TeX always creates--to make it possible to write tests # with stubs that don't necessarily generate all of the same files. targetbase = os.path.join(targetdir, basefile) # if there is a \makeindex there will be a .idx and thus # we have to run makeindex at least once to keep the build # happy even if there is no index. # Same for glossaries, nomenclature, and acronyms src_content = source[0].get_text_contents() run_makeindex = makeindex_re.search(src_content) and not os.path.isfile(targetbase + '.idx') run_nomenclature = makenomenclature_re.search(src_content) and not os.path.isfile(targetbase + '.nlo') run_glossary = makeglossary_re.search(src_content) and not os.path.isfile(targetbase + '.glo') run_glossaries = makeglossaries_re.search(src_content) and not os.path.isfile(targetbase + '.glo') run_acronyms = makeacronyms_re.search(src_content) and not os.path.isfile(targetbase + '.acn') saved_hashes = {} suffix_nodes = {} for suffix in all_suffixes+sum(newglossary_suffix, []): theNode = env.fs.File(targetbase + suffix) suffix_nodes[suffix] = theNode saved_hashes[suffix] = theNode.get_csig() if Verbose: print "hashes: ",saved_hashes must_rerun_latex = True # .aux files already processed by BibTex already_bibtexed = [] # # routine to update MD5 hash and compare # def check_MD5(filenode, suffix): global must_rerun_latex # two calls to clear old csig filenode.clear_memoized_values() filenode.ninfo = filenode.new_ninfo() new_md5 = filenode.get_csig() if saved_hashes[suffix] == new_md5: if Verbose: print "file %s not changed" % (targetbase+suffix) return False # unchanged saved_hashes[suffix] = new_md5 must_rerun_latex = True if Verbose: print "file %s changed, rerunning Latex, new hash = " % (targetbase+suffix), new_md5 return True # changed # generate the file name that latex will generate resultfilename = targetbase + callerSuffix count = 0 while (must_rerun_latex and count < int(env.subst('$LATEXRETRIES'))) : result = XXXLaTeXAction(target, source, env) if result != 0: return result count = count + 1 must_rerun_latex = False # Decide if various things need to be run, or run again. # Read the log file to find warnings/errors logfilename = targetbase + '.log' logContent = '' if os.path.isfile(logfilename): logContent = open(logfilename, "rb").read() # Read the fls file to find all .aux files flsfilename = targetbase + '.fls' flsContent = '' auxfiles = [] if os.path.isfile(flsfilename): flsContent = open(flsfilename, "rb").read() auxfiles = openout_aux_re.findall(flsContent) # remove duplicates dups = {} for x in auxfiles: dups[x] = 1 auxfiles = list(dups.keys()) bcffiles = [] if os.path.isfile(flsfilename): flsContent = open(flsfilename, "rb").read() bcffiles = openout_bcf_re.findall(flsContent) # remove duplicates dups = {} for x in bcffiles: dups[x] = 1 bcffiles = list(dups.keys()) if Verbose: print "auxfiles ",auxfiles print "bcffiles ",bcffiles # Now decide if bibtex will need to be run. # The information that bibtex reads from the .aux file is # pass-independent. If we find (below) that the .bbl file is unchanged, # then the last latex saw a correct bibliography. # Therefore only do this once # Go through all .aux files and remember the files already done. for auxfilename in auxfiles: if auxfilename not in already_bibtexed: already_bibtexed.append(auxfilename) target_aux = os.path.join(targetdir, auxfilename) if os.path.isfile(target_aux): content = open(target_aux, "rb").read() if content.find("bibdata") != -1: if Verbose: print "Need to run bibtex on ",auxfilename bibfile = env.fs.File(SCons.Util.splitext(target_aux)[0]) result = BibTeXAction(bibfile, bibfile, env) if result != 0: check_file_error_message(env['BIBTEX'], 'blg') must_rerun_latex = True # Now decide if biber will need to be run. # When the backend for biblatex is biber (by choice or default) the # citation information is put in the .bcf file. # The information that biber reads from the .bcf file is # pass-independent. If we find (below) that the .bbl file is unchanged, # then the last latex saw a correct bibliography. # Therefore only do this once # Go through all .bcf files and remember the files already done. for bcffilename in bcffiles: if bcffilename not in already_bibtexed: already_bibtexed.append(bcffilename) target_bcf = os.path.join(targetdir, bcffilename) if os.path.isfile(target_bcf): content = open(target_bcf, "rb").read() if content.find("bibdata") != -1: if Verbose: print "Need to run biber on ",bcffilename bibfile = env.fs.File(SCons.Util.splitext(target_bcf)[0]) result = BiberAction(bibfile, bibfile, env) if result != 0: check_file_error_message(env['BIBER'], 'blg') must_rerun_latex = True # Now decide if latex will need to be run again due to index. if check_MD5(suffix_nodes['.idx'],'.idx') or (count == 1 and run_makeindex): # We must run makeindex if Verbose: print "Need to run makeindex" idxfile = suffix_nodes['.idx'] result = MakeIndexAction(idxfile, idxfile, env) if result != 0: check_file_error_message(env['MAKEINDEX'], 'ilg') return result # TO-DO: need to add a way for the user to extend this list for whatever # auxiliary files they create in other (or their own) packages # Harder is case is where an action needs to be called -- that should be rare (I hope?) for index in check_suffixes: check_MD5(suffix_nodes[index],index) # Now decide if latex will need to be run again due to nomenclature. if check_MD5(suffix_nodes['.nlo'],'.nlo') or (count == 1 and run_nomenclature): # We must run makeindex if Verbose: print "Need to run makeindex for nomenclature" nclfile = suffix_nodes['.nlo'] result = MakeNclAction(nclfile, nclfile, env) if result != 0: check_file_error_message('%s (nomenclature)' % env['MAKENCL'], 'nlg') #return result # Now decide if latex will need to be run again due to glossary. if check_MD5(suffix_nodes['.glo'],'.glo') or (count == 1 and run_glossaries) or (count == 1 and run_glossary): # We must run makeindex if Verbose: print "Need to run makeindex for glossary" glofile = suffix_nodes['.glo'] result = MakeGlossaryAction(glofile, glofile, env) if result != 0: check_file_error_message('%s (glossary)' % env['MAKEGLOSSARY'], 'glg') #return result # Now decide if latex will need to be run again due to acronyms. if check_MD5(suffix_nodes['.acn'],'.acn') or (count == 1 and run_acronyms): # We must run makeindex if Verbose: print "Need to run makeindex for acronyms" acrfile = suffix_nodes['.acn'] result = MakeAcronymsAction(acrfile, acrfile, env) if result != 0: check_file_error_message('%s (acronyms)' % env['MAKEACRONYMS'], 'alg') return result # Now decide if latex will need to be run again due to newglossary command. for ig in range(len(newglossary_suffix)): if check_MD5(suffix_nodes[newglossary_suffix[ig][2]],newglossary_suffix[ig][2]) or (count == 1): # We must run makeindex if Verbose: print "Need to run makeindex for newglossary" newglfile = suffix_nodes[newglossary_suffix[ig][2]] MakeNewGlossaryAction = SCons.Action.Action("$MAKENEWGLOSSARY ${SOURCE.filebase}%s -s ${SOURCE.filebase}.ist -t ${SOURCE.filebase}%s -o ${SOURCE.filebase}%s" % (newglossary_suffix[ig][2],newglossary_suffix[ig][0],newglossary_suffix[ig][1]), "$MAKENEWGLOSSARYCOMSTR") result = MakeNewGlossaryAction(newglfile, newglfile, env) if result != 0: check_file_error_message('%s (newglossary)' % env['MAKENEWGLOSSARY'], newglossary_suffix[ig][0]) return result # Now decide if latex needs to be run yet again to resolve warnings. if warning_rerun_re.search(logContent): must_rerun_latex = True if Verbose: print "rerun Latex due to latex or package rerun warning" if rerun_citations_re.search(logContent): must_rerun_latex = True if Verbose: print "rerun Latex due to 'Rerun to get citations correct' warning" if undefined_references_re.search(logContent): must_rerun_latex = True if Verbose: print "rerun Latex due to undefined references or citations" if (count >= int(env.subst('$LATEXRETRIES')) and must_rerun_latex): print "reached max number of retries on Latex ,",int(env.subst('$LATEXRETRIES')) # end of while loop # rename Latex's output to what the target name is if not (str(target[0]) == resultfilename and os.path.isfile(resultfilename)): if os.path.isfile(resultfilename): print "move %s to %s" % (resultfilename, str(target[0]), ) shutil.move(resultfilename,str(target[0])) # Original comment (when TEXPICTS was not restored): # The TEXPICTS enviroment variable is needed by a dvi -> pdf step # later on Mac OSX so leave it # # It is also used when searching for pictures (implicit dependencies). # Why not set the variable again in the respective builder instead # of leaving local modifications in the environment? What if multiple # latex builds in different directories need different TEXPICTS? for var in SCons.Scanner.LaTeX.LaTeX.env_variables: if var == 'TEXPICTS': continue if saved_env[var] is _null: try: del env['ENV'][var] except KeyError: pass # was never set else: env['ENV'][var] = saved_env[var] return result def LaTeXAuxAction(target = None, source= None, env=None): result = InternalLaTeXAuxAction( LaTeXAction, target, source, env ) return result LaTeX_re = re.compile("\\\\document(style|class)") def is_LaTeX(flist,env,abspath): """Scan a file list to decide if it's TeX- or LaTeX-flavored.""" # We need to scan files that are included in case the # \documentclass command is in them. # get path list from both env['TEXINPUTS'] and env['ENV']['TEXINPUTS'] savedpath = modify_env_var(env, 'TEXINPUTS', abspath) paths = env['ENV']['TEXINPUTS'] if SCons.Util.is_List(paths): pass else: # Split at os.pathsep to convert into absolute path paths = paths.split(os.pathsep) # now that we have the path list restore the env if savedpath is _null: try: del env['ENV']['TEXINPUTS'] except KeyError: pass # was never set else: env['ENV']['TEXINPUTS'] = savedpath if Verbose: print "is_LaTeX search path ",paths print "files to search :",flist # Now that we have the search path and file list, check each one for f in flist: if Verbose: print " checking for Latex source ",str(f) content = f.get_text_contents() if LaTeX_re.search(content): if Verbose: print "file %s is a LaTeX file" % str(f) return 1 if Verbose: print "file %s is not a LaTeX file" % str(f) # now find included files inc_files = [ ] inc_files.extend( include_re.findall(content) ) if Verbose: print "files included by '%s': "%str(f),inc_files # inc_files is list of file names as given. need to find them # using TEXINPUTS paths. # search the included files for src in inc_files: srcNode = FindFile(src,['.tex','.ltx','.latex'],paths,env,requireExt=False) # make this a list since is_LaTeX takes a list. fileList = [srcNode,] if Verbose: print "FindFile found ",srcNode if srcNode is not None: file_test = is_LaTeX(fileList, env, abspath) # return on first file that finds latex is needed. if file_test: return file_test if Verbose: print " done scanning ",str(f) return 0 def TeXLaTeXFunction(target = None, source= None, env=None): """A builder for TeX and LaTeX that scans the source file to decide the "flavor" of the source and then executes the appropriate program.""" # find these paths for use in is_LaTeX to search for included files basedir = os.path.split(str(source[0]))[0] abspath = os.path.abspath(basedir) if is_LaTeX(source,env,abspath): result = LaTeXAuxAction(target,source,env) if result != 0: check_file_error_message(env['LATEX']) else: result = TeXAction(target,source,env) if result != 0: check_file_error_message(env['TEX']) return result def TeXLaTeXStrFunction(target = None, source= None, env=None): """A strfunction for TeX and LaTeX that scans the source file to decide the "flavor" of the source and then returns the appropriate command string.""" if env.GetOption("no_exec"): # find these paths for use in is_LaTeX to search for included files basedir = os.path.split(str(source[0]))[0] abspath = os.path.abspath(basedir) if is_LaTeX(source,env,abspath): result = env.subst('$LATEXCOM',0,target,source)+" ..." else: result = env.subst("$TEXCOM",0,target,source)+" ..." else: result = '' return result def tex_eps_emitter(target, source, env): """An emitter for TeX and LaTeX sources when executing tex or latex. It will accept .ps and .eps graphics files """ (target, source) = tex_emitter_core(target, source, env, TexGraphics) return (target, source) def tex_pdf_emitter(target, source, env): """An emitter for TeX and LaTeX sources when executing pdftex or pdflatex. It will accept graphics files of types .pdf, .jpg, .png, .gif, and .tif """ (target, source) = tex_emitter_core(target, source, env, LatexGraphics) return (target, source) def ScanFiles(theFile, target, paths, file_tests, file_tests_search, env, graphics_extensions, targetdir, aux_files): """ For theFile (a Node) update any file_tests and search for graphics files then find all included files and call ScanFiles recursively for each of them""" content = theFile.get_text_contents() if Verbose: print " scanning ",str(theFile) for i in range(len(file_tests_search)): if file_tests[i][0] is None: if Verbose: print "scan i ",i," files_tests[i] ",file_tests[i], file_tests[i][1] file_tests[i][0] = file_tests_search[i].search(content) if Verbose and file_tests[i][0]: print " found match for ",file_tests[i][1][-1] # for newglossary insert the suffixes in file_tests[i] if file_tests[i][0] and file_tests[i][1][-1] == 'newglossary': findresult = file_tests_search[i].findall(content) for l in range(len(findresult)) : (file_tests[i][1]).insert(0,'.'+findresult[l][3]) (file_tests[i][1]).insert(0,'.'+findresult[l][2]) (file_tests[i][1]).insert(0,'.'+findresult[l][0]) suffix_list = ['.'+findresult[l][0],'.'+findresult[l][2],'.'+findresult[l][3] ] newglossary_suffix.append(suffix_list) if Verbose: print " new suffixes for newglossary ",newglossary_suffix incResult = includeOnly_re.search(content) if incResult: aux_files.append(os.path.join(targetdir, incResult.group(1))) if Verbose: print "\include file names : ", aux_files # recursively call this on each of the included files inc_files = [ ] inc_files.extend( include_re.findall(content) ) if Verbose: print "files included by '%s': "%str(theFile),inc_files # inc_files is list of file names as given. need to find them # using TEXINPUTS paths. for src in inc_files: srcNode = FindFile(src,['.tex','.ltx','.latex'],paths,env,requireExt=False) if srcNode is not None: file_tests = ScanFiles(srcNode, target, paths, file_tests, file_tests_search, env, graphics_extensions, targetdir, aux_files) if Verbose: print " done scanning ",str(theFile) return file_tests def tex_emitter_core(target, source, env, graphics_extensions): """An emitter for TeX and LaTeX sources. For LaTeX sources we try and find the common created files that are needed on subsequent runs of latex to finish tables of contents, bibliographies, indices, lists of figures, and hyperlink references. """ basename = SCons.Util.splitext(str(source[0]))[0] basefile = os.path.split(str(basename))[1] targetdir = os.path.split(str(target[0]))[0] targetbase = os.path.join(targetdir, basefile) basedir = os.path.split(str(source[0]))[0] abspath = os.path.abspath(basedir) target[0].attributes.path = abspath # # file names we will make use of in searching the sources and log file # emit_suffixes = ['.aux', '.log', '.ilg', '.blg', '.nls', '.nlg', '.gls', '.glg', '.alg'] + all_suffixes auxfilename = targetbase + '.aux' logfilename = targetbase + '.log' flsfilename = targetbase + '.fls' env.SideEffect(auxfilename,target[0]) env.SideEffect(logfilename,target[0]) env.SideEffect(flsfilename,target[0]) if Verbose: print "side effect :",auxfilename,logfilename,flsfilename env.Clean(target[0],auxfilename) env.Clean(target[0],logfilename) env.Clean(target[0],flsfilename) content = source[0].get_text_contents() # These variables are no longer used. #idx_exists = os.path.isfile(targetbase + '.idx') #nlo_exists = os.path.isfile(targetbase + '.nlo') #glo_exists = os.path.isfile(targetbase + '.glo') #acr_exists = os.path.isfile(targetbase + '.acn') # set up list with the regular expressions # we use to find features used file_tests_search = [auxfile_re, makeindex_re, bibliography_re, bibunit_re, multibib_re, addbibresource_re, tableofcontents_re, listoffigures_re, listoftables_re, hyperref_re, makenomenclature_re, makeglossary_re, makeglossaries_re, makeacronyms_re, beamer_re, newglossary_re ] # set up list with the file suffixes that need emitting # when a feature is found file_tests_suff = [['.aux','aux_file'], ['.idx', '.ind', '.ilg','makeindex'], ['.bbl', '.blg','bibliography'], ['.bbl', '.blg','bibunit'], ['.bbl', '.blg','multibib'], ['.bbl', '.blg','.bcf','addbibresource'], ['.toc','contents'], ['.lof','figures'], ['.lot','tables'], ['.out','hyperref'], ['.nlo', '.nls', '.nlg','nomenclature'], ['.glo', '.gls', '.glg','glossary'], ['.glo', '.gls', '.glg','glossaries'], ['.acn', '.acr', '.alg','acronyms'], ['.nav', '.snm', '.out', '.toc','beamer'], ['newglossary',] ] # for newglossary the suffixes are added as we find the command # build the list of lists file_tests = [] for i in range(len(file_tests_search)): file_tests.append( [None, file_tests_suff[i]] ) # TO-DO: need to add a way for the user to extend this list for whatever # auxiliary files they create in other (or their own) packages # get path list from both env['TEXINPUTS'] and env['ENV']['TEXINPUTS'] savedpath = modify_env_var(env, 'TEXINPUTS', abspath) paths = env['ENV']['TEXINPUTS'] if SCons.Util.is_List(paths): pass else: # Split at os.pathsep to convert into absolute path paths = paths.split(os.pathsep) # now that we have the path list restore the env if savedpath is _null: try: del env['ENV']['TEXINPUTS'] except KeyError: pass # was never set else: env['ENV']['TEXINPUTS'] = savedpath if Verbose: print "search path ",paths # scan all sources for side effect files aux_files = [] file_tests = ScanFiles(source[0], target, paths, file_tests, file_tests_search, env, graphics_extensions, targetdir, aux_files) for (theSearch,suffix_list) in file_tests: # add side effects if feature is present.If file is to be generated,add all side effects if Verbose and theSearch: print "check side effects for ",suffix_list[-1] if (theSearch != None) or (not source[0].exists() ): file_list = [targetbase,] # for bibunit we need a list of files if suffix_list[-1] == 'bibunit': file_basename = os.path.join(targetdir, 'bu*.aux') file_list = glob.glob(file_basename) # remove the suffix '.aux' for i in range(len(file_list)): file_list.append(SCons.Util.splitext(file_list[i])[0]) # for multibib we need a list of files if suffix_list[-1] == 'multibib': for multibibmatch in multibib_re.finditer(content): if Verbose: print "multibib match ",multibibmatch.group(1) if multibibmatch != None: baselist = multibibmatch.group(1).split(',') if Verbose: print "multibib list ", baselist for i in range(len(baselist)): file_list.append(os.path.join(targetdir, baselist[i])) # now define the side effects for file_name in file_list: for suffix in suffix_list[:-1]: env.SideEffect(file_name + suffix,target[0]) if Verbose: print "side effect tst :",file_name + suffix, " target is ",str(target[0]) env.Clean(target[0],file_name + suffix) for aFile in aux_files: aFile_base = SCons.Util.splitext(aFile)[0] env.SideEffect(aFile_base + '.aux',target[0]) if Verbose: print "side effect aux :",aFile_base + '.aux' env.Clean(target[0],aFile_base + '.aux') # read fls file to get all other files that latex creates and will read on the next pass # remove files from list that we explicitly dealt with above if os.path.isfile(flsfilename): content = open(flsfilename, "rb").read() out_files = openout_re.findall(content) myfiles = [auxfilename, logfilename, flsfilename, targetbase+'.dvi',targetbase+'.pdf'] for filename in out_files[:]: if filename in myfiles: out_files.remove(filename) env.SideEffect(out_files,target[0]) if Verbose: print "side effect fls :",out_files env.Clean(target[0],out_files) return (target, source) TeXLaTeXAction = None def generate(env): """Add Builders and construction variables for TeX to an Environment.""" global TeXLaTeXAction if TeXLaTeXAction is None: TeXLaTeXAction = SCons.Action.Action(TeXLaTeXFunction, strfunction=TeXLaTeXStrFunction) env.AppendUnique(LATEXSUFFIXES=SCons.Tool.LaTeXSuffixes) generate_common(env) import dvi dvi.generate(env) bld = env['BUILDERS']['DVI'] bld.add_action('.tex', TeXLaTeXAction) bld.add_emitter('.tex', tex_eps_emitter) def generate_darwin(env): try: environ = env['ENV'] except KeyError: environ = {} env['ENV'] = environ if (platform.system() == 'Darwin'): try: ospath = env['ENV']['PATHOSX'] except: ospath = None if ospath: env.AppendENVPath('PATH', ospath) def generate_common(env): """Add internal Builders and construction variables for LaTeX to an Environment.""" # Add OSX system paths so TeX tools can be found # when a list of tools is given the exists() method is not called generate_darwin(env) # A generic tex file Action, sufficient for all tex files. global TeXAction if TeXAction is None: TeXAction = SCons.Action.Action("$TEXCOM", "$TEXCOMSTR") # An Action to build a latex file. This might be needed more # than once if we are dealing with labels and bibtex. global LaTeXAction if LaTeXAction is None: LaTeXAction = SCons.Action.Action("$LATEXCOM", "$LATEXCOMSTR") # Define an action to run BibTeX on a file. global BibTeXAction if BibTeXAction is None: BibTeXAction = SCons.Action.Action("$BIBTEXCOM", "$BIBTEXCOMSTR") # Define an action to run Biber on a file. global BiberAction if BiberAction is None: BiberAction = SCons.Action.Action("$BIBERCOM", "$BIBERCOMSTR") # Define an action to run MakeIndex on a file. global MakeIndexAction if MakeIndexAction is None: MakeIndexAction = SCons.Action.Action("$MAKEINDEXCOM", "$MAKEINDEXCOMSTR") # Define an action to run MakeIndex on a file for nomenclatures. global MakeNclAction if MakeNclAction is None: MakeNclAction = SCons.Action.Action("$MAKENCLCOM", "$MAKENCLCOMSTR") # Define an action to run MakeIndex on a file for glossaries. global MakeGlossaryAction if MakeGlossaryAction is None: MakeGlossaryAction = SCons.Action.Action("$MAKEGLOSSARYCOM", "$MAKEGLOSSARYCOMSTR") # Define an action to run MakeIndex on a file for acronyms. global MakeAcronymsAction if MakeAcronymsAction is None: MakeAcronymsAction = SCons.Action.Action("$MAKEACRONYMSCOM", "$MAKEACRONYMSCOMSTR") try: environ = env['ENV'] except KeyError: environ = {} env['ENV'] = environ # Some Linux platforms have pdflatex set up in a way # that requires that the HOME environment variable be set. # Add it here if defined. v = os.environ.get('HOME') if v: environ['HOME'] = v CDCOM = 'cd ' if platform.system() == 'Windows': # allow cd command to change drives on Windows CDCOM = 'cd /D ' env['TEX'] = 'tex' env['TEXFLAGS'] = SCons.Util.CLVar('-interaction=nonstopmode -recorder') env['TEXCOM'] = CDCOM + '${TARGET.dir} && $TEX $TEXFLAGS ${SOURCE.file}' env['PDFTEX'] = 'pdftex' env['PDFTEXFLAGS'] = SCons.Util.CLVar('-interaction=nonstopmode -recorder') env['PDFTEXCOM'] = CDCOM + '${TARGET.dir} && $PDFTEX $PDFTEXFLAGS ${SOURCE.file}' env['LATEX'] = 'latex' env['LATEXFLAGS'] = SCons.Util.CLVar('-interaction=nonstopmode -recorder') env['LATEXCOM'] = CDCOM + '${TARGET.dir} && $LATEX $LATEXFLAGS ${SOURCE.file}' env['LATEXRETRIES'] = 4 env['PDFLATEX'] = 'pdflatex' env['PDFLATEXFLAGS'] = SCons.Util.CLVar('-interaction=nonstopmode -recorder') env['PDFLATEXCOM'] = CDCOM + '${TARGET.dir} && $PDFLATEX $PDFLATEXFLAGS ${SOURCE.file}' env['BIBTEX'] = 'bibtex' env['BIBTEXFLAGS'] = SCons.Util.CLVar('') env['BIBTEXCOM'] = CDCOM + '${TARGET.dir} && $BIBTEX $BIBTEXFLAGS ${SOURCE.filebase}' env['BIBER'] = 'biber' env['BIBERFLAGS'] = SCons.Util.CLVar('') env['BIBERCOM'] = CDCOM + '${TARGET.dir} && $BIBER $BIBERFLAGS ${SOURCE.filebase}' env['MAKEINDEX'] = 'makeindex' env['MAKEINDEXFLAGS'] = SCons.Util.CLVar('') env['MAKEINDEXCOM'] = CDCOM + '${TARGET.dir} && $MAKEINDEX $MAKEINDEXFLAGS ${SOURCE.file}' env['MAKEGLOSSARY'] = 'makeindex' env['MAKEGLOSSARYSTYLE'] = '${SOURCE.filebase}.ist' env['MAKEGLOSSARYFLAGS'] = SCons.Util.CLVar('-s ${MAKEGLOSSARYSTYLE} -t ${SOURCE.filebase}.glg') env['MAKEGLOSSARYCOM'] = CDCOM + '${TARGET.dir} && $MAKEGLOSSARY ${SOURCE.filebase}.glo $MAKEGLOSSARYFLAGS -o ${SOURCE.filebase}.gls' env['MAKEACRONYMS'] = 'makeindex' env['MAKEACRONYMSSTYLE'] = '${SOURCE.filebase}.ist' env['MAKEACRONYMSFLAGS'] = SCons.Util.CLVar('-s ${MAKEACRONYMSSTYLE} -t ${SOURCE.filebase}.alg') env['MAKEACRONYMSCOM'] = CDCOM + '${TARGET.dir} && $MAKEACRONYMS ${SOURCE.filebase}.acn $MAKEACRONYMSFLAGS -o ${SOURCE.filebase}.acr' env['MAKENCL'] = 'makeindex' env['MAKENCLSTYLE'] = 'nomencl.ist' env['MAKENCLFLAGS'] = '-s ${MAKENCLSTYLE} -t ${SOURCE.filebase}.nlg' env['MAKENCLCOM'] = CDCOM + '${TARGET.dir} && $MAKENCL ${SOURCE.filebase}.nlo $MAKENCLFLAGS -o ${SOURCE.filebase}.nls' env['MAKENEWGLOSSARY'] = 'makeindex' env['MAKENEWGLOSSARYCOM'] = CDCOM + '${TARGET.dir} && $MAKENEWGLOSSARY ' def exists(env): generate_darwin(env) return env.Detect('tex') # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/textfile.xml0000644000175000017500000001372112114661560022564 0ustar dktrkranzdktrkranz Set construction variables for the &b-Textfile; and &b-Substfile; builders. LINESEPARATOR SUBSTFILEPREFIX SUBSTFILESUFFIX TEXTFILEPREFIX TEXTFILESUFFIX SUBST_DICT The &b-Textfile; builder generates a single text file. The source strings constitute the lines; nested lists of sources are flattened. &cv-LINESEPARATOR; is used to separate the strings. If present, the &cv-SUBST_DICT; construction variable is used to modify the strings before they are written; see the &b-Substfile; description for details. The prefix and suffix specified by the &cv-TEXTFILEPREFIX; and &cv-TEXTFILESUFFIX; construction variables (the null string and .txt by default, respectively) are automatically added to the target if they are not already present. Examples: # builds/writes foo.txt env.Textfile(target = 'foo.txt', source = ['Goethe', 42, 'Schiller']) # builds/writes bar.txt env.Textfile(target = 'bar', source = ['lalala', 'tanteratei'], LINESEPARATOR='|*') # nested lists are flattened automatically env.Textfile(target = 'blob', source = ['lalala', ['Goethe', 42 'Schiller'], 'tanteratei']) # files may be used as input by wraping them in File() env.Textfile(target = 'concat', # concatenate files with a marker between source = [File('concat1'), File('concat2')], LINESEPARATOR = '====================\n') Results are: foo.txt ....8<---- Goethe 42 Schiller ....8<---- (no linefeed at the end) bar.txt: ....8<---- lalala|*tanteratei ....8<---- (no linefeed at the end) blob.txt ....8<---- lalala Goethe 42 Schiller tanteratei ....8<---- (no linefeed at the end) The &b-Substfile; builder generates a single text file by concatenating the source files. Nested lists of sources are flattened. &cv-LINESEPARATOR; is used to separate the source files; see the description of &b-Textfile; for details. If a single source file is present with an .in suffix, the suffix is stripped and the remainder is used as the default target name. The prefix and suffix specified by the &cv-SUBSTFILEPREFIX; and &cv-SUBSTFILESUFFIX; construction variables (the null string by default in both cases) are automatically added to the target if they are not already present. If a construction variable named &cv-SUBST_DICT; is present, it may be either a Python dictionary or a sequence of (key,value) tuples. If the former, the dictionary is converted into a list of tuples in an arbitrary order, so if one key is a prefix of another key or if one substitution could be further expanded by another subsitition, it is unpredictible whether the expansion will occur. Any occurences in the source of a key are replaced by the corresponding value, which may be a Python callable function or a string. If a value is a function, it is first called (with no arguments) to produce a string. The string is subst-expanded and the result replaces the key. env = Environment(tools = ['default', 'textfile']) env['prefix'] = '/usr/bin' script_dict = {'@prefix@': '/bin', @exec_prefix@: '$prefix'} env.Substfile('script.in', SUBST_DICT = script_dict) conf_dict = {'%VERSION%': '1.2.3', '%BASE%': 'MyProg'} env.Substfile('config.h.in', conf_dict, SUBST_DICT = conf_dict) # UNPREDICTABLE - one key is a prefix of another bad_foo = {'$foo': '$foo', '$foobar': '$foobar'} env.Substfile('foo.in', SUBST_DICT = bad_foo) # PREDICTABLE - keys are applied longest first good_foo = [('$foobar', '$foobar'), ('$foo', '$foo')] env.Substfile('foo.in', SUBST_DICT = good_foo) # UNPREDICTABLE - one substitution could be futher expanded bad_bar = {'@bar@': '@soap@', '@soap@': 'lye'} env.Substfile('bar.in', SUBST_DICT = bad_bar) # PREDICTABLE - substitutions are expanded in order good_bar = (('@bar@', '@soap@'), ('@soap@', 'lye')) env.Substfile('bar.in', SUBST_DICT = good_bar) # the SUBST_DICT may be in common (and not an override) substutions = {} subst = Environment(tools = ['textfile'], SUBST_DICT = substitutions) substitutions['@foo@'] = 'foo' subst['SUBST_DICT']['@bar@'] = 'bar' subst.Substfile('pgm1.c', [Value('#include "@foo@.h"'), Value('#include "@bar@.h"'), "common.in", "pgm1.in" ]) subst.Substfile('pgm2.c', [Value('#include "@foo@.h"'), Value('#include "@bar@.h"'), "common.in", "pgm2.in" ]) The separator used by the &b-Substfile; and &b-Textfile; builders. This value is used between sources when constructing the target. It defaults to the current system line separator. The dictionary used by the &b-Substfile; or &b-Textfile; builders for substitution values. It can be anything acceptable to the dict() constructor, so in addition to a dictionary, lists of tuples are also acceptable. The prefix used for &b-Substfile; file names, the null string by default. The suffix used for &b-Substfile; file names, the null string by default. The prefix used for &b-Textfile; file names, the null string by default. The suffix used for &b-Textfile; file names; .txt by default. scons-doc-2.3.0/src/engine/SCons/Tool/bcc32.xml0000644000175000017500000000106012114661560021625 0ustar dktrkranzdktrkranz Sets construction variables for the bcc32 compiler. CC CCFLAGS CFLAGS CCCOM SHCC SHCCFLAGS SHCFLAGS SHCCCOM CPPDEFPREFIX CPPDEFSUFFIX INCPREFIX INCSUFFIX SHOBJSUFFIX CFILESUFFIX _CPPDEFFLAGS _CPPINCFLAGS scons-doc-2.3.0/src/engine/SCons/Tool/386asm.py0000644000175000017500000000426712114661557021624 0ustar dktrkranzdktrkranz"""SCons.Tool.386asm Tool specification for the 386ASM assembler for the Phar Lap ETS embedded operating system. There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Tool.Tool() selection method. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/386asm.py 2013/03/03 09:48:35 garyo" from SCons.Tool.PharLapCommon import addPharLapPaths import SCons.Util as_module = __import__('as', globals(), locals(), []) def generate(env): """Add Builders and construction variables for ar to an Environment.""" as_module.generate(env) env['AS'] = '386asm' env['ASFLAGS'] = SCons.Util.CLVar('') env['ASPPFLAGS'] = '$ASFLAGS' env['ASCOM'] = '$AS $ASFLAGS $SOURCES -o $TARGET' env['ASPPCOM'] = '$CC $ASPPFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS $SOURCES -o $TARGET' addPharLapPaths(env) def exists(env): return env.Detect('386asm') # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/tlib.py0000644000175000017500000000355012114661560021521 0ustar dktrkranzdktrkranz"""SCons.Tool.tlib XXX """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/tlib.py 2013/03/03 09:48:35 garyo" import SCons.Tool import SCons.Tool.bcc32 import SCons.Util def generate(env): SCons.Tool.bcc32.findIt('tlib', env) """Add Builders and construction variables for ar to an Environment.""" SCons.Tool.createStaticLibBuilder(env) env['AR'] = 'tlib' env['ARFLAGS'] = SCons.Util.CLVar('') env['ARCOM'] = '$AR $TARGET $ARFLAGS /a $SOURCES' env['LIBPREFIX'] = '' env['LIBSUFFIX'] = '.lib' def exists(env): return SCons.Tool.bcc32.findIt('tlib', env) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/sgilink.xml0000644000175000017500000000060212114661560022372 0ustar dktrkranzdktrkranz Sets construction variables for the SGI linker. LINK SHLINKFLAGS RPATHPREFIX RPATHSUFFIX scons-doc-2.3.0/src/engine/SCons/Tool/icc.py0000644000175000017500000000423512114661560021326 0ustar dktrkranzdktrkranz"""engine.SCons.Tool.icc Tool-specific initialization for the OS/2 icc compiler. There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Tool.Tool() selection method. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/icc.py 2013/03/03 09:48:35 garyo" import cc def generate(env): """Add Builders and construction variables for the OS/2 to an Environment.""" cc.generate(env) env['CC'] = 'icc' env['CCCOM'] = '$CC $CFLAGS $CCFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS /c $SOURCES /Fo$TARGET' env['CXXCOM'] = '$CXX $CXXFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS /c $SOURCES /Fo$TARGET' env['CPPDEFPREFIX'] = '/D' env['CPPDEFSUFFIX'] = '' env['INCPREFIX'] = '/I' env['INCSUFFIX'] = '' env['CFILESUFFIX'] = '.c' env['CXXFILESUFFIX'] = '.cc' def exists(env): return env.Detect('icc') # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/dmd.py0000644000175000017500000002245612114661560021341 0ustar dktrkranzdktrkranz"""SCons.Tool.dmd Tool-specific initialization for the Digital Mars D compiler. (http://digitalmars.com/d) Coded by Andy Friesen (andy@ikagames.com) 15 November 2003 Amended by Russel Winder (russel@russel.org.uk) 2010-02-07 There are a number of problems with this script at this point in time. The one that irritates me the most is the Windows linker setup. The D linker doesn't have a way to add lib paths on the commandline, as far as I can see. You have to specify paths relative to the SConscript or use absolute paths. To hack around it, add '#/blah'. This will link blah.lib from the directory where SConstruct resides. Compiler variables: DC - The name of the D compiler to use. Defaults to dmd or gdmd, whichever is found. DPATH - List of paths to search for import modules. DVERSIONS - List of version tags to enable when compiling. DDEBUG - List of debug tags to enable when compiling. Linker related variables: LIBS - List of library files to link in. DLINK - Name of the linker to use. Defaults to dmd or gdmd. DLINKFLAGS - List of linker flags. Lib tool variables: DLIB - Name of the lib tool to use. Defaults to lib. DLIBFLAGS - List of flags to pass to the lib tool. LIBS - Same as for the linker. (libraries to pull into the .lib) """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/dmd.py 2013/03/03 09:48:35 garyo" import os import SCons.Action import SCons.Builder import SCons.Defaults import SCons.Scanner.D import SCons.Tool # Adapted from c++.py def isD(source): if not source: return 0 for s in source: if s.sources: ext = os.path.splitext(str(s.sources[0]))[1] if ext == '.d': return 1 return 0 smart_link = {} smart_lib = {} def generate(env): global smart_link global smart_lib static_obj, shared_obj = SCons.Tool.createObjBuilders(env) DAction = SCons.Action.Action('$DCOM', '$DCOMSTR') static_obj.add_action('.d', DAction) shared_obj.add_action('.d', DAction) static_obj.add_emitter('.d', SCons.Defaults.StaticObjectEmitter) shared_obj.add_emitter('.d', SCons.Defaults.SharedObjectEmitter) dc = env.Detect(['dmd', 'gdmd']) env['DC'] = dc env['DCOM'] = '$DC $_DINCFLAGS $_DVERFLAGS $_DDEBUGFLAGS $_DFLAGS -c -of$TARGET $SOURCES' env['_DINCFLAGS'] = '$( ${_concat(DINCPREFIX, DPATH, DINCSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)' env['_DVERFLAGS'] = '$( ${_concat(DVERPREFIX, DVERSIONS, DVERSUFFIX, __env__)} $)' env['_DDEBUGFLAGS'] = '$( ${_concat(DDEBUGPREFIX, DDEBUG, DDEBUGSUFFIX, __env__)} $)' env['_DFLAGS'] = '$( ${_concat(DFLAGPREFIX, DFLAGS, DFLAGSUFFIX, __env__)} $)' env['DPATH'] = ['#/'] env['DFLAGS'] = [] env['DVERSIONS'] = [] env['DDEBUG'] = [] if dc: # Add the path to the standard library. # This is merely for the convenience of the dependency scanner. dmd_path = env.WhereIs(dc) if dmd_path: x = dmd_path.rindex(dc) phobosDir = dmd_path[:x] + '/../src/phobos' if os.path.isdir(phobosDir): env.Append(DPATH = [phobosDir]) env['DINCPREFIX'] = '-I' env['DINCSUFFIX'] = '' env['DVERPREFIX'] = '-version=' env['DVERSUFFIX'] = '' env['DDEBUGPREFIX'] = '-debug=' env['DDEBUGSUFFIX'] = '' env['DFLAGPREFIX'] = '-' env['DFLAGSUFFIX'] = '' env['DFILESUFFIX'] = '.d' # Need to use the Digital Mars linker/lib on windows. # *nix can just use GNU link. if env['PLATFORM'] == 'win32': env['DLINK'] = '$DC' env['DLINKCOM'] = '$DLINK -of$TARGET $SOURCES $DFLAGS $DLINKFLAGS $_DLINKLIBFLAGS' env['DLIB'] = 'lib' env['DLIBCOM'] = '$DLIB $_DLIBFLAGS -c $TARGET $SOURCES $_DLINKLIBFLAGS' env['_DLINKLIBFLAGS'] = '$( ${_concat(DLIBLINKPREFIX, LIBS, DLIBLINKSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)' env['_DLIBFLAGS'] = '$( ${_concat(DLIBFLAGPREFIX, DLIBFLAGS, DLIBFLAGSUFFIX, __env__)} $)' env['DLINKFLAGS'] = [] env['DLIBLINKPREFIX'] = '' env['DLIBLINKSUFFIX'] = '.lib' env['DLIBFLAGPREFIX'] = '-' env['DLIBFLAGSUFFIX'] = '' env['DLINKFLAGPREFIX'] = '-' env['DLINKFLAGSUFFIX'] = '' SCons.Tool.createStaticLibBuilder(env) # Basically, we hijack the link and ar builders with our own. # these builders check for the presence of D source, and swap out # the system's defaults for the Digital Mars tools. If there's no D # source, then we silently return the previous settings. linkcom = env.get('LINKCOM') try: env['SMART_LINKCOM'] = smart_link[linkcom] except KeyError: def _smartLink(source, target, env, for_signature, defaultLinker=linkcom): if isD(source): # XXX I'm not sure how to add a $DLINKCOMSTR variable # so that it works with this _smartLink() logic, # and I don't have a D compiler/linker to try it out, # so we'll leave it alone for now. return '$DLINKCOM' else: return defaultLinker env['SMART_LINKCOM'] = smart_link[linkcom] = _smartLink arcom = env.get('ARCOM') try: env['SMART_ARCOM'] = smart_lib[arcom] except KeyError: def _smartLib(source, target, env, for_signature, defaultLib=arcom): if isD(source): # XXX I'm not sure how to add a $DLIBCOMSTR variable # so that it works with this _smartLib() logic, and # I don't have a D compiler/archiver to try it out, # so we'll leave it alone for now. return '$DLIBCOM' else: return defaultLib env['SMART_ARCOM'] = smart_lib[arcom] = _smartLib # It is worth noting that the final space in these strings is # absolutely pivotal. SCons sees these as actions and not generators # if it is not there. (very bad) env['ARCOM'] = '$SMART_ARCOM ' env['LINKCOM'] = '$SMART_LINKCOM ' else: # assuming linux linkcom = env.get('LINKCOM') try: env['SMART_LINKCOM'] = smart_link[linkcom] except KeyError: def _smartLink(source, target, env, for_signature, defaultLinker=linkcom, dc=dc): if isD(source): try: libs = env['LIBS'] except KeyError: libs = [] if dc == 'dmd': # TODO: This assumes that the dmd executable is in the # bin directory and that the libraries are in a peer # directory lib. This true of the Digital Mars # distribution but . . . import glob dHome = env.WhereIs(dc).replace('/dmd' , '/..') if glob.glob(dHome + '/lib/*phobos2*'): if 'phobos2' not in libs: env.Append(LIBPATH = [dHome + '/lib']) env.Append(LIBS = ['phobos2']) # TODO: Find out when there will be a # 64-bit version of D. env.Append(LINKFLAGS = ['-m32']) else: if 'phobos' not in libs: env.Append(LIBS = ['phobos']) elif dc is 'gdmd': env.Append(LIBS = ['gphobos']) if 'pthread' not in libs: env.Append(LIBS = ['pthread']) if 'm' not in libs: env.Append(LIBS = ['m']) return defaultLinker env['SMART_LINKCOM'] = smart_link[linkcom] = _smartLink env['LINKCOM'] = '$SMART_LINKCOM ' def exists(env): return env.Detect(['dmd', 'gdmd']) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/f90.py0000644000175000017500000000400512114661560021161 0ustar dktrkranzdktrkranz"""engine.SCons.Tool.f90 Tool-specific initialization for the generic Posix f90 Fortran compiler. There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Tool.Tool() selection method. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/f90.py 2013/03/03 09:48:35 garyo" import SCons.Defaults import SCons.Scanner.Fortran import SCons.Tool import SCons.Util from SCons.Tool.FortranCommon import add_all_to_env, add_f90_to_env compilers = ['f90'] def generate(env): add_all_to_env(env) add_f90_to_env(env) fc = env.Detect(compilers) or 'f90' env['F90'] = fc env['SHF90'] = fc env['FORTRAN'] = fc env['SHFORTRAN'] = fc def exists(env): return env.Detect(compilers) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/ipkg.py0000644000175000017500000000471612114661560021526 0ustar dktrkranzdktrkranz"""SCons.Tool.ipkg Tool-specific initialization for ipkg. There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Tool.Tool() selection method. The ipkg tool calls the ipkg-build. Its only argument should be the packages fake_root. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/ipkg.py 2013/03/03 09:48:35 garyo" import os import SCons.Builder def generate(env): """Add Builders and construction variables for ipkg to an Environment.""" try: bld = env['BUILDERS']['Ipkg'] except KeyError: bld = SCons.Builder.Builder( action = '$IPKGCOM', suffix = '$IPKGSUFFIX', source_scanner = None, target_scanner = None) env['BUILDERS']['Ipkg'] = bld env['IPKG'] = 'ipkg-build' env['IPKGCOM'] = '$IPKG $IPKGFLAGS ${SOURCE}' env['IPKGUSER'] = os.popen('id -un').read().strip() env['IPKGGROUP'] = os.popen('id -gn').read().strip() env['IPKGFLAGS'] = SCons.Util.CLVar('-o $IPKGUSER -g $IPKGGROUP') env['IPKGSUFFIX'] = '.ipk' def exists(env): return env.Detect('ipkg-build') # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/msvsTests.py0000644000175000017500000010104612114661560022601 0ustar dktrkranzdktrkranz# # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/msvsTests.py 2013/03/03 09:48:35 garyo" import os import sys import TestCmd import unittest import copy from SCons.Tool.msvs import * import SCons.Util import SCons.Warnings from SCons.Tool.MSCommon.common import debug from SCons.Tool.MSCommon import get_default_version, \ query_versions regdata_6a = r'''[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio] [HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\6.0] [HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\6.0\ServicePacks] "sp3"="" [HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\6.0\Setup] "VsCommonDir"="C:\Program Files\Microsoft Visual Studio\Common" [HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\6.0\Setup\Microsoft Developer Network Library - Visual Studio 6.0a] "ProductDir"="C:\Program Files\Microsoft Visual Studio\MSDN98\98VSa\1033" [HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\6.0\Setup\Microsoft Visual C++] "ProductDir"="C:\Program Files\Microsoft Visual Studio\VC98" '''.split('\n') regdata_6b = r'''[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio] [HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\6.0] "InstallDir"="C:\VS6\Common\IDE\IDE98" [HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\6.0\ServicePacks] "sp5"="" "latest"=dword:00000005 [HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\6.0\Setup] "VsCommonDir"="C:\VS6\Common" [HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\6.0\Setup\Microsoft Visual Basic] "ProductDir"="C:\VS6\VB98" [HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\6.0\Setup\Microsoft Visual C++] "ProductDir"="C:\VS6\VC98" [HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\6.0\Setup\Microsoft Visual Studio] "ProductDir"="C:\VS6" [HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\6.0\Setup\Microsoft VSEE Client] "ProductDir"="C:\VS6\Common\Tools" [HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\6.0\Setup\Visual Studio 98] '''.split('\n') regdata_7 = r''' [HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0] "InstallDir"="C:\Program Files\Microsoft Visual Studio .NET\Common7\IDE\" "Source Directories"="C:\Program Files\Microsoft Visual Studio .NET\Vc7\crt\;C:\Program Files\Microsoft Visual Studio .NET\Vc7\atlmfc\src\mfc\;C:\Program Files\Microsoft Visual Studio .NET\Vc7\atlmfc\src\atl\" [HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\InstalledProducts] [HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\InstalledProducts\CrystalReports] @="#15007" "Package"="{F05E92C6-8346-11D3-B4AD-00A0C9B04E7B}" "ProductDetails"="#15009" "LogoID"="0" "PID"="#15008" "UseInterface"=dword:00000001 [HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\InstalledProducts\Visual Basic.NET] @="" "DefaultProductAttribute"="VB" "Package"="{164B10B9-B200-11D0-8C61-00A0C91E29D5}" "UseInterface"=dword:00000001 [HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\InstalledProducts\Visual C#] @="" "Package"="{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}" "UseInterface"=dword:00000001 "DefaultProductAttribute"="C#" [HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\InstalledProducts\VisualC++] "UseInterface"=dword:00000001 "Package"="{F1C25864-3097-11D2-A5C5-00C04F7968B4}" "DefaultProductAttribute"="VC" [HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\Setup] "Dbghelp_path"="C:\Program Files\Microsoft Visual Studio .NET\Common7\IDE\" "dw_dir"="C:\Program Files\Microsoft Visual Studio .NET\Common7\IDE\" [HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\Setup\MSDN] "ProductDir"="C:\Program Files\Microsoft Visual Studio .NET\Msdn\1033\" [HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\Setup\Servicing\SKU] "Visual Studio .NET Professional - English"="{D0610409-7D65-11D5-A54F-0090278A1BB8}" [HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\Setup\VB] "ProductDir"="C:\Program Files\Microsoft Visual Studio .NET\Vb7\" [HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\Setup\VC] "ProductDir"="C:\Program Files\Microsoft Visual Studio .NET\Vc7\" [HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\Setup\VC#] "ProductDir"="C:\Program Files\Microsoft Visual Studio .NET\VC#\" [HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\Setup\Visual Studio .NET Professional - English] "InstallSuccess"=dword:00000001 [HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\Setup\VS] "EnvironmentDirectory"="C:\Program Files\Microsoft Visual Studio .NET\Common7\IDE\" "EnvironmentPath"="C:\Program Files\Microsoft Visual Studio .NET\Common7\IDE\devenv.exe" "VS7EnvironmentLocation"="C:\Program Files\Microsoft Visual Studio .NET\Common7\IDE\devenv.exe" "MSMDir"="C:\Program Files\Common Files\Merge Modules\" "ProductDir"="C:\Program Files\Microsoft Visual Studio .NET\" "VS7CommonBinDir"="C:\Program Files\Microsoft Visual Studio .NET\Common7\Tools\" "VS7CommonDir"="C:\Program Files\Microsoft Visual Studio .NET\Common7\" "VSUpdateDir"="C:\Program Files\Microsoft Visual Studio .NET\Setup\VSUpdate\" [HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\Setup\VS\BuildNumber] "1033"="7.0.9466" [HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\Setup\VS\Pro] "ProductDir"="C:\Program Files\Microsoft Visual Studio .NET\" [HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\VC] [HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\VC\VC_OBJECTS_PLATFORM_INFO] [HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\VC\VC_OBJECTS_PLATFORM_INFO\Win32] @="{A54AAE91-30C2-11D3-87BF-A04A4CC10000}" [HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\VC\VC_OBJECTS_PLATFORM_INFO\Win32\Directories] "Path Dirs"="$(VCInstallDir)bin;$(VSInstallDir)Common7\Tools\bin\prerelease;$(VSInstallDir)Common7\Tools\bin;$(VSInstallDir)Common7\tools;$(VSInstallDir)Common7\ide;C:\Program Files\HTML Help Workshop\;$(FrameworkSDKDir)bin;$(FrameworkDir)$(FrameworkVersion);C:\perl\bin;C:\cygwin\bin;c:\cygwin\usr\bin;C:\bin;C:\program files\perforce;C:\cygwin\usr\local\bin\i686-pc-cygwin;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem" "Library Dirs"="$(VCInstallDir)lib;$(VCInstallDir)atlmfc\lib;$(VCInstallDir)PlatformSDK\lib\prerelease;$(VCInstallDir)PlatformSDK\lib;$(FrameworkSDKDir)lib" "Include Dirs"="$(VCInstallDir)include;$(VCInstallDir)atlmfc\include;$(VCInstallDir)PlatformSDK\include\prerelease;$(VCInstallDir)PlatformSDK\include;$(FrameworkSDKDir)include" "Source Dirs"="$(VCInstallDir)atlmfc\src\mfc;$(VCInstallDir)atlmfc\src\atl;$(VCInstallDir)crt\src" "Reference Dirs"="" '''.split('\n') regdata_7_1 = r''' [HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.1] @="" "Source Directories"="C:\Program Files\Microsoft Visual Studio .NET 2003\Vc7\crt\src\;C:\Program Files\Microsoft Visual Studio .NET 2003\Vc7\atlmfc\src\mfc\;C:\Program Files\Microsoft Visual Studio .NET 2003\Vc7\atlmfc\src\atl\" "ThisVersionSolutionCLSID"="{246C57AE-40DD-4d6b-9E8D-B0F5757BB2A8}" "ThisVersionDTECLSID"="{8CD2DD97-4EC1-4bc4-9359-89A3EEDD57A6}" "InstallDir"="C:\Program Files\Microsoft Visual Studio .NET 2003\Common7\IDE\" "CLR Version"="v1.1.4322" [HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.1\InstalledProducts] [HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.1\InstalledProducts\Smart Device Extensions] "UseInterface"=dword:00000001 "VS7InstallDir"="C:\Program Files\Microsoft Visual Studio .NET 2003\" "VBDeviceInstallDir"="C:\Program Files\Microsoft Visual Studio .NET 2003\VB7\" "CSharpDeviceInstallDir"="C:\Program Files\Microsoft Visual Studio .NET 2003\VC#\" [HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.1\InstalledProducts\Visual Basic.NET] "UseInterface"=dword:00000001 "Package"="{164B10B9-B200-11D0-8C61-00A0C91E29D5}" "DefaultProductAttribute"="VB" @="" [HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.1\InstalledProducts\Visual C#] "DefaultProductAttribute"="C#" "UseInterface"=dword:00000001 "Package"="{FAE04EC1-301F-11D3-BF4B-00C04F79EFBC}" @="" [HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.1\InstalledProducts\Visual JSharp] @="" "Package"="{E6FDF8B0-F3D1-11D4-8576-0002A516ECE8}" "UseInterface"=dword:00000001 "DefaultProductAttribute"="Visual JSharp" [HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.1\InstalledProducts\VisualC++] "UseInterface"=dword:00000001 "Package"="{F1C25864-3097-11D2-A5C5-00C04F7968B4}" "DefaultProductAttribute"="VC" [HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.1\Setup] "Dbghelp_path"="C:\Program Files\Microsoft Visual Studio .NET 2003\Common7\IDE\" "dw_dir"="C:\Program Files\Microsoft Visual Studio .NET 2003\Common7\IDE\" [HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.1\Setup\CSDPROJ] "ProductDir"="C:\Program Files\Microsoft Visual Studio .NET 2003\VC#\" [HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.1\Setup\JSHPROJ] "ProductDir"="C:\Program Files\Microsoft Visual Studio .NET 2003\VJ#\" [HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.1\Setup\Servicing] "CurrentSULevel"=dword:00000000 "CurrentSPLevel"=dword:00000000 "Server Path"="" [HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.1\Setup\Servicing\Package] "FxSDK"="" "VB"="" "VC"="" "VCS"="" [HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.1\Setup\Servicing\SKU] "Visual Studio .NET Professional 2003 - English"="{20610409-CA18-41A6-9E21-A93AE82EE7C5}" [HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.1\Setup\VB] "ProductDir"="C:\Program Files\Microsoft Visual Studio .NET 2003\Vb7\" [HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.1\Setup\VBDPROJ] "ProductDir"="C:\Program Files\Microsoft Visual Studio .NET 2003\Vb7\" [HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.1\Setup\VC] "ProductDir"="C:\Program Files\Microsoft Visual Studio .NET 2003\Vc7\" [HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.1\Setup\VC#] "ProductDir"="C:\Program Files\Microsoft Visual Studio .NET 2003\VC#\" [HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.1\Setup\Visual Studio .NET Professional 2003 - English] "InstallSuccess"=dword:00000001 [HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.1\Setup\VS] "EnvironmentDirectory"="C:\Program Files\Microsoft Visual Studio .NET 2003\Common7\IDE\" "EnvironmentPath"="C:\Program Files\Microsoft Visual Studio .NET 2003\Common7\IDE\devenv.exe" "VS7EnvironmentLocation"="C:\Program Files\Microsoft Visual Studio .NET 2003\Common7\IDE\devenv.exe" "MSMDir"="C:\Program Files\Common Files\Merge Modules\" "VS7CommonBinDir"="C:\Program Files\Microsoft Visual Studio .NET 2003\Common7\Tools\" "VS7CommonDir"="C:\Program Files\Microsoft Visual Studio .NET 2003\Common7\" "ProductDir"="C:\Program Files\Microsoft Visual Studio .NET 2003\" "VSUpdateDir"="C:\Program Files\Microsoft Visual Studio .NET 2003\Setup\VSUpdate\" [HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.1\Setup\VS\BuildNumber] "1033"="7.1.3088" [HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.1\Setup\VS\Pro] "ProductDir"="C:\Program Files\Microsoft Visual Studio .NET 2003\" [HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.1\VC] [HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.1\VC\VC_OBJECTS_PLATFORM_INFO] [HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.1\VC\VC_OBJECTS_PLATFORM_INFO\Win32] @="{759354D0-6B42-4705-AFFB-56E34D2BC3D4}" [HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.1\VC\VC_OBJECTS_PLATFORM_INFO\Win32\Directories] "Path Dirs"="$(VCInstallDir)bin;$(VSInstallDir)Common7\Tools\bin\prerelease;$(VSInstallDir)Common7\Tools\bin;$(VSInstallDir)Common7\tools;$(VSInstallDir)Common7\ide;C:\Program Files\HTML Help Workshop\;$(FrameworkSDKDir)bin;$(FrameworkDir)$(FrameworkVersion);C:\Perl\bin\;c:\bin;c:\cygwin\bin;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:\Program Files\Common Files\Avid;C:\Program Files\backburner 2\;C:\Program Files\cvsnt;C:\Program Files\Subversion\bin;C:\Program Files\Common Files\Adobe\AGL;C:\Program Files\HTMLDoc" "Library Dirs"="$(VCInstallDir)lib;$(VCInstallDir)atlmfc\lib;$(VCInstallDir)PlatformSDK\lib\prerelease;$(VCInstallDir)PlatformSDK\lib;$(FrameworkSDKDir)lib" "Include Dirs"="$(VCInstallDir)include;$(VCInstallDir)atlmfc\include;$(VCInstallDir)PlatformSDK\include\prerelease;$(VCInstallDir)PlatformSDK\include;$(FrameworkSDKDir)include" "Source Dirs"="$(VCInstallDir)atlmfc\src\mfc;$(VCInstallDir)atlmfc\src\atl;$(VCInstallDir)crt\src" "Reference Dirs"="$(FrameWorkDir)$(FrameWorkVersion)" [HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.1\VC\VC_OBJECTS_PLATFORM_INFO\Win32\ToolDefaultExtensionLists] "VCCLCompilerTool"="*.cpp;*.cxx;*.cc;*.c" "VCLinkerTool"="*.obj;*.res;*.lib;*.rsc" "VCLibrarianTool"="*.obj;*.res;*.lib;*.rsc" "VCMIDLTool"="*.idl;*.odl" "VCCustomBuildTool"="*.bat" "VCResourceCompilerTool"="*.rc" "VCPreBuildEventTool"="*.bat" "VCPreLinkEventTool"="*.bat" "VCPostBuildEventTool"="*.bat" "VCBscMakeTool"="*.sbr" "VCNMakeTool"="" "VCWebServiceProxyGeneratorTool"="*.discomap" "VCWebDeploymentTool"="" "VCALinkTool"="*.resources" "VCManagedResourceCompilerTool"="*.resx" "VCXMLDataGeneratorTool"="*.xsd" "VCManagedWrapperGeneratorTool"="" "VCAuxiliaryManagedWrapperGeneratorTool"="" "VCPrimaryInteropTool"="" '''.split('\n') regdata_8exp = r''' [HKEY_LOCAL_MACHINE\Software\Microsoft\VCExpress\8.0] "CLR Version"="v2.0.50727" "ApplicationID"="VCExpress" "SecurityAppID"="{741726F6-1EAE-4680-86A6-6085E8872CF8}" "InstallDir"="C:\Program Files\Microsoft Visual Studio 8\Common7\IDE\" "EnablePreloadCLR"=dword:00000001 "RestoreAppPath"=dword:00000001 [HKEY_LOCAL_MACHINE\Software\Microsoft\VCExpress\8.0\InstalledProducts] [HKEY_LOCAL_MACHINE\Software\Microsoft\VCExpress\8.0\InstalledProducts\Microsoft Visual C++] "UseInterface"=dword:00000001 "Package"="{F1C25864-3097-11D2-A5C5-00C04F7968B4}" "DefaultProductAttribute"="VC" [HKEY_LOCAL_MACHINE\Software\Microsoft\VCExpress\8.0\Setup] [HKEY_LOCAL_MACHINE\Software\Microsoft\VCExpress\8.0\Setup\VC] "ProductDir"="C:\Program Files\Microsoft Visual Studio 8\VC\" [HKEY_LOCAL_MACHINE\Software\Microsoft\VCExpress\8.0\Setup\VS] "ProductDir"="C:\Program Files\Microsoft Visual Studio 8\" [HKEY_LOCAL_MACHINE\Software\Microsoft\VCExpress\8.0\VC] [HKEY_LOCAL_MACHINE\Software\Microsoft\VCExpress\8.0\VC\VC_OBJECTS_PLATFORM_INFO] [HKEY_LOCAL_MACHINE\Software\Microsoft\VCExpress\8.0\VC\VC_OBJECTS_PLATFORM_INFO\Win32] @="{72f11281-2429-11d7-8bf6-00b0d03daa06}" [HKEY_LOCAL_MACHINE\Software\Microsoft\VCExpress\8.0\VC\VC_OBJECTS_PLATFORM_INFO\Win32\ToolDefaultExtensionLists] "VCCLCompilerTool"="*.cpp;*.cxx;*.cc;*.c" "VCLinkerTool"="*.obj;*.res;*.lib;*.rsc;*.licenses" "VCLibrarianTool"="*.obj;*.res;*.lib;*.rsc" "VCMIDLTool"="*.idl;*.odl" "VCCustomBuildTool"="*.bat" "VCResourceCompilerTool"="*.rc" "VCPreBuildEventTool"="*.bat" "VCPreLinkEventTool"="*.bat" "VCPostBuildEventTool"="*.bat" "VCBscMakeTool"="*.sbr" "VCFxCopTool"="*.dll;*.exe" "VCNMakeTool"="" "VCWebServiceProxyGeneratorTool"="*.discomap" "VCWebDeploymentTool"="" "VCALinkTool"="*.resources" "VCManagedResourceCompilerTool"="*.resx" "VCXMLDataGeneratorTool"="*.xsd" "VCManifestTool"="*.manifest" "VCXDCMakeTool"="*.xdc" '''.split('\n') regdata_80 = r''' [HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\8.0] "CLR Version"="v2.0.50727" "ApplicationID"="VisualStudio" "ThisVersionDTECLSID"="{BA018599-1DB3-44f9-83B4-461454C84BF8}" "ThisVersionSolutionCLSID"="{1B2EEDD6-C203-4d04-BD59-78906E3E8AAB}" "SecurityAppID"="{DF99D4F5-9F04-4CEF-9D39-095821B49C77}" "InstallDir"="C:\Program Files\Microsoft Visual Studio 8\Common7\IDE\" "EnablePreloadCLR"=dword:00000001 "RestoreAppPath"=dword:00000001 "Source Directories"="C:\Program Files\Microsoft Visual Studio 8\VC\crt\src\;C:\Program Files\Microsoft Visual Studio 8\VC\atlmfc\src\mfc\;C:\Program Files\Microsoft Visual Studio 8\VC\atlmfc\src\atl\;C:\Program Files\Microsoft Visual Studio 8\VC\atlmfc\include\" [HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\8.0\InstalledProducts] [HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\8.0\InstalledProducts\Microsoft Visual C++] "UseInterface"=dword:00000001 "Package"="{F1C25864-3097-11D2-A5C5-00C04F7968B4}" "DefaultProductAttribute"="VC" [HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\8.0\Setup] "Dbghelp_path"="C:\Program Files\Microsoft Visual Studio 8\Common7\IDE\" [HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\8.0\Setup\EF] "ProductDir"="C:\Program Files\Microsoft Visual Studio 8\EnterpriseFrameworks\" [HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\8.0\Setup\Microsoft Visual Studio 2005 Professional Edition - ENU] "SrcPath"="d:\vs\" "InstallSuccess"=dword:00000001 [HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\8.0\Setup\VC] "ProductDir"="C:\Program Files\Microsoft Visual Studio 8\VC\" [HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\8.0\Setup\VS] "ProductDir"="C:\Program Files\Microsoft Visual Studio 8\" "VS7EnvironmentLocation"="C:\Program Files\Microsoft Visual Studio 8\Common7\IDE\devenv.exe" "EnvironmentPath"="C:\Program Files\Microsoft Visual Studio 8\Common7\IDE\devenv.exe" "EnvironmentDirectory"="C:\Program Files\Microsoft Visual Studio 8\Common7\IDE\" "VS7CommonDir"="C:\Program Files\Microsoft Visual Studio 8\Common7\" "VS7CommonBinDir"="C:\Program Files\Microsoft Visual Studio 8\Common7\Tools\" [HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\8.0\Setup\VS\BuildNumber] "1033"="8.0.50727.42" [HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\8.0\Setup\VS\Pro] "ProductDir"="C:\Program Files\Microsoft Visual Studio 8\" [HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\8.0\VC] [HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\8.0\VC\VC_OBJECTS_PLATFORM_INFO] [HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\8.0\VC\VC_OBJECTS_PLATFORM_INFO\Win32] @="{72f11281-2429-11d7-8bf6-00b0d03daa06}" [HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\8.0\VC\VC_OBJECTS_PLATFORM_INFO\Win32\ToolDefaultExtensionLists] "VCCLCompilerTool"="*.cpp;*.cxx;*.cc;*.c" "VCLinkerTool"="*.obj;*.res;*.lib;*.rsc;*.licenses" "VCLibrarianTool"="*.obj;*.res;*.lib;*.rsc" "VCMIDLTool"="*.idl;*.odl" "VCCustomBuildTool"="*.bat" "VCResourceCompilerTool"="*.rc" "VCPreBuildEventTool"="*.bat" "VCPreLinkEventTool"="*.bat" "VCPostBuildEventTool"="*.bat" "VCBscMakeTool"="*.sbr" "VCFxCopTool"="*.dll;*.exe" "VCNMakeTool"="" "VCWebServiceProxyGeneratorTool"="*.discomap" "VCWebDeploymentTool"="" "VCALinkTool"="*.resources" "VCManagedResourceCompilerTool"="*.resx" "VCXMLDataGeneratorTool"="*.xsd" "VCManifestTool"="*.manifest" "VCXDCMakeTool"="*.xdc" '''.split('\n') regdata_cv = r'''[HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion] "ProgramFilesDir"="C:\Program Files" "CommonFilesDir"="C:\Program Files\Common Files" "MediaPath"="C:\WINDOWS\Media" '''.split('\n') regdata_none = [] class DummyEnv(object): def __init__(self, dict=None): if dict: self.dict = dict else: self.dict = {} def Dictionary(self, key = None): if not key: return self.dict return self.dict[key] def __setitem__(self,key,value): self.dict[key] = value def __getitem__(self,key): return self.dict[key] def __contains__(self,key): return key in self.dict def has_key(self,name): return name in self.dict class RegKey(object): """key class for storing an 'open' registry key""" def __init__(self,key): self.key = key # Warning: this is NOT case-insensitive, unlike the Windows registry. # So e.g. HKLM\Software is NOT the same key as HKLM\SOFTWARE. class RegNode(object): """node in the dummy registry""" def __init__(self,name): self.valdict = {} self.keydict = {} self.keyarray = [] self.valarray = [] self.name = name def value(self,val): if val in self.valdict: return (self.valdict[val],1) else: raise SCons.Util.RegError def addValue(self,name,val): self.valdict[name] = val self.valarray.append(name) def valindex(self,index): rv = None try: rv = (self.valarray[index],self.valdict[self.valarray[index]],1) except KeyError: raise SCons.Util.RegError return rv def key(self,key,sep = '\\'): if key.find(sep) != -1: keyname, subkeys = key.split(sep,1) else: keyname = key subkeys = "" try: # recurse, and return the lowest level key node if subkeys: return self.keydict[keyname].key(subkeys) else: return self.keydict[keyname] except KeyError: raise SCons.Util.RegError def addKey(self,name,sep = '\\'): if name.find(sep) != -1: keyname, subkeys = name.split(sep, 1) else: keyname = name subkeys = "" if keyname not in self.keydict: self.keydict[keyname] = RegNode(keyname) self.keyarray.append(keyname) # recurse, and return the lowest level key node if subkeys: return self.keydict[keyname].addKey(subkeys) else: return self.keydict[keyname] def keyindex(self,index): return self.keydict[self.keyarray[index]] def __str__(self): return self._doStr() def _doStr(self, indent = ''): rv = "" for value in self.valarray: rv = rv + '%s"%s" = "%s"\n' % (indent, value, self.valdict[value]) for key in self.keyarray: rv = rv + "%s%s: {\n"%(indent, key) rv = rv + self.keydict[key]._doStr(indent + ' ') rv = rv + indent + '}\n' return rv class DummyRegistry(object): """registry class for storing fake registry attributes""" def __init__(self,data): """parse input data into the fake registry""" self.root = RegNode('REGISTRY') self.root.addKey('HKEY_LOCAL_MACHINE') self.root.addKey('HKEY_CURRENT_USER') self.root.addKey('HKEY_USERS') self.root.addKey('HKEY_CLASSES_ROOT') self.parse(data) def parse(self, data): parents = [None, None] parents[0] = self.root keymatch = re.compile('^\[(.*)\]$') valmatch = re.compile('^(?:"(.*)"|[@])="(.*)"$') for line in data: m1 = keymatch.match(line) if m1: # add a key, set it to current parent. # Also create subkey for Wow6432Node of HKLM\Software; # that's where it looks on a 64-bit machine (tests will fail w/o this) parents[0] = self.root.addKey(m1.group(1)) if 'HKEY_LOCAL_MACHINE\\Software' in m1.group(1): p1 = m1.group(1).replace('HKEY_LOCAL_MACHINE\\Software', 'HKEY_LOCAL_MACHINE\\Software\\Wow6432Node') parents[1] = self.root.addKey(p1) else: parents[1] = None else: m2 = valmatch.match(line) if m2: for p in parents: if p: p.addValue(m2.group(1),m2.group(2)) def OpenKeyEx(self,root,key): if root == SCons.Util.HKEY_CLASSES_ROOT: mykey = 'HKEY_CLASSES_ROOT\\' + key if root == SCons.Util.HKEY_USERS: mykey = 'HKEY_USERS\\' + key if root == SCons.Util.HKEY_CURRENT_USER: mykey = 'HKEY_CURRENT_USER\\' + key if root == SCons.Util.HKEY_LOCAL_MACHINE: mykey = 'HKEY_LOCAL_MACHINE\\' + key debug("Open Key:%s"%mykey) return self.root.key(mykey) def DummyOpenKeyEx(root, key): return registry.OpenKeyEx(root,key) def DummyEnumKey(key, index): rv = None try: rv = key.keyarray[index] except IndexError: raise SCons.Util.RegError # print "Enum Key",key.name,"[",index,"] =>",rv return rv def DummyEnumValue(key, index): rv = key.valindex(index) # print "Enum Value",key.name,"[",index,"] =>",rv return rv def DummyQueryValue(key, value): rv = key.value(value) # print "Query Value",key.name+"\\"+value,"=>",rv return rv def DummyExists(path): return 1 class msvsTestCase(unittest.TestCase): """This test case is run several times with different defaults. See its subclasses below.""" def setUp(self): debug("THIS TYPE :%s"%self) global registry registry = self.registry from SCons.Tool.MSCommon.vs import reset_installed_visual_studios reset_installed_visual_studios() def test_get_default_version(self): """Test retrieval of the default visual studio version""" debug("Testing for default version %s"%self.default_version) env = DummyEnv() v1 = get_default_version(env) if v1: assert env['MSVS_VERSION'] == self.default_version, \ ("env['MSVS_VERSION'] != self.default_version",self.default_version, env['MSVS_VERSION']) assert env['MSVS']['VERSION'] == self.default_version, \ ("env['MSVS']['VERSION'] != self.default_version",self.default_version, env['MSVS']['VERSION']) assert v1 == self.default_version, (self.default_version, v1) env = DummyEnv({'MSVS_VERSION':'7.0'}) v2 = get_default_version(env) assert env['MSVS_VERSION'] == '7.0', env['MSVS_VERSION'] assert env['MSVS']['VERSION'] == '7.0', env['MSVS']['VERSION'] assert v2 == '7.0', v2 env = DummyEnv() v3 = get_default_version(env) if v3 == '7.1': override = '7.0' else: override = '7.1' env['MSVS_VERSION'] = override v3 = get_default_version(env) assert env['MSVS_VERSION'] == override, env['MSVS_VERSION'] assert env['MSVS']['VERSION'] == override, env['MSVS']['VERSION'] assert v3 == override, v3 def _TODO_test_merge_default_version(self): """Test the merge_default_version() function""" pass def test_query_versions(self): """Test retrieval of the list of visual studio versions""" v1 = query_versions() assert not v1 or str(v1[0]) == self.highest_version, \ (v1, self.highest_version) assert len(v1) == self.number_of_versions, v1 class msvs6aTestCase(msvsTestCase): """Test MSVS 6 Registry""" registry = DummyRegistry(regdata_6a + regdata_cv) default_version = '6.0' highest_version = '6.0' number_of_versions = 1 install_locs = { '6.0' : {'VSINSTALLDIR': 'C:\\Program Files\\Microsoft Visual Studio\\VC98', 'VCINSTALLDIR': 'C:\\Program Files\\Microsoft Visual Studio\\VC98\\Bin'}, '7.0' : {}, '7.1' : {}, '8.0' : {}, '8.0Exp' : {}, } default_install_loc = install_locs['6.0'] class msvs6bTestCase(msvsTestCase): """Test Other MSVS 6 Registry""" registry = DummyRegistry(regdata_6b + regdata_cv) default_version = '6.0' highest_version = '6.0' number_of_versions = 1 install_locs = { '6.0' : {'VSINSTALLDIR': 'C:\\VS6\\VC98', 'VCINSTALLDIR': 'C:\\VS6\\VC98\\Bin'}, '7.0' : {}, '7.1' : {}, '8.0' : {}, '8.0Exp' : {}, } default_install_loc = install_locs['6.0'] class msvs6and7TestCase(msvsTestCase): """Test MSVS 6 & 7 Registry""" registry = DummyRegistry(regdata_6b + regdata_7 + regdata_cv) default_version = '7.0' highest_version = '7.0' number_of_versions = 2 install_locs = { '6.0' : {'VSINSTALLDIR': 'C:\\VS6\\VC98', 'VCINSTALLDIR': 'C:\\VS6\\VC98\\Bin'}, '7.0' : {'VSINSTALLDIR': 'C:\\Program Files\\Microsoft Visual Studio .NET\\Common7', 'VCINSTALLDIR': 'C:\\Program Files\\Microsoft Visual Studio .NET\\Common7\\Tools'}, '7.1' : {}, '8.0' : {}, '8.0Exp' : {}, } default_install_loc = install_locs['7.0'] class msvs7TestCase(msvsTestCase): """Test MSVS 7 Registry""" registry = DummyRegistry(regdata_7 + regdata_cv) default_version = '7.0' highest_version = '7.0' number_of_versions = 1 install_locs = { '6.0' : {}, '7.0' : {'VSINSTALLDIR': 'C:\\Program Files\\Microsoft Visual Studio .NET\\Common7', 'VCINSTALLDIR': 'C:\\Program Files\\Microsoft Visual Studio .NET\\Common7\\Tools'}, '7.1' : {}, '8.0' : {}, '8.0Exp' : {}, } default_install_loc = install_locs['7.0'] class msvs71TestCase(msvsTestCase): """Test MSVS 7.1 Registry""" registry = DummyRegistry(regdata_7_1 + regdata_cv) default_version = '7.1' highest_version = '7.1' number_of_versions = 1 install_locs = { '6.0' : {}, '7.0' : {}, '7.1' : {'VSINSTALLDIR': 'C:\\Program Files\\Microsoft Visual Studio .NET 2003\\Common7', 'VCINSTALLDIR': 'C:\\Program Files\\Microsoft Visual Studio .NET 2003\\Common7\\Tools'}, '8.0' : {}, '8.0Exp' : {}, } default_install_loc = install_locs['7.1'] class msvs8ExpTestCase(msvsTestCase): # XXX: only one still not working """Test MSVS 8 Express Registry""" registry = DummyRegistry(regdata_8exp + regdata_cv) default_version = '8.0Exp' highest_version = '8.0Exp' number_of_versions = 1 install_locs = { '6.0' : {}, '7.0' : {}, '7.1' : {}, '8.0' : {}, '8.0Exp' : {'VSINSTALLDIR': 'C:\\Program Files\\Microsoft Visual Studio 8', 'VCINSTALLDIR': 'C:\\Program Files\\Microsoft Visual Studio 8\\VC'}, } default_install_loc = install_locs['8.0Exp'] class msvs80TestCase(msvsTestCase): """Test MSVS 8 Registry""" registry = DummyRegistry(regdata_80 + regdata_cv) default_version = '8.0' highest_version = '8.0' number_of_versions = 1 install_locs = { '6.0' : {}, '7.0' : {}, '7.1' : {}, '8.0' : {'VSINSTALLDIR': 'C:\\Program Files\\Microsoft Visual Studio 8', 'VCINSTALLDIR': 'C:\\Program Files\\Microsoft Visual Studio 8\\VC'}, '8.0Exp' : {}, } default_install_loc = install_locs['8.0'] class msvsEmptyTestCase(msvsTestCase): """Test Empty Registry""" registry = DummyRegistry(regdata_none) default_version = '11.0' highest_version = None number_of_versions = 0 install_locs = { '6.0' : {}, '7.0' : {}, '7.1' : {}, '8.0' : {}, '8.0Exp' : {}, } default_install_loc = install_locs['8.0Exp'] if __name__ == "__main__": # only makes sense to test this on win32 if sys.platform != 'win32': sys.stdout.write("NO RESULT for msvsTests.py: '%s' is not win32\n" % sys.platform) sys.exit(0) SCons.Util.RegOpenKeyEx = DummyOpenKeyEx SCons.Util.RegEnumKey = DummyEnumKey SCons.Util.RegEnumValue = DummyEnumValue SCons.Util.RegQueryValueEx = DummyQueryValue os.path.exists = DummyExists # make sure all files exist :-) os.path.isfile = DummyExists # make sure all files are files :-) os.path.isdir = DummyExists # make sure all dirs are dirs :-) exit_val = 0 test_classes = [ msvs6aTestCase, msvs6bTestCase, msvs6and7TestCase, msvs7TestCase, msvs71TestCase, msvs8ExpTestCase, msvs80TestCase, msvsEmptyTestCase, ] for test_class in test_classes: print "TEST: ", test_class.__doc__ back_osenv = copy.deepcopy(os.environ) try: # XXX: overriding the os.environ is bad, but doing it # correctly is too complicated for now. Those tests should # be fixed for k in ['VS71COMNTOOLS', 'VS80COMNTOOLS', 'VS90COMNTOOLS']: if k in os.environ: del os.environ[k] suite = unittest.makeSuite(test_class, 'test_') if not unittest.TextTestRunner().run(suite).wasSuccessful(): exit_val = 1 finally: os.env = back_osenv sys.exit(exit_val) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/JavaCommon.py0000644000175000017500000003071512114661557022632 0ustar dktrkranzdktrkranz"""SCons.Tool.JavaCommon Stuff for processing Java. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/JavaCommon.py 2013/03/03 09:48:35 garyo" import os import os.path import re java_parsing = 1 default_java_version = '1.4' if java_parsing: # Parse Java files for class names. # # This is a really cool parser from Charles Crain # that finds appropriate class names in Java source. # A regular expression that will find, in a java file: # newlines; # double-backslashes; # a single-line comment "//"; # single or double quotes preceeded by a backslash; # single quotes, double quotes, open or close braces, semi-colons, # periods, open or close parentheses; # floating-point numbers; # any alphanumeric token (keyword, class name, specifier); # any alphanumeric token surrounded by angle brackets (generics); # the multi-line comment begin and end tokens /* and */; # array declarations "[]". _reToken = re.compile(r'(\n|\\\\|//|\\[\'"]|[\'"\{\}\;\.\(\)]|' + r'\d*\.\d*|[A-Za-z_][\w\$\.]*|<[A-Za-z_]\w+>|' + r'/\*|\*/|\[\])') class OuterState(object): """The initial state for parsing a Java file for classes, interfaces, and anonymous inner classes.""" def __init__(self, version=default_java_version): if not version in ('1.1', '1.2', '1.3','1.4', '1.5', '1.6', '1.7', '5', '6'): msg = "Java version %s not supported" % version raise NotImplementedError(msg) self.version = version self.listClasses = [] self.listOutputs = [] self.stackBrackets = [] self.brackets = 0 self.nextAnon = 1 self.localClasses = [] self.stackAnonClassBrackets = [] self.anonStacksStack = [[0]] self.package = None def trace(self): pass def __getClassState(self): try: return self.classState except AttributeError: ret = ClassState(self) self.classState = ret return ret def __getPackageState(self): try: return self.packageState except AttributeError: ret = PackageState(self) self.packageState = ret return ret def __getAnonClassState(self): try: return self.anonState except AttributeError: self.outer_state = self ret = SkipState(1, AnonClassState(self)) self.anonState = ret return ret def __getSkipState(self): try: return self.skipState except AttributeError: ret = SkipState(1, self) self.skipState = ret return ret def __getAnonStack(self): return self.anonStacksStack[-1] def openBracket(self): self.brackets = self.brackets + 1 def closeBracket(self): self.brackets = self.brackets - 1 if len(self.stackBrackets) and \ self.brackets == self.stackBrackets[-1]: self.listOutputs.append('$'.join(self.listClasses)) self.localClasses.pop() self.listClasses.pop() self.anonStacksStack.pop() self.stackBrackets.pop() if len(self.stackAnonClassBrackets) and \ self.brackets == self.stackAnonClassBrackets[-1]: self.__getAnonStack().pop() self.stackAnonClassBrackets.pop() def parseToken(self, token): if token[:2] == '//': return IgnoreState('\n', self) elif token == '/*': return IgnoreState('*/', self) elif token == '{': self.openBracket() elif token == '}': self.closeBracket() elif token in [ '"', "'" ]: return IgnoreState(token, self) elif token == "new": # anonymous inner class if len(self.listClasses) > 0: return self.__getAnonClassState() return self.__getSkipState() # Skip the class name elif token in ['class', 'interface', 'enum']: if len(self.listClasses) == 0: self.nextAnon = 1 self.stackBrackets.append(self.brackets) return self.__getClassState() elif token == 'package': return self.__getPackageState() elif token == '.': # Skip the attribute, it might be named "class", in which # case we don't want to treat the following token as # an inner class name... return self.__getSkipState() return self def addAnonClass(self): """Add an anonymous inner class""" if self.version in ('1.1', '1.2', '1.3', '1.4'): clazz = self.listClasses[0] self.listOutputs.append('%s$%d' % (clazz, self.nextAnon)) elif self.version in ('1.5', '1.6', '1.7', '5', '6'): self.stackAnonClassBrackets.append(self.brackets) className = [] className.extend(self.listClasses) self.__getAnonStack()[-1] = self.__getAnonStack()[-1] + 1 for anon in self.__getAnonStack(): className.append(str(anon)) self.listOutputs.append('$'.join(className)) self.nextAnon = self.nextAnon + 1 self.__getAnonStack().append(0) def setPackage(self, package): self.package = package class AnonClassState(object): """A state that looks for anonymous inner classes.""" def __init__(self, old_state): # outer_state is always an instance of OuterState 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. 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[0] == '<' and token[-1] == '>': 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.addAnonClass() return self.old_state.parseToken(token) class SkipState(object): """A state that will skip a specified number of tokens before reverting to the previous state.""" def __init__(self, tokens_to_skip, old_state): self.tokens_to_skip = tokens_to_skip self.old_state = old_state def parseToken(self, token): self.tokens_to_skip = self.tokens_to_skip - 1 if self.tokens_to_skip < 1: return self.old_state return self class ClassState(object): """A state we go into when we hit a class or interface keyword.""" def __init__(self, outer_state): # outer_state is always an instance of OuterState self.outer_state = outer_state def parseToken(self, token): # the next non-whitespace token should be the name of the class if token == '\n': return self # If that's an inner class which is declared in a method, it # requires an index prepended to the class-name, e.g. # 'Foo$1Inner' (Tigris Issue 2087) if self.outer_state.localClasses and \ self.outer_state.stackBrackets[-1] > \ self.outer_state.stackBrackets[-2]+1: locals = self.outer_state.localClasses[-1] try: idx = locals[token] locals[token] = locals[token]+1 except KeyError: locals[token] = 1 token = str(locals[token]) + token self.outer_state.localClasses.append({}) self.outer_state.listClasses.append(token) self.outer_state.anonStacksStack.append([0]) return self.outer_state class IgnoreState(object): """A state that will ignore all tokens until it gets to a specified token.""" def __init__(self, ignore_until, old_state): self.ignore_until = ignore_until self.old_state = old_state def parseToken(self, token): if self.ignore_until == token: return self.old_state return self class PackageState(object): """The state we enter when we encounter the package keyword. We assume the next token will be the package name.""" def __init__(self, outer_state): # outer_state is always an instance of OuterState self.outer_state = outer_state def parseToken(self, token): self.outer_state.setPackage(token) return self.outer_state def parse_java_file(fn, version=default_java_version): return parse_java(open(fn, 'r').read(), version) def parse_java(contents, version=default_java_version, trace=None): """Parse a .java file and return a double of package directory, plus a list of .class files that compiling that .java file will produce""" package = None initial = OuterState(version) currstate = initial for token in _reToken.findall(contents): # The regex produces a bunch of groups, but only one will # have anything in it. currstate = currstate.parseToken(token) if trace: trace(token, currstate) if initial.package: package = initial.package.replace('.', os.sep) return (package, initial.listOutputs) else: # Don't actually parse Java files for class names. # # We might make this a configurable option in the future if # Java-file parsing takes too long (although it shouldn't relative # to how long the Java compiler itself seems to take...). def parse_java_file(fn): """ "Parse" a .java file. This actually just splits the file name, so the assumption here is that the file name matches the public class name, and that the path to the file is the same as the package name. """ return os.path.split(file) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/ifl.py0000644000175000017500000000540612114661560021343 0ustar dktrkranzdktrkranz"""SCons.Tool.ifl Tool-specific initialization for the Intel Fortran compiler. There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Tool.Tool() selection method. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/ifl.py 2013/03/03 09:48:35 garyo" import SCons.Defaults from SCons.Scanner.Fortran import FortranScan from FortranCommon import add_all_to_env def generate(env): """Add Builders and construction variables for ifl to an Environment.""" fscan = FortranScan("FORTRANPATH") SCons.Tool.SourceFileScanner.add_scanner('.i', fscan) SCons.Tool.SourceFileScanner.add_scanner('.i90', fscan) if 'FORTRANFILESUFFIXES' not in env: env['FORTRANFILESUFFIXES'] = ['.i'] else: env['FORTRANFILESUFFIXES'].append('.i') if 'F90FILESUFFIXES' not in env: env['F90FILESUFFIXES'] = ['.i90'] else: env['F90FILESUFFIXES'].append('.i90') add_all_to_env(env) env['FORTRAN'] = 'ifl' env['SHFORTRAN'] = '$FORTRAN' env['FORTRANCOM'] = '$FORTRAN $FORTRANFLAGS $_FORTRANINCFLAGS /c $SOURCES /Fo$TARGET' env['FORTRANPPCOM'] = '$FORTRAN $FORTRANFLAGS $CPPFLAGS $_CPPDEFFLAGS $_FORTRANINCFLAGS /c $SOURCES /Fo$TARGET' env['SHFORTRANCOM'] = '$SHFORTRAN $SHFORTRANFLAGS $_FORTRANINCFLAGS /c $SOURCES /Fo$TARGET' env['SHFORTRANPPCOM'] = '$SHFORTRAN $SHFORTRANFLAGS $CPPFLAGS $_CPPDEFFLAGS $_FORTRANINCFLAGS /c $SOURCES /Fo$TARGET' def exists(env): return env.Detect('ifl') # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/textfile.py0000644000175000017500000001365112114661560022416 0ustar dktrkranzdktrkranz# -*- python -*- # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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. # __doc__ = """ Textfile/Substfile builder for SCons. Create file 'target' which typically is a textfile. The 'source' may be any combination of strings, Nodes, or lists of same. A 'linesep' will be put between any part written and defaults to os.linesep. The only difference between the Textfile builder and the Substfile builder is that strings are converted to Value() nodes for the former and File() nodes for the latter. To insert files in the former or strings in the latter, wrap them in a File() or Value(), respectively. The values of SUBST_DICT first have any construction variables expanded (its keys are not expanded). If a value of SUBST_DICT is a python callable function, it is called and the result is expanded as the value. Values are substituted in a "random" order; if any substitution could be further expanded by another subsitition, it is unpredictible whether the expansion will occur. """ __revision__ = "src/engine/SCons/Tool/textfile.py 2013/03/03 09:48:35 garyo" import SCons import os import re from SCons.Node import Node from SCons.Node.Python import Value from SCons.Util import is_String, is_Sequence, is_Dict def _do_subst(node, subs): """ Fetch the node contents and replace all instances of the keys with their values. For example, if subs is {'%VERSION%': '1.2345', '%BASE%': 'MyProg', '%prefix%': '/bin'}, then all instances of %VERSION% in the file will be replaced with 1.2345 and so forth. """ contents = node.get_text_contents() if not subs: return contents for (k,v) in subs: contents = re.sub(k, v, contents) return contents def _action(target, source, env): # prepare the line separator linesep = env['LINESEPARATOR'] if linesep is None: linesep = os.linesep elif is_String(linesep): pass elif isinstance(linesep, Value): linesep = linesep.get_text_contents() else: raise SCons.Errors.UserError( 'unexpected type/class for LINESEPARATOR: %s' % repr(linesep), None) # create a dictionary to use for the substitutions if 'SUBST_DICT' not in env: subs = None # no substitutions else: d = env['SUBST_DICT'] if is_Dict(d): d = list(d.items()) elif is_Sequence(d): pass else: raise SCons.Errors.UserError('SUBST_DICT must be dict or sequence') subs = [] for (k,v) in d: if callable(v): v = v() if is_String(v): v = env.subst(v) else: v = str(v) subs.append((k,v)) # write the file try: fd = open(target[0].get_path(), "wb") except (OSError,IOError), e: raise SCons.Errors.UserError("Can't write target file %s" % target[0]) # separate lines by 'linesep' only if linesep is not empty lsep = None for s in source: if lsep: fd.write(lsep) fd.write(_do_subst(s, subs)) lsep = linesep fd.close() def _strfunc(target, source, env): return "Creating '%s'" % target[0] def _convert_list_R(newlist, sources): for elem in sources: if is_Sequence(elem): _convert_list_R(newlist, elem) elif isinstance(elem, Node): newlist.append(elem) else: newlist.append(Value(elem)) def _convert_list(target, source, env): if len(target) != 1: raise SCons.Errors.UserError("Only one target file allowed") newlist = [] _convert_list_R(newlist, source) return target, newlist _common_varlist = ['SUBST_DICT', 'LINESEPARATOR'] _text_varlist = _common_varlist + ['TEXTFILEPREFIX', 'TEXTFILESUFFIX'] _text_builder = SCons.Builder.Builder( action = SCons.Action.Action(_action, _strfunc, varlist = _text_varlist), source_factory = Value, emitter = _convert_list, prefix = '$TEXTFILEPREFIX', suffix = '$TEXTFILESUFFIX', ) _subst_varlist = _common_varlist + ['SUBSTFILEPREFIX', 'TEXTFILESUFFIX'] _subst_builder = SCons.Builder.Builder( action = SCons.Action.Action(_action, _strfunc, varlist = _subst_varlist), source_factory = SCons.Node.FS.File, emitter = _convert_list, prefix = '$SUBSTFILEPREFIX', suffix = '$SUBSTFILESUFFIX', src_suffix = ['.in'], ) def generate(env): env['LINESEPARATOR'] = os.linesep env['BUILDERS']['Textfile'] = _text_builder env['TEXTFILEPREFIX'] = '' env['TEXTFILESUFFIX'] = '.txt' env['BUILDERS']['Substfile'] = _subst_builder env['SUBSTFILEPREFIX'] = '' env['SUBSTFILESUFFIX'] = '' def exists(env): return 1 # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/sgiar.xml0000644000175000017500000000070112114661560022037 0ustar dktrkranzdktrkranz Sets construction variables for the SGI library archiver. AR ARFLAGS ARCOMSTR SHLINK SHLINKFLAGS LIBPREFIX LIBSUFFIX ARCOMSTR SHLINKCOMSTR scons-doc-2.3.0/src/engine/SCons/Tool/f90.xml0000644000175000017500000001670712114661560021345 0ustar dktrkranzdktrkranz Set construction variables for generic POSIX Fortran 90 compilers. F90 F90FLAGS F90COM F90PPCOM SHF90 SHF90FLAGS SHF90COM SHF90PPCOM _F90INCFLAGS F90COMSTR F90PPCOMSTR SHF90COMSTR SHF90PPCOMSTR The Fortran 90 compiler. You should normally set the &cv-link-FORTRAN; variable, which specifies the default Fortran compiler for all Fortran versions. You only need to set &cv-link-F90; if you need to use a specific compiler or compiler version for Fortran 90 files. The command line used to compile a Fortran 90 source file to an object file. You only need to set &cv-link-F90COM; if you need to use a specific command line for Fortran 90 files. You should normally set the &cv-link-FORTRANCOM; variable, which specifies the default command line for all Fortran versions. The string displayed when a Fortran 90 source file is compiled to an object file. If this is not set, then &cv-link-F90COM; or &cv-link-FORTRANCOM; (the command line) is displayed. The list of file extensions for which the F90 dialect will be used. By default, this is ['.f90'] The list of file extensions for which the compilation + preprocessor pass for F90 dialect will be used. By default, this is empty General user-specified options that are passed to the Fortran 90 compiler. Note that this variable does not contain (or similar) include search path options that scons generates automatically from &cv-link-F90PATH;. See &cv-link-_F90INCFLAGS; below, for the variable that expands to those options. You only need to set &cv-link-F90FLAGS; if you need to define specific user options for Fortran 90 files. You should normally set the &cv-link-FORTRANFLAGS; variable, which specifies the user-specified options passed to the default Fortran compiler for all Fortran versions. An automatically-generated construction variable containing the Fortran 90 compiler command-line options for specifying directories to be searched for include files. The value of &cv-link-_F90INCFLAGS; is created by appending &cv-link-INCPREFIX; and &cv-link-INCSUFFIX; to the beginning and end of each directory in &cv-link-F90PATH;. The list of directories that the Fortran 90 compiler will search for include directories. The implicit dependency scanner will search these directories for include files. Don't explicitly put include directory arguments in &cv-link-F90FLAGS; because the result will be non-portable and the directories will not be searched by the dependency scanner. Note: directory names in &cv-link-F90PATH; will be looked-up relative to the SConscript directory when they are used in a command. To force &scons; to look-up a directory relative to the root of the source tree use #: You only need to set &cv-link-F90PATH; if you need to define a specific include path for Fortran 90 files. You should normally set the &cv-link-FORTRANPATH; variable, which specifies the include path for the default Fortran compiler for all Fortran versions. env = Environment(F90PATH='#/include') The directory look-up can also be forced using the &Dir;() function: include = Dir('include') env = Environment(F90PATH=include) The directory list will be added to command lines through the automatically-generated &cv-link-_F90INCFLAGS; construction variable, which is constructed by appending the values of the &cv-link-INCPREFIX; and &cv-link-INCSUFFIX; construction variables to the beginning and end of each directory in &cv-link-F90PATH;. Any command lines you define that need the F90PATH directory list should include &cv-link-_F90INCFLAGS;: env = Environment(F90COM="my_compiler $_F90INCFLAGS -c -o $TARGET $SOURCE") The command line used to compile a Fortran 90 source file to an object file after first running the file through the C preprocessor. Any options specified in the &cv-link-F90FLAGS; and &cv-link-CPPFLAGS; construction variables are included on this command line. You only need to set &cv-link-F90PPCOM; if you need to use a specific C-preprocessor command line for Fortran 90 files. You should normally set the &cv-link-FORTRANPPCOM; variable, which specifies the default C-preprocessor command line for all Fortran versions. The string displayed when a Fortran 90 source file is compiled after first running the file through the C preprocessor. If this is not set, then &cv-link-F90PPCOM; or &cv-link-FORTRANPPCOM; (the command line) is displayed. The Fortran 90 compiler used for generating shared-library objects. You should normally set the &cv-link-SHFORTRAN; variable, which specifies the default Fortran compiler for all Fortran versions. You only need to set &cv-link-SHF90; if you need to use a specific compiler or compiler version for Fortran 90 files. The command line used to compile a Fortran 90 source file to a shared-library object file. You only need to set &cv-link-SHF90COM; if you need to use a specific command line for Fortran 90 files. You should normally set the &cv-link-SHFORTRANCOM; variable, which specifies the default command line for all Fortran versions. The string displayed when a Fortran 90 source file is compiled to a shared-library object file. If this is not set, then &cv-link-SHF90COM; or &cv-link-SHFORTRANCOM; (the command line) is displayed. Options that are passed to the Fortran 90 compiler to generated shared-library objects. You only need to set &cv-link-SHF90FLAGS; if you need to define specific user options for Fortran 90 files. You should normally set the &cv-link-SHFORTRANFLAGS; variable, which specifies the user-specified options passed to the default Fortran compiler for all Fortran versions. The command line used to compile a Fortran 90 source file to a shared-library object file after first running the file through the C preprocessor. Any options specified in the &cv-link-SHF90FLAGS; and &cv-link-CPPFLAGS; construction variables are included on this command line. You only need to set &cv-link-SHF90PPCOM; if you need to use a specific C-preprocessor command line for Fortran 90 files. You should normally set the &cv-link-SHFORTRANPPCOM; variable, which specifies the default C-preprocessor command line for all Fortran versions. The string displayed when a Fortran 90 source file is compiled to a shared-library object file after first running the file through the C preprocessor. If this is not set, then &cv-link-SHF90PPCOM; or &cv-link-SHFORTRANPPCOM; (the command line) is displayed. scons-doc-2.3.0/src/engine/SCons/Tool/f95.py0000644000175000017500000000400712114661560021170 0ustar dktrkranzdktrkranz"""engine.SCons.Tool.f95 Tool-specific initialization for the generic Posix f95 Fortran compiler. There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Tool.Tool() selection method. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/f95.py 2013/03/03 09:48:35 garyo" import SCons.Defaults import SCons.Tool import SCons.Util import fortran from SCons.Tool.FortranCommon import add_all_to_env, add_f95_to_env compilers = ['f95'] def generate(env): add_all_to_env(env) add_f95_to_env(env) fcomp = env.Detect(compilers) or 'f95' env['F95'] = fcomp env['SHF95'] = fcomp env['FORTRAN'] = fcomp env['SHFORTRAN'] = fcomp def exists(env): return env.Detect(compilers) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/applelink.xml0000644000175000017500000000477512114661560022730 0ustar dktrkranzdktrkranz Sets construction variables for the Apple linker (similar to the GNU linker). FRAMEWORKPATHPREFIX _FRAMEWORKPATH _FRAMEWORKS LINKCOM SHLINKFLAGS SHLINKCOM LDMODULEPREFIX LDMODULESUFFIX LDMODULEFLAGS LDMODULECOM FRAMEWORKSFLAGS "> On Mac OS X with gcc, general user-supplied frameworks options to be added at the end of a command line building a loadable module. (This has been largely superseded by the &cv-link-FRAMEWORKPATH;, &cv-link-FRAMEWORKPATHPREFIX;, &cv-link-FRAMEWORKPREFIX; and &cv-link-FRAMEWORKS; variables described above.) On Mac OS X with gcc, a list of the framework names to be linked into a program or shared library or bundle. The default value is the empty list. For example: env.AppendUnique(FRAMEWORKS=Split('System Cocoa SystemConfiguration')) On Mac OS X with gcc, the prefix to be used for linking in frameworks (see &cv-link-FRAMEWORKS;). The default value is . On Mac OS X with gcc, an automatically-generated construction variable containing the linker command-line options for linking with FRAMEWORKS. On Mac OS X with gcc, a list containing the paths to search for frameworks. Used by the compiler to find framework-style includes like #include <Fmwk/Header.h>. Used by the linker to find user-specified frameworks when linking (see &cv-link-FRAMEWORKS;). For example: env.AppendUnique(FRAMEWORKPATH='#myframeworkdir') will add ... -Fmyframeworkdir to the compiler and linker command lines. On Mac OS X with gcc, the prefix to be used for the FRAMEWORKPATH entries. (see &cv-link-FRAMEWORKPATH;). The default value is . On Mac OS X with gcc, an automatically-generated construction variable containing the linker command-line options corresponding to &cv-link-FRAMEWORKPATH;. scons-doc-2.3.0/src/engine/SCons/Tool/bcc32.py0000644000175000017500000000567012114661560021470 0ustar dktrkranzdktrkranz"""SCons.Tool.bcc32 XXX """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/bcc32.py 2013/03/03 09:48:35 garyo" import os import os.path import SCons.Defaults import SCons.Tool import SCons.Util def findIt(program, env): # First search in the SCons path and then the OS path: borwin = env.WhereIs(program) or SCons.Util.WhereIs(program) if borwin: dir = os.path.dirname(borwin) env.PrependENVPath('PATH', dir) return borwin def generate(env): findIt('bcc32', env) """Add Builders and construction variables for bcc to an Environment.""" static_obj, shared_obj = SCons.Tool.createObjBuilders(env) for suffix in ['.c', '.cpp']: static_obj.add_action(suffix, SCons.Defaults.CAction) shared_obj.add_action(suffix, SCons.Defaults.ShCAction) static_obj.add_emitter(suffix, SCons.Defaults.StaticObjectEmitter) shared_obj.add_emitter(suffix, SCons.Defaults.SharedObjectEmitter) env['CC'] = 'bcc32' env['CCFLAGS'] = SCons.Util.CLVar('') env['CFLAGS'] = SCons.Util.CLVar('') env['CCCOM'] = '$CC -q $CFLAGS $CCFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS -c -o$TARGET $SOURCES' env['SHCC'] = '$CC' env['SHCCFLAGS'] = SCons.Util.CLVar('$CCFLAGS') env['SHCFLAGS'] = SCons.Util.CLVar('$CFLAGS') env['SHCCCOM'] = '$SHCC -WD $SHCFLAGS $SHCCFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS -c -o$TARGET $SOURCES' env['CPPDEFPREFIX'] = '-D' env['CPPDEFSUFFIX'] = '' env['INCPREFIX'] = '-I' env['INCSUFFIX'] = '' env['SHOBJSUFFIX'] = '.dll' env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME'] = 0 env['CFILESUFFIX'] = '.cpp' def exists(env): return findIt('bcc32', env) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/gnulink.xml0000644000175000017500000000057712114661560022414 0ustar dktrkranzdktrkranz Set construction variables for GNU linker/loader. SHLINKFLAGS RPATHPREFIX RPATHSUFFIX scons-doc-2.3.0/src/engine/SCons/Tool/default.py0000644000175000017500000000336012114661560022212 0ustar dktrkranzdktrkranz"""SCons.Tool.default Initialization with a default tool list. There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Tool.Tool() selection method. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/default.py 2013/03/03 09:48:35 garyo" import SCons.Tool def generate(env): """Add default tools.""" for t in SCons.Tool.tool_list(env['PLATFORM'], env): SCons.Tool.Tool(t)(env) def exists(env): return 1 # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/pdflatex.xml0000644000175000017500000000200412114661560022537 0ustar dktrkranzdktrkranz Sets construction variables for the &pdflatex; utility. PDFLATEX PDFLATEXFLAGS PDFLATEXCOM LATEXRETRIES PDFLATEXCOMSTR The &pdflatex; utility. The command line used to call the &pdflatex; utility. The string displayed when calling the &pdflatex; utility. If this is not set, then &cv-link-PDFLATEXCOM; (the command line) is displayed. env = Environment(PDFLATEX;COMSTR = "Building $TARGET from LaTeX input $SOURCES") General options passed to the &pdflatex; utility. scons-doc-2.3.0/src/engine/SCons/Tool/mssdk.xml0000644000175000017500000000230112114661560022051 0ustar dktrkranzdktrkranz Sets variables for Microsoft Platform SDK and/or Windows SDK. Note that unlike most other Tool modules, mssdk does not set construction variables, but sets the environment variables in the environment &SCons; uses to execute the Microsoft toolchain: %INCLUDE%, %LIB%, %LIBPATH% and %PATH%. MSSDK_DIR MSSDK_VERSION MSVS_VERSION The directory containing the Microsoft SDK (either Platform SDK or Windows SDK) to be used for compilation. The version string of the Microsoft SDK (either Platform SDK or Windows SDK) to be used for compilation. Supported versions include 6.1, 6.0A, 6.0, 2003R2 and 2003R1. scons-doc-2.3.0/src/engine/SCons/Tool/sunf95.xml0000644000175000017500000000063512114661560022071 0ustar dktrkranzdktrkranz Set construction variables for the Sun &f95; Fortran compiler. FORTRAN F95 SHFORTRAN SHF95 SHFORTRANFLAGS SHF95FLAGS scons-doc-2.3.0/src/engine/SCons/Tool/icl.py0000644000175000017500000000365712114661560021346 0ustar dktrkranzdktrkranz"""engine.SCons.Tool.icl Tool-specific initialization for the Intel C/C++ compiler. There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Tool.Tool() selection method. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/icl.py 2013/03/03 09:48:35 garyo" import SCons.Tool.intelc # This has been completely superceded by intelc.py, which can # handle both Windows and Linux versions. def generate(*args, **kw): """Add Builders and construction variables for icl to an Environment.""" return SCons.Tool.intelc.generate(*args, **kw) def exists(*args, **kw): return SCons.Tool.intelc.exists(*args, **kw) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/ar.py0000644000175000017500000000424412114661560021172 0ustar dktrkranzdktrkranz"""SCons.Tool.ar Tool-specific initialization for ar (library archive). There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Tool.Tool() selection method. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/ar.py 2013/03/03 09:48:35 garyo" import SCons.Defaults import SCons.Tool import SCons.Util def generate(env): """Add Builders and construction variables for ar to an Environment.""" SCons.Tool.createStaticLibBuilder(env) env['AR'] = 'ar' env['ARFLAGS'] = SCons.Util.CLVar('rc') env['ARCOM'] = '$AR $ARFLAGS $TARGET $SOURCES' env['LIBPREFIX'] = 'lib' env['LIBSUFFIX'] = '.a' if env.Detect('ranlib'): env['RANLIB'] = 'ranlib' env['RANLIBFLAGS'] = SCons.Util.CLVar('') env['RANLIBCOM'] = '$RANLIB $RANLIBFLAGS $TARGET' def exists(env): return env.Detect('ar') # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/rpm.py0000644000175000017500000001072112114661560021363 0ustar dktrkranzdktrkranz"""SCons.Tool.rpm Tool-specific initialization for rpm. There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Tool.Tool() selection method. The rpm tool calls the rpmbuild command. The first and only argument should a tar.gz consisting of the source file and a specfile. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/rpm.py 2013/03/03 09:48:35 garyo" import os import re import shutil import subprocess import SCons.Builder import SCons.Node.FS import SCons.Util import SCons.Action import SCons.Defaults def get_cmd(source, env): tar_file_with_included_specfile = source if SCons.Util.is_List(source): tar_file_with_included_specfile = source[0] return "%s %s %s"%(env['RPM'], env['RPMFLAGS'], tar_file_with_included_specfile.abspath ) def build_rpm(target, source, env): # create a temporary rpm build root. tmpdir = os.path.join( os.path.dirname( target[0].abspath ), 'rpmtemp' ) if os.path.exists(tmpdir): shutil.rmtree(tmpdir) # now create the mandatory rpm directory structure. for d in ['RPMS', 'SRPMS', 'SPECS', 'BUILD']: os.makedirs( os.path.join( tmpdir, d ) ) # set the topdir as an rpmflag. env.Prepend( RPMFLAGS = '--define \'_topdir %s\'' % tmpdir ) # now call rpmbuild to create the rpm package. handle = subprocess.Popen(get_cmd(source, env), stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=True) output = handle.stdout.read() status = handle.wait() if status: raise SCons.Errors.BuildError( node=target[0], errstr=output, filename=str(target[0]) ) else: # XXX: assume that LC_ALL=c is set while running rpmbuild output_files = re.compile( 'Wrote: (.*)' ).findall( output ) for output, input in zip( output_files, target ): rpm_output = os.path.basename(output) expected = os.path.basename(input.get_path()) assert expected == rpm_output, "got %s but expected %s" % (rpm_output, expected) shutil.copy( output, input.abspath ) # cleanup before leaving. shutil.rmtree(tmpdir) return status def string_rpm(target, source, env): try: return env['RPMCOMSTR'] except KeyError: return get_cmd(source, env) rpmAction = SCons.Action.Action(build_rpm, string_rpm) RpmBuilder = SCons.Builder.Builder(action = SCons.Action.Action('$RPMCOM', '$RPMCOMSTR'), source_scanner = SCons.Defaults.DirScanner, suffix = '$RPMSUFFIX') def generate(env): """Add Builders and construction variables for rpm to an Environment.""" try: bld = env['BUILDERS']['Rpm'] except KeyError: bld = RpmBuilder env['BUILDERS']['Rpm'] = bld env.SetDefault(RPM = 'LC_ALL=c rpmbuild') env.SetDefault(RPMFLAGS = SCons.Util.CLVar('-ta')) env.SetDefault(RPMCOM = rpmAction) env.SetDefault(RPMSUFFIX = '.rpm') def exists(env): return env.Detect('rpmbuild') # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/__init__.xml0000644000175000017500000002756112114661557022514 0ustar dktrkranzdktrkranz Builds a C source file given a lex (.l) or yacc (.y) input file. The suffix specified by the &cv-link-CFILESUFFIX; construction variable (.c by default) is automatically added to the target if it is not already present. Example: # builds foo.c env.CFile(target = 'foo.c', source = 'foo.l') # builds bar.c env.CFile(target = 'bar', source = 'bar.y') Builds a C++ source file given a lex (.ll) or yacc (.yy) input file. The suffix specified by the &cv-link-CXXFILESUFFIX; construction variable (.cc by default) is automatically added to the target if it is not already present. Example: # builds foo.cc env.CXXFile(target = 'foo.cc', source = 'foo.ll') # builds bar.cc env.CXXFile(target = 'bar', source = 'bar.yy') A synonym for the &b-StaticLibrary; builder method. On most systems, this is the same as &b-SharedLibrary;. On Mac OS X (Darwin) platforms, this creates a loadable module bundle. A synonym for the &b-StaticObject; builder method. Builds an executable given one or more object files or C, C++, D, or Fortran source files. If any C, C++, D or Fortran source files are specified, then they will be automatically compiled to object files using the &b-Object; builder method; see that builder method's description for a list of legal source file suffixes and how they are interpreted. The target executable file prefix (specified by the &cv-link-PROGPREFIX; construction variable; nothing by default) and suffix (specified by the &cv-link-PROGSUFFIX; construction variable; by default, .exe on Windows systems, nothing on POSIX systems) are automatically added to the target if not already present. Example: env.Program(target = 'foo', source = ['foo.o', 'bar.c', 'baz.f']) Builds a shared library (.so on a POSIX system, .dll on Windows) given one or more object files or C, C++, D or Fortran source files. If any source files are given, then they will be automatically compiled to object files. The static library prefix and suffix (if any) are automatically added to the target. The target library file prefix (specified by the &cv-link-SHLIBPREFIX; construction variable; by default, lib on POSIX systems, nothing on Windows systems) and suffix (specified by the &cv-link-SHLIBSUFFIX; construction variable; by default, .dll on Windows systems, .so on POSIX systems) are automatically added to the target if not already present. Example: env.SharedLibrary(target = 'bar', source = ['bar.c', 'foo.o']) On Windows systems, the &b-SharedLibrary; builder method will always build an import (.lib) library in addition to the shared (.dll) library, adding a .lib library with the same basename if there is not already a .lib file explicitly listed in the targets. Any object files listed in the source must have been built for a shared library (that is, using the &b-SharedObject; builder method). &scons; will raise an error if there is any mismatch. On some platforms, there is a distinction between a shared library (loaded automatically by the system to resolve external references) and a loadable module (explicitly loaded by user action). For maximum portability, use the &b-LoadableModule; builder for the latter. When the &cv-link-SHLIBVERSION; construction variable is defined a versioned shared library is created. This modifies the &cv-link-SHLINKFLAGS; as required, adds the version number to the library name, and creates the symlinks that are needed. &cv-link-SHLIBVERSION; needs to be of the form X.Y.Z, where X and Y are numbers, and Z is a number but can also contain letters to designate alpha, beta, or release candidate patch levels. This builder may create multiple links to the library. On a POSIX system, for the shared library libbar.so.2.3.1, the links created would be libbar.so, libbar.so.2, and libbar.so.2.3; on a Darwin (OSX) system the library would be libbar.2.3.1.dylib and the link would be libbar.dylib. On Windows systems, specifying register=1 will cause the .dll to be registered after it is built using REGSVR32. The command that is run ("regsvr32" by default) is determined by &cv-link-REGSVR; construction variable, and the flags passed are determined by &cv-link-REGSVRFLAGS;. By default, &cv-link-REGSVRFLAGS; includes the option, to prevent dialogs from popping up and requiring user attention when it is run. If you change &cv-link-REGSVRFLAGS;, be sure to include the option. For example, env.SharedLibrary(target = 'bar', source = ['bar.cxx', 'foo.obj'], register=1) will register bar.dll as a COM object when it is done linking it. Builds an object file for inclusion in a shared library. Source files must have one of the same set of extensions specified above for the &b-StaticObject; builder method. On some platforms building a shared object requires additional compiler option (e.g. for gcc) in addition to those needed to build a normal (static) object, but on some platforms there is no difference between a shared object and a normal (static) one. When there is a difference, SCons will only allow shared objects to be linked into a shared library, and will use a different suffix for shared objects. On platforms where there is no difference, SCons will allow both normal (static) and shared objects to be linked into a shared library, and will use the same suffix for shared and normal (static) objects. The target object file prefix (specified by the &cv-link-SHOBJPREFIX; construction variable; by default, the same as &cv-link-OBJPREFIX;) and suffix (specified by the &cv-link-SHOBJSUFFIX; construction variable) are automatically added to the target if not already present. Examples: env.SharedObject(target = 'ddd', source = 'ddd.c') env.SharedObject(target = 'eee.o', source = 'eee.cpp') env.SharedObject(target = 'fff.obj', source = 'fff.for') Note that the source files will be scanned according to the suffix mappings in the SourceFileScanner object. See the section "Scanner Objects," below, for more information. Builds a static library given one or more object files or C, C++, D or Fortran source files. If any source files are given, then they will be automatically compiled to object files. The static library prefix and suffix (if any) are automatically added to the target. The target library file prefix (specified by the &cv-link-LIBPREFIX; construction variable; by default, lib on POSIX systems, nothing on Windows systems) and suffix (specified by the &cv-link-LIBSUFFIX; construction variable; by default, .lib on Windows systems, .a on POSIX systems) are automatically added to the target if not already present. Example: env.StaticLibrary(target = 'bar', source = ['bar.c', 'foo.o']) Any object files listed in the source must have been built for a static library (that is, using the &b-StaticObject; builder method). &scons; will raise an error if there is any mismatch. Builds a static object file from one or more C, C++, D, or Fortran source files. Source files must have one of the following extensions: .asm assembly language file .ASM assembly language file .c C file .C Windows: C file POSIX: C++ file .cc C++ file .cpp C++ file .cxx C++ file .cxx C++ file .c++ C++ file .C++ C++ file .d D file .f Fortran file .F Windows: Fortran file POSIX: Fortran file + C pre-processor .for Fortran file .FOR Fortran file .fpp Fortran file + C pre-processor .FPP Fortran file + C pre-processor .m Object C file .mm Object C++ file .s assembly language file .S Windows: assembly language file ARM: CodeSourcery Sourcery Lite .sx assembly language file + C pre-processor POSIX: assembly language file + C pre-processor .spp assembly language file + C pre-processor .SPP assembly language file + C pre-processor The target object file prefix (specified by the &cv-link-OBJPREFIX; construction variable; nothing by default) and suffix (specified by the &cv-link-OBJSUFFIX; construction variable; .obj on Windows systems, .o on POSIX systems) are automatically added to the target if not already present. Examples: env.StaticObject(target = 'aaa', source = 'aaa.c') env.StaticObject(target = 'bbb.o', source = 'bbb.c++') env.StaticObject(target = 'ccc.obj', source = 'ccc.f') Note that the source files will be scanned according to the suffix mappings in SourceFileScanner object. See the section "Scanner Objects," below, for more information. The version number of the C compiler. This may or may not be set, depending on the specific C compiler being used. The suffix for C source files. This is used by the internal CFile builder when generating C files from Lex (.l) or YACC (.y) input files. The default suffix, of course, is .c (lower case). On case-insensitive systems (like Windows), SCons also treats .C (upper case) files as C files. The version number of the C++ compiler. This may or may not be set, depending on the specific C++ compiler being used. The suffix for C++ source files. This is used by the internal CXXFile builder when generating C++ files from Lex (.ll) or YACC (.yy) input files. The default suffix is .cc. SCons also treats files with the suffixes .cpp, .cxx, .c++, and .C++ as C++ files, and files with .mm suffixes as Objective C++ files. On case-sensitive systems (Linux, UNIX, and other POSIX-alikes), SCons also treats .C (upper case) files as C++ files. TODO TODO TODO When this construction variable is defined, a versioned shared library is created. This modifies the &cv-link-SHLINKFLAGS; as required, adds the version number to the library name, and creates the symlinks that are needed. &cv-link-SHLIBVERSION; needs to be of the form X.Y.Z, where X and Y are numbers, and Z is a number but can also contain letters to designate alpha, beta, or release candidate patch levels. scons-doc-2.3.0/src/engine/SCons/Tool/dvips.xml0000644000175000017500000000335112114661560022063 0ustar dktrkranzdktrkranz Sets construction variables for the dvips utility. DVIPS DVIPSFLAGS PSCOM PSPREFIX PSSUFFIX PSCOMSTR Builds a .ps file from a .dvi input file (or, by extension, a .tex, .ltx, or .latex input file). The suffix specified by the &cv-link-PSSUFFIX; construction variable (.ps by default) is added automatically to the target if it is not already present. Example: # builds from aaa.tex env.PostScript(target = 'aaa.ps', source = 'aaa.tex') # builds bbb.ps from bbb.dvi env.PostScript(target = 'bbb', source = 'bbb.dvi') The TeX DVI file to PostScript converter. General options passed to the TeX DVI file to PostScript converter. The command line used to convert TeX DVI files into a PostScript file. The string displayed when a TeX DVI file is converted into a PostScript file. If this is not set, then &cv-link-PSCOM; (the command line) is displayed. The prefix used for PostScript file names. The prefix used for PostScript file names. scons-doc-2.3.0/src/engine/SCons/Tool/aixlink.xml0000644000175000017500000000065312114661560022377 0ustar dktrkranzdktrkranz Sets construction variables for the IBM Visual Age linker. LINKFLAGS SHLINKFLAGS SHLIBSUFFIX scons-doc-2.3.0/src/engine/SCons/Tool/javacTests.py0000644000175000017500000000666112114661560022704 0ustar dktrkranzdktrkranz# # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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. # import os import unittest import SCons.Tool.javac class DummyNode(object): def __init__(self, val): self.val = val def __str__(self): return str(self.val) class pathoptTestCase(unittest.TestCase): def assert_pathopt(self, expect, path): popt = SCons.Tool.javac.pathopt('-foopath', 'FOOPATH') env = {'FOOPATH': path} actual = popt(None, None, env, None) self.assertEquals(expect, actual) def assert_pathopt_default(self, expect, path, default): popt = SCons.Tool.javac.pathopt('-foopath', 'FOOPATH', default='DPATH') env = {'FOOPATH': path, 'DPATH': default} actual = popt(None, None, env, None) self.assertEquals(expect, actual) def test_unset(self): self.assert_pathopt([], None) self.assert_pathopt([], '') def test_str(self): self.assert_pathopt(['-foopath', '/foo/bar'], '/foo/bar') def test_list_str(self): self.assert_pathopt(['-foopath', '/foo%s/bar' % os.pathsep], ['/foo', '/bar']) def test_uses_pathsep(self): save = os.pathsep try: os.pathsep = '!' self.assert_pathopt(['-foopath', 'foo!bar'], ['foo', 'bar']) finally: os.pathsep = save def test_node(self): self.assert_pathopt(['-foopath', '/foo'], DummyNode('/foo')) def test_list_node(self): self.assert_pathopt(['-foopath', os.pathsep.join(['/foo','/bar'])], ['/foo', DummyNode('/bar')]) def test_default_str(self): self.assert_pathopt_default( ['-foopath', os.pathsep.join(['/foo','/bar','/baz'])], ['/foo', '/bar'], '/baz') def test_default_list(self): self.assert_pathopt_default( ['-foopath', os.pathsep.join(['/foo','/bar','/baz'])], ['/foo', '/bar'], ['/baz']) def test_default_unset(self): self.assert_pathopt_default( ['-foopath', '/foo'], '/foo', None) self.assert_pathopt_default( ['-foopath', '/foo'], '/foo', '') if __name__ == "__main__": unittest.main() scons-doc-2.3.0/src/engine/SCons/Tool/msvs.xml0000644000175000017500000003442212114661560021731 0ustar dktrkranzdktrkranz Sets construction variables for Microsoft Visual Studio. MSVSPROJECTCOM MSVSSOLUTIONCOM MSVSSCONSCRIPT MSVSSCONS MSVSSCONSFLAGS MSVSSCONSCOM MSVSBUILDCOM MSVSREBUILDCOM MSVSCLEANCOM MSVSENCODING Builds a Microsoft Visual Studio project file, and by default builds a solution file as well. This builds a Visual Studio project file, based on the version of Visual Studio that is configured (either the latest installed version, or the version specified by &cv-link-MSVS_VERSION; in the Environment constructor). For Visual Studio 6, it will generate a .dsp file. For Visual Studio 7 (.NET) and later versions, it will generate a .vcproj file. By default, this also generates a solution file for the specified project, a .dsw file for Visual Studio 6 or a .sln file for Visual Studio 7 (.NET). This behavior may be disabled by specifying auto_build_solution=0 when you call &b-MSVSProject;, in which case you presumably want to build the solution file(s) by calling the &b-MSVSSolution; Builder (see below). The &b-MSVSProject; builder takes several lists of filenames to be placed into the project file. These are currently limited to srcs, incs, localincs, resources, and misc. These are pretty self-explanatory, but it should be noted that these lists are added to the &cv-link-SOURCES; construction variable as strings, NOT as SCons File Nodes. This is because they represent file names to be added to the project file, not the source files used to build the project file. The above filename lists are all optional, although at least one must be specified for the resulting project file to be non-empty. In addition to the above lists of values, the following values may be specified: target: The name of the target .dsp or .vcproj file. The correct suffix for the version of Visual Studio must be used, but the &cv-link-MSVSPROJECTSUFFIX; construction variable will be defined to the correct value (see example below). variant: The name of this particular variant. For Visual Studio 7 projects, this can also be a list of variant names. These are typically things like "Debug" or "Release", but really can be anything you want. For Visual Studio 7 projects, they may also specify a target platform separated from the variant name by a | (vertical pipe) character: Debug|Xbox. The default target platform is Win32. Multiple calls to &b-MSVSProject; with different variants are allowed; all variants will be added to the project file with their appropriate build targets and sources. buildtarget: An optional string, node, or list of strings or nodes (one per build variant), to tell the Visual Studio debugger what output target to use in what build variant. The number of buildtarget entries must match the number of variant entries. runfile: The name of the file that Visual Studio 7 and later will run and debug. This appears as the value of the Output field in the resutling Visual Studio project file. If this is not specified, the default is the same as the specified buildtarget value. Note that because &SCons; always executes its build commands from the directory in which the &SConstruct; file is located, if you generate a project file in a different directory than the &SConstruct; directory, users will not be able to double-click on the file name in compilation error messages displayed in the Visual Studio console output window. This can be remedied by adding the Visual C/C++ /FC compiler option to the &cv-link-CCFLAGS; variable so that the compiler will print the full path name of any files that cause compilation errors. Example usage: barsrcs = ['bar.cpp'], barincs = ['bar.h'], barlocalincs = ['StdAfx.h'] barresources = ['bar.rc','resource.h'] barmisc = ['bar_readme.txt'] dll = env.SharedLibrary(target = 'bar.dll', source = barsrcs) env.MSVSProject(target = 'Bar' + env['MSVSPROJECTSUFFIX'], srcs = barsrcs, incs = barincs, localincs = barlocalincs, resources = barresources, misc = barmisc, buildtarget = dll, variant = 'Release') Builds a Microsoft Visual Studio solution file. This builds a Visual Studio solution file, based on the version of Visual Studio that is configured (either the latest installed version, or the version specified by &cv-link-MSVS_VERSION; in the construction environment). For Visual Studio 6, it will generate a .dsw file. For Visual Studio 7 (.NET), it will generate a .sln file. The following values must be specified: target: The name of the target .dsw or .sln file. The correct suffix for the version of Visual Studio must be used, but the value &cv-link-MSVSSOLUTIONSUFFIX; will be defined to the correct value (see example below). variant: The name of this particular variant, or a list of variant names (the latter is only supported for MSVS 7 solutions). These are typically things like "Debug" or "Release", but really can be anything you want. For MSVS 7 they may also specify target platform, like this "Debug|Xbox". Default platform is Win32. projects: A list of project file names, or Project nodes returned by calls to the &b-MSVSProject; Builder, to be placed into the solution file. It should be noted that these file names are NOT added to the $SOURCES environment variable in form of files, but rather as strings. This is because they represent file names to be added to the solution file, not the source files used to build the solution file. Example Usage: env.MSVSSolution(target = 'Bar' + env['MSVSSOLUTIONSUFFIX'], projects = ['bar' + env['MSVSPROJECTSUFFIX']], variant = 'Release') When the Microsoft Visual Studio tools are initialized, they set up this dictionary with the following keys: VERSION: the version of MSVS being used (can be set via &cv-link-MSVS_VERSION;) VERSIONS: the available versions of MSVS installed VCINSTALLDIR: installed directory of Visual C++ VSINSTALLDIR: installed directory of Visual Studio FRAMEWORKDIR: installed directory of the .NET framework FRAMEWORKVERSIONS: list of installed versions of the .NET framework, sorted latest to oldest. FRAMEWORKVERSION: latest installed version of the .NET framework FRAMEWORKSDKDIR: installed location of the .NET SDK. PLATFORMSDKDIR: installed location of the Platform SDK. PLATFORMSDK_MODULES: dictionary of installed Platform SDK modules, where the dictionary keys are keywords for the various modules, and the values are 2-tuples where the first is the release date, and the second is the version number. If a value isn't set, it wasn't available in the registry. Sets the architecture for which the generated project(s) should build. The default value is x86. amd64 is also supported by &SCons; for some Visual Studio versions. Trying to set &cv-MSVS_ARCH; to an architecture that's not supported for a given Visual Studio version will generate an error. The string placed in a generated Microsoft Visual Studio project file as the value of the ProjectGUID attribute. There is no default value. If not defined, a new GUID is generated. The path name placed in a generated Microsoft Visual Studio project file as the value of the SccAuxPath attribute if the MSVS_SCC_PROVIDER construction variable is also set. There is no default value. The root path of projects in your SCC workspace, i.e the path under which all project and solution files will be generated. It is used as a reference path from which the relative paths of the generated Microsoft Visual Studio project and solution files are computed. The relative project file path is placed as the value of the SccLocalPath attribute of the project file and as the values of the SccProjectFilePathRelativizedFromConnection[i] (where [i] ranges from 0 to the number of projects in the solution) attributes of the GlobalSection(SourceCodeControl) section of the Microsoft Visual Studio solution file. Similarly the relative solution file path is placed as the values of the SccLocalPath[i] (where [i] ranges from 0 to the number of projects in the solution) attributes of the GlobalSection(SourceCodeControl) section of the Microsoft Visual Studio solution file. This is used only if the MSVS_SCC_PROVIDER construction variable is also set. The default value is the current working directory. The project name placed in a generated Microsoft Visual Studio project file as the value of the SccProjectName attribute if the MSVS_SCC_PROVIDER construction variable is also set. In this case the string is also placed in the SccProjectName0 attribute of the GlobalSection(SourceCodeControl) section of the Microsoft Visual Studio solution file. There is no default value. The string placed in a generated Microsoft Visual Studio project file as the value of the SccProvider attribute. The string is also placed in the SccProvider0 attribute of the GlobalSection(SourceCodeControl) section of the Microsoft Visual Studio solution file. There is no default value. Sets the preferred version of Microsoft Visual Studio to use. If &cv-MSVS_VERSION; is not set, &SCons; will (by default) select the latest version of Visual Studio installed on your system. So, if you have version 6 and version 7 (MSVS .NET) installed, it will prefer version 7. You can override this by specifying the MSVS_VERSION variable in the Environment initialization, setting it to the appropriate version ('6.0' or '7.0', for example). If the specified version isn't installed, tool initialization will fail. This is obsolete: use &cv-MSVC_VERSION; instead. If &cv-MSVS_VERSION; is set and &cv-MSVC_VERSION; is not, &cv-MSVC_VERSION; will be set automatically to &cv-MSVS_VERSION;. If both are set to different values, scons will raise an error. The build command line placed in a generated Microsoft Visual Studio project file. The default is to have Visual Studio invoke SCons with any specified build targets. The clean command line placed in a generated Microsoft Visual Studio project file. The default is to have Visual Studio invoke SCons with the -c option to remove any specified targets. The encoding string placed in a generated Microsoft Visual Studio project file. The default is encoding Windows-1252. The action used to generate Microsoft Visual Studio project files. The suffix used for Microsoft Visual Studio project (DSP) files. The default value is .vcproj when using Visual Studio version 7.x (.NET) or later version, and .dsp when using earlier versions of Visual Studio. The rebuild command line placed in a generated Microsoft Visual Studio project file. The default is to have Visual Studio invoke SCons with any specified rebuild targets. The SCons used in generated Microsoft Visual Studio project files. The default is the version of SCons being used to generate the project file. The SCons flags used in generated Microsoft Visual Studio project files. The default SCons command used in generated Microsoft Visual Studio project files. The sconscript file (that is, &SConstruct; or &SConscript; file) that will be invoked by Visual Studio project files (through the &cv-link-MSVSSCONSCOM; variable). The default is the same sconscript file that contains the call to &b-MSVSProject; to build the project file. The action used to generate Microsoft Visual Studio solution files. The suffix used for Microsoft Visual Studio solution (DSW) files. The default value is .sln when using Visual Studio version 7.x (.NET), and .dsw when using earlier versions of Visual Studio. The (optional) path to the SCons library directory, initialized from the external environment. If set, this is used to construct a shorter and more efficient search path in the &cv-link-MSVSSCONS; command line executed from Microsoft Visual Studio project files. scons-doc-2.3.0/src/engine/SCons/Tool/cvf.xml0000644000175000017500000000110712114661560021511 0ustar dktrkranzdktrkranz Sets construction variables for the Compaq Visual Fortran compiler. FORTRAN FORTRANCOM FORTRANPPCOM SHFORTRANCOM SHFORTRANPPCOM OBJSUFFIX FORTRANMODDIR FORTRANMODDIRPREFIX FORTRANMODDIRSUFFIX FORTRANFLAGS SHFORTRANFLAGS _FORTRANMODFLAG _FORTRANINCFLAGS CPPFLAGS _CPPDEFFLAGS scons-doc-2.3.0/src/engine/SCons/Tool/f03.xml0000644000175000017500000001673112114661560021334 0ustar dktrkranzdktrkranz Set construction variables for generic POSIX Fortran 03 compilers. F03 F03FLAGS F03COM F03PPCOM SHF03 SHF03FLAGS SHF03COM SHF03PPCOM _F03INCFLAGS F03COMSTR F03PPCOMSTR SHF03COMSTR SHF03PPCOMSTR The Fortran 03 compiler. You should normally set the &cv-link-FORTRAN; variable, which specifies the default Fortran compiler for all Fortran versions. You only need to set &cv-link-F03; if you need to use a specific compiler or compiler version for Fortran 03 files. The command line used to compile a Fortran 03 source file to an object file. You only need to set &cv-link-F03COM; if you need to use a specific command line for Fortran 03 files. You should normally set the &cv-link-FORTRANCOM; variable, which specifies the default command line for all Fortran versions. The string displayed when a Fortran 03 source file is compiled to an object file. If this is not set, then &cv-link-F03COM; or &cv-link-FORTRANCOM; (the command line) is displayed. The list of file extensions for which the F03 dialect will be used. By default, this is ['.f03'] The list of file extensions for which the compilation + preprocessor pass for F03 dialect will be used. By default, this is empty General user-specified options that are passed to the Fortran 03 compiler. Note that this variable does not contain (or similar) include search path options that scons generates automatically from &cv-link-F03PATH;. See &cv-link-_F03INCFLAGS; below, for the variable that expands to those options. You only need to set &cv-link-F03FLAGS; if you need to define specific user options for Fortran 03 files. You should normally set the &cv-link-FORTRANFLAGS; variable, which specifies the user-specified options passed to the default Fortran compiler for all Fortran versions. An automatically-generated construction variable containing the Fortran 03 compiler command-line options for specifying directories to be searched for include files. The value of &cv-link-_F03INCFLAGS; is created by appending &cv-link-INCPREFIX; and &cv-link-INCSUFFIX; to the beginning and end of each directory in &cv-link-F03PATH;. The list of directories that the Fortran 03 compiler will search for include directories. The implicit dependency scanner will search these directories for include files. Don't explicitly put include directory arguments in &cv-link-F03FLAGS; because the result will be non-portable and the directories will not be searched by the dependency scanner. Note: directory names in &cv-link-F03PATH; will be looked-up relative to the SConscript directory when they are used in a command. To force &scons; to look-up a directory relative to the root of the source tree use #: You only need to set &cv-link-F03PATH; if you need to define a specific include path for Fortran 03 files. You should normally set the &cv-link-FORTRANPATH; variable, which specifies the include path for the default Fortran compiler for all Fortran versions. env = Environment(F03PATH='#/include') The directory look-up can also be forced using the &Dir;() function: include = Dir('include') env = Environment(F03PATH=include) The directory list will be added to command lines through the automatically-generated &cv-link-_F03INCFLAGS; construction variable, which is constructed by appending the values of the &cv-link-INCPREFIX; and &cv-link-INCSUFFIX; construction variables to the beginning and end of each directory in &cv-link-F03PATH;. Any command lines you define that need the F03PATH directory list should include &cv-link-_F03INCFLAGS;: env = Environment(F03COM="my_compiler $_F03INCFLAGS -c -o $TARGET $SOURCE") The command line used to compile a Fortran 03 source file to an object file after first running the file through the C preprocessor. Any options specified in the &cv-link-F03FLAGS; and &cv-link-CPPFLAGS; construction variables are included on this command line. You only need to set &cv-link-F03PPCOM; if you need to use a specific C-preprocessor command line for Fortran 03 files. You should normally set the &cv-link-FORTRANPPCOM; variable, which specifies the default C-preprocessor command line for all Fortran versions. The string displayed when a Fortran 03 source file is compiled to an object file after first running the file through the C preprocessor. If this is not set, then &cv-link-F03PPCOM; or &cv-link-FORTRANPPCOM; (the command line) is displayed. The Fortran 03 compiler used for generating shared-library objects. You should normally set the &cv-link-SHFORTRAN; variable, which specifies the default Fortran compiler for all Fortran versions. You only need to set &cv-link-SHF03; if you need to use a specific compiler or compiler version for Fortran 03 files. The command line used to compile a Fortran 03 source file to a shared-library object file. You only need to set &cv-link-SHF03COM; if you need to use a specific command line for Fortran 03 files. You should normally set the &cv-link-SHFORTRANCOM; variable, which specifies the default command line for all Fortran versions. The string displayed when a Fortran 03 source file is compiled to a shared-library object file. If this is not set, then &cv-link-SHF03COM; or &cv-link-SHFORTRANCOM; (the command line) is displayed. Options that are passed to the Fortran 03 compiler to generated shared-library objects. You only need to set &cv-link-SHF03FLAGS; if you need to define specific user options for Fortran 03 files. You should normally set the &cv-link-SHFORTRANFLAGS; variable, which specifies the user-specified options passed to the default Fortran compiler for all Fortran versions. The command line used to compile a Fortran 03 source file to a shared-library object file after first running the file through the C preprocessor. Any options specified in the &cv-link-SHF03FLAGS; and &cv-link-CPPFLAGS; construction variables are included on this command line. You only need to set &cv-link-SHF03PPCOM; if you need to use a specific C-preprocessor command line for Fortran 03 files. You should normally set the &cv-link-SHFORTRANPPCOM; variable, which specifies the default C-preprocessor command line for all Fortran versions. The string displayed when a Fortran 03 source file is compiled to a shared-library object file after first running the file through the C preprocessor. If this is not set, then &cv-link-SHF03PPCOM; or &cv-link-SHFORTRANPPCOM; (the command line) is displayed. scons-doc-2.3.0/src/engine/SCons/Tool/Subversion.py0000644000175000017500000000524212114661557022734 0ustar dktrkranzdktrkranz"""SCons.Tool.Subversion.py Tool-specific initialization for Subversion. There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Tool.Tool() selection method. """ # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/Subversion.py 2013/03/03 09:48:35 garyo" import os.path import SCons.Action import SCons.Builder import SCons.Util def generate(env): """Add a Builder factory function and construction variables for Subversion to an Environment.""" def SubversionFactory(repos, module='', env=env): """ """ # fail if repos is not an absolute path name? import SCons.Warnings as W W.warn(W.DeprecatedSourceCodeWarning, """The Subversion() factory is deprecated and there is no replacement.""") if module != '': module = os.path.join(module, '') act = SCons.Action.Action('$SVNCOM', '$SVNCOMSTR') return SCons.Builder.Builder(action = act, env = env, SVNREPOSITORY = repos, SVNMODULE = module) #setattr(env, 'Subversion', SubversionFactory) env.Subversion = SubversionFactory env['SVN'] = 'svn' env['SVNFLAGS'] = SCons.Util.CLVar('') env['SVNCOM'] = '$SVN $SVNFLAGS cat $SVNREPOSITORY/$SVNMODULE$TARGET > $TARGET' def exists(env): return env.Detect('svn') # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/javah.xml0000644000175000017500000000471412114661560022033 0ustar dktrkranzdktrkranz Sets construction variables for the &javah; tool. JAVAH JAVAHFLAGS JAVAHCOM JAVACLASSSUFFIX JAVAHCOMSTR JAVACLASSPATH Builds C header and source files for implementing Java native methods. The target can be either a directory in which the header files will be written, or a header file name which will contain all of the definitions. The source can be the names of .class files, the names of .java files to be compiled into .class files by calling the &b-link-Java; builder method, or the objects returned from the &b-Java; builder method. If the construction variable &cv-link-JAVACLASSDIR; is set, either in the environment or in the call to the &b-JavaH; builder method itself, then the value of the variable will be stripped from the beginning of any .class file names. Examples: # builds java_native.h classes = env.Java(target = 'classdir', source = 'src') env.JavaH(target = 'java_native.h', source = classes) # builds include/package_foo.h and include/package_bar.h env.JavaH(target = 'include', source = ['package/foo.class', 'package/bar.class']) # builds export/foo.h and export/bar.h env.JavaH(target = 'export', source = ['classes/foo.class', 'classes/bar.class'], JAVACLASSDIR = 'classes') The Java generator for C header and stub files. The command line used to generate C header and stub files from Java classes. Any options specified in the &cv-link-JAVAHFLAGS; construction variable are included on this command line. The string displayed when C header and stub files are generated from Java classes. If this is not set, then &cv-link-JAVAHCOM; (the command line) is displayed. env = Environment(JAVAHCOMSTR = "Generating header/stub file(s) $TARGETS from $SOURCES") General options passed to the C header and stub file generator for Java classes. scons-doc-2.3.0/src/engine/SCons/Tool/as.py0000644000175000017500000000567112114661560021200 0ustar dktrkranzdktrkranz"""SCons.Tool.as Tool-specific initialization for as, the generic Posix assembler. There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Tool.Tool() selection method. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/as.py 2013/03/03 09:48:35 garyo" import SCons.Defaults import SCons.Tool import SCons.Util assemblers = ['as'] ASSuffixes = ['.s', '.asm', '.ASM'] ASPPSuffixes = ['.spp', '.SPP', '.sx'] if SCons.Util.case_sensitive_suffixes('.s', '.S'): ASPPSuffixes.extend(['.S']) else: ASSuffixes.extend(['.S']) def generate(env): """Add Builders and construction variables for as to an Environment.""" static_obj, shared_obj = SCons.Tool.createObjBuilders(env) for suffix in ASSuffixes: static_obj.add_action(suffix, SCons.Defaults.ASAction) shared_obj.add_action(suffix, SCons.Defaults.ASAction) static_obj.add_emitter(suffix, SCons.Defaults.StaticObjectEmitter) shared_obj.add_emitter(suffix, SCons.Defaults.SharedObjectEmitter) for suffix in ASPPSuffixes: static_obj.add_action(suffix, SCons.Defaults.ASPPAction) shared_obj.add_action(suffix, SCons.Defaults.ASPPAction) static_obj.add_emitter(suffix, SCons.Defaults.StaticObjectEmitter) shared_obj.add_emitter(suffix, SCons.Defaults.SharedObjectEmitter) env['AS'] = env.Detect(assemblers) or 'as' env['ASFLAGS'] = SCons.Util.CLVar('') env['ASCOM'] = '$AS $ASFLAGS -o $TARGET $SOURCES' env['ASPPFLAGS'] = '$ASFLAGS' env['ASPPCOM'] = '$CC $ASPPFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS -c -o $TARGET $SOURCES' def exists(env): return env.Detect(assemblers) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/hpc++.py0000644000175000017500000000523012114661560021464 0ustar dktrkranzdktrkranz"""SCons.Tool.hpc++ Tool-specific initialization for c++ on HP/UX. There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Tool.Tool() selection method. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/hpc++.py 2013/03/03 09:48:35 garyo" import os.path import SCons.Util cplusplus = __import__('c++', globals(), locals(), []) acc = None # search for the acc compiler and linker front end try: dirs = os.listdir('/opt') except (IOError, OSError): # Not being able to read the directory because it doesn't exist # (IOError) or isn't readable (OSError) is okay. dirs = [] for dir in dirs: cc = '/opt/' + dir + '/bin/aCC' if os.path.exists(cc): acc = cc break def generate(env): """Add Builders and construction variables for g++ to an Environment.""" cplusplus.generate(env) if acc: env['CXX'] = acc or 'aCC' env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS +Z') # determine version of aCC line = os.popen(acc + ' -V 2>&1').readline().rstrip() if line.find('aCC: HP ANSI C++') == 0: env['CXXVERSION'] = line.split()[-1] if env['PLATFORM'] == 'cygwin': env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS') else: env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS +Z') def exists(env): return acc # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/sunar.xml0000644000175000017500000000071012114661560022062 0ustar dktrkranzdktrkranz Sets construction variables for the Sun library archiver. AR ARFLAGS ARCOM SHLINK SHLINKFLAGS SHLINKCOM LIBPREFIX LIBSUFFIX ARCOMSTR SHLINKCOMSTR scons-doc-2.3.0/src/engine/SCons/Tool/mslink.xml0000644000175000017500000001513612114661560022237 0ustar dktrkranzdktrkranz Sets construction variables for the Microsoft linker. SHLINK SHLINKFLAGS SHLINKCOM LINK LINKFLAGS LINKCOM LIBDIRPREFIX LIBDIRSUFFIX LIBLINKPREFIX LIBLINKSUFFIX WIN32DEFPREFIX WIN32DEFSUFFIX WINDOWSDEFPREFIX WINDOWSDEFSUFFIX WINDOWS_INSERT_DEF WIN32EXPPREFIX WIN32EXPSUFFIX WINDOWSEXPPREFIX WINDOWSEXPSUFFIX WINDOWSSHLIBMANIFESTPREFIX WINDOWSSHLIBMANIFESTSUFFIX WINDOWSPROGMANIFESTPREFIX WINDOWSPROGMANIFESTSUFFIX REGSVR REGSVRFLAGS REGSVRCOM LDMODULE LDMODULEPREFIX LDMODULESUFFIX LDMODULEFLAGS LDMODULECOM SHLINKCOMSTR LINKCOMSTR REGSVRCOMSTR LDMODULECOMSTR When set to non-zero, suppresses creation of a corresponding Windows static import lib by the SharedLibrary builder when used with MinGW, Microsoft Visual Studio or Metrowerks. This also suppresses creation of an export (.exp) file when using Microsoft Visual Studio. The Microsoft Visual C++ PDB file that will store debugging information for object files, shared libraries, and programs. This variable is ignored by tools other than Microsoft Visual C++. When this variable is defined SCons will add options to the compiler and linker command line to cause them to generate external debugging information, and will also set up the dependencies for the PDB file. Example: env['PDB'] = 'hello.pdb' The Visual C++ compiler switch that SCons uses by default to generate PDB information is . This works correctly with parallel () builds because it embeds the debug information in the intermediate object files, as opposed to sharing a single PDB file between multiple object files. This is also the only way to get debug information embedded into a static library. Using the instead may yield improved link-time performance, although parallel builds will no longer work. You can generate PDB files with the switch by overriding the default &cv-link-CCPDBFLAGS; variable; see the entry for that variable for specific examples. Set this variable to True or 1 to embed the compiler-generated manifest (normally ${TARGET}.manifest) into all Windows exes and DLLs built with this environment, as a resource during their link step. This is done using &cv-link-MT; and &cv-link-MTEXECOM; and &cv-link-MTSHLIBCOM;. The program used on Windows systems to embed manifests into DLLs and EXEs. See also &cv-link-WINDOWS_EMBED_MANIFEST;. Flags passed to the &cv-link-MT; manifest embedding program (Windows only). The Windows command line used to embed manifests into executables. See also &cv-link-MTSHLIBCOM;. The Windows command line used to embed manifests into shared libraries (DLLs). See also &cv-link-MTEXECOM;. The program used on Windows systems to register a newly-built DLL library whenever the &b-SharedLibrary; builder is passed a keyword argument of register=1. The command line used on Windows systems to register a newly-built DLL library whenever the &b-SharedLibrary; builder is passed a keyword argument of register=1. The string displayed when registering a newly-built DLL file. If this is not set, then &cv-link-REGSVRCOM; (the command line) is displayed. Flags passed to the DLL registration program on Windows systems when a newly-built DLL library is registered. By default, this includes the that prevents dialog boxes from popping up and requiring user attention. A deprecated synonym for &cv-link-WINDOWS_INSERT_DEF;. A deprecated synonym for &cv-link-WINDOWSDEFPREFIX;. A deprecated synonym for &cv-link-WINDOWSDEFSUFFIX;. A deprecated synonym for &cv-link-WINDOWSEXPSUFFIX;. A deprecated synonym for &cv-link-WINDOWSEXPSUFFIX;. When this is set to true, a library build of a Windows shared library (.dll file) will also build a corresponding .def file at the same time, if a .def file is not already listed as a build target. The default is 0 (do not build a .def file). When this is set to true, &scons; will be aware of the .manifest files generated by Microsoft Visua C/C++ 8. The prefix used for Windows .def file names. The suffix used for Windows .def file names. The prefix used for Windows .exp file names. The suffix used for Windows .exp file names. The prefix used for executable program .manifest files generated by Microsoft Visual C/C++. The suffix used for executable program .manifest files generated by Microsoft Visual C/C++. The prefix used for shared library .manifest files generated by Microsoft Visual C/C++. The suffix used for shared library .manifest files generated by Microsoft Visual C/C++. scons-doc-2.3.0/src/engine/SCons/Tool/tlib.xml0000644000175000017500000000067712114661560021700 0ustar dktrkranzdktrkranz Sets construction variables for the Borlan tib library archiver. AR ARFLAGS ARCOM LIBPREFIX LIBSUFFIX ARCOMSTR scons-doc-2.3.0/src/engine/SCons/Tool/nasm.xml0000644000175000017500000000070312114661560021672 0ustar dktrkranzdktrkranz Sets construction variables for the nasm Netwide Assembler. AS ASFLAGS ASPPFLAGS ASCOM ASPPCOM ASCOMSTR ASPPCOMSTR scons-doc-2.3.0/src/engine/SCons/Tool/mslib.py0000644000175000017500000000427112114661560021676 0ustar dktrkranzdktrkranz"""SCons.Tool.mslib Tool-specific initialization for lib (MicroSoft library archiver). There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Tool.Tool() selection method. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/mslib.py 2013/03/03 09:48:35 garyo" import SCons.Defaults import SCons.Tool import SCons.Tool.msvs import SCons.Tool.msvc import SCons.Util from MSCommon import msvc_exists, msvc_setup_env_once def generate(env): """Add Builders and construction variables for lib to an Environment.""" SCons.Tool.createStaticLibBuilder(env) # Set-up ms tools paths msvc_setup_env_once(env) env['AR'] = 'lib' env['ARFLAGS'] = SCons.Util.CLVar('/nologo') env['ARCOM'] = "${TEMPFILE('$AR $ARFLAGS /OUT:$TARGET $SOURCES')}" env['LIBPREFIX'] = '' env['LIBSUFFIX'] = '.lib' def exists(env): return msvc_exists() # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/yacc.xml0000644000175000017500000000536012114661560021657 0ustar dktrkranzdktrkranz Sets construction variables for the &yacc; parse generator. YACC YACCFLAGS YACCCOM YACCHFILESUFFIX YACCHXXFILESUFFIX YACCVCGFILESUFFIX YACCCOMSTR The parser generator. The command line used to call the parser generator to generate a source file. The string displayed when generating a source file using the parser generator. If this is not set, then &cv-link-YACCCOM; (the command line) is displayed. env = Environment(YACCCOMSTR = "Yacc'ing $TARGET from $SOURCES") General options passed to the parser generator. If &cv-link-YACCFLAGS; contains a option, SCons assumes that the call will also create a .h file (if the yacc source file ends in a .y suffix) or a .hpp file (if the yacc source file ends in a .yy suffix) The suffix of the C header file generated by the parser generator when the option is used. Note that setting this variable does not cause the parser generator to generate a header file with the specified suffix, it exists to allow you to specify what suffix the parser generator will use of its own accord. The default value is .h. The suffix of the C++ header file generated by the parser generator when the option is used. Note that setting this variable does not cause the parser generator to generate a header file with the specified suffix, it exists to allow you to specify what suffix the parser generator will use of its own accord. The default value is .hpp, except on Mac OS X, where the default is ${TARGET.suffix}.h. because the default &bison; parser generator just appends .h to the name of the generated C++ file. The suffix of the file containing the VCG grammar automaton definition when the option is used. Note that setting this variable does not cause the parser generator to generate a VCG file with the specified suffix, it exists to allow you to specify what suffix the parser generator will use of its own accord. The default value is .vcg. scons-doc-2.3.0/src/engine/SCons/Tool/CVS.xml0000644000175000017500000000457312114661557021406 0ustar dktrkranzdktrkranz Sets construction variables for the CVS source code management system. CVS CVSCOM CVSFLAGS CVSCOFLAGS CVSCOMSTR The CVS executable. Options that are passed to the CVS checkout subcommand. The command line used to fetch source files from a CVS repository. The string displayed when fetching a source file from a CVS repository. If this is not set, then &cv-link-CVSCOM; (the command line) is displayed. General options that are passed to CVS. By default, this is set to -d $CVSREPOSITORY to specify from where the files must be fetched. The path to the CVS repository. This is referenced in the default &cv-link-CVSFLAGS; value. (repository, module) A factory function that returns a Builder object to be used to fetch source files from the specified CVS repository. The returned Builder is intended to be passed to the &f-link-SourceCode; function. This function is deprecated. For details, see the entry for the &f-SourceCode; function. The optional specified module will be added to the beginning of all repository path names; this can be used, in essence, to strip initial directory names from the repository path names, so that you only have to replicate part of the repository directory hierarchy in your local build directory. Examples: # Will fetch foo/bar/src.c # from /usr/local/CVSROOT/foo/bar/src.c. env.SourceCode('.', env.CVS('/usr/local/CVSROOT')) # Will fetch bar/src.c # from /usr/local/CVSROOT/foo/bar/src.c. env.SourceCode('.', env.CVS('/usr/local/CVSROOT', 'foo')) # Will fetch src.c # from /usr/local/CVSROOT/foo/bar/src.c. env.SourceCode('.', env.CVS('/usr/local/CVSROOT', 'foo/bar')) scons-doc-2.3.0/src/engine/SCons/Tool/ToolTests.py0000644000175000017500000000565412114661557022544 0ustar dktrkranzdktrkranz# # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/ToolTests.py 2013/03/03 09:48:35 garyo" import sys import unittest import SCons.Errors import SCons.Tool class ToolTestCase(unittest.TestCase): def test_Tool(self): """Test the Tool() function""" class Environment(object): def __init__(self): self.dict = {} def Detect(self, progs): if not SCons.Util.is_List(progs): progs = [ progs ] return progs[0] def Append(self, **kw): self.dict.update(kw) def __getitem__(self, key): return self.dict[key] def __setitem__(self, key, val): self.dict[key] = val def __contains__(self, key): return self.dict.__contains__(key) def has_key(self, key): return key in self.dict env = Environment() env['BUILDERS'] = {} env['ENV'] = {} env['PLATFORM'] = 'test' t = SCons.Tool.Tool('g++') t(env) assert (env['CXX'] == 'c++' or env['CXX'] == 'g++'), env['CXX'] assert env['INCPREFIX'] == '-I', env['INCPREFIX'] assert env['TOOLS'] == ['g++'], env['TOOLS'] try: SCons.Tool.Tool() except TypeError: pass else: raise try: p = SCons.Tool.Tool('_does_not_exist_') except SCons.Errors.EnvironmentError: pass else: raise if __name__ == "__main__": suite = unittest.makeSuite(ToolTestCase, 'test_') if not unittest.TextTestRunner().run(suite).wasSuccessful(): sys.exit(1) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/pdf.py0000644000175000017500000000574512114661560021350 0ustar dktrkranzdktrkranz"""SCons.Tool.pdf Common PDF Builder definition for various other Tool modules that use it. Add an explicit action to run epstopdf to convert .eps files to .pdf """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/pdf.py 2013/03/03 09:48:35 garyo" import SCons.Builder import SCons.Tool PDFBuilder = None EpsPdfAction = SCons.Action.Action('$EPSTOPDFCOM', '$EPSTOPDFCOMSTR') def generate(env): try: env['BUILDERS']['PDF'] except KeyError: global PDFBuilder if PDFBuilder is None: PDFBuilder = SCons.Builder.Builder(action = {}, source_scanner = SCons.Tool.PDFLaTeXScanner, prefix = '$PDFPREFIX', suffix = '$PDFSUFFIX', emitter = {}, source_ext_match = None, single_source=True) env['BUILDERS']['PDF'] = PDFBuilder env['PDFPREFIX'] = '' env['PDFSUFFIX'] = '.pdf' # put the epstopdf builder in this routine so we can add it after # the pdftex builder so that one is the default for no source suffix def generate2(env): bld = env['BUILDERS']['PDF'] #bld.add_action('.ps', EpsPdfAction) # this is covered by direct Ghostcript action in gs.py bld.add_action('.eps', EpsPdfAction) env['EPSTOPDF'] = 'epstopdf' env['EPSTOPDFFLAGS'] = SCons.Util.CLVar('') env['EPSTOPDFCOM'] = '$EPSTOPDF $EPSTOPDFFLAGS ${SOURCE} --outfile=${TARGET}' def exists(env): # This only puts a skeleton Builder in place, so if someone # references this Tool directly, it's always "available." return 1 # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/mslib.xml0000644000175000017500000000070512114661560022044 0ustar dktrkranzdktrkranz Sets construction variables for the Microsoft mslib library archiver. AR ARFLAGS ARCOM LIBPREFIX LIBSUFFIX ARCOMSTR scons-doc-2.3.0/src/engine/SCons/Tool/lex.xml0000644000175000017500000000200712114661560021523 0ustar dktrkranzdktrkranz Sets construction variables for the &lex; lexical analyser. LEX LEXFLAGS LEXCOM LEXCOMSTR The lexical analyzer generator. The command line used to call the lexical analyzer generator to generate a source file. The string displayed when generating a source file using the lexical analyzer generator. If this is not set, then &cv-link-LEXCOM; (the command line) is displayed. env = Environment(LEXCOMSTR = "Lex'ing $TARGET from $SOURCES") General options passed to the lexical analyzer generator. scons-doc-2.3.0/src/engine/SCons/Tool/sunlink.py0000644000175000017500000000456212114661560022256 0ustar dktrkranzdktrkranz"""SCons.Tool.sunlink Tool-specific initialization for the Sun Solaris (Forte) linker. There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Tool.Tool() selection method. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/sunlink.py 2013/03/03 09:48:35 garyo" import os import os.path import SCons.Util import link ccLinker = None # search for the acc compiler and linker front end try: dirs = os.listdir('/opt') except (IOError, OSError): # Not being able to read the directory because it doesn't exist # (IOError) or isn't readable (OSError) is okay. dirs = [] for d in dirs: linker = '/opt/' + d + '/bin/CC' if os.path.exists(linker): ccLinker = linker break def generate(env): """Add Builders and construction variables for Forte to an Environment.""" link.generate(env) env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -G') env['RPATHPREFIX'] = '-R' env['RPATHSUFFIX'] = '' env['_RPATH'] = '${_concat(RPATHPREFIX, RPATH, RPATHSUFFIX, __env__)}' def exists(env): return ccLinker # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/ifort.xml0000644000175000017500000000073712114661560022066 0ustar dktrkranzdktrkranz Sets construction variables for newer versions of the Intel Fortran compiler for Linux. FORTRAN F77 F90 F95 SHFORTRAN SHF77 SHF90 SHF95 SHFORTRANFLAGS SHF77FLAGS SHF90FLAGS SHF95FLAGS scons-doc-2.3.0/src/engine/SCons/Tool/sgilink.py0000644000175000017500000000422012114661560022222 0ustar dktrkranzdktrkranz"""SCons.Tool.sgilink Tool-specific initialization for the SGI MIPSPro linker on SGI. There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Tool.Tool() selection method. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/sgilink.py 2013/03/03 09:48:35 garyo" import SCons.Util import link linkers = ['CC', 'cc'] def generate(env): """Add Builders and construction variables for MIPSPro to an Environment.""" link.generate(env) env['LINK'] = env.Detect(linkers) or 'cc' env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -shared') # __RPATH is set to $_RPATH in the platform specification if that # platform supports it. env['RPATHPREFIX'] = '-rpath ' env['RPATHSUFFIX'] = '' env['_RPATH'] = '${_concat(RPATHPREFIX, RPATH, RPATHSUFFIX, __env__)}' def exists(env): return env.Detect(linkers) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/aixcc.xml0000644000175000017500000000061512114661560022025 0ustar dktrkranzdktrkranz Sets construction variables for the IBM xlc / Visual Age C compiler. CC SHCC CCVERSION scons-doc-2.3.0/src/engine/SCons/Tool/masm.py0000644000175000017500000000570212114661560021525 0ustar dktrkranzdktrkranz"""SCons.Tool.masm Tool-specific initialization for the Microsoft Assembler. There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Tool.Tool() selection method. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/masm.py 2013/03/03 09:48:35 garyo" import SCons.Defaults import SCons.Tool import SCons.Util ASSuffixes = ['.s', '.asm', '.ASM'] ASPPSuffixes = ['.spp', '.SPP', '.sx'] if SCons.Util.case_sensitive_suffixes('.s', '.S'): ASPPSuffixes.extend(['.S']) else: ASSuffixes.extend(['.S']) def generate(env): """Add Builders and construction variables for masm to an Environment.""" static_obj, shared_obj = SCons.Tool.createObjBuilders(env) for suffix in ASSuffixes: static_obj.add_action(suffix, SCons.Defaults.ASAction) shared_obj.add_action(suffix, SCons.Defaults.ASAction) static_obj.add_emitter(suffix, SCons.Defaults.StaticObjectEmitter) shared_obj.add_emitter(suffix, SCons.Defaults.SharedObjectEmitter) for suffix in ASPPSuffixes: static_obj.add_action(suffix, SCons.Defaults.ASPPAction) shared_obj.add_action(suffix, SCons.Defaults.ASPPAction) static_obj.add_emitter(suffix, SCons.Defaults.StaticObjectEmitter) shared_obj.add_emitter(suffix, SCons.Defaults.SharedObjectEmitter) env['AS'] = 'ml' env['ASFLAGS'] = SCons.Util.CLVar('/nologo') env['ASPPFLAGS'] = '$ASFLAGS' env['ASCOM'] = '$AS $ASFLAGS /c /Fo$TARGET $SOURCES' env['ASPPCOM'] = '$CC $ASPPFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS /c /Fo$TARGET $SOURCES' env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME'] = 1 def exists(env): return env.Detect('ml') # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/install.xml0000644000175000017500000000312112114661560022377 0ustar dktrkranzdktrkranz Sets construction variables for file and directory installation. INSTALL INSTALLSTR Installs one or more source files or directories in the specified target, which must be a directory. The names of the specified source files or directories remain the same within the destination directory. The sources may be given as a string or as a node returned by a builder. env.Install('/usr/local/bin', source = ['foo', 'bar']) Installs one or more source files or directories to specific names, allowing changing a file or directory name as part of the installation. It is an error if the target and source arguments list different numbers of files or directories. Installs a versioned shared library. The &cv-link-SHLIBVERSION; construction variable should be defined in the environment to confirm the version number in the library name. The symlinks appropriate to the architecture will be generated. env.InstallAs(target = '/usr/local/bin/foo', source = 'foo_debug') env.InstallAs(target = ['../lib/libfoo.a', '../lib/libbar.a'], source = ['libFOO.a', 'libBAR.a']) scons-doc-2.3.0/src/engine/SCons/Tool/msvc.py0000644000175000017500000002626512114661560021547 0ustar dktrkranzdktrkranz"""engine.SCons.Tool.msvc Tool-specific initialization for Microsoft Visual C/C++. There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Tool.Tool() selection method. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/msvc.py 2013/03/03 09:48:35 garyo" import os.path import re import sys import SCons.Action import SCons.Builder import SCons.Errors import SCons.Platform.win32 import SCons.Tool import SCons.Tool.msvs import SCons.Util import SCons.Warnings import SCons.Scanner.RC from MSCommon import msvc_exists, msvc_setup_env_once CSuffixes = ['.c', '.C'] CXXSuffixes = ['.cc', '.cpp', '.cxx', '.c++', '.C++'] def validate_vars(env): """Validate the PCH and PCHSTOP construction variables.""" if 'PCH' in env and env['PCH']: if 'PCHSTOP' not in env: raise SCons.Errors.UserError("The PCHSTOP construction must be defined if PCH is defined.") if not SCons.Util.is_String(env['PCHSTOP']): raise SCons.Errors.UserError("The PCHSTOP construction variable must be a string: %r"%env['PCHSTOP']) def pch_emitter(target, source, env): """Adds the object file target.""" validate_vars(env) pch = None obj = None for t in target: if SCons.Util.splitext(str(t))[1] == '.pch': pch = t if SCons.Util.splitext(str(t))[1] == '.obj': obj = t if not obj: obj = SCons.Util.splitext(str(pch))[0]+'.obj' target = [pch, obj] # pch must be first, and obj second for the PCHCOM to work return (target, source) def object_emitter(target, source, env, parent_emitter): """Sets up the PCH dependencies for an object file.""" validate_vars(env) parent_emitter(target, source, env) # Add a dependency, but only if the target (e.g. 'Source1.obj') # doesn't correspond to the pre-compiled header ('Source1.pch'). # If the basenames match, then this was most likely caused by # someone adding the source file to both the env.PCH() and the # env.Program() calls, and adding the explicit dependency would # cause a cycle on the .pch file itself. # # See issue #2505 for a discussion of what to do if it turns # out this assumption causes trouble in the wild: # http://scons.tigris.org/issues/show_bug.cgi?id=2505 if 'PCH' in env: pch = env['PCH'] if str(target[0]) != SCons.Util.splitext(str(pch))[0] + '.obj': env.Depends(target, pch) return (target, source) def static_object_emitter(target, source, env): return object_emitter(target, source, env, SCons.Defaults.StaticObjectEmitter) def shared_object_emitter(target, source, env): return object_emitter(target, source, env, SCons.Defaults.SharedObjectEmitter) pch_action = SCons.Action.Action('$PCHCOM', '$PCHCOMSTR') pch_builder = SCons.Builder.Builder(action=pch_action, suffix='.pch', emitter=pch_emitter, source_scanner=SCons.Tool.SourceFileScanner) # Logic to build .rc files into .res files (resource files) res_scanner = SCons.Scanner.RC.RCScan() res_action = SCons.Action.Action('$RCCOM', '$RCCOMSTR') res_builder = SCons.Builder.Builder(action=res_action, src_suffix='.rc', suffix='.res', src_builder=[], source_scanner=res_scanner) def msvc_batch_key(action, env, target, source): """ Returns a key to identify unique batches of sources for compilation. If batching is enabled (via the $MSVC_BATCH setting), then all target+source pairs that use the same action, defined by the same environment, and have the same target and source directories, will be batched. Returning None specifies that the specified target+source should not be batched with other compilations. """ # Fixing MSVC_BATCH mode. Previous if did not work when MSVC_BATCH # was set to False. This new version should work better. # Note we need to do the env.subst so $MSVC_BATCH can be a reference to # another construction variable, which is why we test for False and 0 # as strings. if not 'MSVC_BATCH' in env or env.subst('$MSVC_BATCH') in ('0', 'False', '', None): # We're not using batching; return no key. return None t = target[0] s = source[0] if os.path.splitext(t.name)[0] != os.path.splitext(s.name)[0]: # The base names are different, so this *must* be compiled # separately; return no key. return None return (id(action), id(env), t.dir, s.dir) def msvc_output_flag(target, source, env, for_signature): """ Returns the correct /Fo flag for batching. If batching is disabled or there's only one source file, then we return an /Fo string that specifies the target explicitly. Otherwise, we return an /Fo string that just specifies the first target's directory (where the Visual C/C++ compiler will put the .obj files). """ # Fixing MSVC_BATCH mode. Previous if did not work when MSVC_BATCH # was set to False. This new version should work better. Removed # len(source)==1 as batch mode can compile only one file # (and it also fixed problem with compiling only one changed file # with batch mode enabled) if not 'MSVC_BATCH' in env or env.subst('$MSVC_BATCH') in ('0', 'False', '', None): return '/Fo$TARGET' else: # The Visual C/C++ compiler requires a \ at the end of the /Fo # option to indicate an output directory. We use os.sep here so # that the test(s) for this can be run on non-Windows systems # without having a hard-coded backslash mess up command-line # argument parsing. return '/Fo${TARGET.dir}' + os.sep CAction = SCons.Action.Action("$CCCOM", "$CCCOMSTR", batch_key=msvc_batch_key, targets='$CHANGED_TARGETS') ShCAction = SCons.Action.Action("$SHCCCOM", "$SHCCCOMSTR", batch_key=msvc_batch_key, targets='$CHANGED_TARGETS') CXXAction = SCons.Action.Action("$CXXCOM", "$CXXCOMSTR", batch_key=msvc_batch_key, targets='$CHANGED_TARGETS') ShCXXAction = SCons.Action.Action("$SHCXXCOM", "$SHCXXCOMSTR", batch_key=msvc_batch_key, targets='$CHANGED_TARGETS') def generate(env): """Add Builders and construction variables for MSVC++ to an Environment.""" static_obj, shared_obj = SCons.Tool.createObjBuilders(env) # TODO(batch): shouldn't reach in to cmdgen this way; necessary # for now to bypass the checks in Builder.DictCmdGenerator.__call__() # and allow .cc and .cpp to be compiled in the same command line. static_obj.cmdgen.source_ext_match = False shared_obj.cmdgen.source_ext_match = False for suffix in CSuffixes: static_obj.add_action(suffix, CAction) shared_obj.add_action(suffix, ShCAction) static_obj.add_emitter(suffix, static_object_emitter) shared_obj.add_emitter(suffix, shared_object_emitter) for suffix in CXXSuffixes: static_obj.add_action(suffix, CXXAction) shared_obj.add_action(suffix, ShCXXAction) static_obj.add_emitter(suffix, static_object_emitter) shared_obj.add_emitter(suffix, shared_object_emitter) env['CCPDBFLAGS'] = SCons.Util.CLVar(['${(PDB and "/Z7") or ""}']) env['CCPCHFLAGS'] = SCons.Util.CLVar(['${(PCH and "/Yu%s \\\"/Fp%s\\\""%(PCHSTOP or "",File(PCH))) or ""}']) env['_MSVC_OUTPUT_FLAG'] = msvc_output_flag env['_CCCOMCOM'] = '$CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS $CCPCHFLAGS $CCPDBFLAGS' env['CC'] = 'cl' env['CCFLAGS'] = SCons.Util.CLVar('/nologo') env['CFLAGS'] = SCons.Util.CLVar('') env['CCCOM'] = '${TEMPFILE("$CC $_MSVC_OUTPUT_FLAG /c $CHANGED_SOURCES $CFLAGS $CCFLAGS $_CCCOMCOM")}' env['SHCC'] = '$CC' env['SHCCFLAGS'] = SCons.Util.CLVar('$CCFLAGS') env['SHCFLAGS'] = SCons.Util.CLVar('$CFLAGS') env['SHCCCOM'] = '${TEMPFILE("$SHCC $_MSVC_OUTPUT_FLAG /c $CHANGED_SOURCES $SHCFLAGS $SHCCFLAGS $_CCCOMCOM")}' env['CXX'] = '$CC' env['CXXFLAGS'] = SCons.Util.CLVar('$( /TP $)') env['CXXCOM'] = '${TEMPFILE("$CXX $_MSVC_OUTPUT_FLAG /c $CHANGED_SOURCES $CXXFLAGS $CCFLAGS $_CCCOMCOM")}' env['SHCXX'] = '$CXX' env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS') env['SHCXXCOM'] = '${TEMPFILE("$SHCXX $_MSVC_OUTPUT_FLAG /c $CHANGED_SOURCES $SHCXXFLAGS $SHCCFLAGS $_CCCOMCOM")}' env['CPPDEFPREFIX'] = '/D' env['CPPDEFSUFFIX'] = '' env['INCPREFIX'] = '/I' env['INCSUFFIX'] = '' # env.Append(OBJEMITTER = [static_object_emitter]) # env.Append(SHOBJEMITTER = [shared_object_emitter]) env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME'] = 1 env['RC'] = 'rc' env['RCFLAGS'] = SCons.Util.CLVar('') env['RCSUFFIXES']=['.rc','.rc2'] env['RCCOM'] = '$RC $_CPPDEFFLAGS $_CPPINCFLAGS $RCFLAGS /fo$TARGET $SOURCES' env['BUILDERS']['RES'] = res_builder env['OBJPREFIX'] = '' env['OBJSUFFIX'] = '.obj' env['SHOBJPREFIX'] = '$OBJPREFIX' env['SHOBJSUFFIX'] = '$OBJSUFFIX' # Set-up ms tools paths msvc_setup_env_once(env) env['CFILESUFFIX'] = '.c' env['CXXFILESUFFIX'] = '.cc' env['PCHPDBFLAGS'] = SCons.Util.CLVar(['${(PDB and "/Yd") or ""}']) env['PCHCOM'] = '$CXX /Fo${TARGETS[1]} $CXXFLAGS $CCFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS /c $SOURCES /Yc$PCHSTOP /Fp${TARGETS[0]} $CCPDBFLAGS $PCHPDBFLAGS' env['BUILDERS']['PCH'] = pch_builder if 'ENV' not in env: env['ENV'] = {} if 'SystemRoot' not in env['ENV']: # required for dlls in the winsxs folders env['ENV']['SystemRoot'] = SCons.Platform.win32.get_system_root() def exists(env): return msvc_exists() # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/linkloc.py0000644000175000017500000000766212114661560022232 0ustar dktrkranzdktrkranz"""SCons.Tool.linkloc Tool specification for the LinkLoc linker for the Phar Lap ETS embedded operating system. There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Tool.Tool() selection method. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/linkloc.py 2013/03/03 09:48:35 garyo" import os.path import re import SCons.Action import SCons.Defaults import SCons.Errors import SCons.Tool import SCons.Util from SCons.Tool.MSCommon import msvs_exists, merge_default_version from SCons.Tool.PharLapCommon import addPharLapPaths _re_linker_command = re.compile(r'(\s)@\s*([^\s]+)') def repl_linker_command(m): # Replaces any linker command file directives (e.g. "@foo.lnk") with # the actual contents of the file. try: f=open(m.group(2), "r") return m.group(1) + f.read() except IOError: # the linker should return an error if it can't # find the linker command file so we will remain quiet. # However, we will replace the @ with a # so we will not continue # to find it with recursive substitution return m.group(1) + '#' + m.group(2) class LinklocGenerator(object): def __init__(self, cmdline): self.cmdline = cmdline def __call__(self, env, target, source, for_signature): if for_signature: # Expand the contents of any linker command files recursively subs = 1 strsub = env.subst(self.cmdline, target=target, source=source) while subs: strsub, subs = _re_linker_command.subn(repl_linker_command, strsub) return strsub else: return "${TEMPFILE('" + self.cmdline + "')}" def generate(env): """Add Builders and construction variables for ar to an Environment.""" SCons.Tool.createSharedLibBuilder(env) SCons.Tool.createProgBuilder(env) env['SUBST_CMD_FILE'] = LinklocGenerator env['SHLINK'] = '$LINK' env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS') env['SHLINKCOM'] = '${SUBST_CMD_FILE("$SHLINK $SHLINKFLAGS $_LIBDIRFLAGS $_LIBFLAGS -dll $TARGET $SOURCES")}' env['SHLIBEMITTER']= None env['LINK'] = "linkloc" env['LINKFLAGS'] = SCons.Util.CLVar('') env['LINKCOM'] = '${SUBST_CMD_FILE("$LINK $LINKFLAGS $_LIBDIRFLAGS $_LIBFLAGS -exe $TARGET $SOURCES")}' env['LIBDIRPREFIX']='-libpath ' env['LIBDIRSUFFIX']='' env['LIBLINKPREFIX']='-lib ' env['LIBLINKSUFFIX']='$LIBSUFFIX' # Set-up ms tools paths for default version merge_default_version(env) addPharLapPaths(env) def exists(env): if msvs_exists(): return env.Detect('linkloc') else: return 0 # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/sgicc.xml0000644000175000017500000000065012114661560022025 0ustar dktrkranzdktrkranz Sets construction variables for the SGI C compiler. CXX SHOBJSUFFIX scons-doc-2.3.0/src/engine/SCons/Tool/RCS.xml0000644000175000017500000000363312114661557021376 0ustar dktrkranzdktrkranz Sets construction variables for the interaction with the Revision Control System. RCS RCS_CO RCS_COFLAGS RCS_COCOM RCS_COCOMSTR The RCS executable. Note that this variable is not actually used for the command to fetch source files from RCS; see the &cv-link-RCS_CO; construction variable, below. The RCS "checkout" executable, used to fetch source files from RCS. The command line used to fetch (checkout) source files from RCS. The string displayed when fetching a source file from RCS. If this is not set, then &cv-link-RCS_COCOM; (the command line) is displayed. Options that are passed to the &cv-link-RCS_CO; command. () A factory function that returns a Builder object to be used to fetch source files from RCS. The returned Builder is intended to be passed to the &f-SourceCode; function: This function is deprecated. For details, see the entry for the &f-SourceCode; function. Examples: env.SourceCode('.', env.RCS()) Note that &scons; will fetch source files from RCS subdirectories automatically, so configuring RCS as demonstrated in the above example should only be necessary if you are fetching from RCS,v files in the same directory as the source files, or if you need to explicitly specify RCS for a specific subdirectory. scons-doc-2.3.0/src/engine/SCons/Tool/g77.xml0000644000175000017500000000060112114661560021335 0ustar dktrkranzdktrkranz Set construction variables for the &g77; Fortran compiler. Calls the &t-f77; Tool module to set variables. scons-doc-2.3.0/src/engine/SCons/Tool/gcc.py0000644000175000017500000000551712114661560021330 0ustar dktrkranzdktrkranz"""SCons.Tool.gcc Tool-specific initialization for gcc. There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Tool.Tool() selection method. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/gcc.py 2013/03/03 09:48:35 garyo" import cc import os import re import subprocess import SCons.Util compilers = ['gcc', 'cc'] def generate(env): """Add Builders and construction variables for gcc to an Environment.""" cc.generate(env) env['CC'] = env.Detect(compilers) or 'gcc' if env['PLATFORM'] in ['cygwin', 'win32']: env['SHCCFLAGS'] = SCons.Util.CLVar('$CCFLAGS') else: env['SHCCFLAGS'] = SCons.Util.CLVar('$CCFLAGS -fPIC') # determine compiler version if env['CC']: #pipe = SCons.Action._subproc(env, [env['CC'], '-dumpversion'], pipe = SCons.Action._subproc(env, [env['CC'], '--version'], stdin = 'devnull', stderr = 'devnull', stdout = subprocess.PIPE) if pipe.wait() != 0: return # -dumpversion was added in GCC 3.0. As long as we're supporting # GCC versions older than that, we should use --version and a # regular expression. #line = pipe.stdout.read().strip() #if line: # env['CCVERSION'] = line line = pipe.stdout.readline() match = re.search(r'[0-9]+(\.[0-9]+)+', line) if match: env['CCVERSION'] = match.group(0) def exists(env): return env.Detect(compilers) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/javac.xml0000644000175000017500000001400512114661560022020 0ustar dktrkranzdktrkranz Sets construction variables for the &javac; compiler. JAVAC JAVACFLAGS JAVACCOM JAVACLASSSUFFIX JAVASUFFIX JAVABOOTCLASSPATH JAVACLASSPATH JAVASOURCEPATH JAVACCOMSTR Builds one or more Java class files. The sources may be any combination of explicit .java files, or directory trees which will be scanned for .java files. SCons will parse each source .java file to find the classes (including inner classes) defined within that file, and from that figure out the target .class files that will be created. The class files will be placed underneath the specified target directory. SCons will also search each Java file for the Java package name, which it assumes can be found on a line beginning with the string package in the first column; the resulting .class files will be placed in a directory reflecting the specified package name. For example, the file Foo.java defining a single public Foo class and containing a package name of sub.dir will generate a corresponding sub/dir/Foo.class class file. Examples: env.Java(target = 'classes', source = 'src') env.Java(target = 'classes', source = ['src1', 'src2']) env.Java(target = 'classes', source = ['File1.java', 'File2.java']) Java source files can use the native encoding for the underlying OS. Since SCons compiles in simple ASCII mode by default, the compiler will generate warnings about unmappable characters, which may lead to errors as the file is processed further. In this case, the user must specify the LANG environment variable to tell the compiler what encoding is used. For portibility, it's best if the encoding is hard-coded so that the compile will work if it is done on a system with a different encoding. env = Environment() env['ENV']['LANG'] = 'en_GB.UTF-8' Specifies the list of directories that will be added to the &javac; command line via the option. The individual directory names will be separated by the operating system's path separate character (: on UNIX/Linux/POSIX, ; on Windows). The Java compiler. The command line used to compile a directory tree containing Java source files to corresponding Java class files. Any options specified in the &cv-link-JAVACFLAGS; construction variable are included on this command line. The string displayed when compiling a directory tree of Java source files to corresponding Java class files. If this is not set, then &cv-link-JAVACCOM; (the command line) is displayed. env = Environment(JAVACCOMSTR = "Compiling class files $TARGETS from $SOURCES") General options that are passed to the Java compiler. The directory in which Java class files may be found. This is stripped from the beginning of any Java .class file names supplied to the JavaH builder. Specifies the list of directories that will be searched for Java .class file. The directories in this list will be added to the &javac; and &javah; command lines via the option. The individual directory names will be separated by the operating system's path separate character (: on UNIX/Linux/POSIX, ; on Windows). Note that this currently just adds the specified directory via the option. &SCons; does not currently search the &cv-JAVACLASSPATH; directories for dependency .class files. The suffix for Java class files; .class by default. Specifies the list of directories that will be searched for input .java file. The directories in this list will be added to the &javac; command line via the option. The individual directory names will be separated by the operating system's path separate character (: on UNIX/Linux/POSIX, ; on Windows). Note that this currently just adds the specified directory via the option. &SCons; does not currently search the &cv-JAVASOURCEPATH; directories for dependency .java files. The suffix for Java files; .java by default. Specifies the Java version being used by the &b-Java; builder. This is not currently used to select one version of the Java compiler vs. another. Instead, you should set this to specify the version of Java supported by your &javac; compiler. The default is 1.4. This is sometimes necessary because Java 1.5 changed the file names that are created for nested anonymous inner classes, which can cause a mismatch with the files that &SCons; expects will be generated by the &javac; compiler. Setting &cv-JAVAVERSION; to 1.5 (or 1.6, as appropriate) can make &SCons; realize that a Java 1.5 or 1.6 build is actually up to date. scons-doc-2.3.0/src/engine/SCons/Tool/m4.xml0000644000175000017500000000233512114661560021257 0ustar dktrkranzdktrkranz Sets construction variables for the &m4; macro processor. M4 M4FLAGS M4COM M4COMSTR Builds an output file from an M4 input file. This uses a default &cv-link-M4FLAGS; value of , which considers all warnings to be fatal and stops on the first warning when using the GNU version of m4. Example: env.M4(target = 'foo.c', source = 'foo.c.m4') The M4 macro preprocessor. The command line used to pass files through the M4 macro preprocessor. The string displayed when a file is passed through the M4 macro preprocessor. If this is not set, then &cv-link-M4COM; (the command line) is displayed. General options passed to the M4 macro preprocessor. scons-doc-2.3.0/src/engine/SCons/Tool/SCCS.py0000644000175000017500000000453112114661557021330 0ustar dktrkranzdktrkranz"""SCons.Tool.SCCS.py Tool-specific initialization for SCCS. There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Tool.Tool() selection method. """ # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/SCCS.py 2013/03/03 09:48:35 garyo" import SCons.Action import SCons.Builder import SCons.Util def generate(env): """Add a Builder factory function and construction variables for SCCS to an Environment.""" def SCCSFactory(env=env): """ """ import SCons.Warnings as W W.warn(W.DeprecatedSourceCodeWarning, """The SCCS() factory is deprecated and there is no replacement.""") act = SCons.Action.Action('$SCCSCOM', '$SCCSCOMSTR') return SCons.Builder.Builder(action = act, env = env) #setattr(env, 'SCCS', SCCSFactory) env.SCCS = SCCSFactory env['SCCS'] = 'sccs' env['SCCSFLAGS'] = SCons.Util.CLVar('') env['SCCSGETFLAGS'] = SCons.Util.CLVar('') env['SCCSCOM'] = '$SCCS $SCCSFLAGS get $SCCSGETFLAGS $TARGET' def exists(env): return env.Detect('sccs') # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/ilink.py0000644000175000017500000000414212114661560021673 0ustar dktrkranzdktrkranz"""SCons.Tool.ilink Tool-specific initialization for the OS/2 ilink linker. There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Tool.Tool() selection method. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/ilink.py 2013/03/03 09:48:35 garyo" import SCons.Defaults import SCons.Tool import SCons.Util def generate(env): """Add Builders and construction variables for ilink to an Environment.""" SCons.Tool.createProgBuilder(env) env['LINK'] = 'ilink' env['LINKFLAGS'] = SCons.Util.CLVar('') env['LINKCOM'] = '$LINK $LINKFLAGS /O:$TARGET $SOURCES $_LIBDIRFLAGS $_LIBFLAGS' env['LIBDIRPREFIX']='/LIBPATH:' env['LIBDIRSUFFIX']='' env['LIBLINKPREFIX']='' env['LIBLINKSUFFIX']='$LIBSUFFIX' def exists(env): return env.Detect('ilink') # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/mwcc.xml0000644000175000017500000000157012114661560021670 0ustar dktrkranzdktrkranz Sets construction variables for the Metrowerks CodeWarrior compiler. MWCW_VERSIONS MWCW_VERSION CC CCCOM CXX CXXCOM SHCC SHCCFLAGS SHCFLAGS SHCCCOM SHCXX SHCXXFLAGS SHCXXCOM CFILESUFFIX CXXFILESUFFIX CPPDEFPREFIX CPPDEFSUFFIX INCPREFIX INCSUFFIX CCCOMSTR CXXCOMSTR SHCCCOMSTR SHCXXCOMSTR The version number of the MetroWerks CodeWarrior C compiler to be used. A list of installed versions of the MetroWerks CodeWarrior C compiler on this system. scons-doc-2.3.0/src/engine/SCons/Tool/ar.xml0000644000175000017500000000306212114661560021337 0ustar dktrkranzdktrkranz Sets construction variables for the &ar; library archiver. AR ARFLAGS ARCOM LIBPREFIX LIBSUFFIX RANLIB RANLIBFLAGS RANLIBCOM The static library archiver. The command line used to generate a static library from object files. The string displayed when an object file is generated from an assembly-language source file. If this is not set, then &cv-link-ARCOM; (the command line) is displayed. env = Environment(ARCOMSTR = "Archiving $TARGET") General options passed to the static library archiver. The archive indexer. The command line used to index a static library archive. The string displayed when a static library archive is indexed. If this is not set, then &cv-link-RANLIBCOM; (the command line) is displayed. env = Environment(RANLIBCOMSTR = "Indexing $TARGET") General options passed to the archive indexer. scons-doc-2.3.0/src/engine/SCons/Tool/ilink32.py0000644000175000017500000000407412114661560022044 0ustar dktrkranzdktrkranz"""SCons.Tool.ilink32 XXX """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/ilink32.py 2013/03/03 09:48:35 garyo" import SCons.Tool import SCons.Tool.bcc32 import SCons.Util def generate(env): """Add Builders and construction variables for Borland ilink to an Environment.""" SCons.Tool.createSharedLibBuilder(env) SCons.Tool.createProgBuilder(env) env['LINK'] = '$CC' env['LINKFLAGS'] = SCons.Util.CLVar('') env['LINKCOM'] = '$LINK -q $LINKFLAGS -e$TARGET $SOURCES $LIBS' env['LIBDIRPREFIX']='' env['LIBDIRSUFFIX']='' env['LIBLINKPREFIX']='' env['LIBLINKSUFFIX']='$LIBSUFFIX' def exists(env): # Uses bcc32 to do linking as it generally knows where the standard # LIBS are and set up the linking correctly return SCons.Tool.bcc32.findIt('bcc32', env) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/g++.py0000644000175000017500000000625612114661560021151 0ustar dktrkranzdktrkranz"""SCons.Tool.g++ Tool-specific initialization for g++. There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Tool.Tool() selection method. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/g++.py 2013/03/03 09:48:35 garyo" import os.path import re import subprocess import SCons.Tool import SCons.Util cplusplus = __import__('c++', globals(), locals(), []) compilers = ['g++'] def generate(env): """Add Builders and construction variables for g++ to an Environment.""" static_obj, shared_obj = SCons.Tool.createObjBuilders(env) cplusplus.generate(env) env['CXX'] = env.Detect(compilers) # platform specific settings if env['PLATFORM'] == 'aix': env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS -mminimal-toc') env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME'] = 1 env['SHOBJSUFFIX'] = '$OBJSUFFIX' elif env['PLATFORM'] == 'hpux': env['SHOBJSUFFIX'] = '.pic.o' elif env['PLATFORM'] == 'sunos': env['SHOBJSUFFIX'] = '.pic.o' # determine compiler version if env['CXX']: #pipe = SCons.Action._subproc(env, [env['CXX'], '-dumpversion'], pipe = SCons.Action._subproc(env, [env['CXX'], '--version'], stdin = 'devnull', stderr = 'devnull', stdout = subprocess.PIPE) if pipe.wait() != 0: return # -dumpversion was added in GCC 3.0. As long as we're supporting # GCC versions older than that, we should use --version and a # regular expression. #line = pipe.stdout.read().strip() #if line: # env['CXXVERSION'] = line line = pipe.stdout.readline() match = re.search(r'[0-9]+(\.[0-9]+)+', line) if match: env['CXXVERSION'] = match.group(0) def exists(env): return env.Detect(compilers) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/midl.xml0000644000175000017500000000270712114661560021667 0ustar dktrkranzdktrkranz Sets construction variables for the Microsoft IDL compiler. MIDL MIDLFLAGS MIDLCOM MIDLCOMSTR Builds a Windows type library (.tlb) file from an input IDL file (.idl). In addition, it will build the associated inteface stub and proxy source files, naming them according to the base name of the .idl file. For example, env.TypeLibrary(source="foo.idl") Will create foo.tlb, foo.h, foo_i.c, foo_p.c and foo_data.c files. The Microsoft IDL compiler. The command line used to pass files to the Microsoft IDL compiler. The string displayed when the Microsoft IDL copmiler is called. If this is not set, then &cv-link-MIDLCOM; (the command line) is displayed. General options passed to the Microsoft IDL compiler. scons-doc-2.3.0/src/engine/SCons/Tool/sunf77.xml0000644000175000017500000000063512114661560022071 0ustar dktrkranzdktrkranz Set construction variables for the Sun &f77; Fortran compiler. FORTRAN F77 SHFORTRAN SHF77 SHFORTRANFLAGS SHF77FLAGS scons-doc-2.3.0/src/engine/SCons/Tool/aixc++.py0000644000175000017500000000526712114661557021656 0ustar dktrkranzdktrkranz"""SCons.Tool.aixc++ Tool-specific initialization for IBM xlC / Visual Age C++ compiler. There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Tool.Tool() selection method. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/aixc++.py 2013/03/03 09:48:35 garyo" import os.path import SCons.Platform.aix cplusplus = __import__('c++', globals(), locals(), []) packages = ['vacpp.cmp.core', 'vacpp.cmp.batch', 'vacpp.cmp.C', 'ibmcxx.cmp'] def get_xlc(env): xlc = env.get('CXX', 'xlC') xlc_r = env.get('SHCXX', 'xlC_r') return SCons.Platform.aix.get_xlc(env, xlc, xlc_r, packages) def smart_cxxflags(source, target, env, for_signature): build_dir = env.GetBuildPath() if build_dir: return '-qtempinc=' + os.path.join(build_dir, 'tempinc') return '' def generate(env): """Add Builders and construction variables for xlC / Visual Age suite to an Environment.""" path, _cxx, _shcxx, version = get_xlc(env) if path: _cxx = os.path.join(path, _cxx) _shcxx = os.path.join(path, _shcxx) cplusplus.generate(env) env['CXX'] = _cxx env['SHCXX'] = _shcxx env['CXXVERSION'] = version env['SHOBJSUFFIX'] = '.pic.o' def exists(env): path, _cxx, _shcxx, version = get_xlc(env) if path and _cxx: xlc = os.path.join(path, _cxx) if os.path.exists(xlc): return xlc return None # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/GettextCommon.py0000644000175000017500000004340412114661557023374 0ustar dktrkranzdktrkranz"""SCons.Tool.GettextCommon module Used by several tools of `gettext` toolset. """ # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/GettextCommon.py 2013/03/03 09:48:35 garyo" import SCons.Warnings import re ############################################################################# class XgettextToolWarning(SCons.Warnings.Warning): pass class XgettextNotFound(XgettextToolWarning): pass class MsginitToolWarning(SCons.Warnings.Warning): pass class MsginitNotFound(MsginitToolWarning): pass class MsgmergeToolWarning(SCons.Warnings.Warning): pass class MsgmergeNotFound(MsgmergeToolWarning): pass class MsgfmtToolWarning(SCons.Warnings.Warning): pass class MsgfmtNotFound(MsgfmtToolWarning): pass ############################################################################# SCons.Warnings.enableWarningClass(XgettextToolWarning) SCons.Warnings.enableWarningClass(XgettextNotFound) SCons.Warnings.enableWarningClass(MsginitToolWarning) SCons.Warnings.enableWarningClass(MsginitNotFound) SCons.Warnings.enableWarningClass(MsgmergeToolWarning) SCons.Warnings.enableWarningClass(MsgmergeNotFound) SCons.Warnings.enableWarningClass(MsgfmtToolWarning) SCons.Warnings.enableWarningClass(MsgfmtNotFound) ############################################################################# ############################################################################# class _POTargetFactory(object): """ A factory of `PO` target files. Factory defaults differ from these of `SCons.Node.FS.FS`. We set `precious` (this is required by builders and actions gettext) and `noclean` flags by default for all produced nodes. """ def __init__( self, env, nodefault = True, alias = None, precious = True , noclean = True ): """ Object constructor. **Arguments** - *env* (`SCons.Environment.Environment`) - *nodefault* (`boolean`) - if `True`, produced nodes will be ignored from default target `'.'` - *alias* (`string`) - if provided, produced nodes will be automatically added to this alias, and alias will be set as `AlwaysBuild` - *precious* (`boolean`) - if `True`, the produced nodes will be set as `Precious`. - *noclen* (`boolean`) - if `True`, the produced nodes will be excluded from `Clean`. """ self.env = env self.alias = alias self.precious = precious self.noclean = noclean self.nodefault = nodefault def _create_node(self, name, factory, directory = None, create = 1): """ Create node, and set it up to factory settings. """ import SCons.Util node = factory(name, directory, create) node.set_noclean(self.noclean) node.set_precious(self.precious) if self.nodefault: self.env.Ignore('.', node) if self.alias: self.env.AlwaysBuild(self.env.Alias(self.alias, node)) return node def Entry(self, name, directory = None, create = 1): """ Create `SCons.Node.FS.Entry` """ return self._create_node(name, self.env.fs.Entry, directory, create) def File(self, name, directory = None, create = 1): """ Create `SCons.Node.FS.File` """ return self._create_node(name, self.env.fs.File, directory, create) ############################################################################# ############################################################################# _re_comment = re.compile(r'(#[^\n\r]+)$', re.M) _re_lang = re.compile(r'([a-zA-Z0-9_]+)', re.M) ############################################################################# def _read_linguas_from_files(env, linguas_files = None): """ Parse `LINGUAS` file and return list of extracted languages """ import SCons.Util import SCons.Environment global _re_comment global _re_lang if not SCons.Util.is_List(linguas_files) \ and not SCons.Util.is_String(linguas_files) \ and not isinstance(linguas_files, SCons.Node.FS.Base) \ and linguas_files: # If, linguas_files==True or such, then read 'LINGUAS' file. linguas_files = [ 'LINGUAS' ] if linguas_files is None: return [] fnodes = env.arg2nodes(linguas_files) linguas = [] for fnode in fnodes: contents = _re_comment.sub("", fnode.get_text_contents()) ls = [ l for l in _re_lang.findall(contents) if l ] linguas.extend(ls) return linguas ############################################################################# ############################################################################# from SCons.Builder import BuilderBase ############################################################################# class _POFileBuilder(BuilderBase): """ `PO` file builder. This is multi-target single-source builder. In typical situation the source is single `POT` file, e.g. `messages.pot`, and there are multiple `PO` targets to be updated from this `POT`. We must run `SCons.Builder.BuilderBase._execute()` separatelly for each target to track dependencies separatelly for each target file. **NOTE**: if we call `SCons.Builder.BuilderBase._execute(.., target, ...)` with target being list of all targets, all targets would be rebuilt each time one of the targets from this list is missing. This would happen, for example, when new language `ll` enters `LINGUAS_FILE` (at this moment there is no `ll.po` file yet). To avoid this, we override `SCons.Builder.BuilerBase._execute()` and call it separatelly for each target. Here we also append to the target list the languages read from `LINGUAS_FILE`. """ # #* The argument for overriding _execute(): We must use environment with # builder overrides applied (see BuilderBase.__init__(). Here it comes for # free. #* The argument against using 'emitter': The emitter is called too late # by BuilderBase._execute(). If user calls, for example: # # env.POUpdate(LINGUAS_FILE = 'LINGUAS') # # the builder throws error, because it is called with target=None, # source=None and is trying to "generate" sources or target list first. # If user calls # # env.POUpdate(['foo', 'baz'], LINGUAS_FILE = 'LINGUAS') # # the env.BuilderWrapper() calls our builder with target=None, # source=['foo', 'baz']. The BuilderBase._execute() then splits execution # and execute iterativelly (recursion) self._execute(None, source[i]). # After that it calls emitter (which is quite too late). The emitter is # also called in each iteration, what makes things yet worse. def __init__(self, env, **kw): if not 'suffix' in kw: kw['suffix'] = '$POSUFFIX' if not 'src_suffix' in kw: kw['src_suffix'] = '$POTSUFFIX' if not 'src_builder' in kw: kw['src_builder'] = '_POTUpdateBuilder' if not 'single_source' in kw: kw['single_source'] = True alias = None if 'target_alias' in kw: alias = kw['target_alias'] del kw['target_alias'] if not 'target_factory' in kw: kw['target_factory'] = _POTargetFactory(env, alias=alias).File BuilderBase.__init__(self, **kw) def _execute(self, env, target, source, *args, **kw): """ Execute builder's actions. Here we append to `target` the languages read from `$LINGUAS_FILE` and apply `SCons.Builder.BuilderBase._execute()` separatelly to each target. The arguments and return value are same as for `SCons.Builder.BuilderBase._execute()`. """ import SCons.Util import SCons.Node linguas_files = None if env.has_key('LINGUAS_FILE') and env['LINGUAS_FILE']: linguas_files = env['LINGUAS_FILE'] # This prevents endless recursion loop (we'll be invoked once for # each target appended here, we must not extend the list again). env['LINGUAS_FILE'] = None linguas = _read_linguas_from_files(env,linguas_files) if SCons.Util.is_List(target): target.extend(linguas) elif target is not None: target = [target] + linguas else: target = linguas if not target: # Let the SCons.BuilderBase to handle this patologic situation return BuilderBase._execute( self, env, target, source, *args, **kw) # The rest is ours if not SCons.Util.is_List(target): target = [ target ] result = [] for tgt in target: r = BuilderBase._execute( self, env, [tgt], source, *args, **kw) result.extend(r) if linguas_files is not None: env['LINGUAS_FILE'] = linguas_files return SCons.Node.NodeList(result) ############################################################################# import SCons.Environment ############################################################################# def _translate(env, target=None, source=SCons.Environment._null, *args, **kw): """ Function for `Translate()` pseudo-builder """ if target is None: target = [] pot = env.POTUpdate(None, source, *args, **kw) po = env.POUpdate(target, pot, *args, **kw) return po ############################################################################# ############################################################################# class RPaths(object): """ Callable object, which returns pathnames relative to SCons current working directory. It seems like `SCons.Node.FS.Base.get_path()` returns absolute paths for nodes that are outside of current working directory (`env.fs.getcwd()`). Here, we often have `SConscript`, `POT` and `PO` files within `po/` directory and source files (e.g. `*.c`) outside of it. When generating `POT` template file, references to source files are written to `POT` template, so a translator may later quickly jump to appropriate source file and line from its `PO` editor (e.g. `poedit`). Relative paths in `PO` file are usually interpreted by `PO` editor as paths relative to the place, where `PO` file lives. The absolute paths would make resultant `POT` file nonportable, as the references would be correct only on the machine, where `POT` file was recently re-created. For such reason, we need a function, which always returns relative paths. This is the purpose of `RPaths` callable object. The `__call__` method returns paths relative to current woking directory, but we assume, that *xgettext(1)* is run from the directory, where target file is going to be created. Note, that this may not work for files distributed over several hosts or across different drives on windows. We assume here, that single local filesystem holds both source files and target `POT` templates. Intended use of `RPaths` - in `xgettext.py`:: def generate(env): from GettextCommon import RPaths ... sources = '$( ${_concat( "", SOURCES, "", __env__, XgettextRPaths, TARGET, SOURCES)} $)' env.Append( ... XGETTEXTCOM = 'XGETTEXT ... ' + sources, ... XgettextRPaths = RPaths(env) ) """ # NOTE: This callable object returns pathnames of dirs/files relative to # current working directory. The pathname remains relative also for entries # that are outside of current working directory (node, that # SCons.Node.FS.File and siblings return absolute path in such case). For # simplicity we compute path relative to current working directory, this # seems be enough for our purposes (don't need TARGET variable and # SCons.Defaults.Variable_Caller stuff). def __init__(self, env): """ Initialize `RPaths` callable object. **Arguments**: - *env* - a `SCons.Environment.Environment` object, defines *current working dir*. """ self.env = env # FIXME: I'm not sure, how it should be implemented (what the *args are in # general, what is **kw). def __call__(self, nodes, *args, **kw): """ Return nodes' paths (strings) relative to current working directory. **Arguments**: - *nodes* ([`SCons.Node.FS.Base`]) - list of nodes. - *args* - currently unused. - *kw* - currently unused. **Returns**: - Tuple of strings, which represent paths relative to current working directory (for given environment). """ # os.path.relpath is available only on python >= 2.6. We use our own # implementation. It's taken from BareNecessities package: # http://jimmyg.org/work/code/barenecessities/index.html from posixpath import curdir def relpath(path, start=curdir): import posixpath """Return a relative version of a path""" if not path: raise ValueError("no path specified") start_list = posixpath.abspath(start).split(posixpath.sep) path_list = posixpath.abspath(path).split(posixpath.sep) # Work out how much of the filepath is shared by start and path. i = len(posixpath.commonprefix([start_list, path_list])) rel_list = [posixpath.pardir] * (len(start_list)-i) + path_list[i:] if not rel_list: return posixpath.curdir return posixpath.join(*rel_list) import os import SCons.Node.FS rpaths = () cwd = self.env.fs.getcwd().get_abspath() for node in nodes: rpath = None if isinstance(node, SCons.Node.FS.Base): rpath = relpath(node.get_abspath(), cwd) # FIXME: Other types possible here? if rpath is not None: rpaths += (rpath,) return rpaths ############################################################################# ############################################################################# def _init_po_files(target, source, env): """ Action function for `POInit` builder. """ nop = lambda target, source, env : 0 if env.has_key('POAUTOINIT'): autoinit = env['POAUTOINIT'] else: autoinit = False # Well, if everything outside works well, this loop should do single # iteration. Otherwise we are rebuilding all the targets even, if just # one has changed (but is this out fault?). for tgt in target: if not tgt.exists(): if autoinit: action = SCons.Action.Action('$MSGINITCOM', '$MSGINITCOMSTR') else: msg = 'File ' + repr(str(tgt)) + ' does not exist. ' \ + 'If you are a translator, you can create it through: \n' \ + '$MSGINITCOM' action = SCons.Action.Action(nop, msg) status = action([tgt], source, env) if status: return status return 0 ############################################################################# ############################################################################# def _detect_xgettext(env): """ Detects *xgettext(1)* binary """ if env.has_key('XGETTEXT'): return env['XGETTEXT'] xgettext = env.Detect('xgettext'); if xgettext: return xgettext raise SCons.Errors.StopError(XgettextNotFound,"Could not detect xgettext") return None ############################################################################# def _xgettext_exists(env): return _detect_xgettext(env) ############################################################################# ############################################################################# def _detect_msginit(env): """ Detects *msginit(1)* program. """ if env.has_key('MSGINIT'): return env['MSGINIT'] msginit = env.Detect('msginit'); if msginit: return msginit raise SCons.Errors.StopError(MsginitNotFound, "Could not detect msginit") return None ############################################################################# def _msginit_exists(env): return _detect_msginit(env) ############################################################################# ############################################################################# def _detect_msgmerge(env): """ Detects *msgmerge(1)* program. """ if env.has_key('MSGMERGE'): return env['MSGMERGE'] msgmerge = env.Detect('msgmerge'); if msgmerge: return msgmerge raise SCons.Errors.StopError(MsgmergeNotFound, "Could not detect msgmerge") return None ############################################################################# def _msgmerge_exists(env): return _detect_msgmerge(env) ############################################################################# ############################################################################# def _detect_msgfmt(env): """ Detects *msgmfmt(1)* program. """ if env.has_key('MSGFMT'): return env['MSGFMT'] msgfmt = env.Detect('msgfmt'); if msgfmt: return msgfmt raise SCons.Errors.StopError(MsgfmtNotFound, "Could not detect msgfmt") return None ############################################################################# def _msgfmt_exists(env): return _detect_msgfmt(env) ############################################################################# ############################################################################# def tool_list(platform, env): """ List tools that shall be generated by top-level `gettext` tool """ return [ 'xgettext', 'msginit', 'msgmerge', 'msgfmt' ] ############################################################################# scons-doc-2.3.0/src/engine/SCons/Tool/sunf95.py0000644000175000017500000000420612114661560021717 0ustar dktrkranzdktrkranz"""SCons.Tool.sunf95 Tool-specific initialization for sunf95, the Sun Studio F95 compiler. There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Tool.Tool() selection method. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/sunf95.py 2013/03/03 09:48:35 garyo" import SCons.Util from FortranCommon import add_all_to_env compilers = ['sunf95', 'f95'] def generate(env): """Add Builders and construction variables for sunf95 to an Environment.""" add_all_to_env(env) fcomp = env.Detect(compilers) or 'f95' env['FORTRAN'] = fcomp env['F95'] = fcomp env['SHFORTRAN'] = '$FORTRAN' env['SHF95'] = '$F95' env['SHFORTRANFLAGS'] = SCons.Util.CLVar('$FORTRANFLAGS -KPIC') env['SHF95FLAGS'] = SCons.Util.CLVar('$F95FLAGS -KPIC') def exists(env): return env.Detect(compilers) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/m4.py0000644000175000017500000000450612114661560021111 0ustar dktrkranzdktrkranz"""SCons.Tool.m4 Tool-specific initialization for m4. There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Tool.Tool() selection method. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/m4.py 2013/03/03 09:48:35 garyo" import SCons.Action import SCons.Builder import SCons.Util def generate(env): """Add Builders and construction variables for m4 to an Environment.""" M4Action = SCons.Action.Action('$M4COM', '$M4COMSTR') bld = SCons.Builder.Builder(action = M4Action, src_suffix = '.m4') env['BUILDERS']['M4'] = bld # .m4 files might include other files, and it would be pretty hard # to write a scanner for it, so let's just cd to the dir of the m4 # file and run from there. # The src_suffix setup is like so: file.c.m4 -> file.c, # file.cpp.m4 -> file.cpp etc. env['M4'] = 'm4' env['M4FLAGS'] = SCons.Util.CLVar('-E') env['M4COM'] = 'cd ${SOURCE.rsrcdir} && $M4 $M4FLAGS < ${SOURCE.file} > ${TARGET.abspath}' def exists(env): return env.Detect('m4') # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/BitKeeper.xml0000644000175000017500000000310412114661557022612 0ustar dktrkranzdktrkranz Sets construction variables for the BitKeeper source code control system. BITKEEPER BITKEEPERGET BITKEEPERGETFLAGS BITKEEPERCOM BITKEEPERCOMSTR The BitKeeper executable. The command line for fetching source files using BitKeeper. The string displayed when fetching a source file using BitKeeper. If this is not set, then &cv-link-BITKEEPERCOM; (the command line) is displayed. The command (&cv-link-BITKEEPER;) and subcommand for fetching source files using BitKeeper. Options that are passed to the BitKeeper get subcommand. () A factory function that returns a Builder object to be used to fetch source files using BitKeeper. The returned Builder is intended to be passed to the &f-SourceCode; function. This function is deprecated. For details, see the entry for the &f-SourceCode; function. Example: env.SourceCode('.', env.BitKeeper()) scons-doc-2.3.0/src/engine/SCons/Tool/filesystem.py0000644000175000017500000000664512114661560022763 0ustar dktrkranzdktrkranz"""SCons.Tool.filesystem Tool-specific initialization for the filesystem tools. There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Tool.Tool() selection method. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/filesystem.py 2013/03/03 09:48:35 garyo" import SCons from SCons.Tool.install import copyFunc copyToBuilder, copyAsBuilder = None, None def copyto_emitter(target, source, env): """ changes the path of the source to be under the target (which are assumed to be directories. """ n_target = [] for t in target: n_target = n_target + [t.File( str( s ) ) for s in source] return (n_target, source) def copy_action_func(target, source, env): assert( len(target) == len(source) ), "\ntarget: %s\nsource: %s" %(list(map(str, target)),list(map(str, source))) for t, s in zip(target, source): if copyFunc(t.get_path(), s.get_path(), env): return 1 return 0 def copy_action_str(target, source, env): return env.subst_target_source(env['COPYSTR'], 0, target, source) copy_action = SCons.Action.Action( copy_action_func, copy_action_str ) def generate(env): try: env['BUILDERS']['CopyTo'] env['BUILDERS']['CopyAs'] except KeyError, e: global copyToBuilder if copyToBuilder is None: copyToBuilder = SCons.Builder.Builder( action = copy_action, target_factory = env.fs.Dir, source_factory = env.fs.Entry, multi = 1, emitter = [ copyto_emitter, ] ) global copyAsBuilder if copyAsBuilder is None: copyAsBuilder = SCons.Builder.Builder( action = copy_action, target_factory = env.fs.Entry, source_factory = env.fs.Entry ) env['BUILDERS']['CopyTo'] = copyToBuilder env['BUILDERS']['CopyAs'] = copyAsBuilder env['COPYSTR'] = 'Copy file(s): "$SOURCES" to "$TARGETS"' def exists(env): return 1 # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/default.xml0000644000175000017500000000057712114661560022371 0ustar dktrkranzdktrkranz Sets variables by calling a default list of Tool modules for the platform on which SCons is running. scons-doc-2.3.0/src/engine/SCons/Tool/msvc.xml0000644000175000017500000002277112114661560021715 0ustar dktrkranzdktrkranz Sets construction variables for the Microsoft Visual C/C++ compiler. CCPDBFLAGS CCPCHFLAGS CC CCFLAGS CFLAGS CCCOM SHCC SHCCFLAGS SHCFLAGS SHCCCOM CXX CXXFLAGS CXXCOM SHCXX SHCXXFLAGS SHCXXCOM CPPDEFPREFIX CPPDEFSUFFIX INCPREFIX INCSUFFIX RC RCFLAGS RCCOM BUILDERS OBJPREFIX OBJSUFFIX SHOBJPREFIX SHOBJSUFFIX CFILESUFFIX CXXFILESUFFIX PCHPDBFLAGS PCHCOM CCCOMSTR SHCCCOMSTR CXXCOMSTR SHCXXCOMSTR PCH PCHSTOP PDB Builds a Microsoft Visual C++ precompiled header. Calling this builder method returns a list of two targets: the PCH as the first element, and the object file as the second element. Normally the object file is ignored. This builder method is only provided when Microsoft Visual C++ is being used as the compiler. The PCH builder method is generally used in conjuction with the PCH construction variable to force object files to use the precompiled header: env['PCH'] = env.PCH('StdAfx.cpp')[0] Builds a Microsoft Visual C++ resource file. This builder method is only provided when Microsoft Visual C++ or MinGW is being used as the compiler. The .res (or .o for MinGW) suffix is added to the target name if no other suffix is given. The source file is scanned for implicit dependencies as though it were a C file. Example: env.RES('resource.rc') Options added to the compiler command line to support building with precompiled headers. The default value expands expands to the appropriate Microsoft Visual C++ command-line options when the &cv-link-PCH; construction variable is set. Options added to the compiler command line to support storing debugging information in a Microsoft Visual C++ PDB file. The default value expands expands to appropriate Microsoft Visual C++ command-line options when the &cv-link-PDB; construction variable is set. The Visual C++ compiler option that SCons uses by default to generate PDB information is . This works correctly with parallel () builds because it embeds the debug information in the intermediate object files, as opposed to sharing a single PDB file between multiple object files. This is also the only way to get debug information embedded into a static library. Using the instead may yield improved link-time performance, although parallel builds will no longer work. You can generate PDB files with the switch by overriding the default &cv-link-CCPDBFLAGS; variable as follows: env['CCPDBFLAGS'] = ['${(PDB and "/Zi /Fd%s" % File(PDB)) or ""}'] An alternative would be to use the to put the debugging information in a separate .pdb file for each object file by overriding the &cv-link-CCPDBFLAGS; variable as follows: env['CCPDBFLAGS'] = '/Zi /Fd${TARGET}.pdb' When set to any true value, specifies that SCons should batch compilation of object files when calling the Microsoft Visual C/C++ compiler. All compilations of source files from the same source directory that generate target files in a same output directory and were configured in SCons using the same construction environment will be built in a single call to the compiler. Only source files that have changed since their object files were built will be passed to each compiler invocation (via the &cv-link-CHANGED_SOURCES; construction variable). Any compilations where the object (target) file base name (minus the .obj) does not match the source file base name will be compiled separately. The Microsoft Visual C++ precompiled header that will be used when compiling object files. This variable is ignored by tools other than Microsoft Visual C++. When this variable is defined SCons will add options to the compiler command line to cause it to use the precompiled header, and will also set up the dependencies for the PCH file. Example: env['PCH'] = 'StdAfx.pch' The command line used by the &b-PCH; builder to generated a precompiled header. The string displayed when generating a precompiled header. If this is not set, then &cv-link-PCHCOM; (the command line) is displayed. A construction variable that, when expanded, adds the /yD flag to the command line only if the &cv-PDB; construction variable is set. This variable specifies how much of a source file is precompiled. This variable is ignored by tools other than Microsoft Visual C++, or when the PCH variable is not being used. When this variable is define it must be a string that is the name of the header that is included at the end of the precompiled portion of the source files, or the empty string if the "#pragma hrdstop" construct is being used: env['PCHSTOP'] = 'StdAfx.h' The resource compiler used to build a Microsoft Visual C++ resource file. The command line used to build a Microsoft Visual C++ resource file. The string displayed when invoking the resource compiler to build a Microsoft Visual C++ resource file. If this is not set, then &cv-link-RCCOM; (the command line) is displayed. The flags passed to the resource compiler by the RES builder. An automatically-generated construction variable containing the command-line options for specifying directories to be searched by the resource compiler. The value of &cv-RCINCFLAGS; is created by appending &cv-RCINCPREFIX; and &cv-RCINCSUFFIX; to the beginning and end of each directory in &cv-CPPPATH;. The prefix (flag) used to specify an include directory on the resource compiler command line. This will be appended to the beginning of each directory in the &cv-CPPPATH; construction variable when the &cv-RCINCFLAGS; variable is expanded. The suffix used to specify an include directory on the resource compiler command line. This will be appended to the end of each directory in the &cv-CPPPATH; construction variable when the &cv-RCINCFLAGS; variable is expanded. Sets the preferred version of Microsoft Visual C/C++ to use. If &cv-MSVC_VERSION; is not set, SCons will (by default) select the latest version of Visual C/C++ installed on your system. If the specified version isn't installed, tool initialization will fail. This variable must be passed as an argument to the Environment() constructor; setting it later has no effect. Set it to an unexpected value (e.g. "XXX") to see the valid values on your system. Use a batch script to set up Microsoft Visual Studio compiler &cv-MSVC_USE_SCRIPT; overrides &cv-MSVC_VERSION; and &cv-TARGET_ARCH;. If set to the name of a Visual Studio .bat file (e.g. vcvars.bat), SCons will run that bat file and extract the relevant variables from the result (typically %INCLUDE%, %LIB%, and %PATH%). Setting MSVC_USE_SCRIPT to None bypasses the Visual Studio autodetection entirely; use this if you are running SCons in a Visual Studio cmd window and importing the shell's environment variables. Sets the host architecture for Visual Studio compiler. If not set, default to the detected host architecture: note that this may depend on the python you are using. This variable must be passed as an argument to the Environment() constructor; setting it later has no effect. Valid values are the same as for &cv-TARGET_ARCH;. This is currently only used on Windows, but in the future it will be used on other OSes as well. Sets the target architecture for Visual Studio compiler (i.e. the arch of the binaries generated by the compiler). If not set, default to &cv-HOST_ARCH;, or, if that is unset, to the architecture of the running machine's OS (note that the python build or architecture has no effect). This variable must be passed as an argument to the Environment() constructor; setting it later has no effect. This is currently only used on Windows, but in the future it will be used on other OSes as well. Valid values for Windows are x86, i386 (for 32 bits); amd64, emt64, x86_64 (for 64 bits); and ia64 (Itanium). For example, if you want to compile 64-bit binaries, you would set TARGET_ARCH='x86_64' in your SCons environment. scons-doc-2.3.0/src/engine/SCons/Tool/__init__.py0000644000175000017500000007631012114661557022340 0ustar dktrkranzdktrkranz"""SCons.Tool SCons tool selection. This looks for modules that define a callable object that can modify a construction environment as appropriate for a given tool (or tool chain). Note that because this subsystem just *selects* a callable that can modify a construction environment, it's possible for people to define their own "tool specification" in an arbitrary callable function. No one needs to use or tie in to this subsystem in order to roll their own tool definition. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/__init__.py 2013/03/03 09:48:35 garyo" import imp import sys import re import os import shutil import SCons.Builder import SCons.Errors import SCons.Node.FS import SCons.Scanner import SCons.Scanner.C import SCons.Scanner.D import SCons.Scanner.LaTeX import SCons.Scanner.Prog DefaultToolpath=[] CScanner = SCons.Scanner.C.CScanner() DScanner = SCons.Scanner.D.DScanner() LaTeXScanner = SCons.Scanner.LaTeX.LaTeXScanner() PDFLaTeXScanner = SCons.Scanner.LaTeX.PDFLaTeXScanner() ProgramScanner = SCons.Scanner.Prog.ProgramScanner() SourceFileScanner = SCons.Scanner.Base({}, name='SourceFileScanner') CSuffixes = [".c", ".C", ".cxx", ".cpp", ".c++", ".cc", ".h", ".H", ".hxx", ".hpp", ".hh", ".F", ".fpp", ".FPP", ".m", ".mm", ".S", ".spp", ".SPP", ".sx"] DSuffixes = ['.d'] IDLSuffixes = [".idl", ".IDL"] LaTeXSuffixes = [".tex", ".ltx", ".latex"] for suffix in CSuffixes: SourceFileScanner.add_scanner(suffix, CScanner) for suffix in DSuffixes: SourceFileScanner.add_scanner(suffix, DScanner) # FIXME: what should be done here? Two scanners scan the same extensions, # but look for different files, e.g., "picture.eps" vs. "picture.pdf". # The builders for DVI and PDF explicitly reference their scanners # I think that means this is not needed??? for suffix in LaTeXSuffixes: SourceFileScanner.add_scanner(suffix, LaTeXScanner) SourceFileScanner.add_scanner(suffix, PDFLaTeXScanner) class Tool(object): def __init__(self, name, toolpath=[], **kw): self.name = name self.toolpath = toolpath + DefaultToolpath # remember these so we can merge them into the call self.init_kw = kw module = self._tool_module() self.generate = module.generate self.exists = module.exists if hasattr(module, 'options'): self.options = module.options def _tool_module(self): # TODO: Interchange zipimport with normal initilization for better error reporting oldpythonpath = sys.path sys.path = self.toolpath + sys.path try: try: file, path, desc = imp.find_module(self.name, self.toolpath) try: return imp.load_module(self.name, file, path, desc) finally: if file: file.close() except ImportError, e: if str(e)!="No module named %s"%self.name: raise SCons.Errors.EnvironmentError(e) try: import zipimport except ImportError: pass else: for aPath in self.toolpath: try: importer = zipimport.zipimporter(aPath) return importer.load_module(self.name) except ImportError, e: pass finally: sys.path = oldpythonpath full_name = 'SCons.Tool.' + self.name try: return sys.modules[full_name] except KeyError: try: smpath = sys.modules['SCons.Tool'].__path__ try: file, path, desc = imp.find_module(self.name, smpath) module = imp.load_module(full_name, file, path, desc) setattr(SCons.Tool, self.name, module) if file: file.close() return module except ImportError, e: if str(e)!="No module named %s"%self.name: raise SCons.Errors.EnvironmentError(e) try: import zipimport importer = zipimport.zipimporter( sys.modules['SCons.Tool'].__path__[0] ) module = importer.load_module(full_name) setattr(SCons.Tool, self.name, module) return module except ImportError, e: m = "No tool named '%s': %s" % (self.name, e) raise SCons.Errors.EnvironmentError(m) except ImportError, e: m = "No tool named '%s': %s" % (self.name, e) raise SCons.Errors.EnvironmentError(m) def __call__(self, env, *args, **kw): if self.init_kw is not None: # Merge call kws into init kws; # but don't bash self.init_kw. if kw is not None: call_kw = kw kw = self.init_kw.copy() kw.update(call_kw) else: kw = self.init_kw env.Append(TOOLS = [ self.name ]) if hasattr(self, 'options'): import SCons.Variables if 'options' not in env: from SCons.Script import ARGUMENTS env['options']=SCons.Variables.Variables(args=ARGUMENTS) opts=env['options'] self.options(opts) opts.Update(env) self.generate(env, *args, **kw) def __str__(self): return self.name ########################################################################## # Create common executable program / library / object builders def createProgBuilder(env): """This is a utility function that creates the Program Builder in an Environment if it is not there already. If it is already there, we return the existing one. """ try: program = env['BUILDERS']['Program'] except KeyError: import SCons.Defaults program = SCons.Builder.Builder(action = SCons.Defaults.LinkAction, emitter = '$PROGEMITTER', prefix = '$PROGPREFIX', suffix = '$PROGSUFFIX', src_suffix = '$OBJSUFFIX', src_builder = 'Object', target_scanner = ProgramScanner) env['BUILDERS']['Program'] = program return program def createStaticLibBuilder(env): """This is a utility function that creates the StaticLibrary Builder in an Environment if it is not there already. If it is already there, we return the existing one. """ try: static_lib = env['BUILDERS']['StaticLibrary'] except KeyError: action_list = [ SCons.Action.Action("$ARCOM", "$ARCOMSTR") ] if env.Detect('ranlib'): ranlib_action = SCons.Action.Action("$RANLIBCOM", "$RANLIBCOMSTR") action_list.append(ranlib_action) static_lib = SCons.Builder.Builder(action = action_list, emitter = '$LIBEMITTER', prefix = '$LIBPREFIX', suffix = '$LIBSUFFIX', src_suffix = '$OBJSUFFIX', src_builder = 'StaticObject') env['BUILDERS']['StaticLibrary'] = static_lib env['BUILDERS']['Library'] = static_lib return static_lib def VersionShLibLinkNames(version, libname, env): """Generate names of symlinks to the versioned shared library""" Verbose = False platform = env.subst('$PLATFORM') shlib_suffix = env.subst('$SHLIBSUFFIX') shlink_flags = SCons.Util.CLVar(env.subst('$SHLINKFLAGS')) linknames = [] if version.count(".") != 2: # We need a version string of the form x.y.z to proceed # Several changes need to be made to support versions like x.y raise ValueError if platform == 'darwin': # For libfoo.x.y.z.dylib, linknames libfoo.so suffix_re = re.escape('.' + version + shlib_suffix) linkname = re.sub(suffix_re, shlib_suffix, libname) if Verbose: print "VersionShLibLinkNames: linkname = ",linkname linknames.append(linkname) elif platform == 'posix': # For libfoo.so.x.y.z, linknames libfoo.so libfoo.so.x.y libfoo.so.x suffix_re = re.escape(shlib_suffix + '.' + version) # First linkname has no version number linkname = re.sub(suffix_re, shlib_suffix, libname) if Verbose: print "VersionShLibLinkNames: linkname = ",linkname linknames.append(linkname) versionparts = version.split('.') major_name = linkname + "." + versionparts[0] minor_name = major_name + "." + versionparts[1] #Only add link for major_name #for linkname in [major_name, minor_name]: for linkname in [major_name, ]: if Verbose: print "VersionShLibLinkNames: linkname ",linkname, ", target ",libname linknames.append(linkname) # note: no Windows case here (win32 or cygwin); # MSVC doesn't support this type of versioned shared libs. # (could probably do something for MinGW though) return linknames def VersionedSharedLibrary(target = None, source= None, env=None): """Build a shared library. If the environment has SHLIBVERSION defined make a versioned shared library and create the appropriate symlinks for the platform we are on""" Verbose = False try: version = env.subst('$SHLIBVERSION') except KeyError: version = None # libname includes the version number if one was given libname = target[0].name platform = env.subst('$PLATFORM') shlib_suffix = env.subst('$SHLIBSUFFIX') shlink_flags = SCons.Util.CLVar(env.subst('$SHLINKFLAGS')) if Verbose: print "VersionShLib: libname = ",libname print "VersionShLib: platform = ",platform print "VersionShLib: shlib_suffix = ",shlib_suffix print "VersionShLib: target = ",str(target[0]) if version: # set the shared library link flags if platform == 'posix': suffix_re = re.escape(shlib_suffix + '.' + version) (major, age, revision) = version.split(".") # soname will have only the major version number in it soname = re.sub(suffix_re, shlib_suffix, libname) + '.' + major shlink_flags += [ '-Wl,-Bsymbolic', '-Wl,-soname=%s' % soname ] if Verbose: print " soname ",soname,", shlink_flags ",shlink_flags elif platform == 'cygwin': shlink_flags += [ '-Wl,-Bsymbolic', '-Wl,--out-implib,${TARGET.base}.a' ] elif platform == 'darwin': shlink_flags += [ '-current_version', '%s' % version, '-compatibility_version', '%s' % version, '-undefined', 'dynamic_lookup' ] if Verbose: print "VersionShLib: shlink_flags = ",shlink_flags envlink = env.Clone() envlink['SHLINKFLAGS'] = shlink_flags else: envlink = env result = SCons.Defaults.ShLinkAction(target, source, envlink) if version: # here we need the full pathname so the links end up in the right directory libname = target[0].path linknames = VersionShLibLinkNames(version, libname, env) if Verbose: print "VerShLib: linknames ",linknames # Here we just need the file name w/o path as the target of the link lib_ver = target[0].name # make symlink of adjacent names in linknames for count in range(len(linknames)): linkname = linknames[count] if count > 0: os.symlink(os.path.basename(linkname),lastname) if Verbose: print "VerShLib: made sym link of %s -> %s" % (lastname,linkname) lastname = linkname # finish chain of sym links with link to the actual library if len(linknames)>0: os.symlink(lib_ver,lastname) if Verbose: print "VerShLib: made sym link of %s -> %s" % (lib_ver,linkname) return result ShLibAction = SCons.Action.Action(VersionedSharedLibrary, None) def createSharedLibBuilder(env): """This is a utility function that creates the SharedLibrary Builder in an Environment if it is not there already. If it is already there, we return the existing one. """ try: shared_lib = env['BUILDERS']['SharedLibrary'] except KeyError: import SCons.Defaults action_list = [ SCons.Defaults.SharedCheck, ShLibAction ] shared_lib = SCons.Builder.Builder(action = action_list, emitter = "$SHLIBEMITTER", prefix = '$SHLIBPREFIX', suffix = '$SHLIBSUFFIX', target_scanner = ProgramScanner, src_suffix = '$SHOBJSUFFIX', src_builder = 'SharedObject') env['BUILDERS']['SharedLibrary'] = shared_lib return shared_lib def createLoadableModuleBuilder(env): """This is a utility function that creates the LoadableModule Builder in an Environment if it is not there already. If it is already there, we return the existing one. """ try: ld_module = env['BUILDERS']['LoadableModule'] except KeyError: import SCons.Defaults action_list = [ SCons.Defaults.SharedCheck, SCons.Defaults.LdModuleLinkAction ] ld_module = SCons.Builder.Builder(action = action_list, emitter = "$LDMODULEEMITTER", prefix = '$LDMODULEPREFIX', suffix = '$LDMODULESUFFIX', target_scanner = ProgramScanner, src_suffix = '$SHOBJSUFFIX', src_builder = 'SharedObject') env['BUILDERS']['LoadableModule'] = ld_module return ld_module def createObjBuilders(env): """This is a utility function that creates the StaticObject and SharedObject Builders in an Environment if they are not there already. If they are there already, we return the existing ones. This is a separate function because soooo many Tools use this functionality. The return is a 2-tuple of (StaticObject, SharedObject) """ try: static_obj = env['BUILDERS']['StaticObject'] except KeyError: static_obj = SCons.Builder.Builder(action = {}, emitter = {}, prefix = '$OBJPREFIX', suffix = '$OBJSUFFIX', src_builder = ['CFile', 'CXXFile'], source_scanner = SourceFileScanner, single_source = 1) env['BUILDERS']['StaticObject'] = static_obj env['BUILDERS']['Object'] = static_obj try: shared_obj = env['BUILDERS']['SharedObject'] except KeyError: shared_obj = SCons.Builder.Builder(action = {}, emitter = {}, prefix = '$SHOBJPREFIX', suffix = '$SHOBJSUFFIX', src_builder = ['CFile', 'CXXFile'], source_scanner = SourceFileScanner, single_source = 1) env['BUILDERS']['SharedObject'] = shared_obj return (static_obj, shared_obj) def createCFileBuilders(env): """This is a utility function that creates the CFile/CXXFile Builders in an Environment if they are not there already. If they are there already, we return the existing ones. This is a separate function because soooo many Tools use this functionality. The return is a 2-tuple of (CFile, CXXFile) """ try: c_file = env['BUILDERS']['CFile'] except KeyError: c_file = SCons.Builder.Builder(action = {}, emitter = {}, suffix = {None:'$CFILESUFFIX'}) env['BUILDERS']['CFile'] = c_file env.SetDefault(CFILESUFFIX = '.c') try: cxx_file = env['BUILDERS']['CXXFile'] except KeyError: cxx_file = SCons.Builder.Builder(action = {}, emitter = {}, suffix = {None:'$CXXFILESUFFIX'}) env['BUILDERS']['CXXFile'] = cxx_file env.SetDefault(CXXFILESUFFIX = '.cc') return (c_file, cxx_file) ########################################################################## # Create common Java builders def CreateJarBuilder(env): try: java_jar = env['BUILDERS']['Jar'] except KeyError: fs = SCons.Node.FS.get_default_fs() jar_com = SCons.Action.Action('$JARCOM', '$JARCOMSTR') java_jar = SCons.Builder.Builder(action = jar_com, suffix = '$JARSUFFIX', src_suffix = '$JAVACLASSSUFIX', src_builder = 'JavaClassFile', source_factory = fs.Entry) env['BUILDERS']['Jar'] = java_jar return java_jar def CreateJavaHBuilder(env): try: java_javah = env['BUILDERS']['JavaH'] except KeyError: fs = SCons.Node.FS.get_default_fs() java_javah_com = SCons.Action.Action('$JAVAHCOM', '$JAVAHCOMSTR') java_javah = SCons.Builder.Builder(action = java_javah_com, src_suffix = '$JAVACLASSSUFFIX', target_factory = fs.Entry, source_factory = fs.File, src_builder = 'JavaClassFile') env['BUILDERS']['JavaH'] = java_javah return java_javah def CreateJavaClassFileBuilder(env): try: java_class_file = env['BUILDERS']['JavaClassFile'] except KeyError: fs = SCons.Node.FS.get_default_fs() javac_com = SCons.Action.Action('$JAVACCOM', '$JAVACCOMSTR') java_class_file = SCons.Builder.Builder(action = javac_com, emitter = {}, #suffix = '$JAVACLASSSUFFIX', src_suffix = '$JAVASUFFIX', src_builder = ['JavaFile'], target_factory = fs.Entry, source_factory = fs.File) env['BUILDERS']['JavaClassFile'] = java_class_file return java_class_file def CreateJavaClassDirBuilder(env): try: java_class_dir = env['BUILDERS']['JavaClassDir'] except KeyError: fs = SCons.Node.FS.get_default_fs() javac_com = SCons.Action.Action('$JAVACCOM', '$JAVACCOMSTR') java_class_dir = SCons.Builder.Builder(action = javac_com, emitter = {}, target_factory = fs.Dir, source_factory = fs.Dir) env['BUILDERS']['JavaClassDir'] = java_class_dir return java_class_dir def CreateJavaFileBuilder(env): try: java_file = env['BUILDERS']['JavaFile'] except KeyError: java_file = SCons.Builder.Builder(action = {}, emitter = {}, suffix = {None:'$JAVASUFFIX'}) env['BUILDERS']['JavaFile'] = java_file env['JAVASUFFIX'] = '.java' return java_file class ToolInitializerMethod(object): """ This is added to a construction environment in place of a method(s) normally called for a Builder (env.Object, env.StaticObject, etc.). When called, it has its associated ToolInitializer object search the specified list of tools and apply the first one that exists to the construction environment. It then calls whatever builder was (presumably) added to the construction environment in place of this particular instance. """ def __init__(self, name, initializer): """ Note: we store the tool name as __name__ so it can be used by the class that attaches this to a construction environment. """ self.__name__ = name self.initializer = initializer def get_builder(self, env): """ Returns the appropriate real Builder for this method name after having the associated ToolInitializer object apply the appropriate Tool module. """ builder = getattr(env, self.__name__) self.initializer.apply_tools(env) builder = getattr(env, self.__name__) if builder is self: # There was no Builder added, which means no valid Tool # for this name was found (or possibly there's a mismatch # between the name we were called by and the Builder name # added by the Tool module). return None self.initializer.remove_methods(env) return builder def __call__(self, env, *args, **kw): """ """ builder = self.get_builder(env) if builder is None: return [], [] return builder(*args, **kw) class ToolInitializer(object): """ A class for delayed initialization of Tools modules. Instances of this class associate a list of Tool modules with a list of Builder method names that will be added by those Tool modules. As part of instantiating this object for a particular construction environment, we also add the appropriate ToolInitializerMethod objects for the various Builder methods that we want to use to delay Tool searches until necessary. """ def __init__(self, env, tools, names): if not SCons.Util.is_List(tools): tools = [tools] if not SCons.Util.is_List(names): names = [names] self.env = env self.tools = tools self.names = names self.methods = {} for name in names: method = ToolInitializerMethod(name, self) self.methods[name] = method env.AddMethod(method) def remove_methods(self, env): """ Removes the methods that were added by the tool initialization so we no longer copy and re-bind them when the construction environment gets cloned. """ for method in self.methods.values(): env.RemoveMethod(method) def apply_tools(self, env): """ Searches the list of associated Tool modules for one that exists, and applies that to the construction environment. """ for t in self.tools: tool = SCons.Tool.Tool(t) if tool.exists(env): env.Tool(tool) return # If we fall through here, there was no tool module found. # This is where we can put an informative error message # about the inability to find the tool. We'll start doing # this as we cut over more pre-defined Builder+Tools to use # the ToolInitializer class. def Initializers(env): ToolInitializer(env, ['install'], ['_InternalInstall', '_InternalInstallAs', '_InternalInstallVersionedLib']) def Install(self, *args, **kw): return self._InternalInstall(*args, **kw) def InstallAs(self, *args, **kw): return self._InternalInstallAs(*args, **kw) def InstallVersionedLib(self, *args, **kw): return self._InternalInstallVersionedLib(*args, **kw) env.AddMethod(Install) env.AddMethod(InstallAs) env.AddMethod(InstallVersionedLib) def FindTool(tools, env): for tool in tools: t = Tool(tool) if t.exists(env): return tool return None def FindAllTools(tools, env): def ToolExists(tool, env=env): return Tool(tool).exists(env) return list(filter (ToolExists, tools)) def tool_list(platform, env): other_plat_tools=[] # XXX this logic about what tool to prefer on which platform # should be moved into either the platform files or # the tool files themselves. # The search orders here are described in the man page. If you # change these search orders, update the man page as well. if str(platform) == 'win32': "prefer Microsoft tools on Windows" linkers = ['mslink', 'gnulink', 'ilink', 'linkloc', 'ilink32' ] c_compilers = ['msvc', 'mingw', 'gcc', 'intelc', 'icl', 'icc', 'cc', 'bcc32' ] cxx_compilers = ['msvc', 'intelc', 'icc', 'g++', 'c++', 'bcc32' ] assemblers = ['masm', 'nasm', 'gas', '386asm' ] fortran_compilers = ['gfortran', 'g77', 'ifl', 'cvf', 'f95', 'f90', 'fortran'] ars = ['mslib', 'ar', 'tlib'] other_plat_tools=['msvs','midl'] elif str(platform) == 'os2': "prefer IBM tools on OS/2" linkers = ['ilink', 'gnulink', ]#'mslink'] c_compilers = ['icc', 'gcc',]# 'msvc', 'cc'] cxx_compilers = ['icc', 'g++',]# 'msvc', 'c++'] assemblers = ['nasm',]# 'masm', 'gas'] fortran_compilers = ['ifl', 'g77'] ars = ['ar',]# 'mslib'] elif str(platform) == 'irix': "prefer MIPSPro on IRIX" linkers = ['sgilink', 'gnulink'] c_compilers = ['sgicc', 'gcc', 'cc'] cxx_compilers = ['sgic++', 'g++', 'c++'] assemblers = ['as', 'gas'] fortran_compilers = ['f95', 'f90', 'f77', 'g77', 'fortran'] ars = ['sgiar'] elif str(platform) == 'sunos': "prefer Forte tools on SunOS" linkers = ['sunlink', 'gnulink'] c_compilers = ['suncc', 'gcc', 'cc'] cxx_compilers = ['sunc++', 'g++', 'c++'] assemblers = ['as', 'gas'] fortran_compilers = ['sunf95', 'sunf90', 'sunf77', 'f95', 'f90', 'f77', 'gfortran', 'g77', 'fortran'] ars = ['sunar'] elif str(platform) == 'hpux': "prefer aCC tools on HP-UX" linkers = ['hplink', 'gnulink'] c_compilers = ['hpcc', 'gcc', 'cc'] cxx_compilers = ['hpc++', 'g++', 'c++'] assemblers = ['as', 'gas'] fortran_compilers = ['f95', 'f90', 'f77', 'g77', 'fortran'] ars = ['ar'] elif str(platform) == 'aix': "prefer AIX Visual Age tools on AIX" linkers = ['aixlink', 'gnulink'] c_compilers = ['aixcc', 'gcc', 'cc'] cxx_compilers = ['aixc++', 'g++', 'c++'] assemblers = ['as', 'gas'] fortran_compilers = ['f95', 'f90', 'aixf77', 'g77', 'fortran'] ars = ['ar'] elif str(platform) == 'darwin': "prefer GNU tools on Mac OS X, except for some linkers and IBM tools" linkers = ['applelink', 'gnulink'] c_compilers = ['gcc', 'cc'] cxx_compilers = ['g++', 'c++'] assemblers = ['as'] fortran_compilers = ['gfortran', 'f95', 'f90', 'g77'] ars = ['ar'] else: "prefer GNU tools on all other platforms" linkers = ['gnulink', 'mslink', 'ilink'] c_compilers = ['gcc', 'msvc', 'intelc', 'icc', 'cc'] cxx_compilers = ['g++', 'msvc', 'intelc', 'icc', 'c++'] assemblers = ['gas', 'nasm', 'masm'] fortran_compilers = ['gfortran', 'g77', 'ifort', 'ifl', 'f95', 'f90', 'f77'] ars = ['ar', 'mslib'] c_compiler = FindTool(c_compilers, env) or c_compilers[0] # XXX this logic about what tool provides what should somehow be # moved into the tool files themselves. if c_compiler and c_compiler == 'mingw': # MinGW contains a linker, C compiler, C++ compiler, # Fortran compiler, archiver and assembler: cxx_compiler = None linker = None assembler = None fortran_compiler = None ar = None else: # Don't use g++ if the C compiler has built-in C++ support: if c_compiler in ('msvc', 'intelc', 'icc'): cxx_compiler = None else: cxx_compiler = FindTool(cxx_compilers, env) or cxx_compilers[0] linker = FindTool(linkers, env) or linkers[0] assembler = FindTool(assemblers, env) or assemblers[0] fortran_compiler = FindTool(fortran_compilers, env) or fortran_compilers[0] ar = FindTool(ars, env) or ars[0] other_tools = FindAllTools(other_plat_tools + [ 'dmd', #TODO: merge 'install' into 'filesystem' and # make 'filesystem' the default 'filesystem', 'm4', 'wix', #'midl', 'msvs', # Parser generators 'lex', 'yacc', # Foreign function interface 'rpcgen', 'swig', # Java 'jar', 'javac', 'javah', 'rmic', # TeX 'dvipdf', 'dvips', 'gs', 'tex', 'latex', 'pdflatex', 'pdftex', # Archivers 'tar', 'zip', 'rpm', # SourceCode factories 'BitKeeper', 'CVS', 'Perforce', 'RCS', 'SCCS', # 'Subversion', ], env) tools = ([linker, c_compiler, cxx_compiler, fortran_compiler, assembler, ar] + other_tools) return [x for x in tools if x] # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/midl.py0000644000175000017500000000603312114661560021513 0ustar dktrkranzdktrkranz"""SCons.Tool.midl Tool-specific initialization for midl (Microsoft IDL compiler). There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Tool.Tool() selection method. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/midl.py 2013/03/03 09:48:35 garyo" import SCons.Action import SCons.Builder import SCons.Defaults import SCons.Scanner.IDL import SCons.Util from MSCommon import msvc_exists def midl_emitter(target, source, env): """Produces a list of outputs from the MIDL compiler""" base, ext = SCons.Util.splitext(str(target[0])) tlb = target[0] incl = base + '.h' interface = base + '_i.c' t = [tlb, incl, interface] midlcom = env['MIDLCOM'] if midlcom.find('/proxy') != -1: proxy = base + '_p.c' t.append(proxy) if midlcom.find('/dlldata') != -1: dlldata = base + '_data.c' t.append(dlldata) return (t,source) idl_scanner = SCons.Scanner.IDL.IDLScan() midl_action = SCons.Action.Action('$MIDLCOM', '$MIDLCOMSTR') midl_builder = SCons.Builder.Builder(action = midl_action, src_suffix = '.idl', suffix='.tlb', emitter = midl_emitter, source_scanner = idl_scanner) def generate(env): """Add Builders and construction variables for midl to an Environment.""" env['MIDL'] = 'MIDL.EXE' env['MIDLFLAGS'] = SCons.Util.CLVar('/nologo') env['MIDLCOM'] = '$MIDL $MIDLFLAGS /tlb ${TARGETS[0]} /h ${TARGETS[1]} /iid ${TARGETS[2]} /proxy ${TARGETS[3]} /dlldata ${TARGETS[4]} $SOURCE 2> NUL' env['BUILDERS']['TypeLibrary'] = midl_builder def exists(env): return msvc_exists() # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/cc.py0000644000175000017500000000725112114661560021156 0ustar dktrkranzdktrkranz"""SCons.Tool.cc Tool-specific initialization for generic Posix C compilers. There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Tool.Tool() selection method. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/cc.py 2013/03/03 09:48:35 garyo" import SCons.Tool import SCons.Defaults import SCons.Util CSuffixes = ['.c', '.m'] if not SCons.Util.case_sensitive_suffixes('.c', '.C'): CSuffixes.append('.C') def add_common_cc_variables(env): """ Add underlying common "C compiler" variables that are used by multiple tools (specifically, c++). """ if '_CCCOMCOM' not in env: env['_CCCOMCOM'] = '$CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS' # It's a hack to test for darwin here, but the alternative # of creating an applecc.py to contain this seems overkill. # Maybe someday the Apple platform will require more setup and # this logic will be moved. env['FRAMEWORKS'] = SCons.Util.CLVar('') env['FRAMEWORKPATH'] = SCons.Util.CLVar('') if env['PLATFORM'] == 'darwin': env['_CCCOMCOM'] = env['_CCCOMCOM'] + ' $_FRAMEWORKPATH' if 'CCFLAGS' not in env: env['CCFLAGS'] = SCons.Util.CLVar('') if 'SHCCFLAGS' not in env: env['SHCCFLAGS'] = SCons.Util.CLVar('$CCFLAGS') def generate(env): """ Add Builders and construction variables for C compilers to an Environment. """ static_obj, shared_obj = SCons.Tool.createObjBuilders(env) for suffix in CSuffixes: static_obj.add_action(suffix, SCons.Defaults.CAction) shared_obj.add_action(suffix, SCons.Defaults.ShCAction) static_obj.add_emitter(suffix, SCons.Defaults.StaticObjectEmitter) shared_obj.add_emitter(suffix, SCons.Defaults.SharedObjectEmitter) add_common_cc_variables(env) env['CC'] = 'cc' env['CFLAGS'] = SCons.Util.CLVar('') env['CCCOM'] = '$CC -o $TARGET -c $CFLAGS $CCFLAGS $_CCCOMCOM $SOURCES' env['SHCC'] = '$CC' env['SHCFLAGS'] = SCons.Util.CLVar('$CFLAGS') env['SHCCCOM'] = '$SHCC -o $TARGET -c $SHCFLAGS $SHCCFLAGS $_CCCOMCOM $SOURCES' env['CPPDEFPREFIX'] = '-D' env['CPPDEFSUFFIX'] = '' env['INCPREFIX'] = '-I' env['INCSUFFIX'] = '' env['SHOBJSUFFIX'] = '.os' env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME'] = 0 env['CFILESUFFIX'] = '.c' def exists(env): return env.Detect('cc') # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/hpcc.py0000644000175000017500000000352312114661560021504 0ustar dktrkranzdktrkranz"""SCons.Tool.hpcc Tool-specific initialization for HP aCC and cc. There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Tool.Tool() selection method. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/hpcc.py 2013/03/03 09:48:35 garyo" import SCons.Util import cc def generate(env): """Add Builders and construction variables for aCC & cc to an Environment.""" cc.generate(env) env['CXX'] = 'aCC' env['SHCCFLAGS'] = SCons.Util.CLVar('$CCFLAGS +Z') def exists(env): return env.Detect('aCC') # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/dvips.py0000644000175000017500000000666212114661560021723 0ustar dktrkranzdktrkranz"""SCons.Tool.dvips Tool-specific initialization for dvips. There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Tool.Tool() selection method. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/dvips.py 2013/03/03 09:48:35 garyo" import SCons.Action import SCons.Builder import SCons.Tool.dvipdf import SCons.Util def DviPsFunction(target = None, source= None, env=None): result = SCons.Tool.dvipdf.DviPdfPsFunction(PSAction,target,source,env) return result def DviPsStrFunction(target = None, source= None, env=None): """A strfunction for dvipdf that returns the appropriate command string for the no_exec options.""" if env.GetOption("no_exec"): result = env.subst('$PSCOM',0,target,source) else: result = '' return result PSAction = None DVIPSAction = None PSBuilder = None def generate(env): """Add Builders and construction variables for dvips to an Environment.""" global PSAction if PSAction is None: PSAction = SCons.Action.Action('$PSCOM', '$PSCOMSTR') global DVIPSAction if DVIPSAction is None: DVIPSAction = SCons.Action.Action(DviPsFunction, strfunction = DviPsStrFunction) global PSBuilder if PSBuilder is None: PSBuilder = SCons.Builder.Builder(action = PSAction, prefix = '$PSPREFIX', suffix = '$PSSUFFIX', src_suffix = '.dvi', src_builder = 'DVI', single_source=True) env['BUILDERS']['PostScript'] = PSBuilder env['DVIPS'] = 'dvips' env['DVIPSFLAGS'] = SCons.Util.CLVar('') # I'm not quite sure I got the directories and filenames right for variant_dir # We need to be in the correct directory for the sake of latex \includegraphics eps included files. env['PSCOM'] = 'cd ${TARGET.dir} && $DVIPS $DVIPSFLAGS -o ${TARGET.file} ${SOURCE.file}' env['PSPREFIX'] = '' env['PSSUFFIX'] = '.ps' def exists(env): SCons.Tool.tex.generate_darwin(env) return env.Detect('dvips') # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/mwld.xml0000644000175000017500000000076512114661560021707 0ustar dktrkranzdktrkranz Sets construction variables for the Metrowerks CodeWarrior linker. AR ARCOM LIBDIRPREFIX LIBDIRSUFFIX LIBLINKPREFIX LIBLINKSUFFIX LINK LINKCOM SHLINK SHLINKFLAGS SHLINKCOM scons-doc-2.3.0/src/engine/SCons/Tool/gs.xml0000644000175000017500000000171112114661560021345 0ustar dktrkranzdktrkranz Set construction variables for Ghostscript. GS GSFLAGS GSCOM GSCOMSTR The Ghostscript program used to convert PostScript to PDF files. The Ghostscript command line used to convert PostScript to PDF files. The string displayed when Ghostscript is used to convert a PostScript file to a PDF file. If this is not set, then &cv-link-GSCOM; (the command line) is displayed. General options passed to the Ghostscript program when converting PostScript to PDF files. scons-doc-2.3.0/src/engine/SCons/Tool/aixlink.py0000644000175000017500000000503212114661560022223 0ustar dktrkranzdktrkranz"""SCons.Tool.aixlink Tool-specific initialization for the IBM Visual Age linker. There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Tool.Tool() selection method. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/aixlink.py 2013/03/03 09:48:35 garyo" import os import os.path import SCons.Util import aixcc import link cplusplus = __import__('c++', globals(), locals(), []) def smart_linkflags(source, target, env, for_signature): if cplusplus.iscplusplus(source): build_dir = env.subst('$BUILDDIR', target=target, source=source) if build_dir: return '-qtempinc=' + os.path.join(build_dir, 'tempinc') return '' def generate(env): """ Add Builders and construction variables for Visual Age linker to an Environment. """ link.generate(env) env['SMARTLINKFLAGS'] = smart_linkflags env['LINKFLAGS'] = SCons.Util.CLVar('$SMARTLINKFLAGS') env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -qmkshrobj -qsuppress=1501-218') env['SHLIBSUFFIX'] = '.a' def exists(env): path, _cc, _shcc, version = aixcc.get_xlc(env) if path and _cc: xlc = os.path.join(path, _cc) if os.path.exists(xlc): return xlc return None # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/Perforce.xml0000644000175000017500000000324212114661557022510 0ustar dktrkranzdktrkranz Sets construction variables for interacting with the Perforce source code management system. P4 P4FLAGS P4COM P4COMSTR The Perforce executable. The command line used to fetch source files from Perforce. The string displayed when fetching a source file from Perforce. If this is not set, then &cv-link-P4COM; (the command line) is displayed. General options that are passed to Perforce. () A factory function that returns a Builder object to be used to fetch source files from the Perforce source code management system. The returned Builder is intended to be passed to the &f-SourceCode; function. This function is deprecated. For details, see the entry for the &f-SourceCode; function. Example: env.SourceCode('.', env.Perforce()) Perforce uses a number of external environment variables for its operation. Consequently, this function adds the following variables from the user's external environment to the construction environment's ENV dictionary: P4CHARSET, P4CLIENT, P4LANGUAGE, P4PASSWD, P4PORT, P4USER, SystemRoot, USER, and USERNAME. scons-doc-2.3.0/src/engine/SCons/Tool/zip.xml0000644000175000017500000000430412114661560021537 0ustar dktrkranzdktrkranz Sets construction variables for the &zip; archiver. ZIP ZIPFLAGS ZIPCOM ZIPCOMPRESSION ZIPSUFFIX ZIPCOMSTR Builds a zip archive of the specified files and/or directories. Unlike most builder methods, the &b-Zip; builder method may be called multiple times for a given target; each additional call adds to the list of entries that will be built into the archive. Any source directories will be scanned for changes to any on-disk files, regardless of whether or not &scons; knows about them from other Builder or function calls. env.Zip('src.zip', 'src') # Create the stuff.zip file. env.Zip('stuff', ['subdir1', 'subdir2']) # Also add "another" to the stuff.tar file. env.Zip('stuff', 'another') The zip compression and file packaging utility. The command line used to call the zip utility, or the internal Python function used to create a zip archive. The string displayed when archiving files using the zip utility. If this is not set, then &cv-link-ZIPCOM; (the command line or internal Python function) is displayed. env = Environment(ZIPCOMSTR = "Zipping $TARGET") The compression flag from the Python zipfile module used by the internal Python function to control whether the zip archive is compressed or not. The default value is zipfile.ZIP_DEFLATED, which creates a compressed zip archive. This value has no effect if the zipfile module is unavailable. General options passed to the zip utility. The suffix used for zip file names. scons-doc-2.3.0/src/engine/SCons/Tool/dvipdf.py0000644000175000017500000001006412114661560022041 0ustar dktrkranzdktrkranz"""SCons.Tool.dvipdf Tool-specific initialization for dvipdf. There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Tool.Tool() selection method. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/dvipdf.py 2013/03/03 09:48:35 garyo" import SCons.Action import SCons.Defaults import SCons.Tool.pdf import SCons.Tool.tex import SCons.Util _null = SCons.Scanner.LaTeX._null def DviPdfPsFunction(XXXDviAction, target = None, source= None, env=None): """A builder for DVI files that sets the TEXPICTS environment variable before running dvi2ps or dvipdf.""" try: abspath = source[0].attributes.path except AttributeError : abspath = '' saved_env = SCons.Scanner.LaTeX.modify_env_var(env, 'TEXPICTS', abspath) result = XXXDviAction(target, source, env) if saved_env is _null: try: del env['ENV']['TEXPICTS'] except KeyError: pass # was never set else: env['ENV']['TEXPICTS'] = saved_env return result def DviPdfFunction(target = None, source= None, env=None): result = DviPdfPsFunction(PDFAction,target,source,env) return result def DviPdfStrFunction(target = None, source= None, env=None): """A strfunction for dvipdf that returns the appropriate command string for the no_exec options.""" if env.GetOption("no_exec"): result = env.subst('$DVIPDFCOM',0,target,source) else: result = '' return result PDFAction = None DVIPDFAction = None def PDFEmitter(target, source, env): """Strips any .aux or .log files from the input source list. These are created by the TeX Builder that in all likelihood was used to generate the .dvi file we're using as input, and we only care about the .dvi file. """ def strip_suffixes(n): return not SCons.Util.splitext(str(n))[1] in ['.aux', '.log'] source = list(filter(strip_suffixes, source)) return (target, source) def generate(env): """Add Builders and construction variables for dvipdf to an Environment.""" global PDFAction if PDFAction is None: PDFAction = SCons.Action.Action('$DVIPDFCOM', '$DVIPDFCOMSTR') global DVIPDFAction if DVIPDFAction is None: DVIPDFAction = SCons.Action.Action(DviPdfFunction, strfunction = DviPdfStrFunction) import pdf pdf.generate(env) bld = env['BUILDERS']['PDF'] bld.add_action('.dvi', DVIPDFAction) bld.add_emitter('.dvi', PDFEmitter) env['DVIPDF'] = 'dvipdf' env['DVIPDFFLAGS'] = SCons.Util.CLVar('') env['DVIPDFCOM'] = 'cd ${TARGET.dir} && $DVIPDF $DVIPDFFLAGS ${SOURCE.file} ${TARGET.file}' # Deprecated synonym. env['PDFCOM'] = ['$DVIPDFCOM'] def exists(env): SCons.Tool.tex.generate_darwin(env) return env.Detect('dvipdf') # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/fortran.xml0000644000175000017500000002117712114661560022417 0ustar dktrkranzdktrkranz Set construction variables for generic POSIX Fortran compilers. FORTRAN FORTRANFLAGS FORTRANCOM SHFORTRAN SHFORTRANFLAGS SHFORTRANCOM SHFORTRANPPCOM FORTRANCOMSTR FORTRANPPCOMSTR SHFORTRANCOMSTR SHFORTRANPPCOMSTR The default Fortran compiler for all versions of Fortran. The command line used to compile a Fortran source file to an object file. By default, any options specified in the &cv-link-FORTRANFLAGS;, &cv-link-CPPFLAGS;, &cv-link-_CPPDEFFLAGS;, &cv-link-_FORTRANMODFLAG;, and &cv-link-_FORTRANINCFLAGS; construction variables are included on this command line. The string displayed when a Fortran source file is compiled to an object file. If this is not set, then &cv-link-FORTRANCOM; (the command line) is displayed. The list of file extensions for which the FORTRAN dialect will be used. By default, this is ['.f', '.for', '.ftn'] The list of file extensions for which the compilation + preprocessor pass for FORTRAN dialect will be used. By default, this is ['.fpp', '.FPP'] General user-specified options that are passed to the Fortran compiler. Note that this variable does not contain (or similar) include or module search path options that scons generates automatically from &cv-link-FORTRANPATH;. See &cv-link-_FORTRANINCFLAGS; and &cv-link-_FORTRANMODFLAG;, below, for the variables that expand those options. An automatically-generated construction variable containing the Fortran compiler command-line options for specifying directories to be searched for include files and module files. The value of &cv-link-_FORTRANINCFLAGS; is created by prepending/appending &cv-link-INCPREFIX; and &cv-link-INCSUFFIX; to the beginning and end of each directory in &cv-link-FORTRANPATH;. Directory location where the Fortran compiler should place any module files it generates. This variable is empty, by default. Some Fortran compilers will internally append this directory in the search path for module files, as well. The prefix used to specify a module directory on the Fortran compiler command line. This will be appended to the beginning of the directory in the &cv-link-FORTRANMODDIR; construction variables when the &cv-link-_FORTRANMODFLAG; variables is automatically generated. The suffix used to specify a module directory on the Fortran compiler command line. This will be appended to the beginning of the directory in the &cv-link-FORTRANMODDIR; construction variables when the &cv-link-_FORTRANMODFLAG; variables is automatically generated. An automatically-generated construction variable containing the Fortran compiler command-line option for specifying the directory location where the Fortran compiler should place any module files that happen to get generated during compilation. The value of &cv-link-_FORTRANMODFLAG; is created by prepending/appending &cv-link-FORTRANMODDIRPREFIX; and &cv-link-FORTRANMODDIRSUFFIX; to the beginning and end of the directory in &cv-link-FORTRANMODDIR;. The module file prefix used by the Fortran compiler. SCons assumes that the Fortran compiler follows the quasi-standard naming convention for module files of module_name.mod. As a result, this variable is left empty, by default. For situations in which the compiler does not necessarily follow the normal convention, the user may use this variable. Its value will be appended to every module file name as scons attempts to resolve dependencies. The module file suffix used by the Fortran compiler. SCons assumes that the Fortran compiler follows the quasi-standard naming convention for module files of module_name.mod. As a result, this variable is set to ".mod", by default. For situations in which the compiler does not necessarily follow the normal convention, the user may use this variable. Its value will be appended to every module file name as scons attempts to resolve dependencies. The list of directories that the Fortran compiler will search for include files and (for some compilers) module files. The Fortran implicit dependency scanner will search these directories for include files (but not module files since they are autogenerated and, as such, may not actually exist at the time the scan takes place). Don't explicitly put include directory arguments in FORTRANFLAGS because the result will be non-portable and the directories will not be searched by the dependency scanner. Note: directory names in FORTRANPATH will be looked-up relative to the SConscript directory when they are used in a command. To force &scons; to look-up a directory relative to the root of the source tree use #: env = Environment(FORTRANPATH='#/include') The directory look-up can also be forced using the &Dir;() function: include = Dir('include') env = Environment(FORTRANPATH=include) The directory list will be added to command lines through the automatically-generated &cv-link-_FORTRANINCFLAGS; construction variable, which is constructed by appending the values of the &cv-link-INCPREFIX; and &cv-link-INCSUFFIX; construction variables to the beginning and end of each directory in &cv-link-FORTRANPATH;. Any command lines you define that need the FORTRANPATH directory list should include &cv-link-_FORTRANINCFLAGS;: env = Environment(FORTRANCOM="my_compiler $_FORTRANINCFLAGS -c -o $TARGET $SOURCE") The command line used to compile a Fortran source file to an object file after first running the file through the C preprocessor. By default, any options specified in the &cv-link-FORTRANFLAGS;, &cv-link-CPPFLAGS;, &cv-link-_CPPDEFFLAGS;, &cv-link-_FORTRANMODFLAG;, and &cv-link-_FORTRANINCFLAGS; construction variables are included on this command line. The string displayed when a Fortran source file is compiled to an object file after first running the file through the C preprocessor. If this is not set, then &cv-link-FORTRANPPCOM; (the command line) is displayed. The list of suffixes of files that will be scanned for Fortran implicit dependencies (INCLUDE lines and USE statements). The default list is: [".f", ".F", ".for", ".FOR", ".ftn", ".FTN", ".fpp", ".FPP", ".f77", ".F77", ".f90", ".F90", ".f95", ".F95"] The default Fortran compiler used for generating shared-library objects. The command line used to compile a Fortran source file to a shared-library object file. The string displayed when a Fortran source file is compiled to a shared-library object file. If this is not set, then &cv-link-SHFORTRANCOM; (the command line) is displayed. Options that are passed to the Fortran compiler to generate shared-library objects. The command line used to compile a Fortran source file to a shared-library object file after first running the file through the C preprocessor. Any options specified in the &cv-link-SHFORTRANFLAGS; and &cv-link-CPPFLAGS; construction variables are included on this command line. The string displayed when a Fortran source file is compiled to a shared-library object file after first running the file through the C preprocessor. If this is not set, then &cv-link-SHFORTRANPPCOM; (the command line) is displayed. scons-doc-2.3.0/src/engine/SCons/Tool/mingw.py0000644000175000017500000001462112114661560021711 0ustar dktrkranzdktrkranz"""SCons.Tool.gcc Tool-specific initialization for MinGW (http://www.mingw.org/) There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Tool.Tool() selection method. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/mingw.py 2013/03/03 09:48:35 garyo" import os import os.path import SCons.Action import SCons.Builder import SCons.Defaults import SCons.Tool import SCons.Util # This is what we search for to find mingw: key_program = 'mingw32-gcc' def find(env): # First search in the SCons path path=env.WhereIs(key_program) if (path): return path # then the OS path: path=SCons.Util.WhereIs(key_program) if (path): return path # If that doesn't work try default location for mingw save_path=env['ENV']['PATH'] env.AppendENVPath('PATH',r'c:\MinGW\bin') path =env.WhereIs(key_program) if not path: env['ENV']['PATH']=save_path return path def shlib_generator(target, source, env, for_signature): cmd = SCons.Util.CLVar(['$SHLINK', '$SHLINKFLAGS']) dll = env.FindIxes(target, 'SHLIBPREFIX', 'SHLIBSUFFIX') if dll: cmd.extend(['-o', dll]) cmd.extend(['$SOURCES', '$_LIBDIRFLAGS', '$_LIBFLAGS']) implib = env.FindIxes(target, 'LIBPREFIX', 'LIBSUFFIX') if implib: cmd.append('-Wl,--out-implib,'+implib.get_string(for_signature)) def_target = env.FindIxes(target, 'WINDOWSDEFPREFIX', 'WINDOWSDEFSUFFIX') insert_def = env.subst("$WINDOWS_INSERT_DEF") if not insert_def in ['', '0', 0] and def_target: \ cmd.append('-Wl,--output-def,'+def_target.get_string(for_signature)) return [cmd] def shlib_emitter(target, source, env): dll = env.FindIxes(target, 'SHLIBPREFIX', 'SHLIBSUFFIX') no_import_lib = env.get('no_import_lib', 0) if not dll: raise SCons.Errors.UserError("A shared library should have exactly one target with the suffix: %s" % env.subst("$SHLIBSUFFIX")) if not no_import_lib and \ not env.FindIxes(target, 'LIBPREFIX', 'LIBSUFFIX'): # Create list of target libraries as strings targetStrings=env.ReplaceIxes(dll, 'SHLIBPREFIX', 'SHLIBSUFFIX', 'LIBPREFIX', 'LIBSUFFIX') # Now add file nodes to target list target.append(env.fs.File(targetStrings)) # Append a def file target if there isn't already a def file target # or a def file source or the user has explicitly asked for the target # to be emitted. def_source = env.FindIxes(source, 'WINDOWSDEFPREFIX', 'WINDOWSDEFSUFFIX') def_target = env.FindIxes(target, 'WINDOWSDEFPREFIX', 'WINDOWSDEFSUFFIX') skip_def_insert = env.subst("$WINDOWS_INSERT_DEF") in ['', '0', 0] if not def_source and not def_target and not skip_def_insert: # Create list of target libraries and def files as strings targetStrings=env.ReplaceIxes(dll, 'SHLIBPREFIX', 'SHLIBSUFFIX', 'WINDOWSDEFPREFIX', 'WINDOWSDEFSUFFIX') # Now add file nodes to target list target.append(env.fs.File(targetStrings)) return (target, source) shlib_action = SCons.Action.Action(shlib_generator, generator=1) res_action = SCons.Action.Action('$RCCOM', '$RCCOMSTR') res_builder = SCons.Builder.Builder(action=res_action, suffix='.o', source_scanner=SCons.Tool.SourceFileScanner) SCons.Tool.SourceFileScanner.add_scanner('.rc', SCons.Defaults.CScan) def generate(env): mingw = find(env) if mingw: dir = os.path.dirname(mingw) env.PrependENVPath('PATH', dir ) # Most of mingw is the same as gcc and friends... gnu_tools = ['gcc', 'g++', 'gnulink', 'ar', 'gas', 'gfortran', 'm4'] for tool in gnu_tools: SCons.Tool.Tool(tool)(env) #... but a few things differ: env['CC'] = 'gcc' env['SHCCFLAGS'] = SCons.Util.CLVar('$CCFLAGS') env['CXX'] = 'g++' env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS') env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -shared') env['SHLINKCOM'] = shlib_action env['LDMODULECOM'] = shlib_action env.Append(SHLIBEMITTER = [shlib_emitter]) env['AS'] = 'as' env['WIN32DEFPREFIX'] = '' env['WIN32DEFSUFFIX'] = '.def' env['WINDOWSDEFPREFIX'] = '${WIN32DEFPREFIX}' env['WINDOWSDEFSUFFIX'] = '${WIN32DEFSUFFIX}' env['SHOBJSUFFIX'] = '.o' env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME'] = 1 env['RC'] = 'windres' env['RCFLAGS'] = SCons.Util.CLVar('') env['RCINCFLAGS'] = '$( ${_concat(RCINCPREFIX, CPPPATH, RCINCSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)' env['RCINCPREFIX'] = '--include-dir ' env['RCINCSUFFIX'] = '' env['RCCOM'] = '$RC $_CPPDEFFLAGS $RCINCFLAGS ${RCINCPREFIX} ${SOURCE.dir} $RCFLAGS -i $SOURCE -o $TARGET' env['BUILDERS']['RES'] = res_builder # Some setting from the platform also have to be overridden: env['OBJSUFFIX'] = '.o' env['LIBPREFIX'] = 'lib' env['LIBSUFFIX'] = '.a' env['PROGSUFFIX'] = '.exe' def exists(env): return find(env) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/suncc.xml0000644000175000017500000000060112114661560022044 0ustar dktrkranzdktrkranz Sets construction variables for the Sun C compiler. CXX SHCCFLAGS SHOBJPREFIX SHOBJSUFFIX scons-doc-2.3.0/src/engine/SCons/Tool/gnulink.py0000644000175000017500000000420512114661560022234 0ustar dktrkranzdktrkranz"""SCons.Tool.gnulink Tool-specific initialization for the gnu linker. There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Tool.Tool() selection method. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/gnulink.py 2013/03/03 09:48:35 garyo" import SCons.Util import link linkers = ['g++', 'gcc'] def generate(env): """Add Builders and construction variables for gnulink to an Environment.""" link.generate(env) if env['PLATFORM'] == 'hpux': env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -shared -fPIC') # __RPATH is set to $_RPATH in the platform specification if that # platform supports it. env['RPATHPREFIX'] = '-Wl,-rpath=' env['RPATHSUFFIX'] = '' env['_RPATH'] = '${_concat(RPATHPREFIX, RPATH, RPATHSUFFIX, __env__)}' def exists(env): return env.Detect(linkers) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/msgfmt.xml0000644000175000017500000000603612114661560022236 0ustar dktrkranzdktrkranz This scons tool is a part of scons &t-link-gettext; toolset. It provides scons interface to msgfmt(1) command, which generates binary message catalog (MO) from a textual translation description (PO). MOSUFFIX MSGFMT MSGFMTCOM MSGFMTCOMSTR MSGFMTFLAGS POSUFFIX LINGUAS_FILE This builder belongs to &t-link-msgfmt; tool. The builder compiles PO files to MO files. Example 1. Create pl.mo and en.mo by compiling pl.po and en.po: # ... env.MOFiles(['pl', 'en']) Example 2. Compile files for languages defined in LINGUAS file: # ... env.MOFiles(LINGUAS_FILE = 1) Example 3. Create pl.mo and en.mo by compiling pl.po and en.po plus files for languages defined in LINGUAS file: # ... env.MOFiles(['pl', 'en'], LINGUAS_FILE = 1) Example 4. Compile files for languages defined in LINGUAS file (another version): # ... env['LINGUAS_FILE'] = 1 env.MOFiles() Suffix used for MO files (default: '.mo'). See &t-link-msgfmt; tool and &b-link-MOFiles; builder. Absolute path to msgfmt(1) binary, found by Detect(). See &t-link-msgfmt; tool and &b-link-MOFiles; builder. Complete command line to run msgfmt(1) program. See &t-link-msgfmt; tool and &b-link-MOFiles; builder. String to display when msgfmt(1) is invoked (default: '', which means ``print &cv-link-MSGFMTCOM;''). See &t-link-msgfmt; tool and &b-link-MOFiles; builder. Additional flags to msgfmt(1). See &t-link-msgfmt; tool and &b-link-MOFiles; builder. scons-doc-2.3.0/src/engine/SCons/Tool/FortranCommon.py0000644000175000017500000002407512114661557023366 0ustar dktrkranzdktrkranz"""SCons.Tool.FortranCommon Stuff for processing Fortran, common to all fortran dialects. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/FortranCommon.py 2013/03/03 09:48:35 garyo" import re import os.path import SCons.Action import SCons.Defaults import SCons.Scanner.Fortran import SCons.Tool import SCons.Util def isfortran(env, source): """Return 1 if any of code in source has fortran files in it, 0 otherwise.""" try: fsuffixes = env['FORTRANSUFFIXES'] except KeyError: # If no FORTRANSUFFIXES, no fortran tool, so there is no need to look # for fortran sources. return 0 if not source: # Source might be None for unusual cases like SConf. return 0 for s in source: if s.sources: ext = os.path.splitext(str(s.sources[0]))[1] if ext in fsuffixes: return 1 return 0 def _fortranEmitter(target, source, env): node = source[0].rfile() if not node.exists() and not node.is_derived(): print "Could not locate " + str(node.name) return ([], []) mod_regex = """(?i)^\s*MODULE\s+(?!PROCEDURE)(\w+)""" cre = re.compile(mod_regex,re.M) # Retrieve all USE'd module names modules = cre.findall(node.get_text_contents()) # Remove unique items from the list modules = SCons.Util.unique(modules) # Convert module name to a .mod filename suffix = env.subst('$FORTRANMODSUFFIX', target=target, source=source) moddir = env.subst('$FORTRANMODDIR', target=target, source=source) modules = [x.lower() + suffix for x in modules] for m in modules: target.append(env.fs.File(m, moddir)) return (target, source) def FortranEmitter(target, source, env): target, source = _fortranEmitter(target, source, env) return SCons.Defaults.StaticObjectEmitter(target, source, env) def ShFortranEmitter(target, source, env): target, source = _fortranEmitter(target, source, env) return SCons.Defaults.SharedObjectEmitter(target, source, env) def ComputeFortranSuffixes(suffixes, ppsuffixes): """suffixes are fortran source files, and ppsuffixes the ones to be pre-processed. Both should be sequences, not strings.""" assert len(suffixes) > 0 s = suffixes[0] sup = s.upper() upper_suffixes = [_.upper() for _ in suffixes] if SCons.Util.case_sensitive_suffixes(s, sup): ppsuffixes.extend(upper_suffixes) else: suffixes.extend(upper_suffixes) def CreateDialectActions(dialect): """Create dialect specific actions.""" CompAction = SCons.Action.Action('$%sCOM ' % dialect, '$%sCOMSTR' % dialect) CompPPAction = SCons.Action.Action('$%sPPCOM ' % dialect, '$%sPPCOMSTR' % dialect) ShCompAction = SCons.Action.Action('$SH%sCOM ' % dialect, '$SH%sCOMSTR' % dialect) ShCompPPAction = SCons.Action.Action('$SH%sPPCOM ' % dialect, '$SH%sPPCOMSTR' % dialect) return CompAction, CompPPAction, ShCompAction, ShCompPPAction def DialectAddToEnv(env, dialect, suffixes, ppsuffixes, support_module = 0): """Add dialect specific construction variables.""" ComputeFortranSuffixes(suffixes, ppsuffixes) fscan = SCons.Scanner.Fortran.FortranScan("%sPATH" % dialect) for suffix in suffixes + ppsuffixes: SCons.Tool.SourceFileScanner.add_scanner(suffix, fscan) env.AppendUnique(FORTRANSUFFIXES = suffixes + ppsuffixes) compaction, compppaction, shcompaction, shcompppaction = \ CreateDialectActions(dialect) static_obj, shared_obj = SCons.Tool.createObjBuilders(env) for suffix in suffixes: static_obj.add_action(suffix, compaction) shared_obj.add_action(suffix, shcompaction) static_obj.add_emitter(suffix, FortranEmitter) shared_obj.add_emitter(suffix, ShFortranEmitter) for suffix in ppsuffixes: static_obj.add_action(suffix, compppaction) shared_obj.add_action(suffix, shcompppaction) static_obj.add_emitter(suffix, FortranEmitter) shared_obj.add_emitter(suffix, ShFortranEmitter) if '%sFLAGS' % dialect not in env: env['%sFLAGS' % dialect] = SCons.Util.CLVar('') if 'SH%sFLAGS' % dialect not in env: env['SH%sFLAGS' % dialect] = SCons.Util.CLVar('$%sFLAGS' % dialect) # If a tool does not define fortran prefix/suffix for include path, use C ones if 'INC%sPREFIX' % dialect not in env: env['INC%sPREFIX' % dialect] = '$INCPREFIX' if 'INC%sSUFFIX' % dialect not in env: env['INC%sSUFFIX' % dialect] = '$INCSUFFIX' env['_%sINCFLAGS' % dialect] = '$( ${_concat(INC%sPREFIX, %sPATH, INC%sSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)' % (dialect, dialect, dialect) if support_module == 1: env['%sCOM' % dialect] = '$%s -o $TARGET -c $%sFLAGS $_%sINCFLAGS $_FORTRANMODFLAG $SOURCES' % (dialect, dialect, dialect) env['%sPPCOM' % dialect] = '$%s -o $TARGET -c $%sFLAGS $CPPFLAGS $_CPPDEFFLAGS $_%sINCFLAGS $_FORTRANMODFLAG $SOURCES' % (dialect, dialect, dialect) env['SH%sCOM' % dialect] = '$SH%s -o $TARGET -c $SH%sFLAGS $_%sINCFLAGS $_FORTRANMODFLAG $SOURCES' % (dialect, dialect, dialect) env['SH%sPPCOM' % dialect] = '$SH%s -o $TARGET -c $SH%sFLAGS $CPPFLAGS $_CPPDEFFLAGS $_%sINCFLAGS $_FORTRANMODFLAG $SOURCES' % (dialect, dialect, dialect) else: env['%sCOM' % dialect] = '$%s -o $TARGET -c $%sFLAGS $_%sINCFLAGS $SOURCES' % (dialect, dialect, dialect) env['%sPPCOM' % dialect] = '$%s -o $TARGET -c $%sFLAGS $CPPFLAGS $_CPPDEFFLAGS $_%sINCFLAGS $SOURCES' % (dialect, dialect, dialect) env['SH%sCOM' % dialect] = '$SH%s -o $TARGET -c $SH%sFLAGS $_%sINCFLAGS $SOURCES' % (dialect, dialect, dialect) env['SH%sPPCOM' % dialect] = '$SH%s -o $TARGET -c $SH%sFLAGS $CPPFLAGS $_CPPDEFFLAGS $_%sINCFLAGS $SOURCES' % (dialect, dialect, dialect) def add_fortran_to_env(env): """Add Builders and construction variables for Fortran to an Environment.""" try: FortranSuffixes = env['FORTRANFILESUFFIXES'] except KeyError: FortranSuffixes = ['.f', '.for', '.ftn'] #print "Adding %s to fortran suffixes" % FortranSuffixes try: FortranPPSuffixes = env['FORTRANPPFILESUFFIXES'] except KeyError: FortranPPSuffixes = ['.fpp', '.FPP'] DialectAddToEnv(env, "FORTRAN", FortranSuffixes, FortranPPSuffixes, support_module = 1) env['FORTRANMODPREFIX'] = '' # like $LIBPREFIX env['FORTRANMODSUFFIX'] = '.mod' # like $LIBSUFFIX env['FORTRANMODDIR'] = '' # where the compiler should place .mod files env['FORTRANMODDIRPREFIX'] = '' # some prefix to $FORTRANMODDIR - similar to $INCPREFIX env['FORTRANMODDIRSUFFIX'] = '' # some suffix to $FORTRANMODDIR - similar to $INCSUFFIX env['_FORTRANMODFLAG'] = '$( ${_concat(FORTRANMODDIRPREFIX, FORTRANMODDIR, FORTRANMODDIRSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)' def add_f77_to_env(env): """Add Builders and construction variables for f77 to an Environment.""" try: F77Suffixes = env['F77FILESUFFIXES'] except KeyError: F77Suffixes = ['.f77'] #print "Adding %s to f77 suffixes" % F77Suffixes try: F77PPSuffixes = env['F77PPFILESUFFIXES'] except KeyError: F77PPSuffixes = [] DialectAddToEnv(env, "F77", F77Suffixes, F77PPSuffixes) def add_f90_to_env(env): """Add Builders and construction variables for f90 to an Environment.""" try: F90Suffixes = env['F90FILESUFFIXES'] except KeyError: F90Suffixes = ['.f90'] #print "Adding %s to f90 suffixes" % F90Suffixes try: F90PPSuffixes = env['F90PPFILESUFFIXES'] except KeyError: F90PPSuffixes = [] DialectAddToEnv(env, "F90", F90Suffixes, F90PPSuffixes, support_module = 1) def add_f95_to_env(env): """Add Builders and construction variables for f95 to an Environment.""" try: F95Suffixes = env['F95FILESUFFIXES'] except KeyError: F95Suffixes = ['.f95'] #print "Adding %s to f95 suffixes" % F95Suffixes try: F95PPSuffixes = env['F95PPFILESUFFIXES'] except KeyError: F95PPSuffixes = [] DialectAddToEnv(env, "F95", F95Suffixes, F95PPSuffixes, support_module = 1) def add_f03_to_env(env): """Add Builders and construction variables for f03 to an Environment.""" try: F03Suffixes = env['F03FILESUFFIXES'] except KeyError: F03Suffixes = ['.f03'] #print "Adding %s to f95 suffixes" % F95Suffixes try: F03PPSuffixes = env['F03PPFILESUFFIXES'] except KeyError: F03PPSuffixes = [] DialectAddToEnv(env, "F03", F03Suffixes, F03PPSuffixes, support_module = 1) def add_all_to_env(env): """Add builders and construction variables for all supported fortran dialects.""" add_fortran_to_env(env) add_f77_to_env(env) add_f90_to_env(env) add_f95_to_env(env) add_f03_to_env(env) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/msvs.py0000644000175000017500000021712512114661560021564 0ustar dktrkranzdktrkranz"""SCons.Tool.msvs Tool-specific initialization for Microsoft Visual Studio project files. There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Tool.Tool() selection method. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/msvs.py 2013/03/03 09:48:35 garyo" import SCons.compat import base64 import hashlib import ntpath import os # compat layer imports "cPickle" for us if it's available. import pickle import re import sys import SCons.Builder import SCons.Node.FS import SCons.Platform.win32 import SCons.Script.SConscript import SCons.PathList import SCons.Util import SCons.Warnings from MSCommon import msvc_exists, msvc_setup_env_once from SCons.Defaults import processDefines ############################################################################## # Below here are the classes and functions for generation of # DSP/DSW/SLN/VCPROJ files. ############################################################################## def xmlify(s): s = s.replace("&", "&") # do this first s = s.replace("'", "'") s = s.replace('"', """) return s # Process a CPPPATH list in includes, given the env, target and source. # Returns a tuple of nodes. def processIncludes(includes, env, target, source): return SCons.PathList.PathList(includes).subst_path(env, target, source) external_makefile_guid = '{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}' def _generateGUID(slnfile, name): """This generates a dummy GUID for the sln file to use. It is based on the MD5 signatures of the sln filename plus the name of the project. It basically just needs to be unique, and not change with each invocation.""" m = hashlib.md5() # Normalize the slnfile path to a Windows path (\ separators) so # the generated file has a consistent GUID even if we generate # it on a non-Windows platform. m.update(ntpath.normpath(str(slnfile)) + str(name)) solution = m.hexdigest().upper() # convert most of the signature to GUID form (discard the rest) solution = "{" + solution[:8] + "-" + solution[8:12] + "-" + solution[12:16] + "-" + solution[16:20] + "-" + solution[20:32] + "}" return solution version_re = re.compile(r'(\d+\.\d+)(.*)') def msvs_parse_version(s): """ Split a Visual Studio version, which may in fact be something like '7.0Exp', into is version number (returned as a float) and trailing "suite" portion. """ num, suite = version_re.match(s).groups() return float(num), suite # os.path.relpath has been introduced in Python 2.6 # We define it locally for earlier versions of Python def relpath(path, start=os.path.curdir): """Return a relative version of a path""" import sys if not path: raise ValueError("no path specified") start_list = os.path.abspath(start).split(os.sep) path_list = os.path.abspath(path).split(os.sep) if 'posix' in sys.builtin_module_names: # Work out how much of the filepath is shared by start and path. i = len(os.path.commonprefix([start_list, path_list])) else: if start_list[0].lower() != path_list[0].lower(): unc_path, rest = os.path.splitunc(path) unc_start, rest = os.path.splitunc(start) if bool(unc_path) ^ bool(unc_start): raise ValueError("Cannot mix UNC and non-UNC paths (%s and %s)" % (path, start)) else: raise ValueError("path is on drive %s, start on drive %s" % (path_list[0], start_list[0])) # Work out how much of the filepath is shared by start and path. for i in range(min(len(start_list), len(path_list))): if start_list[i].lower() != path_list[i].lower(): break else: i += 1 rel_list = [os.pardir] * (len(start_list)-i) + path_list[i:] if not rel_list: return os.path.curdir return os.path.join(*rel_list) if not "relpath" in os.path.__all__: os.path.relpath = relpath # This is how we re-invoke SCons from inside MSVS Project files. # The problem is that we might have been invoked as either scons.bat # or scons.py. If we were invoked directly as scons.py, then we could # use sys.argv[0] to find the SCons "executable," but that doesn't work # if we were invoked as scons.bat, which uses "python -c" to execute # things and ends up with "-c" as sys.argv[0]. Consequently, we have # the MSVS Project file invoke SCons the same way that scons.bat does, # which works regardless of how we were invoked. def getExecScriptMain(env, xml=None): scons_home = env.get('SCONS_HOME') if not scons_home and 'SCONS_LIB_DIR' in os.environ: scons_home = os.environ['SCONS_LIB_DIR'] if scons_home: exec_script_main = "from os.path import join; import sys; sys.path = [ r'%s' ] + sys.path; import SCons.Script; SCons.Script.main()" % scons_home else: version = SCons.__version__ exec_script_main = "from os.path import join; import sys; sys.path = [ join(sys.prefix, 'Lib', 'site-packages', 'scons-%(version)s'), join(sys.prefix, 'scons-%(version)s'), join(sys.prefix, 'Lib', 'site-packages', 'scons'), join(sys.prefix, 'scons') ] + sys.path; import SCons.Script; SCons.Script.main()" % locals() if xml: exec_script_main = xmlify(exec_script_main) return exec_script_main # The string for the Python executable we tell the Project file to use # is either sys.executable or, if an external PYTHON_ROOT environment # variable exists, $(PYTHON)ROOT\\python.exe (generalized a little to # pluck the actual executable name from sys.executable). try: python_root = os.environ['PYTHON_ROOT'] except KeyError: python_executable = sys.executable else: python_executable = os.path.join('$$(PYTHON_ROOT)', os.path.split(sys.executable)[1]) class Config(object): pass def splitFully(path): dir, base = os.path.split(path) if dir and dir != '' and dir != path: return splitFully(dir)+[base] if base == '': return [] return [base] def makeHierarchy(sources): '''Break a list of files into a hierarchy; for each value, if it is a string, then it is a file. If it is a dictionary, it is a folder. The string is the original path of the file.''' hierarchy = {} for file in sources: path = splitFully(file) if len(path): dict = hierarchy for part in path[:-1]: if part not in dict: dict[part] = {} dict = dict[part] dict[path[-1]] = file #else: # print 'Warning: failed to decompose path for '+str(file) return hierarchy class _DSPGenerator(object): """ Base class for DSP generators """ srcargs = [ 'srcs', 'incs', 'localincs', 'resources', 'misc'] def __init__(self, dspfile, source, env): self.dspfile = str(dspfile) try: get_abspath = dspfile.get_abspath except AttributeError: self.dspabs = os.path.abspath(dspfile) else: self.dspabs = get_abspath() if 'variant' not in env: raise SCons.Errors.InternalError("You must specify a 'variant' argument (i.e. 'Debug' or " +\ "'Release') to create an MSVSProject.") elif SCons.Util.is_String(env['variant']): variants = [env['variant']] elif SCons.Util.is_List(env['variant']): variants = env['variant'] if 'buildtarget' not in env or env['buildtarget'] == None: buildtarget = [''] elif SCons.Util.is_String(env['buildtarget']): buildtarget = [env['buildtarget']] elif SCons.Util.is_List(env['buildtarget']): if len(env['buildtarget']) != len(variants): raise SCons.Errors.InternalError("Sizes of 'buildtarget' and 'variant' lists must be the same.") buildtarget = [] for bt in env['buildtarget']: if SCons.Util.is_String(bt): buildtarget.append(bt) else: buildtarget.append(bt.get_abspath()) else: buildtarget = [env['buildtarget'].get_abspath()] if len(buildtarget) == 1: bt = buildtarget[0] buildtarget = [] for _ in variants: buildtarget.append(bt) if 'outdir' not in env or env['outdir'] == None: outdir = [''] elif SCons.Util.is_String(env['outdir']): outdir = [env['outdir']] elif SCons.Util.is_List(env['outdir']): if len(env['outdir']) != len(variants): raise SCons.Errors.InternalError("Sizes of 'outdir' and 'variant' lists must be the same.") outdir = [] for s in env['outdir']: if SCons.Util.is_String(s): outdir.append(s) else: outdir.append(s.get_abspath()) else: outdir = [env['outdir'].get_abspath()] if len(outdir) == 1: s = outdir[0] outdir = [] for v in variants: outdir.append(s) if 'runfile' not in env or env['runfile'] == None: runfile = buildtarget[-1:] elif SCons.Util.is_String(env['runfile']): runfile = [env['runfile']] elif SCons.Util.is_List(env['runfile']): if len(env['runfile']) != len(variants): raise SCons.Errors.InternalError("Sizes of 'runfile' and 'variant' lists must be the same.") runfile = [] for s in env['runfile']: if SCons.Util.is_String(s): runfile.append(s) else: runfile.append(s.get_abspath()) else: runfile = [env['runfile'].get_abspath()] if len(runfile) == 1: s = runfile[0] runfile = [] for v in variants: runfile.append(s) self.sconscript = env['MSVSSCONSCRIPT'] cmdargs = env.get('cmdargs', '') self.env = env if 'name' in self.env: self.name = self.env['name'] else: self.name = os.path.basename(SCons.Util.splitext(self.dspfile)[0]) self.name = self.env.subst(self.name) sourcenames = [ 'Source Files', 'Header Files', 'Local Headers', 'Resource Files', 'Other Files'] self.sources = {} for n in sourcenames: self.sources[n] = [] self.configs = {} self.nokeep = 0 if 'nokeep' in env and env['variant'] != 0: self.nokeep = 1 if self.nokeep == 0 and os.path.exists(self.dspabs): self.Parse() for t in zip(sourcenames,self.srcargs): if t[1] in self.env: if SCons.Util.is_List(self.env[t[1]]): for i in self.env[t[1]]: if not i in self.sources[t[0]]: self.sources[t[0]].append(i) else: if not self.env[t[1]] in self.sources[t[0]]: self.sources[t[0]].append(self.env[t[1]]) for n in sourcenames: #TODO 2.4: compat layer supports sorted(key=) but not sort(key=) #TODO 2.4: self.sources[n].sort(key=lambda a: a.lower()) self.sources[n] = sorted(self.sources[n], key=lambda a: a.lower()) def AddConfig(self, variant, buildtarget, outdir, runfile, cmdargs, dspfile=dspfile): config = Config() config.buildtarget = buildtarget config.outdir = outdir config.cmdargs = cmdargs config.runfile = runfile match = re.match('(.*)\|(.*)', variant) if match: config.variant = match.group(1) config.platform = match.group(2) else: config.variant = variant config.platform = 'Win32' self.configs[variant] = config print "Adding '" + self.name + ' - ' + config.variant + '|' + config.platform + "' to '" + str(dspfile) + "'" for i in range(len(variants)): AddConfig(self, variants[i], buildtarget[i], outdir[i], runfile[i], cmdargs) self.platforms = [] for key in self.configs.keys(): platform = self.configs[key].platform if not platform in self.platforms: self.platforms.append(platform) def Build(self): pass V6DSPHeader = """\ # Microsoft Developer Studio Project File - Name="%(name)s" - Package Owner=<4> # Microsoft Developer Studio Generated Build File, Format Version 6.00 # ** DO NOT EDIT ** # TARGTYPE "Win32 (x86) External Target" 0x0106 CFG=%(name)s - Win32 %(confkey)s !MESSAGE This is not a valid makefile. To build this project using NMAKE, !MESSAGE use the Export Makefile command and run !MESSAGE !MESSAGE NMAKE /f "%(name)s.mak". !MESSAGE !MESSAGE You can specify a configuration when running NMAKE !MESSAGE by defining the macro CFG on the command line. For example: !MESSAGE !MESSAGE NMAKE /f "%(name)s.mak" CFG="%(name)s - Win32 %(confkey)s" !MESSAGE !MESSAGE Possible choices for configuration are: !MESSAGE """ class _GenerateV6DSP(_DSPGenerator): """Generates a Project file for MSVS 6.0""" def PrintHeader(self): # pick a default config confkeys = sorted(self.configs.keys()) name = self.name confkey = confkeys[0] self.file.write(V6DSPHeader % locals()) for kind in confkeys: self.file.write('!MESSAGE "%s - Win32 %s" (based on "Win32 (x86) External Target")\n' % (name, kind)) self.file.write('!MESSAGE \n\n') def PrintProject(self): name = self.name self.file.write('# Begin Project\n' '# PROP AllowPerConfigDependencies 0\n' '# PROP Scc_ProjName ""\n' '# PROP Scc_LocalPath ""\n\n') first = 1 confkeys = sorted(self.configs.keys()) for kind in confkeys: outdir = self.configs[kind].outdir buildtarget = self.configs[kind].buildtarget if first == 1: self.file.write('!IF "$(CFG)" == "%s - Win32 %s"\n\n' % (name, kind)) first = 0 else: self.file.write('\n!ELSEIF "$(CFG)" == "%s - Win32 %s"\n\n' % (name, kind)) env_has_buildtarget = 'MSVSBUILDTARGET' in self.env if not env_has_buildtarget: self.env['MSVSBUILDTARGET'] = buildtarget # have to write this twice, once with the BASE settings, and once without for base in ("BASE ",""): self.file.write('# PROP %sUse_MFC 0\n' '# PROP %sUse_Debug_Libraries ' % (base, base)) if kind.lower().find('debug') < 0: self.file.write('0\n') else: self.file.write('1\n') self.file.write('# PROP %sOutput_Dir "%s"\n' '# PROP %sIntermediate_Dir "%s"\n' % (base,outdir,base,outdir)) cmd = 'echo Starting SCons && ' + self.env.subst('$MSVSBUILDCOM', 1) self.file.write('# PROP %sCmd_Line "%s"\n' '# PROP %sRebuild_Opt "-c && %s"\n' '# PROP %sTarget_File "%s"\n' '# PROP %sBsc_Name ""\n' '# PROP %sTarget_Dir ""\n'\ %(base,cmd,base,cmd,base,buildtarget,base,base)) if not env_has_buildtarget: del self.env['MSVSBUILDTARGET'] self.file.write('\n!ENDIF\n\n' '# Begin Target\n\n') for kind in confkeys: self.file.write('# Name "%s - Win32 %s"\n' % (name,kind)) self.file.write('\n') first = 0 for kind in confkeys: if first == 0: self.file.write('!IF "$(CFG)" == "%s - Win32 %s"\n\n' % (name,kind)) first = 1 else: self.file.write('!ELSEIF "$(CFG)" == "%s - Win32 %s"\n\n' % (name,kind)) self.file.write('!ENDIF \n\n') self.PrintSourceFiles() self.file.write('# End Target\n' '# End Project\n') if self.nokeep == 0: # now we pickle some data and add it to the file -- MSDEV will ignore it. pdata = pickle.dumps(self.configs,1) pdata = base64.encodestring(pdata) self.file.write(pdata + '\n') pdata = pickle.dumps(self.sources,1) pdata = base64.encodestring(pdata) self.file.write(pdata + '\n') def PrintSourceFiles(self): categories = {'Source Files': 'cpp|c|cxx|l|y|def|odl|idl|hpj|bat', 'Header Files': 'h|hpp|hxx|hm|inl', 'Local Headers': 'h|hpp|hxx|hm|inl', 'Resource Files': 'r|rc|ico|cur|bmp|dlg|rc2|rct|bin|cnt|rtf|gif|jpg|jpeg|jpe', 'Other Files': ''} for kind in sorted(categories.keys(), key=lambda a: a.lower()): if not self.sources[kind]: continue # skip empty groups self.file.write('# Begin Group "' + kind + '"\n\n') typelist = categories[kind].replace('|', ';') self.file.write('# PROP Default_Filter "' + typelist + '"\n') for file in self.sources[kind]: file = os.path.normpath(file) self.file.write('# Begin Source File\n\n' 'SOURCE="' + file + '"\n' '# End Source File\n') self.file.write('# End Group\n') # add the SConscript file outside of the groups self.file.write('# Begin Source File\n\n' 'SOURCE="' + str(self.sconscript) + '"\n' '# End Source File\n') def Parse(self): try: dspfile = open(self.dspabs,'r') except IOError: return # doesn't exist yet, so can't add anything to configs. line = dspfile.readline() while line: if line.find("# End Project") > -1: break line = dspfile.readline() line = dspfile.readline() datas = line while line and line != '\n': line = dspfile.readline() datas = datas + line # OK, we've found our little pickled cache of data. try: datas = base64.decodestring(datas) data = pickle.loads(datas) except KeyboardInterrupt: raise except: return # unable to unpickle any data for some reason self.configs.update(data) data = None line = dspfile.readline() datas = line while line and line != '\n': line = dspfile.readline() datas = datas + line # OK, we've found our little pickled cache of data. # it has a "# " in front of it, so we strip that. try: datas = base64.decodestring(datas) data = pickle.loads(datas) except KeyboardInterrupt: raise except: return # unable to unpickle any data for some reason self.sources.update(data) def Build(self): try: self.file = open(self.dspabs,'w') except IOError, detail: raise SCons.Errors.InternalError('Unable to open "' + self.dspabs + '" for writing:' + str(detail)) else: self.PrintHeader() self.PrintProject() self.file.close() V7DSPHeader = """\ """ V7DSPConfiguration = """\ \t\t \t\t\t \t\t """ V8DSPHeader = """\ """ V8DSPConfiguration = """\ \t\t \t\t\t \t\t """ class _GenerateV7DSP(_DSPGenerator): """Generates a Project file for MSVS .NET""" def __init__(self, dspfile, source, env): _DSPGenerator.__init__(self, dspfile, source, env) self.version = env['MSVS_VERSION'] self.version_num, self.suite = msvs_parse_version(self.version) if self.version_num >= 9.0: self.versionstr = '9.00' self.dspheader = V8DSPHeader self.dspconfiguration = V8DSPConfiguration elif self.version_num >= 8.0: self.versionstr = '8.00' self.dspheader = V8DSPHeader self.dspconfiguration = V8DSPConfiguration else: if self.version_num >= 7.1: self.versionstr = '7.10' else: self.versionstr = '7.00' self.dspheader = V7DSPHeader self.dspconfiguration = V7DSPConfiguration self.file = None def PrintHeader(self): env = self.env versionstr = self.versionstr name = self.name encoding = self.env.subst('$MSVSENCODING') scc_provider = env.get('MSVS_SCC_PROVIDER', '') scc_project_name = env.get('MSVS_SCC_PROJECT_NAME', '') scc_aux_path = env.get('MSVS_SCC_AUX_PATH', '') # MSVS_SCC_LOCAL_PATH is kept for backwards compatibility purpose and should # be deprecated as soon as possible. scc_local_path_legacy = env.get('MSVS_SCC_LOCAL_PATH', '') scc_connection_root = env.get('MSVS_SCC_CONNECTION_ROOT', os.curdir) scc_local_path = os.path.relpath(scc_connection_root, os.path.dirname(self.dspabs)) project_guid = env.get('MSVS_PROJECT_GUID', '') if not project_guid: project_guid = _generateGUID(self.dspfile, '') if scc_provider != '': scc_attrs = '\tSccProjectName="%s"\n' % scc_project_name if scc_aux_path != '': scc_attrs += '\tSccAuxPath="%s"\n' % scc_aux_path scc_attrs += ('\tSccLocalPath="%s"\n' '\tSccProvider="%s"' % (scc_local_path, scc_provider)) elif scc_local_path_legacy != '': # This case is kept for backwards compatibility purpose and should # be deprecated as soon as possible. scc_attrs = ('\tSccProjectName="%s"\n' '\tSccLocalPath="%s"' % (scc_project_name, scc_local_path_legacy)) else: self.dspheader = self.dspheader.replace('%(scc_attrs)s\n', '') self.file.write(self.dspheader % locals()) self.file.write('\t\n') for platform in self.platforms: self.file.write( '\t\t\n' % platform) self.file.write('\t\n') if self.version_num >= 8.0: self.file.write('\t\n' '\t\n') def PrintProject(self): self.file.write('\t\n') confkeys = sorted(self.configs.keys()) for kind in confkeys: variant = self.configs[kind].variant platform = self.configs[kind].platform outdir = self.configs[kind].outdir buildtarget = self.configs[kind].buildtarget runfile = self.configs[kind].runfile cmdargs = self.configs[kind].cmdargs env_has_buildtarget = 'MSVSBUILDTARGET' in self.env if not env_has_buildtarget: self.env['MSVSBUILDTARGET'] = buildtarget starting = 'echo Starting SCons && ' if cmdargs: cmdargs = ' ' + cmdargs else: cmdargs = '' buildcmd = xmlify(starting + self.env.subst('$MSVSBUILDCOM', 1) + cmdargs) rebuildcmd = xmlify(starting + self.env.subst('$MSVSREBUILDCOM', 1) + cmdargs) cleancmd = xmlify(starting + self.env.subst('$MSVSCLEANCOM', 1) + cmdargs) # This isn't perfect; CPPDEFINES and CPPPATH can contain $TARGET and $SOURCE, # so they could vary depending on the command being generated. This code # assumes they don't. preprocdefs = xmlify(';'.join(processDefines(self.env.get('CPPDEFINES', [])))) includepath_Dirs = processIncludes(self.env.get('CPPPATH', []), self.env, None, None) includepath = xmlify(';'.join([str(x) for x in includepath_Dirs])) if not env_has_buildtarget: del self.env['MSVSBUILDTARGET'] self.file.write(self.dspconfiguration % locals()) self.file.write('\t\n') if self.version_num >= 7.1: self.file.write('\t\n' '\t\n') self.PrintSourceFiles() self.file.write('\n') if self.nokeep == 0: # now we pickle some data and add it to the file -- MSDEV will ignore it. pdata = pickle.dumps(self.configs,1) pdata = base64.encodestring(pdata) self.file.write('\n') def printSources(self, hierarchy, commonprefix): sorteditems = sorted(hierarchy.items(), key=lambda a: a[0].lower()) # First folders, then files for key, value in sorteditems: if SCons.Util.is_Dict(value): self.file.write('\t\t\t\n' % (key)) self.printSources(value, commonprefix) self.file.write('\t\t\t\n') for key, value in sorteditems: if SCons.Util.is_String(value): file = value if commonprefix: file = os.path.join(commonprefix, value) file = os.path.normpath(file) self.file.write('\t\t\t\n' '\t\t\t\n' % (file)) def PrintSourceFiles(self): categories = {'Source Files': 'cpp;c;cxx;l;y;def;odl;idl;hpj;bat', 'Header Files': 'h;hpp;hxx;hm;inl', 'Local Headers': 'h;hpp;hxx;hm;inl', 'Resource Files': 'r;rc;ico;cur;bmp;dlg;rc2;rct;bin;cnt;rtf;gif;jpg;jpeg;jpe', 'Other Files': ''} self.file.write('\t\n') cats = sorted([k for k in categories.keys() if self.sources[k]], key=lambda a: a.lower()) for kind in cats: if len(cats) > 1: self.file.write('\t\t\n' % (kind, categories[kind])) sources = self.sources[kind] # First remove any common prefix commonprefix = None s = list(map(os.path.normpath, sources)) # take the dirname because the prefix may include parts # of the filenames (e.g. if you have 'dir\abcd' and # 'dir\acde' then the cp will be 'dir\a' ) cp = os.path.dirname( os.path.commonprefix(s) ) if cp and s[0][len(cp)] == os.sep: # +1 because the filename starts after the separator sources = [s[len(cp)+1:] for s in sources] commonprefix = cp hierarchy = makeHierarchy(sources) self.printSources(hierarchy, commonprefix=commonprefix) if len(cats)>1: self.file.write('\t\t\n') # add the SConscript file outside of the groups self.file.write('\t\t\n' '\t\t\n' % str(self.sconscript)) self.file.write('\t\n' '\t\n' '\t\n') def Parse(self): try: dspfile = open(self.dspabs,'r') except IOError: return # doesn't exist yet, so can't add anything to configs. line = dspfile.readline() while line: if line.find('\n') def printFilters(self, hierarchy, name): sorteditems = sorted(hierarchy.items(), key = lambda a: a[0].lower()) for key, value in sorteditems: if SCons.Util.is_Dict(value): filter_name = name + '\\' + key self.filters_file.write('\t\t\n' '\t\t\t%s\n' '\t\t\n' % (filter_name, _generateGUID(self.dspabs, filter_name))) self.printFilters(value, filter_name) def printSources(self, hierarchy, kind, commonprefix, filter_name): keywords = {'Source Files': 'ClCompile', 'Header Files': 'ClInclude', 'Local Headers': 'ClInclude', 'Resource Files': 'None', 'Other Files': 'None'} sorteditems = sorted(hierarchy.items(), key = lambda a: a[0].lower()) # First folders, then files for key, value in sorteditems: if SCons.Util.is_Dict(value): self.printSources(value, kind, commonprefix, filter_name + '\\' + key) for key, value in sorteditems: if SCons.Util.is_String(value): file = value if commonprefix: file = os.path.join(commonprefix, value) file = os.path.normpath(file) self.file.write('\t\t<%s Include="%s" />\n' % (keywords[kind], file)) self.filters_file.write('\t\t<%s Include="%s">\n' '\t\t\t%s\n' '\t\t\n' % (keywords[kind], file, filter_name, keywords[kind])) def PrintSourceFiles(self): categories = {'Source Files': 'cpp;c;cxx;l;y;def;odl;idl;hpj;bat', 'Header Files': 'h;hpp;hxx;hm;inl', 'Local Headers': 'h;hpp;hxx;hm;inl', 'Resource Files': 'r;rc;ico;cur;bmp;dlg;rc2;rct;bin;cnt;rtf;gif;jpg;jpeg;jpe', 'Other Files': ''} cats = sorted([k for k in categories.keys() if self.sources[k]], key = lambda a: a.lower()) # print vcxproj.filters file first self.filters_file.write('\t\n') for kind in cats: self.filters_file.write('\t\t\n' '\t\t\t{7b42d31d-d53c-4868-8b92-ca2bc9fc052f}\n' '\t\t\t%s\n' '\t\t\n' % (kind, categories[kind])) # First remove any common prefix sources = self.sources[kind] commonprefix = None s = list(map(os.path.normpath, sources)) # take the dirname because the prefix may include parts # of the filenames (e.g. if you have 'dir\abcd' and # 'dir\acde' then the cp will be 'dir\a' ) cp = os.path.dirname( os.path.commonprefix(s) ) if cp and s[0][len(cp)] == os.sep: # +1 because the filename starts after the separator sources = [s[len(cp)+1:] for s in sources] commonprefix = cp hierarchy = makeHierarchy(sources) self.printFilters(hierarchy, kind) self.filters_file.write('\t\n') # then print files and filters for kind in cats: self.file.write('\t\n') self.filters_file.write('\t\n') # First remove any common prefix sources = self.sources[kind] commonprefix = None s = list(map(os.path.normpath, sources)) # take the dirname because the prefix may include parts # of the filenames (e.g. if you have 'dir\abcd' and # 'dir\acde' then the cp will be 'dir\a' ) cp = os.path.dirname( os.path.commonprefix(s) ) if cp and s[0][len(cp)] == os.sep: # +1 because the filename starts after the separator sources = [s[len(cp)+1:] for s in sources] commonprefix = cp hierarchy = makeHierarchy(sources) self.printSources(hierarchy, kind, commonprefix, kind) self.file.write('\t\n') self.filters_file.write('\t\n') # add the SConscript file outside of the groups self.file.write('\t\n' '\t\t\n' #'\t\t\n' '\t\n' % str(self.sconscript)) def Parse(self): print "_GenerateV10DSP.Parse()" def Build(self): try: self.file = open(self.dspabs, 'w') except IOError, detail: raise SCons.Errors.InternalError('Unable to open "' + self.dspabs + '" for writing:' + str(detail)) else: self.PrintHeader() self.PrintProject() self.file.close() class _DSWGenerator(object): """ Base class for DSW generators """ def __init__(self, dswfile, source, env): self.dswfile = os.path.normpath(str(dswfile)) self.dsw_folder_path = os.path.dirname(os.path.abspath(self.dswfile)) self.env = env if 'projects' not in env: raise SCons.Errors.UserError("You must specify a 'projects' argument to create an MSVSSolution.") projects = env['projects'] if not SCons.Util.is_List(projects): raise SCons.Errors.InternalError("The 'projects' argument must be a list of nodes.") projects = SCons.Util.flatten(projects) if len(projects) < 1: raise SCons.Errors.UserError("You must specify at least one project to create an MSVSSolution.") self.dspfiles = list(map(str, projects)) if 'name' in self.env: self.name = self.env['name'] else: self.name = os.path.basename(SCons.Util.splitext(self.dswfile)[0]) self.name = self.env.subst(self.name) def Build(self): pass class _GenerateV7DSW(_DSWGenerator): """Generates a Solution file for MSVS .NET""" def __init__(self, dswfile, source, env): _DSWGenerator.__init__(self, dswfile, source, env) self.file = None self.version = self.env['MSVS_VERSION'] self.version_num, self.suite = msvs_parse_version(self.version) self.versionstr = '7.00' if self.version_num >= 11.0: self.versionstr = '12.00' elif self.version_num >= 10.0: self.versionstr = '11.00' elif self.version_num >= 9.0: self.versionstr = '10.00' elif self.version_num >= 8.0: self.versionstr = '9.00' elif self.version_num >= 7.1: self.versionstr = '8.00' if 'slnguid' in env and env['slnguid']: self.slnguid = env['slnguid'] else: self.slnguid = _generateGUID(dswfile, self.name) self.configs = {} self.nokeep = 0 if 'nokeep' in env and env['variant'] != 0: self.nokeep = 1 if self.nokeep == 0 and os.path.exists(self.dswfile): self.Parse() def AddConfig(self, variant, dswfile=dswfile): config = Config() match = re.match('(.*)\|(.*)', variant) if match: config.variant = match.group(1) config.platform = match.group(2) else: config.variant = variant config.platform = 'Win32' self.configs[variant] = config print "Adding '" + self.name + ' - ' + config.variant + '|' + config.platform + "' to '" + str(dswfile) + "'" if 'variant' not in env: raise SCons.Errors.InternalError("You must specify a 'variant' argument (i.e. 'Debug' or " +\ "'Release') to create an MSVS Solution File.") elif SCons.Util.is_String(env['variant']): AddConfig(self, env['variant']) elif SCons.Util.is_List(env['variant']): for variant in env['variant']: AddConfig(self, variant) self.platforms = [] for key in self.configs.keys(): platform = self.configs[key].platform if not platform in self.platforms: self.platforms.append(platform) def GenerateProjectFilesInfo(self): for dspfile in self.dspfiles: dsp_folder_path, name = os.path.split(dspfile) dsp_folder_path = os.path.abspath(dsp_folder_path) dsp_relative_folder_path = os.path.relpath(dsp_folder_path, self.dsw_folder_path) if dsp_relative_folder_path == os.curdir: dsp_relative_file_path = name else: dsp_relative_file_path = os.path.join(dsp_relative_folder_path, name) dspfile_info = {'NAME': name, 'GUID': _generateGUID(dspfile, ''), 'FOLDER_PATH': dsp_folder_path, 'FILE_PATH': dspfile, 'SLN_RELATIVE_FOLDER_PATH': dsp_relative_folder_path, 'SLN_RELATIVE_FILE_PATH': dsp_relative_file_path} self.dspfiles_info.append(dspfile_info) self.dspfiles_info = [] GenerateProjectFilesInfo(self) def Parse(self): try: dswfile = open(self.dswfile,'r') except IOError: return # doesn't exist yet, so can't add anything to configs. line = dswfile.readline() while line: if line[:9] == "EndGlobal": break line = dswfile.readline() line = dswfile.readline() datas = line while line: line = dswfile.readline() datas = datas + line # OK, we've found our little pickled cache of data. try: datas = base64.decodestring(datas) data = pickle.loads(datas) except KeyboardInterrupt: raise except: return # unable to unpickle any data for some reason self.configs.update(data) def PrintSolution(self): """Writes a solution file""" self.file.write('Microsoft Visual Studio Solution File, Format Version %s\n' % self.versionstr) if self.version_num >= 11.0: self.file.write('# Visual Studio 11\n') elif self.version_num >= 10.0: self.file.write('# Visual Studio 2010\n') elif self.version_num >= 9.0: self.file.write('# Visual Studio 2008\n') elif self.version_num >= 8.0: self.file.write('# Visual Studio 2005\n') for dspinfo in self.dspfiles_info: name = dspinfo['NAME'] base, suffix = SCons.Util.splitext(name) if suffix == '.vcproj': name = base self.file.write('Project("%s") = "%s", "%s", "%s"\n' % (external_makefile_guid, name, dspinfo['SLN_RELATIVE_FILE_PATH'], dspinfo['GUID'])) if self.version_num >= 7.1 and self.version_num < 8.0: self.file.write('\tProjectSection(ProjectDependencies) = postProject\n' '\tEndProjectSection\n') self.file.write('EndProject\n') self.file.write('Global\n') env = self.env if 'MSVS_SCC_PROVIDER' in env: scc_number_of_projects = len(self.dspfiles) + 1 slnguid = self.slnguid scc_provider = env.get('MSVS_SCC_PROVIDER', '').replace(' ', r'\u0020') scc_project_name = env.get('MSVS_SCC_PROJECT_NAME', '').replace(' ', r'\u0020') scc_connection_root = env.get('MSVS_SCC_CONNECTION_ROOT', os.curdir) scc_local_path = os.path.relpath(scc_connection_root, self.dsw_folder_path).replace('\\', '\\\\') self.file.write('\tGlobalSection(SourceCodeControl) = preSolution\n' '\t\tSccNumberOfProjects = %(scc_number_of_projects)d\n' '\t\tSccProjectName0 = %(scc_project_name)s\n' '\t\tSccLocalPath0 = %(scc_local_path)s\n' '\t\tSccProvider0 = %(scc_provider)s\n' '\t\tCanCheckoutShared = true\n' % locals()) sln_relative_path_from_scc = os.path.relpath(self.dsw_folder_path, scc_connection_root) if sln_relative_path_from_scc != os.curdir: self.file.write('\t\tSccProjectFilePathRelativizedFromConnection0 = %s\\\\\n' % sln_relative_path_from_scc.replace('\\', '\\\\')) if self.version_num < 8.0: # When present, SolutionUniqueID is automatically removed by VS 2005 # TODO: check for Visual Studio versions newer than 2005 self.file.write('\t\tSolutionUniqueID = %s\n' % slnguid) for dspinfo in self.dspfiles_info: i = self.dspfiles_info.index(dspinfo) + 1 dsp_relative_file_path = dspinfo['SLN_RELATIVE_FILE_PATH'].replace('\\', '\\\\') dsp_scc_relative_folder_path = os.path.relpath(dspinfo['FOLDER_PATH'], scc_connection_root).replace('\\', '\\\\') self.file.write('\t\tSccProjectUniqueName%(i)s = %(dsp_relative_file_path)s\n' '\t\tSccLocalPath%(i)d = %(scc_local_path)s\n' '\t\tCanCheckoutShared = true\n' '\t\tSccProjectFilePathRelativizedFromConnection%(i)s = %(dsp_scc_relative_folder_path)s\\\\\n' % locals()) self.file.write('\tEndGlobalSection\n') if self.version_num >= 8.0: self.file.write('\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n') else: self.file.write('\tGlobalSection(SolutionConfiguration) = preSolution\n') confkeys = sorted(self.configs.keys()) cnt = 0 for name in confkeys: variant = self.configs[name].variant platform = self.configs[name].platform if self.version_num >= 8.0: self.file.write('\t\t%s|%s = %s|%s\n' % (variant, platform, variant, platform)) else: self.file.write('\t\tConfigName.%d = %s\n' % (cnt, variant)) cnt = cnt + 1 self.file.write('\tEndGlobalSection\n') if self.version_num <= 7.1: self.file.write('\tGlobalSection(ProjectDependencies) = postSolution\n' '\tEndGlobalSection\n') if self.version_num >= 8.0: self.file.write('\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n') else: self.file.write('\tGlobalSection(ProjectConfiguration) = postSolution\n') for name in confkeys: variant = self.configs[name].variant platform = self.configs[name].platform if self.version_num >= 8.0: for dspinfo in self.dspfiles_info: guid = dspinfo['GUID'] 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: for dspinfo in self.dspfiles_info: guid = dspinfo['GUID'] 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') if self.version_num >= 8.0: self.file.write('\tGlobalSection(SolutionProperties) = preSolution\n' '\t\tHideSolutionNode = FALSE\n' '\tEndGlobalSection\n') else: self.file.write('\tGlobalSection(ExtensibilityGlobals) = postSolution\n' '\tEndGlobalSection\n' '\tGlobalSection(ExtensibilityAddIns) = postSolution\n' '\tEndGlobalSection\n') self.file.write('EndGlobal\n') if self.nokeep == 0: pdata = pickle.dumps(self.configs,1) pdata = base64.encodestring(pdata) self.file.write(pdata + '\n') def Build(self): try: self.file = open(self.dswfile,'w') except IOError, detail: raise SCons.Errors.InternalError('Unable to open "' + self.dswfile + '" for writing:' + str(detail)) else: self.PrintSolution() self.file.close() V6DSWHeader = """\ Microsoft Developer Studio Workspace File, Format Version 6.00 # WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! ############################################################################### Project: "%(name)s"="%(dspfile)s" - Package Owner=<4> Package=<5> {{{ }}} Package=<4> {{{ }}} ############################################################################### Global: Package=<5> {{{ }}} Package=<3> {{{ }}} ############################################################################### """ class _GenerateV6DSW(_DSWGenerator): """Generates a Workspace file for MSVS 6.0""" def PrintWorkspace(self): """ writes a DSW file """ name = self.name dspfile = os.path.relpath(self.dspfiles[0], self.dsw_folder_path) self.file.write(V6DSWHeader % locals()) def Build(self): try: self.file = open(self.dswfile,'w') except IOError, detail: raise SCons.Errors.InternalError('Unable to open "' + self.dswfile + '" for writing:' + str(detail)) else: self.PrintWorkspace() self.file.close() def GenerateDSP(dspfile, source, env): """Generates a Project file based on the version of MSVS that is being used""" version_num = 6.0 if 'MSVS_VERSION' in env: version_num, suite = msvs_parse_version(env['MSVS_VERSION']) if version_num >= 10.0: g = _GenerateV10DSP(dspfile, source, env) g.Build() elif version_num >= 7.0: g = _GenerateV7DSP(dspfile, source, env) g.Build() else: g = _GenerateV6DSP(dspfile, source, env) g.Build() def GenerateDSW(dswfile, source, env): """Generates a Solution/Workspace file based on the version of MSVS that is being used""" version_num = 6.0 if 'MSVS_VERSION' in env: version_num, suite = msvs_parse_version(env['MSVS_VERSION']) if version_num >= 7.0: g = _GenerateV7DSW(dswfile, source, env) g.Build() else: g = _GenerateV6DSW(dswfile, source, env) g.Build() ############################################################################## # Above here are the classes and functions for generation of # DSP/DSW/SLN/VCPROJ files. ############################################################################## def GetMSVSProjectSuffix(target, source, env, for_signature): return env['MSVS']['PROJECTSUFFIX'] def GetMSVSSolutionSuffix(target, source, env, for_signature): return env['MSVS']['SOLUTIONSUFFIX'] def GenerateProject(target, source, env): # generate the dsp file, according to the version of MSVS. builddspfile = target[0] dspfile = builddspfile.srcnode() # this detects whether or not we're using a VariantDir if not dspfile is builddspfile: try: bdsp = open(str(builddspfile), "w+") except IOError, detail: print 'Unable to open "' + str(dspfile) + '" for writing:',detail,'\n' raise bdsp.write("This is just a placeholder file.\nThe real project file is here:\n%s\n" % dspfile.get_abspath()) GenerateDSP(dspfile, source, env) if env.get('auto_build_solution', 1): builddswfile = target[1] dswfile = builddswfile.srcnode() if not dswfile is builddswfile: try: bdsw = open(str(builddswfile), "w+") except IOError, detail: print 'Unable to open "' + str(dspfile) + '" for writing:',detail,'\n' raise bdsw.write("This is just a placeholder file.\nThe real workspace file is here:\n%s\n" % dswfile.get_abspath()) GenerateDSW(dswfile, source, env) def GenerateSolution(target, source, env): GenerateDSW(target[0], source, env) def projectEmitter(target, source, env): """Sets up the DSP dependencies.""" # todo: Not sure what sets source to what user has passed as target, # but this is what happens. When that is fixed, we also won't have # to make the user always append env['MSVSPROJECTSUFFIX'] to target. if source[0] == target[0]: source = [] # make sure the suffix is correct for the version of MSVS we're running. (base, suff) = SCons.Util.splitext(str(target[0])) suff = env.subst('$MSVSPROJECTSUFFIX') target[0] = base + suff if not source: source = 'prj_inputs:' source = source + env.subst('$MSVSSCONSCOM', 1) source = source + env.subst('$MSVSENCODING', 1) # Project file depends on CPPDEFINES and CPPPATH preprocdefs = xmlify(';'.join(processDefines(env.get('CPPDEFINES', [])))) includepath_Dirs = processIncludes(env.get('CPPPATH', []), env, None, None) includepath = xmlify(';'.join([str(x) for x in includepath_Dirs])) source = source + "; ppdefs:%s incpath:%s"%(preprocdefs, includepath) if 'buildtarget' in env and env['buildtarget'] != None: if SCons.Util.is_String(env['buildtarget']): source = source + ' "%s"' % env['buildtarget'] elif SCons.Util.is_List(env['buildtarget']): for bt in env['buildtarget']: if SCons.Util.is_String(bt): source = source + ' "%s"' % bt else: try: source = source + ' "%s"' % bt.get_abspath() except AttributeError: raise SCons.Errors.InternalError("buildtarget can be a string, a node, a list of strings or nodes, or None") else: try: source = source + ' "%s"' % env['buildtarget'].get_abspath() except AttributeError: raise SCons.Errors.InternalError("buildtarget can be a string, a node, a list of strings or nodes, or None") if 'outdir' in env and env['outdir'] != None: if SCons.Util.is_String(env['outdir']): source = source + ' "%s"' % env['outdir'] elif SCons.Util.is_List(env['outdir']): for s in env['outdir']: if SCons.Util.is_String(s): source = source + ' "%s"' % s else: try: source = source + ' "%s"' % s.get_abspath() except AttributeError: raise SCons.Errors.InternalError("outdir can be a string, a node, a list of strings or nodes, or None") else: try: source = source + ' "%s"' % env['outdir'].get_abspath() except AttributeError: raise SCons.Errors.InternalError("outdir can be a string, a node, a list of strings or nodes, or None") if 'name' in env: if SCons.Util.is_String(env['name']): source = source + ' "%s"' % env['name'] else: raise SCons.Errors.InternalError("name must be a string") if 'variant' in env: if SCons.Util.is_String(env['variant']): source = source + ' "%s"' % env['variant'] elif SCons.Util.is_List(env['variant']): for variant in env['variant']: if SCons.Util.is_String(variant): source = source + ' "%s"' % variant else: raise SCons.Errors.InternalError("name must be a string or a list of strings") else: raise SCons.Errors.InternalError("variant must be a string or a list of strings") else: raise SCons.Errors.InternalError("variant must be specified") for s in _DSPGenerator.srcargs: if s in env: if SCons.Util.is_String(env[s]): source = source + ' "%s' % env[s] elif SCons.Util.is_List(env[s]): for t in env[s]: if SCons.Util.is_String(t): source = source + ' "%s"' % t else: raise SCons.Errors.InternalError(s + " must be a string or a list of strings") else: raise SCons.Errors.InternalError(s + " must be a string or a list of strings") source = source + ' "%s"' % str(target[0]) source = [SCons.Node.Python.Value(source)] targetlist = [target[0]] sourcelist = source if env.get('auto_build_solution', 1): env['projects'] = [env.File(t).srcnode() for t in targetlist] t, s = solutionEmitter(target, target, env) targetlist = targetlist + t # Beginning with Visual Studio 2010 for each project file (.vcxproj) we have additional file (.vcxproj.filters) if float(env['MSVS_VERSION']) >= 10.0: targetlist.append(targetlist[0] + '.filters') return (targetlist, sourcelist) def solutionEmitter(target, source, env): """Sets up the DSW dependencies.""" # todo: Not sure what sets source to what user has passed as target, # but this is what happens. When that is fixed, we also won't have # to make the user always append env['MSVSSOLUTIONSUFFIX'] to target. if source[0] == target[0]: source = [] # make sure the suffix is correct for the version of MSVS we're running. (base, suff) = SCons.Util.splitext(str(target[0])) suff = env.subst('$MSVSSOLUTIONSUFFIX') target[0] = base + suff if not source: source = 'sln_inputs:' if 'name' in env: if SCons.Util.is_String(env['name']): source = source + ' "%s"' % env['name'] else: raise SCons.Errors.InternalError("name must be a string") if 'variant' in env: if SCons.Util.is_String(env['variant']): source = source + ' "%s"' % env['variant'] elif SCons.Util.is_List(env['variant']): for variant in env['variant']: if SCons.Util.is_String(variant): source = source + ' "%s"' % variant else: raise SCons.Errors.InternalError("name must be a string or a list of strings") else: raise SCons.Errors.InternalError("variant must be a string or a list of strings") else: raise SCons.Errors.InternalError("variant must be specified") if 'slnguid' in env: if SCons.Util.is_String(env['slnguid']): source = source + ' "%s"' % env['slnguid'] else: raise SCons.Errors.InternalError("slnguid must be a string") if 'projects' in env: if SCons.Util.is_String(env['projects']): source = source + ' "%s"' % env['projects'] elif SCons.Util.is_List(env['projects']): for t in env['projects']: if SCons.Util.is_String(t): source = source + ' "%s"' % t source = source + ' "%s"' % str(target[0]) source = [SCons.Node.Python.Value(source)] return ([target[0]], source) projectAction = SCons.Action.Action(GenerateProject, None) solutionAction = SCons.Action.Action(GenerateSolution, None) projectBuilder = SCons.Builder.Builder(action = '$MSVSPROJECTCOM', suffix = '$MSVSPROJECTSUFFIX', emitter = projectEmitter) solutionBuilder = SCons.Builder.Builder(action = '$MSVSSOLUTIONCOM', suffix = '$MSVSSOLUTIONSUFFIX', emitter = solutionEmitter) default_MSVS_SConscript = None def generate(env): """Add Builders and construction variables for Microsoft Visual Studio project files to an Environment.""" try: env['BUILDERS']['MSVSProject'] except KeyError: env['BUILDERS']['MSVSProject'] = projectBuilder try: env['BUILDERS']['MSVSSolution'] except KeyError: env['BUILDERS']['MSVSSolution'] = solutionBuilder env['MSVSPROJECTCOM'] = projectAction env['MSVSSOLUTIONCOM'] = solutionAction if SCons.Script.call_stack: # XXX Need to find a way to abstract this; the build engine # shouldn't depend on anything in SCons.Script. env['MSVSSCONSCRIPT'] = SCons.Script.call_stack[0].sconscript else: global default_MSVS_SConscript if default_MSVS_SConscript is None: default_MSVS_SConscript = env.File('SConstruct') env['MSVSSCONSCRIPT'] = default_MSVS_SConscript env['MSVSSCONS'] = '"%s" -c "%s"' % (python_executable, getExecScriptMain(env)) env['MSVSSCONSFLAGS'] = '-C "${MSVSSCONSCRIPT.dir.abspath}" -f ${MSVSSCONSCRIPT.name}' env['MSVSSCONSCOM'] = '$MSVSSCONS $MSVSSCONSFLAGS' env['MSVSBUILDCOM'] = '$MSVSSCONSCOM "$MSVSBUILDTARGET"' env['MSVSREBUILDCOM'] = '$MSVSSCONSCOM "$MSVSBUILDTARGET"' env['MSVSCLEANCOM'] = '$MSVSSCONSCOM -c "$MSVSBUILDTARGET"' # Set-up ms tools paths for default version msvc_setup_env_once(env) if 'MSVS_VERSION' in env: version_num, suite = msvs_parse_version(env['MSVS_VERSION']) else: (version_num, suite) = (7.0, None) # guess at a default if 'MSVS' not in env: env['MSVS'] = {} if (version_num < 7.0): env['MSVS']['PROJECTSUFFIX'] = '.dsp' env['MSVS']['SOLUTIONSUFFIX'] = '.dsw' elif (version_num < 10.0): env['MSVS']['PROJECTSUFFIX'] = '.vcproj' env['MSVS']['SOLUTIONSUFFIX'] = '.sln' else: env['MSVS']['PROJECTSUFFIX'] = '.vcxproj' env['MSVS']['SOLUTIONSUFFIX'] = '.sln' if (version_num >= 10.0): env['MSVSENCODING'] = 'utf-8' else: env['MSVSENCODING'] = 'Windows-1252' env['GET_MSVSPROJECTSUFFIX'] = GetMSVSProjectSuffix env['GET_MSVSSOLUTIONSUFFIX'] = GetMSVSSolutionSuffix env['MSVSPROJECTSUFFIX'] = '${GET_MSVSPROJECTSUFFIX}' env['MSVSSOLUTIONSUFFIX'] = '${GET_MSVSSOLUTIONSUFFIX}' env['SCONS_HOME'] = os.environ.get('SCONS_HOME') def exists(env): return msvc_exists() # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/mingw.xml0000644000175000017500000000117312114661560022057 0ustar dktrkranzdktrkranz Sets construction variables for MinGW (Minimal Gnu on Windows). CC SHCCFLAGS CXX SHCXXFLAGS SHLINKFLAGS SHLINKCOM LDMODULECOM AS WINDOWSDEFPREFIX WINDOWSDEFSUFFIX SHOBJSUFFIX RC RCFLAGS RCINCFLAGS RCINCPREFIX RCINCSUFFIX RCCOM OBJSUFFIX LIBPREFIX LIBSUFFIX SHLINKCOMSTR RCCOMSTR scons-doc-2.3.0/src/engine/SCons/Tool/BitKeeper.py0000644000175000017500000000470212114661557022447 0ustar dktrkranzdktrkranz"""SCons.Tool.BitKeeper.py Tool-specific initialization for the BitKeeper source code control system. There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Tool.Tool() selection method. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Tool/BitKeeper.py 2013/03/03 09:48:35 garyo" import SCons.Action import SCons.Builder import SCons.Util def generate(env): """Add a Builder factory function and construction variables for BitKeeper to an Environment.""" def BitKeeperFactory(env=env): """ """ import SCons.Warnings as W W.warn(W.DeprecatedSourceCodeWarning, """The BitKeeper() factory is deprecated and there is no replacement.""") act = SCons.Action.Action("$BITKEEPERCOM", "$BITKEEPERCOMSTR") return SCons.Builder.Builder(action = act, env = env) #setattr(env, 'BitKeeper', BitKeeperFactory) env.BitKeeper = BitKeeperFactory env['BITKEEPER'] = 'bk' env['BITKEEPERGET'] = '$BITKEEPER get' env['BITKEEPERGETFLAGS'] = SCons.Util.CLVar('') env['BITKEEPERCOM'] = '$BITKEEPERGET $BITKEEPERGETFLAGS $TARGET' def exists(env): return env.Detect('bk') # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Tool/pdftex.xml0000644000175000017500000000202312114661560022223 0ustar dktrkranzdktrkranz Sets construction variables for the &pdftex; utility. PDFTEX PDFTEXFLAGS PDFTEXCOM PDFLATEX PDFLATEXFLAGS PDFLATEXCOM LATEXRETRIES PDFTEXCOMSTR PDFLATEXCOMSTR The &pdftex; utility. The command line used to call the &pdftex; utility. The string displayed when calling the &pdftex; utility. If this is not set, then &cv-link-PDFTEXCOM; (the command line) is displayed. env = Environment(PDFTEXCOMSTR = "Building $TARGET from TeX input $SOURCES") General options passed to the &pdftex; utility. scons-doc-2.3.0/src/engine/SCons/Environment.xml0000644000175000017500000022675412114661557022351 0ustar dktrkranzdktrkranz A dictionary mapping the names of the builders available through this environment to underlying Builder objects. Builders named Alias, CFile, CXXFile, DVI, Library, Object, PDF, PostScript, and Program are available by default. If you initialize this variable when an Environment is created: env = Environment(BUILDERS = {'NewBuilder' : foo}) the default Builders will no longer be available. To use a new Builder object in addition to the default Builders, add your new Builder object like this: env = Environment() env.Append(BUILDERS = {'NewBuilder' : foo}) or this: env = Environment() env['BUILDERS]['NewBuilder'] = foo A function that converts a string into a Dir instance relative to the target being built. A dictionary of environment variables to use when invoking commands. When &cv-ENV; is used in a command all list values will be joined using the path separator and any other non-string values will simply be coerced to a string. Note that, by default, &scons; does not propagate the environment in force when you execute &scons; to the commands used to build target files. This is so that builds will be guaranteed repeatable regardless of the environment variables set at the time &scons; is invoked. If you want to propagate your environment variables to the commands executed to build target files, you must do so explicitly: import os env = Environment(ENV = os.environ) Note that you can choose only to propagate certain environment variables. A common example is the system PATH environment variable, so that &scons; uses the same utilities as the invoking shell (or other process): import os env = Environment(ENV = {'PATH' : os.environ['PATH']}) A function that converts a string into a File instance relative to the target being built. A list of the available implicit dependency scanners. New file scanners may be added by appending to this list, although the more flexible approach is to associate scanners with a specific Builder. See the sections "Builder Objects" and "Scanner Objects," below, for more information. A reserved variable name that may not be set or used in a construction environment. (See "Variable Substitution," below.) A reserved variable name that may not be set or used in a construction environment. (See "Variable Substitution," below.) A reserved variable name that may not be set or used in a construction environment. (See "Variable Substitution," below.) A reserved variable name that may not be set or used in a construction environment. (See "Variable Substitution," below.) A reserved variable name that may not be set or used in a construction environment. (See "Variable Substitution," below.) A reserved variable name that may not be set or used in a construction environment. (See "Variable Substitution," below.) A reserved variable name that may not be set or used in a construction environment. (See "Variable Substitution," below.) A reserved variable name that may not be set or used in a construction environment. (See "Variable Substitution," below.) A list of the names of the Tool specifications that are part of this construction environment. (action, [cmd/str/fun, [var, ...]] [option=value, ...]) Creates an Action object for the specified action. See the section "Action Objects," below, for a complete explanation of the arguments and behavior. Note that the env.Action() form of the invocation will expand construction variables in any argument strings, including the action argument, at the time it is called using the construction variables in the env construction environment through which env.Action() was called. The Action() form delays all variable expansion until the Action object is actually used. (object, function, [name]) (function, [name]) When called with the AddMethod() form, adds the specified function to the specified object as the specified method name. When called with the env.AddMethod() form, adds the specified function to the construction environment env as the specified method name. In both cases, if name is omitted or None, the name of the specified function itself is used for the method name. Examples: # 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') (target, action) Arranges for the specified action to be performed after the specified target has been built. The specified action(s) may be an Action object, or anything that can be converted into an Action object (see below). When multiple targets are supplied, the action may be called multiple times, once after each action that generates one or more targets in the list. (target, action) Arranges for the specified action to be performed before the specified target is built. The specified action(s) may be an Action object, or anything that can be converted into an Action object (see below). When multiple targets are specified, the action(s) may be called multiple times, once before each action that generates one or more targets in the list. Note that if any of the targets are built in multiple steps, the action will be invoked just before the "final" action that specifically generates the specified target(s). For example, when building an executable program from a specified source .c file via an intermediate object file: foo = Program('foo.c') AddPreAction(foo, 'pre_action') The specified pre_action would be executed before &scons; calls the link command that actually generates the executable program binary foo, not before compiling the foo.c file into an object file. (alias, [targets, [action]]) Creates one or more phony targets that expand to one or more other targets. An optional action (command) or list of actions can be specified that will be executed whenever the any of the alias targets are out-of-date. Returns the Node object representing the alias, which exists outside of any file system. This Node object, or the alias name, may be used as a dependency of any other target, including another alias. &f-Alias; can be called multiple times for the same alias to add additional targets to the alias, or additional actions to the list for this alias. Examples: Alias('install') Alias('install', '/usr/bin') Alias(['install', 'install-lib'], '/usr/local/lib') env.Alias('install', ['/usr/local/bin', '/usr/local/lib']) env.Alias('install', ['/usr/local/man']) env.Alias('update', ['file1', 'file2'], "update_database $SOURCES") (target, ...) Marks each given target so that it is always assumed to be out of date, and will always be rebuilt if needed. Note, however, that &f-AlwaysBuild; does not add its target(s) to the default target list, so the targets will only be built if they are specified on the command line, or are a dependent of a target specified on the command line--but they will always be built if so specified. Multiple targets can be passed in to a single call to &f-AlwaysBuild;. (key=val, [...]) Appends the specified keyword arguments to the end of construction variables in the environment. If the Environment does not have the specified construction variable, it is simply added to the environment. If the values of the construction variable and the keyword argument are the same type, then the two values will be simply added together. Otherwise, the construction variable and the value of the keyword argument are both coerced to lists, and the lists are added together. (See also the Prepend method, below.) Example: env.Append(CCFLAGS = ' -g', FOO = ['foo.yyy']) (name, newpath, [envname, sep, delete_existing]) This appends new path elements to the given path in the specified external environment (ENV by default). This will only add any particular path once (leaving the last one it encounters and ignoring the rest, to preserve path order), and to help assure this, will normalize all paths (using os.path.normpath and os.path.normcase). This can also handle the case where the given old path variable is a list instead of a string, in which case a list will be returned instead of a string. If delete_existing is 0, then adding a path that already exists will not move it to the end; it will stay where it is in the list. Example: print 'before:',env['ENV']['INCLUDE'] include_path = '/foo/bar:/foo' env.AppendENVPath('INCLUDE', include_path) print 'after:',env['ENV']['INCLUDE'] yields: before: /foo:/biz after: /biz:/foo/bar:/foo (key=val, [...], delete_existing=0) Appends the specified keyword arguments to the end of construction variables in the environment. If the Environment does not have the specified construction variable, it is simply added to the environment. If the construction variable being appended to is a list, then any value(s) that already exist in the construction variable will not be added again to the list. However, if delete_existing is 1, existing matching values are removed first, so existing values in the arg list move to the end of the list. Example: env.AppendUnique(CCFLAGS = '-g', FOO = ['foo.yyy']) (build_dir, src_dir, [duplicate]) Deprecated synonyms for &f-VariantDir; and env.VariantDir(). The build_dir argument becomes the variant_dir argument of &f-VariantDir; or env.VariantDir(). (action, [arguments]) Creates a Builder object for the specified action. See the section "Builder Objects," below, for a complete explanation of the arguments and behavior. Note that the env.Builder() form of the invocation will expand construction variables in any arguments strings, including the action argument, at the time it is called using the construction variables in the env construction environment through which env.Builder() was called. The &f-Builder; form delays all variable expansion until after the Builder object is actually called. (cache_dir) Specifies that &scons; will maintain a cache of derived files in cache_dir. The derived files in the cache will be shared among all the builds using the same &f-CacheDir; call. Specifying a cache_dir of None disables derived file caching. Calling env.CacheDir() will only affect targets built through the specified construction environment. Calling &f-CacheDir; sets a global default that will be used by all targets built through construction environments that do not have an env.CacheDir() specified. When a CacheDir() is being used and &scons; finds a derived file that needs to be rebuilt, it will first look in the cache to see if a derived file has already been built from identical input files and an identical build action (as incorporated into the MD5 build signature). If so, &scons; will retrieve the file from the cache. If the derived file is not present in the cache, &scons; will rebuild it and then place a copy of the built file in the cache (identified by its MD5 build signature), so that it may be retrieved by other builds that need to build the same derived file from identical inputs. Use of a specified &f-CacheDir; may be disabled for any invocation by using the option. If the option is used, &scons; will place a copy of all derived files in the cache, even if they already existed and were not built by this invocation. This is useful to populate a cache the first time &f-CacheDir; is added to a build, or after using the option. When using &f-CacheDir;, &scons; will report, "Retrieved `file' from cache," unless the option is being used. When the option is used, &scons; will print the action that would have been used to build the file, without any indication that the file was actually retrieved from the cache. This is useful to generate build logs that are equivalent regardless of whether a given derived file has been built in-place or retrieved from the cache. The &f-link-NoCache; method can be used to disable caching of specific files. This can be useful if inputs and/or outputs of some tool are impossible to predict or prohibitively large. (targets, files_or_dirs) This specifies a list of files or directories which should be removed whenever the targets are specified with the command line option. The specified targets may be a list or an individual target. Multiple calls to &f-Clean; are legal, and create new targets or add files and directories to the clean list for the specified targets. Multiple files or directories should be specified either as separate arguments to the &f-Clean; method, or as a list. &f-Clean; will also accept the return value of any of the construction environment Builder methods. Examples: The related &f-link-NoClean; function overrides calling &f-Clean; for the same target, and any targets passed to both functions will not be removed by the option. Examples: Clean('foo', ['bar', 'baz']) Clean('dist', env.Program('hello', 'hello.c')) Clean(['foo', 'bar'], 'something_else_to_clean') In this example, installing the project creates a subdirectory for the documentation. This statement causes the subdirectory to be removed if the project is deinstalled. Clean(docdir, os.path.join(docdir, projectname)) ([key=val, ...]) Returns a separate copy of a construction environment. If there are any keyword arguments specified, they are added to the returned copy, overwriting any existing values for the keywords. Example: env2 = env.Clone() env3 = env.Clone(CCFLAGS = '-g') Additionally, a list of tools and a toolpath may be specified, as in the Environment constructor: def MyTool(env): env['FOO'] = 'bar' env4 = env.Clone(tools = ['msvc', MyTool]) The parse_flags keyword argument is also recognized: # create an environment for compiling programs that use wxWidgets wx_env = env.Clone(parse_flags = '!wx-config --cflags --cxxflags') The &b-Command; "Builder" is actually implemented as a function that looks like a Builder, but actually takes an additional argument of the action from which the Builder should be made. See the &f-link-Command; function description for the calling syntax and details. (target, source, action, [key=val, ...]) Executes a specific action (or list of actions) to build a target file or files. This is more convenient than defining a separate Builder object for a single special-case build. As a special case, the source_scanner keyword argument can be used to specify a Scanner object that will be used to scan the sources. (The global DirScanner object can be used if any of the sources will be directories that must be scanned on-disk for changes to files that aren't already specified in other Builder of function calls.) Any other keyword arguments specified override any same-named existing construction variables. An action can be an external command, specified as a string, or a callable Python object; see "Action Objects," below, for more complete information. Also note that a string specifying an external command may be preceded by an @ (at-sign) to suppress printing the command in question, or by a - (hyphen) to ignore the exit status of the external command. Examples: env.Command('foo.out', 'foo.in', "$FOO_BUILD < $SOURCES > $TARGET") env.Command('bar.out', 'bar.in', ["rm -f $TARGET", "$BAR_BUILD < $SOURCES > $TARGET"], ENV = {'PATH' : '/usr/local/bin/'}) def rename(env, target, source): import os os.rename('.tmp', str(target[0])) env.Command('baz.out', 'baz.in', ["$BAZ_BUILD < $SOURCES > .tmp", rename ]) Note that the &f-Command; function will usually assume, by default, that the specified targets and/or sources are Files, if no other part of the configuration identifies what type of entry it is. If necessary, you can explicitly specify that targets or source nodes should be treated as directoriese by using the &f-link-Dir; or env.Dir() functions. Examples: env.Command('ddd.list', Dir('ddd'), 'ls -l $SOURCE > $TARGET') env['DISTDIR'] = 'destination/directory' env.Command(env.Dir('$DISTDIR')), None, make_distdir) (Also note that SCons will usually automatically create any directory necessary to hold a target file, so you normally don't need to create directories by hand.) (env, [custom_tests, conf_dir, log_file, config_h]) ([custom_tests, conf_dir, log_file, config_h]) Creates a Configure object for integrated functionality similar to GNU autoconf. See the section "Configure Contexts," below, for a complete explanation of the arguments and behavior. ([key=val, ...]) A now-deprecated synonym for env.Clone(). (function) Specifies that all up-to-date decisions for targets built through this construction environment will be handled by the specified function. The function can be one of the following strings that specify the type of decision function to be performed: timestamp-newer Specifies that a target shall be considered out of date and rebuilt if the dependency's timestamp is newer than the target file's timestamp. This is the behavior of the classic Make utility, and make can be used a synonym for timestamp-newer. timestamp-match Specifies that a target shall be considered out of date and rebuilt if the dependency's timestamp is different than the timestamp recorded the last time the target was built. This provides behavior very similar to the classic Make utility (in particular, files are not opened up so that their contents can be checksummed) except that the target will also be rebuilt if a dependency file has been restored to a version with an earlier timestamp, such as can happen when restoring files from backup archives. MD5 Specifies that a target shall be considered out of date and rebuilt if the dependency's content has changed sine the last time the target was built, as determined be performing an MD5 checksum on the dependency's contents and comparing it to the checksum recorded the last time the target was built. content can be used as a synonym for MD5. MD5-timestamp Specifies that a target shall be considered out of date and rebuilt if the dependency's content has changed sine the last time the target was built, except that dependencies with a timestamp that matches the last time the target was rebuilt will be assumed to be up-to-date and not rebuilt. This provides behavior very similar to the MD5 behavior of always checksumming file contents, with an optimization of not checking the contents of files whose timestamps haven't changed. The drawback is that SCons will not detect if a file's content has changed but its timestamp is the same, as might happen in an automated script that runs a build, updates a file, and runs the build again, all within a single second. Examples: # Use exact timestamp matches by default. Decider('timestamp-match') # Use MD5 content signatures for any targets built # with the attached construction environment. env.Decider('content') In addition to the above already-available functions, the function argument may be an actual Python function that takes the following three arguments: dependency The Node (file) which should cause the target to be rebuilt if it has "changed" since the last tme target was built. target The Node (file) being built. In the normal case, this is what should get rebuilt if the dependency has "changed." prev_ni Stored information about the state of the dependency the last time the target was built. This can be consulted to match various file characteristics such as the timestamp, size, or content signature. The function should return a True (non-zero) value if the dependency has "changed" since the last time the target was built (indicating that the target should be rebuilt), and False (zero) otherwise (indicating that the target should not be rebuilt). Note that the decision can be made using whatever criteria are appopriate. Ignoring some or all of the function arguments is perfectly normal. Example: def my_decider(dependency, target, prev_ni): return not os.path.exists(str(target)) env.Decider(my_decider) (target, dependency) Specifies an explicit dependency; the target will be rebuilt whenever the dependency has changed. Both the specified target and dependency can be a string (usually the path name of a file or directory) or Node objects, or a list of strings or Node objects (such as returned by a Builder call). This should only be necessary for cases where the dependency is not caught by a Scanner for the file. Example: env.Depends('foo', 'other-input-file-for-foo') mylib = env.Library('mylib.c') installed_lib = env.Install('lib', mylib) bar = env.Program('bar.c') # Arrange for the library to be copied into the installation # directory before trying to build the "bar" program. # (Note that this is for example only. A "real" library # dependency would normally be configured through the $LIBS # and $LIBPATH variables, not using an env.Depends() call.) env.Depends(bar, installed_lib) ([vars]) Returns a dictionary object containing copies of all of the construction variables in the environment. If there are any variable names specified, only the specified construction variables are returned in the dictionary. Example: dict = env.Dictionary() cc_dict = env.Dictionary('CC', 'CCFLAGS', 'CCCOM') (name, [directory]) This returns a Directory Node, an object that represents the specified directory name. name can be a relative or absolute path. directory is an optional directory that will be used as the parent directory. If no directory is specified, the current script's directory is used as the parent. If name is a list, SCons returns a list of Dir nodes. Construction variables are expanded in name. Directory Nodes can be used anywhere you would supply a string as a directory name to a Builder method or function. Directory Nodes have attributes and methods that are useful in many situations; see "File and Directory Nodes," below. ([key]) Returns a pretty printable representation of the environment. key, if not None, should be a string containing the name of the variable of interest. This SConstruct: env=Environment() print env.Dump('CCCOM') will print: '$CC -c -o $TARGET $CCFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS $SOURCES' While this SConstruct: env=Environment() print env.Dump() will print: { 'AR': 'ar', 'ARCOM': '$AR $ARFLAGS $TARGET $SOURCES\n$RANLIB $RANLIBFLAGS $TARGET', 'ARFLAGS': ['r'], 'AS': 'as', 'ASCOM': '$AS $ASFLAGS -o $TARGET $SOURCES', 'ASFLAGS': [], ... ([key=value, ...]) Return a new construction environment initialized with the specified key=value pairs. (action, [strfunction, varlist]) Executes an Action object. The specified action may be an Action object (see the section "Action Objects," below, for a complete explanation of the arguments and behavior), or it may be a command-line string, list of commands, or executable Python function, each of which will be converted into an Action object and then executed. The exit value of the command or return value of the Python function will be returned. Note that &scons; will print an error message if the executed action fails--that is, exits with or returns a non-zero value. &scons; will not, however, automatically terminate the build if the specified action fails. If you want the build to stop in response to a failed &f-Execute; call, you must explicitly check for a non-zero return value: Execute(Copy('file.out', 'file.in')) if Execute("mkdir sub/dir/ectory"): # The mkdir failed, don't try to build. Exit(1) (name, [directory]) This returns a File Node, an object that represents the specified file name. name can be a relative or absolute path. directory is an optional directory that will be used as the parent directory. If name is a list, SCons returns a list of File nodes. Construction variables are expanded in name. File Nodes can be used anywhere you would supply a string as a file name to a Builder method or function. File Nodes have attributes and methods that are useful in many situations; see "File and Directory Nodes," below. (file, dirs) Search for file in the path specified by dirs. dirs may be a list of directory names or a single directory name. In addition to searching for files that exist in the filesystem, this function also searches for derived files that have not yet been built. Example: foo = env.FindFile('foo', ['dir1', 'dir2']) () Returns the list of targets set up by the &b-link-Install; or &b-link-InstallAs; builders. This function serves as a convenient method to select the contents of a binary package. Example: Install( '/bin', [ 'executable_a', 'executable_b' ] ) # will return the file node list # [ '/bin/executable_a', '/bin/executable_b' ] FindInstalledFiles() Install( '/lib', [ 'some_library' ] ) # will return the file node list # [ '/bin/executable_a', '/bin/executable_b', '/lib/some_library' ] FindInstalledFiles() (node='"."') Returns the list of nodes which serve as the source of the built files. It does so by inspecting the dependency tree starting at the optional argument node which defaults to the '"."'-node. It will then return all leaves of node. These are all children which have no further children. This function is a convenient method to select the contents of a Source Package. Example: Program( 'src/main_a.c' ) Program( 'src/main_b.c' ) Program( 'main_c.c' ) # returns ['main_c.c', 'src/main_a.c', 'SConstruct', 'src/main_b.c'] FindSourceFiles() # returns ['src/main_b.c', 'src/main_a.c' ] FindSourceFiles( 'src' ) As you can see build support files (SConstruct in the above example) will also be returned by this function. (sequence) Takes a sequence (that is, a Python list or tuple) that may contain nested sequences and returns a flattened list containing all of the individual elements in any sequence. This can be helpful for collecting the lists returned by calls to Builders; other Builders will automatically flatten lists specified as input, but direct Python manipulation of these lists does not. Examples: foo = Object('foo.c') bar = Object('bar.c') # Because `foo' and `bar' are lists returned by the Object() Builder, # `objects' will be a list containing nested lists: objects = ['f1.o', foo, 'f2.o', bar, 'f3.o'] # Passing such a list to another Builder is all right because # the Builder will flatten the list automatically: Program(source = objects) # If you need to manipulate the list directly using Python, you need to # call Flatten() yourself, or otherwise handle nested lists: for object in Flatten(objects): print str(object) (file, [...]) Returns the &scons; path name (or names) for the specified file (or files). The specified file or files may be &scons; Nodes or strings representing path names. (pattern, [ondisk, source, strings]) Returns Nodes (or strings) that match the specified pattern, relative to the directory of the current &SConscript; file. The env.Glob() form performs string substition on pattern and returns whatever matches the resulting expanded pattern. The specified pattern uses Unix shell style metacharacters for matching: * matches everything ? matches any single character [seq] matches any character in seq [!seq] matches any char not in seq If the first character of a filename is a dot, it must be matched explicitly. Character matches do not span directory separators. The &f-Glob; knows about repositories (see the &f-link-Repository; function) and source directories (see the &f-link-VariantDir; function) and returns a Node (or string, if so configured) in the local (SConscript) directory if matching Node is found anywhere in a corresponding repository or source directory. The ondisk argument may be set to False (or any other non-true value) to disable the search for matches on disk, thereby only returning matches among already-configured File or Dir Nodes. The default behavior is to return corresponding Nodes for any on-disk matches found. The source argument may be set to True (or any equivalent value) to specify that, when the local directory is a &f-VariantDir;, the returned Nodes should be from the corresponding source directory, not the local directory. The strings argument may be set to True (or any equivalent value) to have the &f-Glob; function return strings, not Nodes, that represent the matched files or directories. The returned strings will be relative to the local (SConscript) directory. (Note that This may make it easier to perform arbitrary manipulation of file names, but if the returned strings are passed to a different &SConscript; file, any Node translation will be relative to the other &SConscript; directory, not the original &SConscript; directory.) Examples: Program('foo', Glob('*.c')) Zip('/tmp/everything', Glob('.??*') + Glob('*')) (target, dependency) The specified dependency file(s) will be ignored when deciding if the target file(s) need to be rebuilt. You can also use &f-Ignore; to remove a target from the default build. In order to do this you must specify the directory the target will be built in as the target, and the file you want to skip building as the dependency. Note that this will only remove the dependencies listed from the files built by default. It will still be built if that dependency is needed by another object being built. See the third and forth examples below. Examples: env.Ignore('foo', 'foo.c') env.Ignore('bar', ['bar1.h', 'bar2.h']) env.Ignore('.','foobar.obj') env.Ignore('bar','bar/foobar.obj') (string) The specified string will be preserved as-is and not have construction variables expanded. (targets) The specified targets will have copies made in the local tree, even if an already up-to-date copy exists in a repository. Returns a list of the target Node or Nodes. (arg, [unique]) Merges the specified arg values to the construction environment's construction variables. If the arg argument is not a dictionary, it is converted to one by calling &f-link-env-ParseFlags; on the argument before the values are merged. Note that arg must be a single value, so multiple strings must be passed in as a list, not as separate arguments to &f-env-MergeFlags;. By default, duplicate values are eliminated; you can, however, specify unique=0 to allow duplicate values to be added. When eliminating duplicate values, any construction variables that end with the string PATH keep the left-most unique value. All other construction variables keep the right-most unique value. Examples: # Add an optimization flag to $CCFLAGS. env.MergeFlags('-O3') # Combine the flags returned from running pkg-config with an optimization # flag and merge the result into the construction variables. env.MergeFlags(['!pkg-config gtk+-2.0 --cflags', '-O3']) # Combine an optimization flag with the flags returned from running pkg-config # twice and merge the result into the construction variables. env.MergeFlags(['-O3', '!pkg-config gtk+-2.0 --cflags --libs', '!pkg-config libpng12 --cflags --libs']) (target, ...) Specifies a list of files which should not be cached whenever the &f-link-CacheDir; method has been activated. The specified targets may be a list or an individual target. Multiple files should be specified either as separate arguments to the &f-NoCache; method, or as a list. &f-NoCache; will also accept the return value of any of the construction environment Builder methods. Calling &f-NoCache; on directories and other non-File Node types has no effect because only File Nodes are cached. Examples: NoCache('foo.elf') NoCache(env.Program('hello', 'hello.c')) (target, ...) Specifies a list of files or directories which should not be removed whenever the targets (or their dependencies) are specified with the command line option. The specified targets may be a list or an individual target. Multiple calls to &f-NoClean; are legal, and prevent each specified target from being removed by calls to the option. Multiple files or directories should be specified either as separate arguments to the &f-NoClean; method, or as a list. &f-NoClean; will also accept the return value of any of the construction environment Builder methods. Calling &f-NoClean; for a target overrides calling &f-link-Clean; for the same target, and any targets passed to both functions will not be removed by the option. Examples: NoClean('foo.elf') NoClean(env.Program('hello', 'hello.c')) (command, [function, unique]) Calls the specified function to modify the environment as specified by the output of command. The default function is &f-link-env-MergeFlags;, which expects the output of a typical *-config command (for example, gtk-config) and adds the options to the appropriate construction variables. By default, duplicate values are not added to any construction variables; you can specify unique=0 to allow duplicate values to be added. Interpreted options and the construction variables they affect are as specified for the &f-link-env-ParseFlags; method (which this method calls). See that method's description, below, for a table of options and construction variables. (filename, [must_exist, only_one]) Parses the contents of the specified filename as a list of dependencies in the style of &Make; or mkdep, and explicitly establishes all of the listed dependencies. By default, it is not an error if the specified filename does not exist. The optional must_exist argument may be set to a non-zero value to have scons throw an exception and generate an error if the file does not exist, or is otherwise inaccessible. The optional only_one argument may be set to a non-zero value to have scons thrown an exception and generate an error if the file contains dependency information for more than one target. This can provide a small sanity check for files intended to be generated by, for example, the gcc -M flag, which should typically only write dependency information for one output file into a corresponding .d file. The filename and all of the files listed therein will be interpreted relative to the directory of the &SConscript; file which calls the &f-ParseDepends; function. (flags, ...) Parses one or more strings containing typical command-line flags for GCC tool chains and returns a dictionary with the flag values separated into the appropriate SCons construction variables. This is intended as a companion to the &f-link-env-MergeFlags; method, but allows for the values in the returned dictionary to be modified, if necessary, before merging them into the construction environment. (Note that &f-env-MergeFlags; will call this method if its argument is not a dictionary, so it is usually not necessary to call &f-link-env-ParseFlags; directly unless you want to manipulate the values.) If the first character in any string is an exclamation mark (!), the rest of the string is executed as a command, and the output from the command is parsed as GCC tool chain command-line flags and added to the resulting dictionary. Flag values are translated accordig to the prefix found, and added to the following construction variables: -arch CCFLAGS, LINKFLAGS -D CPPDEFINES -framework FRAMEWORKS -frameworkdir= FRAMEWORKPATH -include CCFLAGS -isysroot CCFLAGS, LINKFLAGS -I CPPPATH -l LIBS -L LIBPATH -mno-cygwin CCFLAGS, LINKFLAGS -mwindows LINKFLAGS -pthread CCFLAGS, LINKFLAGS -std= CFLAGS -Wa, ASFLAGS, CCFLAGS -Wl,-rpath= RPATH -Wl,-R, RPATH -Wl,-R RPATH -Wl, LINKFLAGS -Wp, CPPFLAGS - CCFLAGS + CCFLAGS, LINKFLAGS Any other strings not associated with options are assumed to be the names of libraries and added to the &cv-LIBS; construction variable. Examples (all of which produce the same result): dict = env.ParseFlags('-O2 -Dfoo -Dbar=1') dict = env.ParseFlags('-O2', '-Dfoo', '-Dbar=1') dict = env.ParseFlags(['-O2', '-Dfoo -Dbar=1']) dict = env.ParseFlags('-O2', '!echo -Dfoo -Dbar=1') (string) The &f-Platform; form returns a callable object that can be used to initialize a construction environment using the platform keyword of the &f-Environment; function. Example: env = Environment(platform = Platform('win32')) The &f-env-Platform; form applies the callable object for the specified platform string to the environment through which the method was called. env.Platform('posix') Note that the win32 platform adds the SystemDrive and SystemRoot variables from the user's external environment to the construction environment's &cv-link-ENV; dictionary. This is so that any executed commands that use sockets to connect with other systems (such as fetching source files from external CVS repository specifications like :pserver:anonymous@cvs.sourceforge.net:/cvsroot/scons) will work on Windows systems. (key=val, [...]) Appends the specified keyword arguments to the beginning of construction variables in the environment. If the Environment does not have the specified construction variable, it is simply added to the environment. If the values of the construction variable and the keyword argument are the same type, then the two values will be simply added together. Otherwise, the construction variable and the value of the keyword argument are both coerced to lists, and the lists are added together. (See also the Append method, above.) Example: env.Prepend(CCFLAGS = '-g ', FOO = ['foo.yyy']) (name, newpath, [envname, sep, delete_existing]) This appends new path elements to the given path in the specified external environment (&cv-ENV; by default). This will only add any particular path once (leaving the first one it encounters and ignoring the rest, to preserve path order), and to help assure this, will normalize all paths (using os.path.normpath and os.path.normcase). This can also handle the case where the given old path variable is a list instead of a string, in which case a list will be returned instead of a string. If delete_existing is 0, then adding a path that already exists will not move it to the beginning; it will stay where it is in the list. Example: print 'before:',env['ENV']['INCLUDE'] include_path = '/foo/bar:/foo' env.PrependENVPath('INCLUDE', include_path) print 'after:',env['ENV']['INCLUDE'] The above example will print: before: /biz:/foo after: /foo/bar:/foo:/biz (key=val, delete_existing=0, [...]) Appends the specified keyword arguments to the beginning of construction variables in the environment. If the Environment does not have the specified construction variable, it is simply added to the environment. If the construction variable being appended to is a list, then any value(s) that already exist in the construction variable will not be added again to the list. However, if delete_existing is 1, existing matching values are removed first, so existing values in the arg list move to the front of the list. Example: env.PrependUnique(CCFLAGS = '-g', FOO = ['foo.yyy']) (key=val, [...]) Replaces construction variables in the Environment with the specified keyword arguments. Example: env.Replace(CCFLAGS = '-g', FOO = 'foo.xxx') (directory) Specifies that directory is a repository to be searched for files. Multiple calls to &f-Repository; are legal, and each one adds to the list of repositories that will be searched. To &scons;, a repository is a copy of the source tree, from the top-level directory on down, which may contain both source files and derived files that can be used to build targets in the local source tree. The canonical example would be an official source tree maintained by an integrator. If the repository contains derived files, then the derived files should have been built using &scons;, so that the repository contains the necessary signature information to allow &scons; to figure out when it is appropriate to use the repository copy of a derived file, instead of building one locally. Note that if an up-to-date derived file already exists in a repository, &scons; will not make a copy in the local directory tree. In order to guarantee that a local copy will be made, use the &f-link-Local; method. (target, prerequisite) Specifies an order-only relationship between the specified target file(s) and the specified prerequisite file(s). The prerequisite file(s) will be (re)built, if necessary, before the target file(s), but the target file(s) do not actually depend on the prerequisites and will not be rebuilt simply because the prerequisite file(s) change. Example: env.Requires('foo', 'file-that-must-be-built-before-foo') (function, [argument, keys, path_function, node_class, node_factory, scan_check, recursive]) Creates a Scanner object for the specified function. See the section "Scanner Objects," below, for a complete explanation of the arguments and behavior. (value) By default, &scons; changes its working directory to the directory in which each subsidiary SConscript file lives. This behavior may be disabled by specifying either: SConscriptChdir(0) env.SConscriptChdir(0) in which case &scons; will stay in the top-level directory while reading all SConscript files. (This may be necessary when building from repositories, when all the directories in which SConscript files may be found don't necessarily exist locally.) You may enable and disable this ability by calling SConscriptChdir() multiple times. Example: env = Environment() SConscriptChdir(0) SConscript('foo/SConscript') # will not chdir to foo env.SConscriptChdir(1) SConscript('bar/SConscript') # will chdir to bar ([file, dbm_module]) This tells &scons; to store all file signatures in the specified database file. If the file name is omitted, .sconsign is used by default. (The actual file name(s) stored on disk may have an appropriated suffix appended by the dbm_module.) If file is not an absolute path name, the file is placed in the same directory as the top-level &SConstruct; file. If file is None, then &scons; will store file signatures in a separate .sconsign file in each directory, not in one global database file. (This was the default behavior prior to SCons 0.96.91 and 0.97.) The optional dbm_module argument can be used to specify which Python database module The default is to use a custom SCons.dblite module that uses pickled Python data structures, and which works on all Python versions. Examples: # Explicitly stores signatures in ".sconsign.dblite" # in the top-level SConstruct directory (the # default behavior). SConsignFile() # Stores signatures in the file "etc/scons-signatures" # relative to the top-level SConstruct directory. SConsignFile("etc/scons-signatures") # Stores signatures in the specified absolute file name. SConsignFile("/home/me/SCons/signatures") # Stores signatures in a separate .sconsign file # in each directory. SConsignFile(None) (key=val, [...]) Sets construction variables to default values specified with the keyword arguments if (and only if) the variables are not already set. The following statements are equivalent: env.SetDefault(FOO = 'foo') if 'FOO' not in env: env['FOO'] = 'foo' (side_effect, target) Declares side_effect as a side effect of building target. Both side_effect and target can be a list, a file name, or a node. A side effect is a target file that is created or updated as a side effect of building other targets. For example, a Windows PDB file is created as a side effect of building the .obj files for a static library, and various log files are created updated as side effects of various TeX commands. If a target is a side effect of multiple build commands, &scons; will ensure that only one set of commands is executed at a time. Consequently, you only need to use this method for side-effect targets that are built as a result of multiple build commands. Because multiple build commands may update the same side effect file, by default the side_effect target is not automatically removed when the target is removed by the option. (Note, however, that the side_effect might be removed as part of cleaning the directory in which it lives.) If you want to make sure the side_effect is cleaned whenever a specific target is cleaned, you must specify this explicitly with the &f-link-Clean; or &f-env-Clean; function. (entries, builder) This function and its associate factory functions are deprecated. There is no replacement. The intended use was to keep a local tree in sync with an archive, but in actuality the function only causes the archive to be fetched on the first run. Synchronizing with the archive is best done external to &SCons;. Arrange for non-existent source files to be fetched from a source code management system using the specified builder. The specified entries may be a Node, string or list of both, and may represent either individual source files or directories in which source files can be found. For any non-existent source files, &scons; will search up the directory tree and use the first &f-SourceCode; builder it finds. The specified builder may be None, in which case &scons; will not use a builder to fetch source files for the specified entries, even if a &f-SourceCode; builder has been specified for a directory higher up the tree. &scons; will, by default, fetch files from SCCS or RCS subdirectories without explicit configuration. This takes some extra processing time to search for the necessary source code management files on disk. You can avoid these extra searches and speed up your build a little by disabling these searches as follows: env.SourceCode('.', None) Note that if the specified builder is one you create by hand, it must have an associated construction environment to use when fetching a source file. &scons; provides a set of canned factory functions that return appropriate Builders for various popular source code management systems. Canonical examples of invocation include: env.SourceCode('.', env.BitKeeper('/usr/local/BKsources')) env.SourceCode('src', env.CVS('/usr/local/CVSROOT')) env.SourceCode('/', env.RCS()) env.SourceCode(['f1.c', 'f2.c'], env.SCCS()) env.SourceCode('no_source.c', None) (type) Note: Although it is not yet officially deprecated, use of this function is discouraged. See the &f-link-Decider; function for a more flexible and straightforward way to configure SCons' decision-making. The &f-SourceSignatures; function tells &scons; how to decide if a source file (a file that is not built from any other files) has changed since the last time it was used to build a particular target file. Legal values are MD5 or timestamp. If the environment method is used, the specified type of source signature is only used when deciding whether targets built with that environment are up-to-date or must be rebuilt. If the global function is used, the specified type of source signature becomes the default used for all decisions about whether targets are up-to-date. MD5 means &scons; decides that a source file has changed if the MD5 checksum of its contents has changed since the last time it was used to rebuild a particular target file. timestamp means &scons; decides that a source file has changed if its timestamp (modification time) has changed since the last time it was used to rebuild a particular target file. (Note that although this is similar to the behavior of Make, by default it will also rebuild if the dependency is older than the last time it was used to rebuild the target file.) There is no different between the two behaviors for Python &f-Value; node objects. MD5 signatures take longer to compute, but are more accurate than timestamp signatures. The default value is MD5. Note that the default &f-link-TargetSignatures; setting (see below) is to use this &f-SourceSignatures; setting for any target files that are used to build other target files. Consequently, changing the value of &f-SourceSignatures; will, by default, affect the up-to-date decision for all files in the build (or all files built with a specific construction environment when &f-env-SourceSignatures; is used). (arg) Returns a list of file names or other objects. If arg is a string, it will be split on strings of white-space characters within the string, making it easier to write long lists of file names. If arg is already a list, the list will be returned untouched. If arg is any other type of object, it will be returned as a list containing just the object. Example: files = Split("f1.c f2.c f3.c") files = env.Split("f4.c f5.c f6.c") files = Split(""" f7.c f8.c f9.c """) (input, [raw, target, source, conv]) Performs construction variable interpolation on the specified string or sequence argument input. By default, leading or trailing white space will be removed from the result. and all sequences of white space will be compressed to a single space character. Additionally, any $( and $) character sequences will be stripped from the returned string, The optional raw argument may be set to 1 if you want to preserve white space and $(-$) sequences. The raw argument may be set to 2 if you want to strip all characters between any $( and $) pairs (as is done for signature calculation). If the input is a sequence (list or tuple), the individual elements of the sequence will be expanded, and the results will be returned as a list. The optional target and source keyword arguments must be set to lists of target and source nodes, respectively, if you want the &cv-TARGET;, &cv-TARGETS;, &cv-SOURCE; and &cv-SOURCES; to be available for expansion. This is usually necessary if you are calling &f-env-subst; from within a Python function used as an SCons action. Returned string values or sequence elements are converted to their string representation by default. The optional conv argument may specify a conversion function that will be used in place of the default. For example, if you want Python objects (including SCons Nodes) to be returned as Python objects, you can use the Python λ idiom to pass in an unnamed function that simply returns its unconverted argument. Example: print env.subst("The C compiler is: $CC") def compile(target, source, env): sourceDir = env.subst("${SOURCE.srcdir}", target=target, source=source) source_nodes = env.subst('$EXPAND_TO_NODELIST', conv=lambda x: x) (type) Note: Although it is not yet officially deprecated, use of this function is discouraged. See the &f-link-Decider; function for a more flexible and straightforward way to configure SCons' decision-making. The &f-TargetSignatures; function tells &scons; how to decide if a target file (a file that is built from any other files) has changed since the last time it was used to build some other target file. Legal values are "build"; "content" (or its synonym "MD5"); "timestamp"; or "source". If the environment method is used, the specified type of target signature is only used for targets built with that environment. If the global function is used, the specified type of signature becomes the default used for all target files that don't have an explicit target signature type specified for their environments. "content" (or its synonym "MD5") means &scons; decides that a target file has changed if the MD5 checksum of its contents has changed since the last time it was used to rebuild some other target file. This means &scons; will open up MD5 sum the contents of target files after they're built, and may decide that it does not need to rebuild "downstream" target files if a file was rebuilt with exactly the same contents as the last time. "timestamp" means &scons; decides that a target file has changed if its timestamp (modification time) has changed since the last time it was used to rebuild some other target file. (Note that although this is similar to the behavior of Make, by default it will also rebuild if the dependency is older than the last time it was used to rebuild the target file.) "source" means &scons; decides that a target file has changed as specified by the corresponding &f-SourceSignatures; setting ("MD5" or "timestamp"). This means that &scons; will treat all input files to a target the same way, regardless of whether they are source files or have been built from other files. "build" means &scons; decides that a target file has changed if it has been rebuilt in this invocation or if its content or timestamp have changed as specified by the corresponding &f-SourceSignatures; setting. This "propagates" the status of a rebuilt file so that other "downstream" target files will always be rebuilt, even if the contents or the timestamp have not changed. "build" signatures are fastest because "content" (or "MD5") signatures take longer to compute, but are more accurate than "timestamp" signatures, and can prevent unnecessary "downstream" rebuilds when a target file is rebuilt to the exact same contents as the previous build. The "source" setting provides the most consistent behavior when other target files may be rebuilt from both source and target input files. The default value is "source". Because the default setting is "source", using &f-SourceSignatures; is generally preferable to &f-TargetSignatures;, so that the up-to-date decision will be consistent for all files (or all files built with a specific construction environment). Use of &f-TargetSignatures; provides specific control for how built target files affect their "downstream" dependencies. (string, [toolpath, **kw]) The &f-Tool; form of the function returns a callable object that can be used to initialize a construction environment using the tools keyword of the Environment() method. The object may be called with a construction environment as an argument, in which case the object will add the necessary variables to the construction environment and the name of the tool will be added to the &cv-link-TOOLS; construction variable. Additional keyword arguments are passed to the tool's generate() method. Examples: env = Environment(tools = [ Tool('msvc') ]) env = Environment() t = Tool('msvc') t(env) # adds 'msvc' to the TOOLS variable u = Tool('opengl', toolpath = ['tools']) u(env) # adds 'opengl' to the TOOLS variable The &f-env-Tool; form of the function applies the callable object for the specified tool string to the environment through which the method was called. Additional keyword arguments are passed to the tool's generate() method. env.Tool('gcc') env.Tool('opengl', toolpath = ['build/tools']) (value, [built_value]) Returns a Node object representing the specified Python value. Value Nodes can be used as dependencies of targets. If the result of calling str(value) changes between SCons runs, any targets depending on Value(value) will be rebuilt. (This is true even when using timestamps to decide if files are up-to-date.) When using timestamp source signatures, Value Nodes' timestamps are equal to the system time when the Node is created. The returned Value Node object has a write() method that can be used to "build" a Value Node by setting a new value. The optional built_value argument can be specified when the Value Node is created to indicate the Node should already be considered "built." There is a corresponding read() method that will return the built value of the Node. Examples: env = Environment() def create(target, source, env): # A function that will write a 'prefix=$SOURCE' # string into the file name specified as the # $TARGET. f = open(str(target[0]), 'wb') f.write('prefix=' + source[0].get_contents()) # Fetch the prefix= argument, if any, from the command # line, and use /usr/local as the default. prefix = ARGUMENTS.get('prefix', '/usr/local') # Attach a .Config() builder for the above function action # to the construction environment. env['BUILDERS']['Config'] = Builder(action = create) env.Config(target = 'package-config', source = Value(prefix)) def build_value(target, source, env): # A function that "builds" a Python Value by updating # the the Python value with the contents of the file # specified as the source of the Builder call ($SOURCE). target[0].write(source[0].get_contents()) output = env.Value('before') input = env.Value('after') # Attach a .UpdateValue() builder for the above function # action to the construction environment. env['BUILDERS']['UpdateValue'] = Builder(action = build_value) env.UpdateValue(target = Value(output), source = Value(input)) (variant_dir, src_dir, [duplicate]) Use the &f-VariantDir; function to create a copy of your sources in another location: if a name under variant_dir is not found but exists under src_dir, the file or directory is copied to variant_dir. Target files can be built in a different directory than the original sources by simply refering to the sources (and targets) within the variant tree. &f-VariantDir; can be called multiple times with the same src_dir to set up multiple builds with different options (variants). The src_dir location must be in or underneath the SConstruct file's directory, and variant_dir may not be underneath src_dir. The default behavior is for &scons; to physically duplicate the source files in the variant tree. Thus, a build performed in the variant tree is guaranteed to be identical to a build performed in the source tree even if intermediate source files are generated during the build, or preprocessors or other scanners search for included files relative to the source file, or individual compilers or other invoked tools are hard-coded to put derived files in the same directory as source files. If possible on the platform, the duplication is performed by linking rather than copying; see also the command-line option. Moreover, only the files needed for the build are duplicated; files and directories that are not used are not present in variant_dir. Duplicating the source tree may be disabled by setting the duplicate argument to 0 (zero). This will cause &scons; to invoke Builders using the path names of source files in src_dir and the path names of derived files within variant_dir. This is always more efficient than duplicate=1, and is usually safe for most builds (but see above for cases that may cause problems). Note that &f-VariantDir; works most naturally with a subsidiary SConscript file. However, you would then call the subsidiary SConscript file not in the source directory, but in the variant_dir, regardless of the value of duplicate. This is how you tell &scons; which variant of a source tree to build: # run src/SConscript in two variant directories VariantDir('build/variant1', 'src') SConscript('build/variant1/SConscript') VariantDir('build/variant2', 'src') SConscript('build/variant2/SConscript') See also the &f-link-SConscript; function, described above, for another way to specify a variant directory in conjunction with calling a subsidiary SConscript file. Examples: # use names in the build directory, not the source directory VariantDir('build', 'src', duplicate=0) Program('build/prog', 'build/source.c') # this builds both the source and docs in a separate subtree VariantDir('build', '.', duplicate=0) SConscript(dirs=['build/src','build/doc']) # same as previous example, but only uses SConscript SConscript(dirs='src', variant_dir='build/src', duplicate=0) SConscript(dirs='doc', variant_dir='build/doc', duplicate=0) (program, [path, pathext, reject]) Searches for the specified executable program, returning the full path name to the program if it is found, and returning None if not. Searches the specified path, the value of the calling environment's PATH (env['ENV']['PATH']), or the user's current external PATH (os.environ['PATH']) by default. On Windows systems, searches for executable programs with any of the file extensions listed in the specified pathext, the calling environment's PATHEXT (env['ENV']['PATHEXT']) or the user's current PATHEXT (os.environ['PATHEXT']) by default. Will not select any path name or names in the specified reject list, if any. scons-doc-2.3.0/src/engine/SCons/cppTests.py0000644000175000017500000004220312114661560021455 0ustar dktrkranzdktrkranz# # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/cppTests.py 2013/03/03 09:48:35 garyo" import atexit import sys import unittest import cpp basic_input = """ #include "file1-yes" #include """ substitution_input = """ #define FILE3 "file3-yes" #define FILE4 #include FILE3 #include FILE4 #define XXX_FILE5 YYY_FILE5 #define YYY_FILE5 ZZZ_FILE5 #define ZZZ_FILE5 FILE5 #define FILE5 "file5-yes" #define FILE6 #define XXX_FILE6 YYY_FILE6 #define YYY_FILE6 ZZZ_FILE6 #define ZZZ_FILE6 FILE6 #include XXX_FILE5 #include XXX_FILE6 """ ifdef_input = """ #define DEFINED 0 #ifdef DEFINED #include "file7-yes" #else #include "file7-no" #endif #ifdef NOT_DEFINED #include #else #include #endif """ if_boolean_input = """ #define ZERO 0 #define ONE 1 #if ZERO #include "file9-no" #else #include "file9-yes" #endif #if ONE #include #else #include #endif #if ZERO #include "file11-no-1" #elif ZERO #include "file11-no-2" #else #include "file11-yes" #endif #if ZERO #include #elif ONE #include #else #include #endif #if ONE #include "file13-yes" #elif ZERO #include "file13-no-1" #else #include "file13-no-2" #endif #if ONE #include #elif ONE #include #else #include #endif """ if_defined_input = """ #define DEFINED 0 #if defined(DEFINED) #include "file15-yes" #endif #if ! defined(DEFINED) #include #else #include #endif #if defined DEFINED #include "file17-yes" #endif #if ! defined DEFINED #include #else #include #endif """ expression_input = """ #define ZERO 0 #define ONE 1 #if ZERO && ZERO #include "file19-no" #else #include "file19-yes" #endif #if ZERO && ONE #include #else #include #endif #if ONE && ZERO #include "file21-no" #else #include "file21-yes" #endif #if ONE && ONE #include #else #include #endif #if ZERO || ZERO #include "file23-no" #else #include "file23-yes" #endif #if ZERO || ONE #include #else #include #endif #if ONE || ZERO #include "file25-yes" #else #include "file25-no" #endif #if ONE || ONE #include #else #include #endif #if ONE == ONE #include "file27-yes" #else #include "file27-no" #endif #if ONE != ONE #include #else #include #endif #if ! (ONE == ONE) #include "file29-no" #else #include "file29-yes" #endif #if ! (ONE != ONE) #include #else #include #endif """ undef_input = """ #define UNDEFINE 0 #ifdef UNDEFINE #include "file31-yes" #else #include "file31-no" #endif #undef UNDEFINE #ifdef UNDEFINE #include #else #include #endif """ macro_function_input = """ #define ZERO 0 #define ONE 1 #define FUNC33(x) "file33-yes" #define FUNC34(x) #include FUNC33(ZERO) #include FUNC34(ZERO) #define FILE35 "file35-yes" #define FILE36 #define FUNC35(x, y) FILE35 #define FUNC36(x, y) FILE36 #include FUNC35(ZERO, ONE) #include FUNC36(ZERO, ONE) #define FILE37 "file37-yes" #define FILE38 #define FUNC37a(x, y) FILE37 #define FUNC38a(x, y) FILE38 #define FUNC37b(x, y) FUNC37a(x, y) #define FUNC38b(x, y) FUNC38a(x, y) #define FUNC37c(x, y) FUNC37b(x, y) #define FUNC38c(x, y) FUNC38b(x, y) #include FUNC37c(ZERO, ONE) #include FUNC38c(ZERO, ONE) #define FILE39 "file39-yes" #define FILE40 #define FUNC39a(x0, y0) FILE39 #define FUNC40a(x0, y0) FILE40 #define FUNC39b(x1, y2) FUNC39a(x1, y1) #define FUNC40b(x1, y2) FUNC40a(x1, y1) #define FUNC39c(x2, y2) FUNC39b(x2, y2) #define FUNC40c(x2, y2) FUNC40b(x2, y2) #include FUNC39c(ZERO, ONE) #include FUNC40c(ZERO, ONE) /* Make sure we don't die if the expansion isn't a string. */ #define FUNC_INTEGER(x) 1 /* Make sure one-character names are recognized. */ #define _(x) translate(x) #undef _ """ token_pasting_input = """ #define PASTE_QUOTE(q, name) q##name##-yes##q #define PASTE_ANGLE(name) <##name##-yes> #define FUNC41 PASTE_QUOTE(", file41) #define FUNC42 PASTE_ANGLE(file42) #include FUNC41 #include FUNC42 """ no_space_input = """ #include #include"file44-yes" """ # pp_class = PreProcessor # #pp_class = DumbPreProcessor # pp = pp_class(current = ".", # cpppath = ['/usr/include'], # print_all = 1) # #pp(open(sys.argv[1]).read()) # pp(input) class cppTestCase(unittest.TestCase): def setUp(self): self.cpp = self.cpp_class(current = ".", cpppath = ['/usr/include']) def test_basic(self): """Test basic #include scanning""" expect = self.basic_expect result = self.cpp.process_contents(basic_input) assert expect == result, (expect, result) def test_substitution(self): """Test substitution of #include files using CPP variables""" expect = self.substitution_expect result = self.cpp.process_contents(substitution_input) assert expect == result, (expect, result) def test_ifdef(self): """Test basic #ifdef processing""" expect = self.ifdef_expect result = self.cpp.process_contents(ifdef_input) assert expect == result, (expect, result) def test_if_boolean(self): """Test #if with Boolean values""" expect = self.if_boolean_expect result = self.cpp.process_contents(if_boolean_input) assert expect == result, (expect, result) def test_if_defined(self): """Test #if defined() idioms""" expect = self.if_defined_expect result = self.cpp.process_contents(if_defined_input) assert expect == result, (expect, result) def test_expression(self): """Test #if with arithmetic expressions""" expect = self.expression_expect result = self.cpp.process_contents(expression_input) assert expect == result, (expect, result) def test_undef(self): """Test #undef handling""" expect = self.undef_expect result = self.cpp.process_contents(undef_input) assert expect == result, (expect, result) def test_macro_function(self): """Test using macro functions to express file names""" expect = self.macro_function_expect result = self.cpp.process_contents(macro_function_input) assert expect == result, (expect, result) def test_token_pasting(self): """Test token-pasting to construct file names""" expect = self.token_pasting_expect result = self.cpp.process_contents(token_pasting_input) assert expect == result, (expect, result) def test_no_space(self): """Test no space between #include and the quote""" expect = self.no_space_expect result = self.cpp.process_contents(no_space_input) assert expect == result, (expect, result) class cppAllTestCase(cppTestCase): def setUp(self): self.cpp = self.cpp_class(current = ".", cpppath = ['/usr/include'], all=1) class PreProcessorTestCase(cppAllTestCase): cpp_class = cpp.PreProcessor basic_expect = [ ('include', '"', 'file1-yes'), ('include', '<', 'file2-yes'), ] substitution_expect = [ ('include', '"', 'file3-yes'), ('include', '<', 'file4-yes'), ('include', '"', 'file5-yes'), ('include', '<', 'file6-yes'), ] ifdef_expect = [ ('include', '"', 'file7-yes'), ('include', '<', 'file8-yes'), ] if_boolean_expect = [ ('include', '"', 'file9-yes'), ('include', '<', 'file10-yes'), ('include', '"', 'file11-yes'), ('include', '<', 'file12-yes'), ('include', '"', 'file13-yes'), ('include', '<', 'file14-yes'), ] if_defined_expect = [ ('include', '"', 'file15-yes'), ('include', '<', 'file16-yes'), ('include', '"', 'file17-yes'), ('include', '<', 'file18-yes'), ] expression_expect = [ ('include', '"', 'file19-yes'), ('include', '<', 'file20-yes'), ('include', '"', 'file21-yes'), ('include', '<', 'file22-yes'), ('include', '"', 'file23-yes'), ('include', '<', 'file24-yes'), ('include', '"', 'file25-yes'), ('include', '<', 'file26-yes'), ('include', '"', 'file27-yes'), ('include', '<', 'file28-yes'), ('include', '"', 'file29-yes'), ('include', '<', 'file30-yes'), ] undef_expect = [ ('include', '"', 'file31-yes'), ('include', '<', 'file32-yes'), ] macro_function_expect = [ ('include', '"', 'file33-yes'), ('include', '<', 'file34-yes'), ('include', '"', 'file35-yes'), ('include', '<', 'file36-yes'), ('include', '"', 'file37-yes'), ('include', '<', 'file38-yes'), ('include', '"', 'file39-yes'), ('include', '<', 'file40-yes'), ] token_pasting_expect = [ ('include', '"', 'file41-yes'), ('include', '<', 'file42-yes'), ] no_space_expect = [ ('include', '<', 'file43-yes'), ('include', '"', 'file44-yes'), ] class DumbPreProcessorTestCase(cppAllTestCase): cpp_class = cpp.DumbPreProcessor basic_expect = [ ('include', '"', 'file1-yes'), ('include', '<', 'file2-yes'), ] substitution_expect = [ ('include', '"', 'file3-yes'), ('include', '<', 'file4-yes'), ('include', '"', 'file5-yes'), ('include', '<', 'file6-yes'), ] ifdef_expect = [ ('include', '"', 'file7-yes'), ('include', '"', 'file7-no'), ('include', '<', 'file8-no'), ('include', '<', 'file8-yes'), ] if_boolean_expect = [ ('include', '"', 'file9-no'), ('include', '"', 'file9-yes'), ('include', '<', 'file10-yes'), ('include', '<', 'file10-no'), ('include', '"', 'file11-no-1'), ('include', '"', 'file11-no-2'), ('include', '"', 'file11-yes'), ('include', '<', 'file12-no-1'), ('include', '<', 'file12-yes'), ('include', '<', 'file12-no-2'), ('include', '"', 'file13-yes'), ('include', '"', 'file13-no-1'), ('include', '"', 'file13-no-2'), ('include', '<', 'file14-yes'), ('include', '<', 'file14-no-1'), ('include', '<', 'file14-no-2'), ] if_defined_expect = [ ('include', '"', 'file15-yes'), ('include', '<', 'file16-no'), ('include', '<', 'file16-yes'), ('include', '"', 'file17-yes'), ('include', '<', 'file18-no'), ('include', '<', 'file18-yes'), ] expression_expect = [ ('include', '"', 'file19-no'), ('include', '"', 'file19-yes'), ('include', '<', 'file20-no'), ('include', '<', 'file20-yes'), ('include', '"', 'file21-no'), ('include', '"', 'file21-yes'), ('include', '<', 'file22-yes'), ('include', '<', 'file22-no'), ('include', '"', 'file23-no'), ('include', '"', 'file23-yes'), ('include', '<', 'file24-yes'), ('include', '<', 'file24-no'), ('include', '"', 'file25-yes'), ('include', '"', 'file25-no'), ('include', '<', 'file26-yes'), ('include', '<', 'file26-no'), ('include', '"', 'file27-yes'), ('include', '"', 'file27-no'), ('include', '<', 'file28-no'), ('include', '<', 'file28-yes'), ('include', '"', 'file29-no'), ('include', '"', 'file29-yes'), ('include', '<', 'file30-yes'), ('include', '<', 'file30-no'), ] undef_expect = [ ('include', '"', 'file31-yes'), ('include', '"', 'file31-no'), ('include', '<', 'file32-no'), ('include', '<', 'file32-yes'), ] macro_function_expect = [ ('include', '"', 'file33-yes'), ('include', '<', 'file34-yes'), ('include', '"', 'file35-yes'), ('include', '<', 'file36-yes'), ('include', '"', 'file37-yes'), ('include', '<', 'file38-yes'), ('include', '"', 'file39-yes'), ('include', '<', 'file40-yes'), ] token_pasting_expect = [ ('include', '"', 'file41-yes'), ('include', '<', 'file42-yes'), ] no_space_expect = [ ('include', '<', 'file43-yes'), ('include', '"', 'file44-yes'), ] import os import re import shutil import tempfile tempfile.template = 'cppTests.' if os.name in ('posix', 'nt'): tempfile.template = 'cppTests.' + str(os.getpid()) + '.' else: tempfile.template = 'cppTests.' _Cleanup = [] def _clean(): for dir in _Cleanup: if os.path.exists(dir): shutil.rmtree(dir) atexit.register(_clean) class fileTestCase(unittest.TestCase): cpp_class = cpp.DumbPreProcessor def setUp(self): try: path = tempfile.mktemp(prefix=tempfile.template) except TypeError: # The tempfile.mktemp() function in earlier versions of Python # has no prefix argument, but uses the tempfile.template # value that we set above. path = tempfile.mktemp() _Cleanup.append(path) os.mkdir(path) self.tempdir = path self.orig_cwd = os.getcwd() os.chdir(path) def tearDown(self): os.chdir(self.orig_cwd) shutil.rmtree(self.tempdir) _Cleanup.remove(self.tempdir) def strip_initial_spaces(self, s): #lines = s.split('\n') lines = s.split('\n') spaces = re.match(' *', lines[0]).group(0) def strip_spaces(l, spaces=spaces): #if l.startswith(spaces): if l[:len(spaces)] == spaces: l = l[len(spaces):] return l #return '\n'.join([ strip_spaces(l) for l in lines ]) return '\n'.join(map(strip_spaces, lines)) def write(self, file, contents): open(file, 'w').write(self.strip_initial_spaces(contents)) def test_basic(self): """Test basic file inclusion""" self.write('f1.h', """\ #include "f2.h" """) self.write('f2.h', """\ #include """) self.write('f3.h', """\ """) p = cpp.DumbPreProcessor(current = os.curdir, cpppath = [os.curdir]) result = p('f1.h') assert result == ['f2.h', 'f3.h'], result def test_current_file(self): """Test use of the .current_file attribute""" self.write('f1.h', """\ #include """) self.write('f2.h', """\ #include "f3.h" """) self.write('f3.h', """\ """) class MyPreProcessor(cpp.DumbPreProcessor): def __init__(self, *args, **kw): cpp.DumbPreProcessor.__init__(self, *args, **kw) self.files = [] def __call__(self, file): self.files.append(file) return cpp.DumbPreProcessor.__call__(self, file) def scons_current_file(self, t): r = cpp.DumbPreProcessor.scons_current_file(self, t) self.files.append(self.current_file) return r p = MyPreProcessor(current = os.curdir, cpppath = [os.curdir]) result = p('f1.h') assert result == ['f2.h', 'f3.h'], result assert p.files == ['f1.h', 'f2.h', 'f3.h', 'f2.h', 'f1.h'], p.files if __name__ == '__main__': suite = unittest.TestSuite() tclasses = [ PreProcessorTestCase, DumbPreProcessorTestCase, fileTestCase, ] for tclass in tclasses: names = unittest.getTestCaseNames(tclass, 'test_') try: names = list(set(names)) except NameError: pass names.sort() suite.addTests(list(map(tclass, names))) if not unittest.TextTestRunner().run(suite).wasSuccessful(): sys.exit(1) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/JobTests.py0000644000175000017500000004623412114661557021423 0ustar dktrkranzdktrkranz# # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/JobTests.py 2013/03/03 09:48:35 garyo" import unittest import random import math import SCons.Job import sys import time # a large number num_sines = 10000 # how many parallel jobs to perform for the test num_jobs = 11 # how many tasks to perform for the test num_tasks = num_jobs*5 class DummyLock(object): "fake lock class to use if threads are not supported" def acquire(self): pass def release(self): pass class NoThreadsException(object): "raised by the ParallelTestCase if threads are not supported" def __str__(self): return "the interpreter doesn't support threads" class Task(object): """A dummy task class for testing purposes.""" def __init__(self, i, taskmaster): self.i = i self.taskmaster = taskmaster self.was_executed = 0 self.was_prepared = 0 def prepare(self): self.was_prepared = 1 def _do_something(self): pass def needs_execute(self): return True def execute(self): self.taskmaster.test_case.failUnless(self.was_prepared, "the task wasn't prepared") self.taskmaster.guard.acquire() self.taskmaster.begin_list.append(self.i) self.taskmaster.guard.release() self._do_something() self.was_executed = 1 self.taskmaster.guard.acquire() self.taskmaster.end_list.append(self.i) self.taskmaster.guard.release() def executed(self): self.taskmaster.num_executed = self.taskmaster.num_executed + 1 self.taskmaster.test_case.failUnless(self.was_prepared, "the task wasn't prepared") self.taskmaster.test_case.failUnless(self.was_executed, "the task wasn't really executed") self.taskmaster.test_case.failUnless(isinstance(self, Task), "the task wasn't really a Task instance") def failed(self): self.taskmaster.num_failed = self.taskmaster.num_failed + 1 self.taskmaster.stop = 1 self.taskmaster.test_case.failUnless(self.was_prepared, "the task wasn't prepared") def postprocess(self): self.taskmaster.num_postprocessed = self.taskmaster.num_postprocessed + 1 class RandomTask(Task): def _do_something(self): # do something that will take some random amount of time: for i in range(random.randrange(0, num_sines, 1)): x = math.sin(i) time.sleep(0.01) class ExceptionTask(object): """A dummy task class for testing purposes.""" def __init__(self, i, taskmaster): self.taskmaster = taskmaster self.was_prepared = 0 def prepare(self): self.was_prepared = 1 def needs_execute(self): return True def execute(self): raise Exception def executed(self): self.taskmaster.num_executed = self.taskmaster.num_executed + 1 self.taskmaster.test_case.failUnless(self.was_prepared, "the task wasn't prepared") self.taskmaster.test_case.failUnless(self.was_executed, "the task wasn't really executed") self.taskmaster.test_case.failUnless(self.__class__ is Task, "the task wasn't really a Task instance") def failed(self): self.taskmaster.num_failed = self.taskmaster.num_failed + 1 self.taskmaster.stop = 1 self.taskmaster.test_case.failUnless(self.was_prepared, "the task wasn't prepared") def postprocess(self): self.taskmaster.num_postprocessed = self.taskmaster.num_postprocessed + 1 def exception_set(self): self.taskmaster.exception_set() class Taskmaster(object): """A dummy taskmaster class for testing the job classes.""" def __init__(self, n, test_case, Task): """n is the number of dummy tasks to perform.""" self.test_case = test_case self.stop = None self.num_tasks = n self.num_iterated = 0 self.num_executed = 0 self.num_failed = 0 self.num_postprocessed = 0 self.Task = Task # 'guard' guards 'task_begin_list' and 'task_end_list' try: import threading self.guard = threading.Lock() except: self.guard = DummyLock() # keep track of the order tasks are begun in self.begin_list = [] # keep track of the order tasks are completed in self.end_list = [] def next_task(self): if self.stop or self.all_tasks_are_iterated(): return None else: self.num_iterated = self.num_iterated + 1 return self.Task(self.num_iterated, self) def all_tasks_are_executed(self): return self.num_executed == self.num_tasks def all_tasks_are_iterated(self): return self.num_iterated == self.num_tasks def all_tasks_are_postprocessed(self): return self.num_postprocessed == self.num_tasks def tasks_were_serial(self): "analyze the task order to see if they were serial" serial = 1 # assume the tasks were serial for i in range(num_tasks): serial = serial and (self.begin_list[i] == self.end_list[i] == (i + 1)) return serial def exception_set(self): pass def cleanup(self): pass SaveThreadPool = None ThreadPoolCallList = [] class ParallelTestCase(unittest.TestCase): def runTest(self): "test parallel jobs" try: import threading except: raise NoThreadsException() taskmaster = Taskmaster(num_tasks, self, RandomTask) jobs = SCons.Job.Jobs(num_jobs, taskmaster) jobs.run() self.failUnless(not taskmaster.tasks_were_serial(), "the tasks were not executed in parallel") self.failUnless(taskmaster.all_tasks_are_executed(), "all the tests were not executed") self.failUnless(taskmaster.all_tasks_are_iterated(), "all the tests were not iterated over") self.failUnless(taskmaster.all_tasks_are_postprocessed(), "all the tests were not postprocessed") self.failIf(taskmaster.num_failed, "some task(s) failed to execute") # Verify that parallel jobs will pull all of the completed tasks # out of the queue at once, instead of one by one. We do this by # replacing the default ThreadPool class with one that records the # order in which tasks are put() and get() to/from the pool, and # which sleeps a little bit before call get() to let the initial # tasks complete and get their notifications on the resultsQueue. class SleepTask(Task): def _do_something(self): time.sleep(0.1) global SaveThreadPool SaveThreadPool = SCons.Job.ThreadPool class WaitThreadPool(SaveThreadPool): def put(self, task): ThreadPoolCallList.append('put(%s)' % task.i) return SaveThreadPool.put(self, task) def get(self): time.sleep(0.5) result = SaveThreadPool.get(self) ThreadPoolCallList.append('get(%s)' % result[0].i) return result SCons.Job.ThreadPool = WaitThreadPool try: taskmaster = Taskmaster(3, self, SleepTask) jobs = SCons.Job.Jobs(2, taskmaster) jobs.run() # The key here is that we get(1) and get(2) from the # resultsQueue before we put(3), but get(1) and get(2) can # be in either order depending on how the first two parallel # tasks get scheduled by the operating system. expect = [ ['put(1)', 'put(2)', 'get(1)', 'get(2)', 'put(3)', 'get(3)'], ['put(1)', 'put(2)', 'get(2)', 'get(1)', 'put(3)', 'get(3)'], ] assert ThreadPoolCallList in expect, ThreadPoolCallList finally: SCons.Job.ThreadPool = SaveThreadPool class SerialTestCase(unittest.TestCase): def runTest(self): "test a serial job" taskmaster = Taskmaster(num_tasks, self, RandomTask) jobs = SCons.Job.Jobs(1, taskmaster) jobs.run() self.failUnless(taskmaster.tasks_were_serial(), "the tasks were not executed in series") self.failUnless(taskmaster.all_tasks_are_executed(), "all the tests were not executed") self.failUnless(taskmaster.all_tasks_are_iterated(), "all the tests were not iterated over") self.failUnless(taskmaster.all_tasks_are_postprocessed(), "all the tests were not postprocessed") self.failIf(taskmaster.num_failed, "some task(s) failed to execute") class NoParallelTestCase(unittest.TestCase): def runTest(self): "test handling lack of parallel support" def NoParallel(tm, num, stack_size): raise NameError save_Parallel = SCons.Job.Parallel SCons.Job.Parallel = NoParallel try: taskmaster = Taskmaster(num_tasks, self, RandomTask) jobs = SCons.Job.Jobs(2, taskmaster) self.failUnless(jobs.num_jobs == 1, "unexpected number of jobs %d" % jobs.num_jobs) jobs.run() self.failUnless(taskmaster.tasks_were_serial(), "the tasks were not executed in series") self.failUnless(taskmaster.all_tasks_are_executed(), "all the tests were not executed") self.failUnless(taskmaster.all_tasks_are_iterated(), "all the tests were not iterated over") self.failUnless(taskmaster.all_tasks_are_postprocessed(), "all the tests were not postprocessed") self.failIf(taskmaster.num_failed, "some task(s) failed to execute") finally: SCons.Job.Parallel = save_Parallel class SerialExceptionTestCase(unittest.TestCase): def runTest(self): "test a serial job with tasks that raise exceptions" taskmaster = Taskmaster(num_tasks, self, ExceptionTask) jobs = SCons.Job.Jobs(1, taskmaster) jobs.run() self.failIf(taskmaster.num_executed, "a task was executed") self.failUnless(taskmaster.num_iterated == 1, "exactly one task should have been iterated") self.failUnless(taskmaster.num_failed == 1, "exactly one task should have failed") self.failUnless(taskmaster.num_postprocessed == 1, "exactly one task should have been postprocessed") class ParallelExceptionTestCase(unittest.TestCase): def runTest(self): "test parallel jobs with tasks that raise exceptions" taskmaster = Taskmaster(num_tasks, self, ExceptionTask) jobs = SCons.Job.Jobs(num_jobs, taskmaster) jobs.run() self.failIf(taskmaster.num_executed, "a task was executed") self.failUnless(taskmaster.num_iterated >= 1, "one or more task should have been iterated") self.failUnless(taskmaster.num_failed >= 1, "one or more tasks should have failed") self.failUnless(taskmaster.num_postprocessed >= 1, "one or more tasks should have been postprocessed") #--------------------------------------------------------------------- # Above tested Job object with contrived Task and Taskmaster objects. # Now test Job object with actual Task and Taskmaster objects. import SCons.Taskmaster import SCons.Node import time class DummyNodeInfo(object): def update(self, obj): pass class testnode (SCons.Node.Node): def __init__(self): SCons.Node.Node.__init__(self) self.expect_to_be = SCons.Node.executed self.ninfo = DummyNodeInfo() class goodnode (testnode): def __init__(self): SCons.Node.Node.__init__(self) self.expect_to_be = SCons.Node.up_to_date self.ninfo = DummyNodeInfo() class slowgoodnode (goodnode): def prepare(self): # Delay to allow scheduled Jobs to run while the dispatcher # sleeps. Keep this short because it affects the time taken # by this test. time.sleep(0.15) goodnode.prepare(self) class badnode (goodnode): def __init__(self): goodnode.__init__(self) self.expect_to_be = SCons.Node.failed def build(self, **kw): raise Exception('badnode exception') class slowbadnode (badnode): def build(self, **kw): # Appears to take a while to build, allowing faster builds to # overlap. Time duration is not especially important, but if # it is faster than slowgoodnode then these could complete # while the scheduler is sleeping. time.sleep(0.05) raise Exception('slowbadnode exception') class badpreparenode (badnode): def prepare(self): raise Exception('badpreparenode exception') class _SConsTaskTest(unittest.TestCase): def _test_seq(self, num_jobs): for node_seq in [ [goodnode], [badnode], [slowbadnode], [slowgoodnode], [badpreparenode], [goodnode, badnode], [slowgoodnode, badnode], [goodnode, slowbadnode], [goodnode, goodnode, goodnode, slowbadnode], [goodnode, slowbadnode, badpreparenode, slowgoodnode], [goodnode, slowbadnode, slowgoodnode, badnode] ]: self._do_test(num_jobs, node_seq) def _do_test(self, num_jobs, node_seq): testnodes = [] for tnum in range(num_tasks): testnodes.append(node_seq[tnum % len(node_seq)]()) taskmaster = SCons.Taskmaster.Taskmaster(testnodes, tasker=SCons.Taskmaster.AlwaysTask) jobs = SCons.Job.Jobs(num_jobs, taskmaster) # Exceptions thrown by tasks are not actually propagated to # this level, but are instead stored in the Taskmaster. jobs.run() # Now figure out if tests proceeded correctly. The first test # that fails will shutdown the initiation of subsequent tests, # but any tests currently queued for execution will still be # processed, and any tests that completed before the failure # would have resulted in new tests being queued for execution. # Apply the following operational heuristics of Job.py: # 0) An initial jobset of tasks will be queued before any # good/bad results are obtained (from "execute" of task in # thread). # 1) A goodnode will complete immediately on its thread and # allow another node to be queued for execution. # 2) A badnode will complete immediately and suppress any # subsequent execution queuing, but all currently queued # tasks will still be processed. # 3) A slowbadnode will fail later. It will block slots in # the job queue. Nodes that complete immediately will # allow other nodes to be queued in their place, and this # will continue until either (#2) above or until all job # slots are filled with slowbadnode entries. # One approach to validating this test would be to try to # determine exactly how many nodes executed, how many didn't, # and the results of each, and then to assert failure on any # mismatch (including the total number of built nodes). # However, while this is possible to do for a single-processor # system, it is nearly impossible to predict correctly for a # multi-processor system and still test the characteristics of # delayed execution nodes. Stated another way, multithreading # is inherently non-deterministic unless you can completely # characterize the entire system, and since that's not # possible here, we shouldn't try. # Therefore, this test will simply scan the set of nodes to # see if the node was executed or not and if it was executed # that it obtained the expected value for that node # (i.e. verifying we don't get failure crossovers or # mislabelling of results). for N in testnodes: state = N.get_state() self.failUnless(state in [SCons.Node.no_state, N.expect_to_be], "Node %s got unexpected result: %s" % (N, state)) self.failUnless([N for N in testnodes if N.get_state()], "no nodes ran at all.") class SerialTaskTest(_SConsTaskTest): def runTest(self): "test serial jobs with actual Taskmaster and Task" self._test_seq(1) class ParallelTaskTest(_SConsTaskTest): def runTest(self): "test parallel jobs with actual Taskmaster and Task" self._test_seq(num_jobs) #--------------------------------------------------------------------- def suite(): suite = unittest.TestSuite() suite.addTest(ParallelTestCase()) suite.addTest(SerialTestCase()) suite.addTest(NoParallelTestCase()) suite.addTest(SerialExceptionTestCase()) suite.addTest(ParallelExceptionTestCase()) suite.addTest(SerialTaskTest()) suite.addTest(ParallelTaskTest()) return suite if __name__ == "__main__": runner = unittest.TextTestRunner() result = runner.run(suite()) if (len(result.failures) == 0 and len(result.errors) == 1 and isinstance(result.errors[0][0], SerialTestCase) and isinstance(result.errors[0][1][0], NoThreadsException)): sys.exit(2) elif not result.wasSuccessful(): sys.exit(1) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/WarningsTests.py0000644000175000017500000001124112114661560022461 0ustar dktrkranzdktrkranz# # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/WarningsTests.py 2013/03/03 09:48:35 garyo" import sys import unittest import SCons.Warnings class TestOutput(object): def __call__(self, x): args = x.args[0] if len(args) == 1: args = args[0] self.out = str(args) class WarningsTestCase(unittest.TestCase): def test_Warning(self): """Test warn function.""" # Reset global state SCons.Warnings._enabled = [] SCons.Warnings._warningAsException = 0 to = TestOutput() SCons.Warnings._warningOut=to SCons.Warnings.enableWarningClass(SCons.Warnings.Warning) SCons.Warnings.warn(SCons.Warnings.DeprecatedWarning, "Foo") assert to.out == "Foo", to.out SCons.Warnings.warn(SCons.Warnings.DependencyWarning, "Foo", 1) assert to.out == "('Foo', 1)", to.out def test_WarningAsExc(self): """Test warnings as exceptions.""" # Reset global state SCons.Warnings._enabled = [] SCons.Warnings._warningAsException = 0 SCons.Warnings.enableWarningClass(SCons.Warnings.Warning) old = SCons.Warnings.warningAsException() assert old == 0, old exc_caught = 0 try: SCons.Warnings.warn(SCons.Warnings.Warning, "Foo") except: exc_caught = 1 assert exc_caught == 1 old = SCons.Warnings.warningAsException(old) assert old == 1, old exc_caught = 0 try: SCons.Warnings.warn(SCons.Warnings.Warning, "Foo") except: exc_caught = 1 assert exc_caught == 0 def test_Disable(self): """Test disabling/enabling warnings.""" # Reset global state SCons.Warnings._enabled = [] SCons.Warnings._warningAsException = 0 to = TestOutput() SCons.Warnings._warningOut=to to.out = None # No warnings by default SCons.Warnings.warn(SCons.Warnings.DeprecatedWarning, "Foo") assert to.out is None, to.out SCons.Warnings.enableWarningClass(SCons.Warnings.Warning) SCons.Warnings.warn(SCons.Warnings.DeprecatedWarning, "Foo") assert to.out == "Foo", to.out to.out = None SCons.Warnings.suppressWarningClass(SCons.Warnings.DeprecatedWarning) SCons.Warnings.warn(SCons.Warnings.DeprecatedWarning, "Foo") assert to.out is None, to.out SCons.Warnings.warn(SCons.Warnings.MandatoryDeprecatedWarning, "Foo") assert to.out is None, to.out # Dependency warnings should still be enabled though SCons.Warnings.enableWarningClass(SCons.Warnings.Warning) SCons.Warnings.warn(SCons.Warnings.DependencyWarning, "Foo") assert to.out == "Foo", to.out # Try reenabling all warnings... SCons.Warnings.enableWarningClass(SCons.Warnings.Warning) SCons.Warnings.enableWarningClass(SCons.Warnings.Warning) SCons.Warnings.warn(SCons.Warnings.DeprecatedWarning, "Foo") assert to.out == "Foo", to.out if __name__ == "__main__": suite = unittest.makeSuite(WarningsTestCase, 'test_') if not unittest.TextTestRunner().run(suite).wasSuccessful(): sys.exit(1) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Util.py0000644000175000017500000013765212114661560020602 0ustar dktrkranzdktrkranz"""SCons.Util Various utility functions go here. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Util.py 2013/03/03 09:48:35 garyo" import os import sys import copy import re import types from collections import UserDict, UserList, UserString # Don't "from types import ..." these because we need to get at the # types module later to look for UnicodeType. InstanceType = types.InstanceType MethodType = types.MethodType FunctionType = types.FunctionType try: unicode except NameError: UnicodeType = None else: UnicodeType = unicode def dictify(keys, values, result={}): for k, v in zip(keys, values): result[k] = v return result _altsep = os.altsep if _altsep is None and sys.platform == 'win32': # My ActivePython 2.0.1 doesn't set os.altsep! What gives? _altsep = '/' if _altsep: def rightmost_separator(path, sep): return max(path.rfind(sep), path.rfind(_altsep)) else: def rightmost_separator(path, sep): return path.rfind(sep) # First two from the Python Cookbook, just for completeness. # (Yeah, yeah, YAGNI...) def containsAny(str, set): """Check whether sequence str contains ANY of the items in set.""" for c in set: if c in str: return 1 return 0 def containsAll(str, set): """Check whether sequence str contains ALL of the items in set.""" for c in set: if c not in str: return 0 return 1 def containsOnly(str, set): """Check whether sequence str contains ONLY items in set.""" for c in str: if c not in set: return 0 return 1 def splitext(path): "Same as os.path.splitext() but faster." sep = rightmost_separator(path, os.sep) dot = path.rfind('.') # An ext is only real if it has at least one non-digit char if dot > sep and not containsOnly(path[dot:], "0123456789."): return path[:dot],path[dot:] else: return path,"" def updrive(path): """ Make the drive letter (if any) upper case. This is useful because Windows is inconsitent on the case of the drive letter, which can cause inconsistencies when calculating command signatures. """ drive, rest = os.path.splitdrive(path) if drive: path = drive.upper() + rest return path class NodeList(UserList): """This class is almost exactly like a regular list of Nodes (actually it can hold any object), with one important difference. If you try to get an attribute from this list, it will return that attribute from every item in the list. For example: >>> someList = NodeList([ ' foo ', ' bar ' ]) >>> someList.strip() [ 'foo', 'bar' ] """ def __nonzero__(self): return len(self.data) != 0 def __str__(self): return ' '.join(map(str, self.data)) def __iter__(self): return iter(self.data) def __call__(self, *args, **kwargs): result = [x(*args, **kwargs) for x in self.data] return self.__class__(result) def __getattr__(self, name): result = [getattr(x, name) for x in self.data] return self.__class__(result) _get_env_var = re.compile(r'^\$([_a-zA-Z]\w*|{[_a-zA-Z]\w*})$') def get_environment_var(varstr): """Given a string, first determine if it looks like a reference to a single environment variable, like "$FOO" or "${FOO}". If so, return that variable with no decorations ("FOO"). If not, return None.""" mo=_get_env_var.match(to_String(varstr)) if mo: var = mo.group(1) if var[0] == '{': return var[1:-1] else: return var else: return None class DisplayEngine(object): print_it = True def __call__(self, text, append_newline=1): if not self.print_it: return if append_newline: text = text + '\n' try: sys.stdout.write(unicode(text)) except IOError: # Stdout might be connected to a pipe that has been closed # by now. The most likely reason for the pipe being closed # is that the user has press ctrl-c. It this is the case, # then SCons is currently shutdown. We therefore ignore # IOError's here so that SCons can continue and shutdown # properly so that the .sconsign is correctly written # before SCons exits. pass def set_mode(self, mode): self.print_it = mode def render_tree(root, child_func, prune=0, margin=[0], visited={}): """ Render a tree of nodes into an ASCII tree view. root - the root node of the tree child_func - the function called to get the children of a node prune - don't visit the same node twice margin - the format of the left margin to use for children of root. 1 results in a pipe, and 0 results in no pipe. visited - a dictionary of visited nodes in the current branch if not prune, or in the whole tree if prune. """ rname = str(root) children = child_func(root) retval = "" for pipe in margin[:-1]: if pipe: retval = retval + "| " else: retval = retval + " " if rname in visited: return retval + "+-[" + rname + "]\n" retval = retval + "+-" + rname + "\n" if not prune: visited = copy.copy(visited) visited[rname] = 1 for i in range(len(children)): margin.append(i 0 last = t[0] lasti = i = 1 while i < n: if t[i] != last: t[lasti] = last = t[i] lasti = lasti + 1 i = i + 1 return t[:lasti] del t # Brute force is all that's left. u = [] for x in s: if x not in u: u.append(x) return u # From Alex Martelli, # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52560 # ASPN: Python Cookbook: Remove duplicates from a sequence # First comment, dated 2001/10/13. # (Also in the printed Python Cookbook.) def uniquer(seq, idfun=None): if idfun is None: def idfun(x): return x seen = {} result = [] for item in seq: marker = idfun(item) # in old Python versions: # if seen.has_key(marker) # but in new ones: if marker in seen: continue seen[marker] = 1 result.append(item) return result # A more efficient implementation of Alex's uniquer(), this avoids the # idfun() argument and function-call overhead by assuming that all # items in the sequence are hashable. def uniquer_hashables(seq): seen = {} result = [] for item in seq: #if not item in seen: if item not in seen: seen[item] = 1 result.append(item) return result # Much of the logic here was originally based on recipe 4.9 from the # Python CookBook, but we had to dumb it way down for Python 1.5.2. class LogicalLines(object): def __init__(self, fileobj): self.fileobj = fileobj def readline(self): result = [] while True: line = self.fileobj.readline() if not line: break if line[-2:] == '\\\n': result.append(line[:-2]) else: result.append(line) break return ''.join(result) def readlines(self): result = [] while True: line = self.readline() if not line: break result.append(line) return result class UniqueList(UserList): def __init__(self, seq = []): UserList.__init__(self, seq) self.unique = True def __make_unique(self): if not self.unique: self.data = uniquer_hashables(self.data) self.unique = True def __lt__(self, other): self.__make_unique() return UserList.__lt__(self, other) def __le__(self, other): self.__make_unique() return UserList.__le__(self, other) def __eq__(self, other): self.__make_unique() return UserList.__eq__(self, other) def __ne__(self, other): self.__make_unique() return UserList.__ne__(self, other) def __gt__(self, other): self.__make_unique() return UserList.__gt__(self, other) def __ge__(self, other): self.__make_unique() return UserList.__ge__(self, other) def __cmp__(self, other): self.__make_unique() return UserList.__cmp__(self, other) def __len__(self): self.__make_unique() return UserList.__len__(self) def __getitem__(self, i): self.__make_unique() return UserList.__getitem__(self, i) def __setitem__(self, i, item): UserList.__setitem__(self, i, item) self.unique = False def __getslice__(self, i, j): self.__make_unique() return UserList.__getslice__(self, i, j) def __setslice__(self, i, j, other): UserList.__setslice__(self, i, j, other) self.unique = False def __add__(self, other): result = UserList.__add__(self, other) result.unique = False return result def __radd__(self, other): result = UserList.__radd__(self, other) result.unique = False return result def __iadd__(self, other): result = UserList.__iadd__(self, other) result.unique = False return result def __mul__(self, other): result = UserList.__mul__(self, other) result.unique = False return result def __rmul__(self, other): result = UserList.__rmul__(self, other) result.unique = False return result def __imul__(self, other): result = UserList.__imul__(self, other) result.unique = False return result def append(self, item): UserList.append(self, item) self.unique = False def insert(self, i): UserList.insert(self, i) self.unique = False def count(self, item): self.__make_unique() return UserList.count(self, item) def index(self, item): self.__make_unique() return UserList.index(self, item) def reverse(self): self.__make_unique() UserList.reverse(self) def sort(self, *args, **kwds): self.__make_unique() return UserList.sort(self, *args, **kwds) def extend(self, other): UserList.extend(self, other) self.unique = False class Unbuffered(object): """ A proxy class that wraps a file object, flushing after every write, and delegating everything else to the wrapped object. """ def __init__(self, file): self.file = file self.softspace = 0 ## backward compatibility; not supported in Py3k def write(self, arg): try: self.file.write(arg) self.file.flush() except IOError: # Stdout might be connected to a pipe that has been closed # by now. The most likely reason for the pipe being closed # is that the user has press ctrl-c. It this is the case, # then SCons is currently shutdown. We therefore ignore # IOError's here so that SCons can continue and shutdown # properly so that the .sconsign is correctly written # before SCons exits. pass def __getattr__(self, attr): return getattr(self.file, attr) def make_path_relative(path): """ makes an absolute path name to a relative pathname. """ if os.path.isabs(path): drive_s,path = os.path.splitdrive(path) import re if not drive_s: path=re.compile("/*(.*)").findall(path)[0] else: path=path[1:] assert( not os.path.isabs( path ) ), path return path # 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(obj, 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) """ if name is None: name = function.func_name else: function = RenameFunction(function, name) if hasattr(obj, '__class__') and obj.__class__ is not type: # "obj" is an instance, so it gets a bound method. setattr(obj, name, MethodType(function, obj, obj.__class__)) else: # "obj" is a class, so it gets an unbound method. setattr(obj, name, MethodType(function, None, obj)) def RenameFunction(function, name): """ Returns a function identical to the specified function, but with the specified name. """ return FunctionType(function.func_code, function.func_globals, name, function.func_defaults) md5 = False def MD5signature(s): return str(s) def MD5filesignature(fname, chunksize=65536): f = open(fname, "rb") result = f.read() f.close() return result try: import hashlib except ImportError: pass else: if hasattr(hashlib, 'md5'): md5 = True def MD5signature(s): m = hashlib.md5() m.update(str(s)) return m.hexdigest() def MD5filesignature(fname, chunksize=65536): m = hashlib.md5() f = open(fname, "rb") while True: blck = f.read(chunksize) if not blck: break m.update(str(blck)) f.close() return m.hexdigest() def MD5collect(signatures): """ Collects a list of signatures into an aggregate signature. signatures - a list of signatures returns - the aggregate signature """ if len(signatures) == 1: return signatures[0] else: return MD5signature(', '.join(signatures)) def silent_intern(x): """ Perform sys.intern() on the passed argument and return the result. If the input is ineligible (e.g. a unicode string) the original argument is returned and no exception is thrown. """ try: return sys.intern(x) except TypeError: return x # From Dinu C. Gherman, # Python Cookbook, second edition, recipe 6.17, p. 277. # Also: # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/68205 # ASPN: Python Cookbook: Null Object Design Pattern #TODO??? class Null(object): class Null(object): """ Null objects always and reliably "do nothing." """ def __new__(cls, *args, **kwargs): if not '_instance' in vars(cls): cls._instance = super(Null, cls).__new__(cls, *args, **kwargs) return cls._instance def __init__(self, *args, **kwargs): pass def __call__(self, *args, **kwargs): return self def __repr__(self): return "Null(0x%08X)" % id(self) def __nonzero__(self): return False def __getattr__(self, name): return self def __setattr__(self, name, value): return self def __delattr__(self, name): return self class NullSeq(Null): def __len__(self): return 0 def __iter__(self): return iter(()) def __getitem__(self, i): return self def __delitem__(self, i): return self def __setitem__(self, i, v): return self del __revision__ # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Script/0000755000175000017500000000000012114661557020547 5ustar dktrkranzdktrkranzscons-doc-2.3.0/src/engine/SCons/Script/Main.py0000644000175000017500000014520012114661557022007 0ustar dktrkranzdktrkranz"""SCons.Script This file implements the main() function used by the scons script. Architecturally, this *is* the scons script, and will likely only be called from the external "scons" wrapper. Consequently, anything here should not be, or be considered, part of the build engine. If it's something that we expect other software to want to use, it should go in some other module. If it's specific to the "scons" script invocation, it goes here. """ unsupported_python_version = (2, 3, 0) deprecated_python_version = (2, 7, 0) # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Script/Main.py 2013/03/03 09:48:35 garyo" import SCons.compat import os import sys import time import traceback # Strip the script directory from sys.path() so on case-insensitive # (Windows) systems Python doesn't think that the "scons" script is the # "SCons" package. Replace it with our own version directory so, if # if they're there, we pick up the right version of the build engine # modules. #sys.path = [os.path.join(sys.prefix, # 'lib', # 'scons-%d' % SCons.__version__)] + sys.path[1:] import SCons.CacheDir import SCons.Debug import SCons.Defaults import SCons.Environment import SCons.Errors import SCons.Job import SCons.Node import SCons.Node.FS import SCons.Platform import SCons.SConf import SCons.Script import SCons.Taskmaster import SCons.Util import SCons.Warnings import SCons.Script.Interactive def fetch_win32_parallel_msg(): # A subsidiary function that exists solely to isolate this import # so we don't have to pull it in on all platforms, and so that an # in-line "import" statement in the _main() function below doesn't # cause warnings about local names shadowing use of the 'SCons' # globl in nest scopes and UnboundLocalErrors and the like in some # versions (2.1) of Python. import SCons.Platform.win32 return SCons.Platform.win32.parallel_msg # class SConsPrintHelpException(Exception): pass display = SCons.Util.display progress_display = SCons.Util.DisplayEngine() first_command_start = None last_command_end = None class Progressor(object): prev = '' count = 0 target_string = '$TARGET' def __init__(self, obj, interval=1, file=None, overwrite=False): if file is None: file = sys.stdout self.obj = obj self.file = file self.interval = interval self.overwrite = overwrite if callable(obj): self.func = obj elif SCons.Util.is_List(obj): self.func = self.spinner elif obj.find(self.target_string) != -1: self.func = self.replace_string else: self.func = self.string def write(self, s): self.file.write(s) self.file.flush() self.prev = s def erase_previous(self): if self.prev: length = len(self.prev) if self.prev[-1] in ('\n', '\r'): length = length - 1 self.write(' ' * length + '\r') self.prev = '' def spinner(self, node): self.write(self.obj[self.count % len(self.obj)]) def string(self, node): self.write(self.obj) def replace_string(self, node): self.write(self.obj.replace(self.target_string, str(node))) def __call__(self, node): self.count = self.count + 1 if (self.count % self.interval) == 0: if self.overwrite: self.erase_previous() self.func(node) ProgressObject = SCons.Util.Null() def Progress(*args, **kw): global ProgressObject ProgressObject = Progressor(*args, **kw) # Task control. # _BuildFailures = [] def GetBuildFailures(): return _BuildFailures class BuildTask(SCons.Taskmaster.OutOfDateTask): """An SCons build task.""" progress = ProgressObject def display(self, message): display('scons: ' + message) def prepare(self): self.progress(self.targets[0]) return SCons.Taskmaster.OutOfDateTask.prepare(self) def needs_execute(self): if SCons.Taskmaster.OutOfDateTask.needs_execute(self): return True if self.top and self.targets[0].has_builder(): display("scons: `%s' is up to date." % str(self.node)) return False def execute(self): if print_time: start_time = time.time() global first_command_start if first_command_start is None: first_command_start = start_time SCons.Taskmaster.OutOfDateTask.execute(self) if print_time: global cumulative_command_time global last_command_end finish_time = time.time() last_command_end = finish_time cumulative_command_time = cumulative_command_time+finish_time-start_time sys.stdout.write("Command execution time: %s: %f seconds\n"%(str(self.node), finish_time-start_time)) def do_failed(self, status=2): _BuildFailures.append(self.exception[1]) global exit_status global this_build_status if self.options.ignore_errors: SCons.Taskmaster.OutOfDateTask.executed(self) elif self.options.keep_going: SCons.Taskmaster.OutOfDateTask.fail_continue(self) exit_status = status this_build_status = status else: SCons.Taskmaster.OutOfDateTask.fail_stop(self) exit_status = status this_build_status = status def executed(self): t = self.targets[0] if self.top and not t.has_builder() and not t.side_effect: if not t.exists(): if t.__class__.__name__ in ('File', 'Dir', 'Entry'): errstr="Do not know how to make %s target `%s' (%s)." % (t.__class__.__name__, t, t.abspath) else: # Alias or Python or ... errstr="Do not know how to make %s target `%s'." % (t.__class__.__name__, t) sys.stderr.write("scons: *** " + errstr) if not self.options.keep_going: sys.stderr.write(" Stop.") sys.stderr.write("\n") try: raise SCons.Errors.BuildError(t, errstr) except KeyboardInterrupt: raise except: self.exception_set() self.do_failed() else: print "scons: Nothing to be done for `%s'." % t SCons.Taskmaster.OutOfDateTask.executed(self) else: SCons.Taskmaster.OutOfDateTask.executed(self) def failed(self): # Handle the failure of a build task. The primary purpose here # is to display the various types of Errors and Exceptions # appropriately. exc_info = self.exc_info() try: t, e, tb = exc_info except ValueError: t, e = exc_info tb = None if t is None: # The Taskmaster didn't record an exception for this Task; # see if the sys module has one. try: t, e, tb = sys.exc_info()[:] except ValueError: t, e = exc_info tb = None # Deprecated string exceptions will have their string stored # in the first entry of the tuple. if e is None: e = t buildError = SCons.Errors.convert_to_BuildError(e) if not buildError.node: buildError.node = self.node node = buildError.node if not SCons.Util.is_List(node): node = [ node ] nodename = ', '.join(map(str, node)) errfmt = "scons: *** [%s] %s\n" sys.stderr.write(errfmt % (nodename, buildError)) if (buildError.exc_info[2] and buildError.exc_info[1] and not isinstance( buildError.exc_info[1], (EnvironmentError, SCons.Errors.StopError, SCons.Errors.UserError))): type, value, trace = buildError.exc_info traceback.print_exception(type, value, trace) elif tb and print_stacktrace: sys.stderr.write("scons: internal stack trace:\n") traceback.print_tb(tb, file=sys.stderr) self.exception = (e, buildError, tb) # type, value, traceback self.do_failed(buildError.exitstatus) self.exc_clear() def postprocess(self): if self.top: t = self.targets[0] for tp in self.options.tree_printers: tp.display(t) if self.options.debug_includes: tree = t.render_include_tree() if tree: print print tree SCons.Taskmaster.OutOfDateTask.postprocess(self) def make_ready(self): """Make a task ready for execution""" SCons.Taskmaster.OutOfDateTask.make_ready(self) if self.out_of_date and self.options.debug_explain: explanation = self.out_of_date[0].explain() if explanation: sys.stdout.write("scons: " + explanation) class CleanTask(SCons.Taskmaster.AlwaysTask): """An SCons clean task.""" def fs_delete(self, path, pathstr, remove=1): try: if os.path.lexists(path): if os.path.isfile(path) or os.path.islink(path): if remove: os.unlink(path) display("Removed " + pathstr) elif os.path.isdir(path) and not os.path.islink(path): # delete everything in the dir for e in sorted(os.listdir(path)): p = os.path.join(path, e) s = os.path.join(pathstr, e) if os.path.isfile(p): if remove: os.unlink(p) display("Removed " + s) else: self.fs_delete(p, s, remove) # then delete dir itself if remove: os.rmdir(path) display("Removed directory " + pathstr) else: errstr = "Path '%s' exists but isn't a file or directory." raise SCons.Errors.UserError(errstr % (pathstr)) except SCons.Errors.UserError, e: print e except (IOError, OSError), e: print "scons: Could not remove '%s':" % pathstr, e.strerror def show(self): target = self.targets[0] if (target.has_builder() or target.side_effect) and not target.noclean: for t in self.targets: if not t.isdir(): display("Removed " + str(t)) if target in SCons.Environment.CleanTargets: files = SCons.Environment.CleanTargets[target] for f in files: self.fs_delete(f.abspath, str(f), 0) def remove(self): target = self.targets[0] if (target.has_builder() or target.side_effect) and not target.noclean: for t in self.targets: try: removed = t.remove() except OSError, e: # An OSError may indicate something like a permissions # issue, an IOError would indicate something like # the file not existing. In either case, print a # message and keep going to try to remove as many # targets aa possible. print "scons: Could not remove '%s':" % str(t), e.strerror else: if removed: display("Removed " + str(t)) if target in SCons.Environment.CleanTargets: files = SCons.Environment.CleanTargets[target] for f in files: self.fs_delete(f.abspath, str(f)) execute = remove # We want the Taskmaster to update the Node states (and therefore # handle reference counts, etc.), but we don't want to call # back to the Node's post-build methods, which would do things # we don't want, like store .sconsign information. executed = SCons.Taskmaster.Task.executed_without_callbacks # Have the taskmaster arrange to "execute" all of the targets, because # we'll figure out ourselves (in remove() or show() above) whether # anything really needs to be done. make_ready = SCons.Taskmaster.Task.make_ready_all def prepare(self): pass class QuestionTask(SCons.Taskmaster.AlwaysTask): """An SCons task for the -q (question) option.""" def prepare(self): pass def execute(self): if self.targets[0].get_state() != SCons.Node.up_to_date or \ (self.top and not self.targets[0].exists()): global exit_status global this_build_status exit_status = 1 this_build_status = 1 self.tm.stop() def executed(self): pass class TreePrinter(object): def __init__(self, derived=False, prune=False, status=False): self.derived = derived self.prune = prune self.status = status def get_all_children(self, node): return node.all_children() def get_derived_children(self, node): children = node.all_children(None) return [x for x in children if x.has_builder()] def display(self, t): if self.derived: func = self.get_derived_children else: func = self.get_all_children s = self.status and 2 or 0 SCons.Util.print_tree(t, func, prune=self.prune, showtags=s) def python_version_string(): return sys.version.split()[0] def python_version_unsupported(version=sys.version_info): return version < unsupported_python_version def python_version_deprecated(version=sys.version_info): return version < deprecated_python_version # Global variables print_objects = 0 print_memoizer = 0 print_stacktrace = 0 print_time = 0 sconscript_time = 0 cumulative_command_time = 0 exit_status = 0 # final exit status, assume success by default this_build_status = 0 # "exit status" of an individual build num_jobs = None delayed_warnings = [] class FakeOptionParser(object): """ A do-nothing option parser, used for the initial OptionsParser variable. During normal SCons operation, the OptionsParser is created right away by the main() function. Certain tests scripts however, can introspect on different Tool modules, the initialization of which can try to add a new, local option to an otherwise uninitialized OptionsParser object. This allows that introspection to happen without blowing up. """ class FakeOptionValues(object): def __getattr__(self, attr): return None values = FakeOptionValues() def add_local_option(self, *args, **kw): pass OptionsParser = FakeOptionParser() def AddOption(*args, **kw): if 'default' not in kw: kw['default'] = None result = OptionsParser.add_local_option(*args, **kw) return result def GetOption(name): return getattr(OptionsParser.values, name) def SetOption(name, value): return OptionsParser.values.set_option(name, value) # class Stats(object): def __init__(self): self.stats = [] self.labels = [] self.append = self.do_nothing self.print_stats = self.do_nothing def enable(self, outfp): self.outfp = outfp self.append = self.do_append self.print_stats = self.do_print def do_nothing(self, *args, **kw): pass class CountStats(Stats): def do_append(self, label): self.labels.append(label) self.stats.append(SCons.Debug.fetchLoggedInstances()) def do_print(self): stats_table = {} for s in self.stats: for n in [t[0] for t in s]: stats_table[n] = [0, 0, 0, 0] i = 0 for s in self.stats: for n, c in s: stats_table[n][i] = c i = i + 1 self.outfp.write("Object counts:\n") pre = [" "] post = [" %s\n"] l = len(self.stats) fmt1 = ''.join(pre + [' %7s']*l + post) fmt2 = ''.join(pre + [' %7d']*l + post) labels = self.labels[:l] labels.append(("", "Class")) self.outfp.write(fmt1 % tuple([x[0] for x in labels])) self.outfp.write(fmt1 % tuple([x[1] for x in labels])) for k in sorted(stats_table.keys()): r = stats_table[k][:l] + [k] self.outfp.write(fmt2 % tuple(r)) count_stats = CountStats() class MemStats(Stats): def do_append(self, label): self.labels.append(label) self.stats.append(SCons.Debug.memory()) def do_print(self): fmt = 'Memory %-32s %12d\n' for label, stats in zip(self.labels, self.stats): self.outfp.write(fmt % (label, stats)) memory_stats = MemStats() # utility functions def _scons_syntax_error(e): """Handle syntax errors. Print out a message and show where the error occurred. """ etype, value, tb = sys.exc_info() lines = traceback.format_exception_only(etype, value) for line in lines: sys.stderr.write(line+'\n') sys.exit(2) def find_deepest_user_frame(tb): """ Find the deepest stack frame that is not part of SCons. Input is a "pre-processed" stack trace in the form returned by traceback.extract_tb() or traceback.extract_stack() """ tb.reverse() # find the deepest traceback frame that is not part # of SCons: for frame in tb: filename = frame[0] if filename.find(os.sep+'SCons'+os.sep) == -1: return frame return tb[0] def _scons_user_error(e): """Handle user errors. Print out a message and a description of the error, along with the line number and routine where it occured. The file and line number will be the deepest stack frame that is not part of SCons itself. """ global print_stacktrace etype, value, tb = sys.exc_info() if print_stacktrace: traceback.print_exception(etype, value, tb) filename, lineno, routine, dummy = find_deepest_user_frame(traceback.extract_tb(tb)) sys.stderr.write("\nscons: *** %s\n" % value) sys.stderr.write('File "%s", line %d, in %s\n' % (filename, lineno, routine)) sys.exit(2) def _scons_user_warning(e): """Handle user warnings. Print out a message and a description of the warning, along with the line number and routine where it occured. The file and line number will be the deepest stack frame that is not part of SCons itself. """ etype, value, tb = sys.exc_info() filename, lineno, routine, dummy = find_deepest_user_frame(traceback.extract_tb(tb)) sys.stderr.write("\nscons: warning: %s\n" % e) sys.stderr.write('File "%s", line %d, in %s\n' % (filename, lineno, routine)) def _scons_internal_warning(e): """Slightly different from _scons_user_warning in that we use the *current call stack* rather than sys.exc_info() to get our stack trace. This is used by the warnings framework to print warnings.""" filename, lineno, routine, dummy = find_deepest_user_frame(traceback.extract_stack()) sys.stderr.write("\nscons: warning: %s\n" % e.args[0]) sys.stderr.write('File "%s", line %d, in %s\n' % (filename, lineno, routine)) def _scons_internal_error(): """Handle all errors but user errors. Print out a message telling the user what to do in this case and print a normal trace. """ print 'internal error' traceback.print_exc() sys.exit(2) def _SConstruct_exists(dirname='', repositories=[], filelist=None): """This function checks that an SConstruct file exists in a directory. If so, it returns the path of the file. By default, it checks the current directory. """ if not filelist: filelist = ['SConstruct', 'Sconstruct', 'sconstruct'] for file in filelist: sfile = os.path.join(dirname, file) if os.path.isfile(sfile): return sfile if not os.path.isabs(sfile): for rep in repositories: if os.path.isfile(os.path.join(rep, sfile)): return sfile return None def _set_debug_values(options): global print_memoizer, print_objects, print_stacktrace, print_time debug_values = options.debug if "count" in debug_values: # All of the object counts are within "if __debug__:" blocks, # which get stripped when running optimized (with python -O or # from compiled *.pyo files). Provide a warning if __debug__ is # stripped, so it doesn't just look like --debug=count is broken. enable_count = False if __debug__: enable_count = True if enable_count: count_stats.enable(sys.stdout) else: msg = "--debug=count is not supported when running SCons\n" + \ "\twith the python -O option or optimized (.pyo) modules." SCons.Warnings.warn(SCons.Warnings.NoObjectCountWarning, msg) if "dtree" in debug_values: options.tree_printers.append(TreePrinter(derived=True)) options.debug_explain = ("explain" in debug_values) if "findlibs" in debug_values: SCons.Scanner.Prog.print_find_libs = "findlibs" options.debug_includes = ("includes" in debug_values) print_memoizer = ("memoizer" in debug_values) if "memory" in debug_values: memory_stats.enable(sys.stdout) print_objects = ("objects" in debug_values) if "presub" in debug_values: SCons.Action.print_actions_presub = 1 if "stacktrace" in debug_values: print_stacktrace = 1 if "stree" in debug_values: options.tree_printers.append(TreePrinter(status=True)) if "time" in debug_values: print_time = 1 if "tree" in debug_values: options.tree_printers.append(TreePrinter()) if "prepare" in debug_values: SCons.Taskmaster.print_prepare = 1 if "duplicate" in debug_values: SCons.Node.FS.print_duplicate = 1 def _create_path(plist): path = '.' for d in plist: if os.path.isabs(d): path = d else: path = path + '/' + d return path def _load_site_scons_dir(topdir, site_dir_name=None): """Load the site_scons dir under topdir. Prepends site_scons to sys.path, imports site_scons/site_init.py, and prepends site_scons/site_tools to default toolpath.""" if site_dir_name: err_if_not_found = True # user specified: err if missing else: site_dir_name = "site_scons" err_if_not_found = False site_dir = os.path.join(topdir, site_dir_name) if not os.path.exists(site_dir): if err_if_not_found: raise SCons.Errors.UserError("site dir %s not found."%site_dir) return site_init_filename = "site_init.py" site_init_modname = "site_init" site_tools_dirname = "site_tools" # prepend to sys.path sys.path = [os.path.abspath(site_dir)] + sys.path site_init_file = os.path.join(site_dir, site_init_filename) site_tools_dir = os.path.join(site_dir, site_tools_dirname) if os.path.exists(site_init_file): import imp, re # TODO(2.4): turn this into try:-except:-finally: try: try: fp, pathname, description = imp.find_module(site_init_modname, [site_dir]) # Load the file into SCons.Script namespace. This is # opaque and clever; m is the module object for the # SCons.Script module, and the exec ... in call executes a # file (or string containing code) in the context of the # module's dictionary, so anything that code defines ends # up adding to that module. This is really short, but all # the error checking makes it longer. try: m = sys.modules['SCons.Script'] except Exception, e: fmt = 'cannot import site_init.py: missing SCons.Script module %s' raise SCons.Errors.InternalError(fmt % repr(e)) try: sfx = description[0] modname = os.path.basename(pathname)[:-len(sfx)] site_m = {"__file__": pathname, "__name__": modname, "__doc__": None} re_special = re.compile("__[^_]+__") for k in m.__dict__.keys(): if not re_special.match(k): site_m[k] = m.__dict__[k] # This is the magic. exec fp in site_m except KeyboardInterrupt: raise except Exception, e: fmt = '*** Error loading site_init file %s:\n' sys.stderr.write(fmt % repr(site_init_file)) raise else: for k in site_m: if not re_special.match(k): m.__dict__[k] = site_m[k] except KeyboardInterrupt: raise except ImportError, e: fmt = '*** cannot import site init file %s:\n' sys.stderr.write(fmt % repr(site_init_file)) raise finally: if fp: fp.close() if os.path.exists(site_tools_dir): # prepend to DefaultToolpath SCons.Tool.DefaultToolpath.insert(0, os.path.abspath(site_tools_dir)) def _load_all_site_scons_dirs(topdir, verbose=None): """Load all of the predefined site_scons dir. Order is significant; we load them in order from most generic (machine-wide) to most specific (topdir). The verbose argument is only for testing. """ platform = SCons.Platform.platform_default() def homedir(d): return os.path.expanduser('~/'+d) if platform == 'win32' or platform == 'cygwin': # Note we use $ here instead of %...% because older # pythons (prior to 2.6?) didn't expand %...% on Windows. # This set of dirs should work on XP, Vista, 7 and later. sysdirs=[ os.path.expandvars('$ALLUSERSPROFILE\\Application Data\\scons'), os.path.expandvars('$USERPROFILE\\Local Settings\\Application Data\\scons')] appdatadir = os.path.expandvars('$APPDATA\\scons') if appdatadir not in sysdirs: sysdirs.append(appdatadir) sysdirs.append(homedir('.scons')) elif platform == 'darwin': # MacOS X sysdirs=['/Library/Application Support/SCons', '/opt/local/share/scons', # (for MacPorts) '/sw/share/scons', # (for Fink) homedir('Library/Application Support/SCons'), homedir('.scons')] elif platform == 'sunos': # Solaris sysdirs=['/opt/sfw/scons', '/usr/share/scons', homedir('.scons')] else: # Linux, HPUX, etc. # assume posix-like, i.e. platform == 'posix' sysdirs=['/usr/share/scons', homedir('.scons')] dirs=sysdirs + [topdir] for d in dirs: if verbose: # this is used by unit tests. print "Loading site dir ", d _load_site_scons_dir(d) def test_load_all_site_scons_dirs(d): _load_all_site_scons_dirs(d, True) def version_string(label, module): version = module.__version__ build = module.__build__ if build: if build[0] != '.': build = '.' + build version = version + build fmt = "\t%s: v%s, %s, by %s on %s\n" return fmt % (label, version, module.__date__, module.__developer__, module.__buildsys__) def path_string(label, module): path = module.__path__ return "\t%s path: %s\n"%(label,path) def _main(parser): global exit_status global this_build_status options = parser.values # Here's where everything really happens. # First order of business: set up default warnings and then # handle the user's warning options, so that we can issue (or # suppress) appropriate warnings about anything that might happen, # as configured by the user. default_warnings = [ SCons.Warnings.WarningOnByDefault, SCons.Warnings.DeprecatedWarning, ] for warning in default_warnings: SCons.Warnings.enableWarningClass(warning) SCons.Warnings._warningOut = _scons_internal_warning SCons.Warnings.process_warn_strings(options.warn) # Now that we have the warnings configuration set up, we can actually # issue (or suppress) any warnings about warning-worthy things that # occurred while the command-line options were getting parsed. try: dw = options.delayed_warnings except AttributeError: pass else: delayed_warnings.extend(dw) for warning_type, message in delayed_warnings: SCons.Warnings.warn(warning_type, message) if options.diskcheck: SCons.Node.FS.set_diskcheck(options.diskcheck) # Next, we want to create the FS object that represents the outside # world's file system, as that's central to a lot of initialization. # To do this, however, we need to be in the directory from which we # want to start everything, which means first handling any relevant # options that might cause us to chdir somewhere (-C, -D, -U, -u). if options.directory: script_dir = os.path.abspath(_create_path(options.directory)) else: script_dir = os.getcwd() target_top = None if options.climb_up: target_top = '.' # directory to prepend to targets while script_dir and not _SConstruct_exists(script_dir, options.repository, options.file): script_dir, last_part = os.path.split(script_dir) if last_part: target_top = os.path.join(last_part, target_top) else: script_dir = '' if script_dir and script_dir != os.getcwd(): if not options.silent: display("scons: Entering directory `%s'" % script_dir) try: os.chdir(script_dir) except OSError: sys.stderr.write("Could not change directory to %s\n" % script_dir) # Now that we're in the top-level SConstruct directory, go ahead # and initialize the FS object that represents the file system, # and make it the build engine default. fs = SCons.Node.FS.get_default_fs() for rep in options.repository: fs.Repository(rep) # Now that we have the FS object, the next order of business is to # check for an SConstruct file (or other specified config file). # If there isn't one, we can bail before doing any more work. scripts = [] if options.file: scripts.extend(options.file) if not scripts: sfile = _SConstruct_exists(repositories=options.repository, filelist=options.file) if sfile: scripts.append(sfile) if not scripts: 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. raise SConsPrintHelpException raise SCons.Errors.UserError("No SConstruct file found.") if scripts[0] == "-": d = fs.getcwd() else: d = fs.File(scripts[0]).dir fs.set_SConstruct_dir(d) _set_debug_values(options) SCons.Node.implicit_cache = options.implicit_cache SCons.Node.implicit_deps_changed = options.implicit_deps_changed SCons.Node.implicit_deps_unchanged = options.implicit_deps_unchanged if options.no_exec: SCons.SConf.dryrun = 1 SCons.Action.execute_actions = None if options.question: SCons.SConf.dryrun = 1 if options.clean: SCons.SConf.SetBuildType('clean') if options.help: SCons.SConf.SetBuildType('help') SCons.SConf.SetCacheMode(options.config) SCons.SConf.SetProgressDisplay(progress_display) if options.no_progress or options.silent: progress_display.set_mode(0) if options.site_dir: _load_site_scons_dir(d.path, options.site_dir) elif not options.no_site_dir: _load_all_site_scons_dirs(d.path) if options.include_dir: sys.path = options.include_dir + sys.path # That should cover (most of) the options. Next, set up the variables # that hold command-line arguments, so the SConscript files that we # read and execute have access to them. targets = [] xmit_args = [] for a in parser.largs: if a[:1] == '-': continue if '=' in a: xmit_args.append(a) else: targets.append(a) SCons.Script._Add_Targets(targets + parser.rargs) SCons.Script._Add_Arguments(xmit_args) # If stdout is not a tty, replace it with a wrapper object to call flush # after every write. # # Tty devices automatically flush after every newline, so the replacement # isn't necessary. Furthermore, if we replace sys.stdout, the readline # module will no longer work. This affects the behavior during # --interactive mode. --interactive should only be used when stdin and # stdout refer to a tty. if not hasattr(sys.stdout, 'isatty') or not sys.stdout.isatty(): sys.stdout = SCons.Util.Unbuffered(sys.stdout) if not hasattr(sys.stderr, 'isatty') or not sys.stderr.isatty(): sys.stderr = SCons.Util.Unbuffered(sys.stderr) memory_stats.append('before reading SConscript files:') count_stats.append(('pre-', 'read')) # And here's where we (finally) read the SConscript files. progress_display("scons: Reading SConscript files ...") start_time = time.time() try: for script in scripts: SCons.Script._SConscript._SConscript(fs, script) except SCons.Errors.StopError, e: # We had problems reading an SConscript file, such as it # couldn't be copied in to the VariantDir. Since we're just # reading SConscript files and haven't started building # things yet, stop regardless of whether they used -i or -k # or anything else. sys.stderr.write("scons: *** %s Stop.\n" % e) exit_status = 2 sys.exit(exit_status) global sconscript_time sconscript_time = time.time() - start_time progress_display("scons: done reading SConscript files.") memory_stats.append('after reading SConscript files:') count_stats.append(('post-', 'read')) # Re-{enable,disable} warnings in case they disabled some in # the SConscript file. # # We delay enabling the PythonVersionWarning class until here so that, # if they explicity disabled it in either in the command line or in # $SCONSFLAGS, or in the SConscript file, then the search through # the list of deprecated warning classes will find that disabling # first and not issue the warning. #SCons.Warnings.enableWarningClass(SCons.Warnings.PythonVersionWarning) SCons.Warnings.process_warn_strings(options.warn) # Now that we've read the SConscript files, we can check for the # warning about deprecated Python versions--delayed until here # in case they disabled the warning in the SConscript files. if python_version_deprecated(): msg = "Support for pre-%s Python version (%s) is deprecated.\n" + \ " If this will cause hardship, contact dev@scons.tigris.org." deprecated_version_string = ".".join(map(str, deprecated_python_version)) SCons.Warnings.warn(SCons.Warnings.PythonVersionWarning, msg % (deprecated_version_string, python_version_string())) if not options.help: SCons.SConf.CreateConfigHBuilder(SCons.Defaults.DefaultEnvironment()) # Now re-parse the command-line options (any to the left of a '--' # argument, that is) with any user-defined command-line options that # the SConscript files may have added to the parser object. This will # emit the appropriate error message and exit if any unknown option # was specified on the command line. parser.preserve_unknown_options = False parser.parse_args(parser.largs, options) if options.help: help_text = SCons.Script.help_text if help_text is None: # They specified -h, but there was no Help() inside the # SConscript files. Give them the options usage. raise SConsPrintHelpException else: print help_text print "Use scons -H for help about command-line options." exit_status = 0 return # Change directory to the top-level SConstruct directory, then tell # the Node.FS subsystem that we're all done reading the SConscript # files and calling Repository() and VariantDir() and changing # directories and the like, so it can go ahead and start memoizing # the string values of file system nodes. fs.chdir(fs.Top) SCons.Node.FS.save_strings(1) # Now that we've read the SConscripts we can set the options # that are SConscript settable: SCons.Node.implicit_cache = options.implicit_cache SCons.Node.FS.set_duplicate(options.duplicate) fs.set_max_drift(options.max_drift) SCons.Job.explicit_stack_size = options.stack_size if options.md5_chunksize: SCons.Node.FS.File.md5_chunksize = options.md5_chunksize platform = SCons.Platform.platform_module() if options.interactive: SCons.Script.Interactive.interact(fs, OptionsParser, options, targets, target_top) else: # Build the targets nodes = _build_targets(fs, options, targets, target_top) if not nodes: exit_status = 2 def _build_targets(fs, options, targets, target_top): global this_build_status this_build_status = 0 progress_display.set_mode(not (options.no_progress or options.silent)) display.set_mode(not options.silent) SCons.Action.print_actions = not options.silent SCons.Action.execute_actions = not options.no_exec SCons.Node.FS.do_store_info = not options.no_exec SCons.SConf.dryrun = options.no_exec if options.diskcheck: SCons.Node.FS.set_diskcheck(options.diskcheck) SCons.CacheDir.cache_enabled = not options.cache_disable SCons.CacheDir.cache_debug = options.cache_debug SCons.CacheDir.cache_force = options.cache_force SCons.CacheDir.cache_show = options.cache_show if options.no_exec: CleanTask.execute = CleanTask.show else: CleanTask.execute = CleanTask.remove lookup_top = None if targets or SCons.Script.BUILD_TARGETS != SCons.Script._build_plus_default: # They specified targets on the command line or modified # BUILD_TARGETS in the SConscript file(s), so if they used -u, # -U or -D, we have to look up targets relative to the top, # but we build whatever they specified. if target_top: lookup_top = fs.Dir(target_top) target_top = None targets = SCons.Script.BUILD_TARGETS else: # There are no targets specified on the command line, # so if they used -u, -U or -D, we may have to restrict # what actually gets built. d = None if target_top: if options.climb_up == 1: # -u, local directory and below target_top = fs.Dir(target_top) lookup_top = target_top elif options.climb_up == 2: # -D, all Default() targets target_top = None lookup_top = None elif options.climb_up == 3: # -U, local SConscript Default() targets target_top = fs.Dir(target_top) def check_dir(x, target_top=target_top): if hasattr(x, 'cwd') and not x.cwd is None: cwd = x.cwd.srcnode() return cwd == target_top else: # x doesn't have a cwd, so it's either not a target, # or not a file, so go ahead and keep it as a default # target and let the engine sort it out: return 1 d = list(filter(check_dir, SCons.Script.DEFAULT_TARGETS)) SCons.Script.DEFAULT_TARGETS[:] = d target_top = None lookup_top = None targets = SCons.Script._Get_Default_Targets(d, fs) if not targets: sys.stderr.write("scons: *** No targets specified and no Default() targets found. Stop.\n") return None def Entry(x, ltop=lookup_top, ttop=target_top, fs=fs): if isinstance(x, SCons.Node.Node): node = x else: node = None # Why would ltop be None? Unfortunately this happens. if ltop is None: ltop = '' # Curdir becomes important when SCons is called with -u, -C, # or similar option that changes directory, and so the paths # of targets given on the command line need to be adjusted. curdir = os.path.join(os.getcwd(), str(ltop)) for lookup in SCons.Node.arg2nodes_lookups: node = lookup(x, curdir=curdir) if node is not None: break if node is None: node = fs.Entry(x, directory=ltop, create=1) if ttop and not node.is_under(ttop): if isinstance(node, SCons.Node.FS.Dir) and ttop.is_under(node): node = ttop else: node = None return node nodes = [_f for _f in map(Entry, targets) if _f] task_class = BuildTask # default action is to build targets opening_message = "Building targets ..." closing_message = "done building targets." if options.keep_going: failure_message = "done building targets (errors occurred during build)." else: failure_message = "building terminated because of errors." if options.question: task_class = QuestionTask try: if options.clean: task_class = CleanTask opening_message = "Cleaning targets ..." closing_message = "done cleaning targets." if options.keep_going: failure_message = "done cleaning targets (errors occurred during clean)." else: failure_message = "cleaning terminated because of errors." except AttributeError: pass task_class.progress = ProgressObject 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 for i in range(len(d)-1, 0, -1): j = int(random.random() * (i+1)) d[i], d[j] = d[j], d[i] return d else: def order(dependencies): """Leave the order of dependencies alone.""" return dependencies if options.taskmastertrace_file == '-': tmtrace = sys.stdout elif options.taskmastertrace_file: tmtrace = open(options.taskmastertrace_file, 'wb') else: tmtrace = None taskmaster = SCons.Taskmaster.Taskmaster(nodes, task_class, order, tmtrace) # Let the BuildTask objects get at the options to respond to the # various print_* settings, tree_printer list, etc. BuildTask.options = options global num_jobs num_jobs = options.num_jobs jobs = SCons.Job.Jobs(num_jobs, taskmaster) if num_jobs > 1: msg = None if jobs.num_jobs == 1: msg = "parallel builds are unsupported by this version of Python;\n" + \ "\tignoring -j or num_jobs option.\n" elif sys.platform == 'win32': msg = fetch_win32_parallel_msg() if msg: SCons.Warnings.warn(SCons.Warnings.NoParallelSupportWarning, msg) memory_stats.append('before building targets:') count_stats.append(('pre-', 'build')) def jobs_postfunc( jobs=jobs, options=options, closing_message=closing_message, failure_message=failure_message ): if jobs.were_interrupted(): if not options.no_progress and not options.silent: sys.stderr.write("scons: Build interrupted.\n") global exit_status global this_build_status exit_status = 2 this_build_status = 2 if this_build_status: progress_display("scons: " + failure_message) else: progress_display("scons: " + closing_message) if not options.no_exec: if jobs.were_interrupted(): progress_display("scons: writing .sconsign file.") SCons.SConsign.write() progress_display("scons: " + opening_message) jobs.run(postfunc = jobs_postfunc) memory_stats.append('after building targets:') count_stats.append(('post-', 'build')) return nodes def _exec_main(parser, values): sconsflags = os.environ.get('SCONSFLAGS', '') all_args = sconsflags.split() + sys.argv[1:] options, args = parser.parse_args(all_args, values) if isinstance(options.debug, list) and "pdb" in options.debug: import pdb pdb.Pdb().runcall(_main, parser) elif options.profile_file: # compat layer imports "cProfile" for us if it's available. from profile import Profile # Some versions of Python 2.4 shipped a profiler that had the # wrong 'c_exception' entry in its dispatch table. Make sure # we have the right one. (This may put an unnecessary entry # in the table in earlier versions of Python, but its presence # shouldn't hurt anything). try: dispatch = Profile.dispatch except AttributeError: pass else: dispatch['c_exception'] = Profile.trace_dispatch_return prof = Profile() try: prof.runcall(_main, parser) except SConsPrintHelpException, e: prof.dump_stats(options.profile_file) raise e except SystemExit: pass prof.dump_stats(options.profile_file) else: _main(parser) def main(): global OptionsParser global exit_status global first_command_start # Check up front for a Python version we do not support. We # delay the check for deprecated Python versions until later, # after the SConscript files have been read, in case they # disable that warning. if python_version_unsupported(): msg = "scons: *** SCons version %s does not run under Python version %s.\n" sys.stderr.write(msg % (SCons.__version__, python_version_string())) sys.exit(1) parts = ["SCons by Steven Knight et al.:\n"] try: import __main__ parts.append(version_string("script", __main__)) except (ImportError, AttributeError): # On Windows there is no scons.py, so there is no # __main__.__version__, hence there is no script version. pass parts.append(version_string("engine", SCons)) parts.append(path_string("engine", SCons)) parts.append("Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation") version = ''.join(parts) import SConsOptions parser = SConsOptions.Parser(version) values = SConsOptions.SConsValues(parser.get_default_values()) OptionsParser = parser try: _exec_main(parser, values) except SystemExit, s: if s: exit_status = s except KeyboardInterrupt: print("scons: Build interrupted.") sys.exit(2) except SyntaxError, e: _scons_syntax_error(e) except SCons.Errors.InternalError: _scons_internal_error() except SCons.Errors.UserError, e: _scons_user_error(e) except SConsPrintHelpException: parser.print_help() exit_status = 0 except SCons.Errors.BuildError, e: exit_status = e.exitstatus except: # An exception here is likely a builtin Python exception Python # code in an SConscript file. Show them precisely what the # problem was and where it happened. SCons.Script._SConscript.SConscript_exception() sys.exit(2) memory_stats.print_stats() count_stats.print_stats() if print_objects: SCons.Debug.listLoggedInstances('*') #SCons.Debug.dumpLoggedInstances('*') if print_memoizer: SCons.Memoize.Dump("Memoizer (memory cache) hits and misses:") # Dump any development debug info that may have been enabled. # These are purely for internal debugging during development, so # there's no need to control them with --debug= options; they're # controlled by changing the source code. SCons.Debug.dump_caller_counts() SCons.Taskmaster.dump_stats() if print_time: total_time = time.time() - SCons.Script.start_time if num_jobs == 1: ct = cumulative_command_time else: if last_command_end is None or first_command_start is None: ct = 0.0 else: ct = last_command_end - first_command_start scons_time = total_time - sconscript_time - ct print "Total build time: %f seconds"%total_time print "Total SConscript file execution time: %f seconds"%sconscript_time print "Total SCons execution time: %f seconds"%scons_time print "Total command execution time: %f seconds"%ct sys.exit(exit_status) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Script/SConscriptTests.py0000644000175000017500000000267312114661557024243 0ustar dktrkranzdktrkranz# # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Script/SConscriptTests.py 2013/03/03 09:48:35 garyo" import SCons.Script.SConscript # all of the SConscript.py tests are in test/SConscript.py # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Script/SConscript.py0000644000175000017500000005720712114661557023223 0ustar dktrkranzdktrkranz"""SCons.Script.SConscript This module defines the Python API provided to SConscript and SConstruct files. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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. from __future__ import division __revision__ = "src/engine/SCons/Script/SConscript.py 2013/03/03 09:48:35 garyo" import SCons import SCons.Action import SCons.Builder import SCons.Defaults import SCons.Environment import SCons.Errors import SCons.Node import SCons.Node.Alias import SCons.Node.FS import SCons.Platform import SCons.SConf import SCons.Script.Main import SCons.Tool import SCons.Util import collections import os import os.path import re import sys import traceback # The following variables used to live in this module. Some # SConscript files out there may have referred to them directly as # SCons.Script.SConscript.*. This is now supported by some special # handling towards the bottom of the SConscript.__init__.py module. #Arguments = {} #ArgList = [] #BuildTargets = TargetList() #CommandLineTargets = [] #DefaultTargets = [] class SConscriptReturn(Exception): pass launch_dir = os.path.abspath(os.curdir) GlobalDict = None # global exports set by Export(): global_exports = {} # chdir flag sconscript_chdir = 1 def get_calling_namespaces(): """Return the locals and globals for the function that called into this module in the current call stack.""" try: 1//0 except ZeroDivisionError: # Don't start iterating with the current stack-frame to # prevent creating reference cycles (f_back is safe). frame = sys.exc_info()[2].tb_frame.f_back # Find the first frame that *isn't* from this file. This means # that we expect all of the SCons frames that implement an Export() # or SConscript() call to be in this file, so that we can identify # the first non-Script.SConscript frame as the user's local calling # environment, and the locals and globals dictionaries from that # frame as the calling namespaces. See the comment below preceding # the DefaultEnvironmentCall block for even more explanation. while frame.f_globals.get("__name__") == __name__: frame = frame.f_back return frame.f_locals, frame.f_globals def compute_exports(exports): """Compute a dictionary of exports given one of the parameters to the Export() function or the exports argument to SConscript().""" loc, glob = get_calling_namespaces() retval = {} try: for export in exports: if SCons.Util.is_Dict(export): retval.update(export) else: try: retval[export] = loc[export] except KeyError: retval[export] = glob[export] except KeyError, x: raise SCons.Errors.UserError("Export of non-existent variable '%s'"%x) return retval class Frame(object): """A frame on the SConstruct/SConscript call stack""" def __init__(self, fs, exports, sconscript): self.globals = BuildDefaultGlobals() self.retval = None self.prev_dir = fs.getcwd() self.exports = compute_exports(exports) # exports from the calling SConscript # make sure the sconscript attr is a Node. if isinstance(sconscript, SCons.Node.Node): self.sconscript = sconscript elif sconscript == '-': self.sconscript = None else: self.sconscript = fs.File(str(sconscript)) # the SConstruct/SConscript call stack: call_stack = [] # For documentation on the methods in this file, see the scons man-page def Return(*vars, **kw): retval = [] try: fvars = SCons.Util.flatten(vars) for var in fvars: for v in var.split(): retval.append(call_stack[-1].globals[v]) except KeyError, x: raise SCons.Errors.UserError("Return of non-existent variable '%s'"%x) if len(retval) == 1: call_stack[-1].retval = retval[0] else: call_stack[-1].retval = tuple(retval) stop = kw.get('stop', True) if stop: raise SConscriptReturn stack_bottom = '% Stack boTTom %' # hard to define a variable w/this name :) def _SConscript(fs, *files, **kw): top = fs.Top sd = fs.SConstruct_dir.rdir() exports = kw.get('exports', []) # evaluate each SConscript file results = [] for fn in files: call_stack.append(Frame(fs, exports, fn)) old_sys_path = sys.path try: SCons.Script.sconscript_reading = SCons.Script.sconscript_reading + 1 if fn == "-": exec sys.stdin in call_stack[-1].globals else: if isinstance(fn, SCons.Node.Node): f = fn else: f = fs.File(str(fn)) _file_ = None # Change directory to the top of the source # tree to make sure the os's cwd and the cwd of # fs match so we can open the SConscript. fs.chdir(top, change_os_dir=1) if f.rexists(): actual = f.rfile() _file_ = open(actual.get_abspath(), "r") elif f.srcnode().rexists(): actual = f.srcnode().rfile() _file_ = open(actual.get_abspath(), "r") elif f.has_src_builder(): # The SConscript file apparently exists in a source # code management system. Build it, but then clear # the builder so that it doesn't get built *again* # during the actual build phase. f.build() f.built() f.builder_set(None) if f.exists(): _file_ = open(f.get_abspath(), "r") if _file_: # Chdir to the SConscript directory. Use a path # name relative to the SConstruct file so that if # we're using the -f option, we're essentially # creating a parallel SConscript directory structure # in our local directory tree. # # XXX This is broken for multiple-repository cases # where the SConstruct and SConscript files might be # in different Repositories. For now, cross that # bridge when someone comes to it. try: src_dir = kw['src_dir'] except KeyError: ldir = fs.Dir(f.dir.get_path(sd)) else: ldir = fs.Dir(src_dir) if not ldir.is_under(f.dir): # They specified a source directory, but # it's above the SConscript directory. # Do the sensible thing and just use the # SConcript directory. ldir = fs.Dir(f.dir.get_path(sd)) try: fs.chdir(ldir, change_os_dir=sconscript_chdir) except OSError: # There was no local directory, so we should be # able to chdir to the Repository directory. # Note that we do this directly, not through # fs.chdir(), because we still need to # interpret the stuff within the SConscript file # relative to where we are logically. fs.chdir(ldir, change_os_dir=0) os.chdir(actual.dir.get_abspath()) # Append the SConscript directory to the beginning # of sys.path so Python modules in the SConscript # directory can be easily imported. sys.path = [ f.dir.get_abspath() ] + sys.path # This is the magic line that actually reads up # and executes the stuff in the SConscript file. # The locals for this frame contain the special # bottom-of-the-stack marker so that any # exceptions that occur when processing this # SConscript can base the printed frames at this # level and not show SCons internals as well. call_stack[-1].globals.update({stack_bottom:1}) old_file = call_stack[-1].globals.get('__file__') try: del call_stack[-1].globals['__file__'] except KeyError: pass try: try: exec _file_ in call_stack[-1].globals except SConscriptReturn: pass finally: if old_file is not None: call_stack[-1].globals.update({__file__:old_file}) else: SCons.Warnings.warn(SCons.Warnings.MissingSConscriptWarning, "Ignoring missing SConscript '%s'" % f.path) finally: SCons.Script.sconscript_reading = SCons.Script.sconscript_reading - 1 sys.path = old_sys_path frame = call_stack.pop() try: fs.chdir(frame.prev_dir, change_os_dir=sconscript_chdir) except OSError: # There was no local directory, so chdir to the # Repository directory. Like above, we do this # directly. fs.chdir(frame.prev_dir, change_os_dir=0) rdir = frame.prev_dir.rdir() rdir._create() # Make sure there's a directory there. try: os.chdir(rdir.get_abspath()) except OSError, e: # We still couldn't chdir there, so raise the error, # but only if actions are being executed. # # If the -n option was used, the directory would *not* # have been created and we should just carry on and # let things muddle through. This isn't guaranteed # to work if the SConscript files are reading things # from disk (for example), but it should work well # enough for most configurations. if SCons.Action.execute_actions: raise e results.append(frame.retval) # if we only have one script, don't return a tuple if len(results) == 1: return results[0] else: return tuple(results) def SConscript_exception(file=sys.stderr): """Print an exception stack trace just for the SConscript file(s). This will show users who have Python errors where the problem is, without cluttering the output with all of the internal calls leading up to where we exec the SConscript.""" exc_type, exc_value, exc_tb = sys.exc_info() tb = exc_tb while tb and stack_bottom not in tb.tb_frame.f_locals: tb = tb.tb_next if not tb: # We did not find our exec statement, so this was actually a bug # in SCons itself. Show the whole stack. tb = exc_tb stack = traceback.extract_tb(tb) try: type = exc_type.__name__ except AttributeError: type = str(exc_type) if type[:11] == "exceptions.": type = type[11:] file.write('%s: %s:\n' % (type, exc_value)) for fname, line, func, text in stack: file.write(' File "%s", line %d:\n' % (fname, line)) file.write(' %s\n' % text) def annotate(node): """Annotate a node with the stack frame describing the SConscript file and line number that created it.""" tb = sys.exc_info()[2] while tb and stack_bottom not in tb.tb_frame.f_locals: tb = tb.tb_next if not tb: # We did not find any exec of an SConscript file: what?! raise SCons.Errors.InternalError("could not find SConscript stack frame") node.creator = traceback.extract_stack(tb)[0] # The following line would cause each Node to be annotated using the # above function. Unfortunately, this is a *huge* performance hit, so # leave this disabled until we find a more efficient mechanism. #SCons.Node.Annotate = annotate class SConsEnvironment(SCons.Environment.Base): """An Environment subclass that contains all of the methods that are particular to the wrapper SCons interface and which aren't (or shouldn't be) part of the build engine itself. Note that not all of the methods of this class have corresponding global functions, there are some private methods. """ # # Private methods of an SConsEnvironment. # def _exceeds_version(self, major, minor, v_major, v_minor): """Return 1 if 'major' and 'minor' are greater than the version in 'v_major' and 'v_minor', and 0 otherwise.""" return (major > v_major or (major == v_major and minor > v_minor)) def _get_major_minor_revision(self, version_string): """Split a version string into major, minor and (optionally) revision parts. This is complicated by the fact that a version string can be something like 3.2b1.""" version = version_string.split(' ')[0].split('.') v_major = int(version[0]) v_minor = int(re.match('\d+', version[1]).group()) if len(version) >= 3: v_revision = int(re.match('\d+', version[2]).group()) else: v_revision = 0 return v_major, v_minor, v_revision def _get_SConscript_filenames(self, ls, kw): """ Convert the parameters passed to SConscript() calls into a list of files and export variables. If the parameters are invalid, throws SCons.Errors.UserError. Returns a tuple (l, e) where l is a list of SConscript filenames and e is a list of exports. """ exports = [] if len(ls) == 0: try: dirs = kw["dirs"] except KeyError: raise SCons.Errors.UserError("Invalid SConscript usage - no parameters") if not SCons.Util.is_List(dirs): dirs = [ dirs ] dirs = list(map(str, dirs)) name = kw.get('name', 'SConscript') files = [os.path.join(n, name) for n in dirs] elif len(ls) == 1: files = ls[0] elif len(ls) == 2: files = ls[0] exports = self.Split(ls[1]) else: raise SCons.Errors.UserError("Invalid SConscript() usage - too many arguments") if not SCons.Util.is_List(files): files = [ files ] if kw.get('exports'): exports.extend(self.Split(kw['exports'])) variant_dir = kw.get('variant_dir') or kw.get('build_dir') if variant_dir: if len(files) != 1: raise SCons.Errors.UserError("Invalid SConscript() usage - can only specify one SConscript with a variant_dir") duplicate = kw.get('duplicate', 1) src_dir = kw.get('src_dir') if not src_dir: src_dir, fname = os.path.split(str(files[0])) files = [os.path.join(str(variant_dir), fname)] else: if not isinstance(src_dir, SCons.Node.Node): src_dir = self.fs.Dir(src_dir) fn = files[0] if not isinstance(fn, SCons.Node.Node): fn = self.fs.File(fn) if fn.is_under(src_dir): # Get path relative to the source directory. fname = fn.get_path(src_dir) files = [os.path.join(str(variant_dir), fname)] else: files = [fn.abspath] kw['src_dir'] = variant_dir self.fs.VariantDir(variant_dir, src_dir, duplicate) return (files, exports) # # Public methods of an SConsEnvironment. These get # entry points in the global name space so they can be called # as global functions. # def Configure(self, *args, **kw): if not SCons.Script.sconscript_reading: raise SCons.Errors.UserError("Calling Configure from Builders is not supported.") kw['_depth'] = kw.get('_depth', 0) + 1 return SCons.Environment.Base.Configure(self, *args, **kw) def Default(self, *targets): SCons.Script._Set_Default_Targets(self, targets) def EnsureSConsVersion(self, major, minor, revision=0): """Exit abnormally if the SCons version is not late enough.""" scons_ver = self._get_major_minor_revision(SCons.__version__) if scons_ver < (major, minor, revision): if revision: scons_ver_string = '%d.%d.%d' % (major, minor, revision) else: scons_ver_string = '%d.%d' % (major, minor) print "SCons %s or greater required, but you have SCons %s" % \ (scons_ver_string, SCons.__version__) sys.exit(2) def EnsurePythonVersion(self, major, minor): """Exit abnormally if the Python version is not late enough.""" if sys.version_info < (major, minor): v = sys.version.split()[0] print "Python %d.%d or greater required, but you have Python %s" %(major,minor,v) sys.exit(2) def Exit(self, value=0): sys.exit(value) def Export(self, *vars, **kw): for var in vars: global_exports.update(compute_exports(self.Split(var))) global_exports.update(kw) def GetLaunchDir(self): global launch_dir return launch_dir def GetOption(self, name): name = self.subst(name) return SCons.Script.Main.GetOption(name) def Help(self, text): text = self.subst(text, raw=1) SCons.Script.HelpFunction(text) def Import(self, *vars): try: frame = call_stack[-1] globals = frame.globals exports = frame.exports for var in vars: var = self.Split(var) for v in var: if v == '*': globals.update(global_exports) globals.update(exports) else: if v in exports: globals[v] = exports[v] else: globals[v] = global_exports[v] except KeyError,x: raise SCons.Errors.UserError("Import of non-existent variable '%s'"%x) def SConscript(self, *ls, **kw): if 'build_dir' in kw: msg = """The build_dir keyword has been deprecated; use the variant_dir keyword instead.""" SCons.Warnings.warn(SCons.Warnings.DeprecatedBuildDirWarning, msg) def subst_element(x, subst=self.subst): if SCons.Util.is_List(x): x = list(map(subst, x)) else: x = subst(x) return x ls = list(map(subst_element, ls)) subst_kw = {} for key, val in kw.items(): if SCons.Util.is_String(val): val = self.subst(val) elif SCons.Util.is_List(val): result = [] for v in val: if SCons.Util.is_String(v): v = self.subst(v) result.append(v) val = result subst_kw[key] = val files, exports = self._get_SConscript_filenames(ls, subst_kw) subst_kw['exports'] = exports return _SConscript(self.fs, *files, **subst_kw) def SConscriptChdir(self, flag): global sconscript_chdir sconscript_chdir = flag def SetOption(self, name, value): name = self.subst(name) SCons.Script.Main.SetOption(name, value) # # # SCons.Environment.Environment = SConsEnvironment def Configure(*args, **kw): if not SCons.Script.sconscript_reading: raise SCons.Errors.UserError("Calling Configure from Builders is not supported.") kw['_depth'] = 1 return SCons.SConf.SConf(*args, **kw) # It's very important that the DefaultEnvironmentCall() class stay in this # file, with the get_calling_namespaces() function, the compute_exports() # function, the Frame class and the SConsEnvironment.Export() method. # These things make up the calling stack leading up to the actual global # Export() or SConscript() call that the user issued. We want to allow # users to export local variables that they define, like so: # # def func(): # x = 1 # Export('x') # # To support this, the get_calling_namespaces() function assumes that # the *first* stack frame that's not from this file is the local frame # for the Export() or SConscript() call. _DefaultEnvironmentProxy = None def get_DefaultEnvironmentProxy(): global _DefaultEnvironmentProxy if not _DefaultEnvironmentProxy: default_env = SCons.Defaults.DefaultEnvironment() _DefaultEnvironmentProxy = SCons.Environment.NoSubstitutionProxy(default_env) return _DefaultEnvironmentProxy class DefaultEnvironmentCall(object): """A class that implements "global function" calls of Environment methods by fetching the specified method from the DefaultEnvironment's class. Note that this uses an intermediate proxy class instead of calling the DefaultEnvironment method directly so that the proxy can override the subst() method and thereby prevent expansion of construction variables (since from the user's point of view this was called as a global function, with no associated construction environment).""" def __init__(self, method_name, subst=0): self.method_name = method_name if subst: self.factory = SCons.Defaults.DefaultEnvironment else: self.factory = get_DefaultEnvironmentProxy def __call__(self, *args, **kw): env = self.factory() method = getattr(env, self.method_name) return method(*args, **kw) def BuildDefaultGlobals(): """ Create a dictionary containing all the default globals for SConstruct and SConscript files. """ global GlobalDict if GlobalDict is None: GlobalDict = {} import SCons.Script d = SCons.Script.__dict__ def not_a_module(m, d=d, mtype=type(SCons.Script)): return not isinstance(d[m], mtype) for m in filter(not_a_module, dir(SCons.Script)): GlobalDict[m] = d[m] return GlobalDict.copy() # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Script/MainTests.py0000644000175000017500000000412712114661557023034 0ustar dktrkranzdktrkranz# # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Script/MainTests.py 2013/03/03 09:48:35 garyo" import unittest import SCons.Errors import SCons.Script.Main # Unit tests of various classes within SCons.Script.Main.py. # # Most of the tests of this functionality are actually end-to-end scripts # in the test/ hierarchy. # # This module is for specific bits of functionality that we can test # more effectively here, instead of in an end-to-end test that would # have to reach into SCons.Script.Main for various classes or other bits # of private functionality. if __name__ == "__main__": suite = unittest.TestSuite() tclasses = [] for tclass in tclasses: names = unittest.getTestCaseNames(tclass, 'test_') suite.addTests(list(map(tclass, names))) if not unittest.TextTestRunner().run(suite).wasSuccessful(): sys.exit(1) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Script/SConscript.xml0000644000175000017500000003000112114661557023352 0ustar dktrkranzdktrkranz (targets) This specifies a list of default targets, which will be built by &scons; if no explicit targets are given on the command line. Multiple calls to &f-Default; are legal, and add to the list of default targets. Multiple targets should be specified as separate arguments to the &f-Default; method, or as a list. &f-Default; will also accept the Node returned by any of a construction environment's builder methods. Examples: Default('foo', 'bar', 'baz') env.Default(['a', 'b', 'c']) hello = env.Program('hello', 'hello.c') env.Default(hello) An argument to &f-Default; of None will clear all default targets. Later calls to &f-Default; will add to the (now empty) default-target list like normal. The current list of targets added using the &f-Default; function or method is available in the DEFAULT_TARGETS list; see below. (major, minor) Ensure that the Python version is at least major.minor. This function will print out an error message and exit SCons with a non-zero exit code if the actual Python version is not late enough. Example: EnsurePythonVersion(2,2) (major, minor, [revision]) Ensure that the SCons version is at least major.minor, or major.minor.revision. if revision is specified. This function will print out an error message and exit SCons with a non-zero exit code if the actual SCons version is not late enough. Examples: EnsureSConsVersion(0,14) EnsureSConsVersion(0,96,90) ([value]) This tells &scons; to exit immediately with the specified value. A default exit value of 0 (zero) is used if no value is specified. (vars) This tells &scons; to export a list of variables from the current SConscript file to all other SConscript files. The exported variables are kept in a global collection, so subsequent calls to &f-Export; will over-write previous exports that have the same name. Multiple variable names can be passed to &f-Export; as separate arguments or as a list. Keyword arguments can be used to provide names and their values. A dictionary can be used to map variables to a different name when exported. Both local variables and global variables can be exported. Examples: env = Environment() # Make env available for all SConscript files to Import(). Export("env") package = 'my_name' # Make env and package available for all SConscript files:. Export("env", "package") # Make env and package available for all SConscript files: Export(["env", "package"]) # Make env available using the name debug: Export(debug = env) # Make env available using the name debug: Export({"debug":env}) Note that the &f-SConscript; function supports an exports argument that makes it easier to to export a variable or set of variables to a single SConscript file. See the description of the &f-SConscript; function, below. () Returns the absolute path name of the directory from which &scons; was initially invoked. This can be useful when using the , or options, which internally change to the directory in which the &SConstruct; file is found. (text) This specifies help text to be printed if the argument is given to &scons;. If &f-Help; is called multiple times, the text is appended together in the order that &f-Help; is called. (vars) This tells &scons; to import a list of variables into the current SConscript file. This will import variables that were exported with &f-Export; or in the exports argument to &f-link-SConscript;. Variables exported by &f-SConscript; have precedence. Multiple variable names can be passed to &f-Import; as separate arguments or as a list. The variable "*" can be used to import all variables. Examples: Import("env") Import("env", "variable") Import(["env", "variable"]) Import("*") ([vars..., stop=]) By default, this stops processing the current SConscript file and returns to the calling SConscript file the values of the variables named in the vars string arguments. Multiple strings contaning variable names may be passed to &f-Return;. Any strings that contain white space The optional stop= keyword argument may be set to a false value to continue processing the rest of the SConscript file after the &f-Return; call. This was the default behavior prior to SCons 0.98. However, the values returned are still the values of the variables in the named vars at the point &f-Return; is called. Examples: # Returns without returning a value. Return() # Returns the value of the 'foo' Python variable. Return("foo") # Returns the values of the Python variables 'foo' and 'bar'. Return("foo", "bar") # Returns the values of Python variables 'val1' and 'val2'. Return('val1 val2') (scripts, [exports, variant_dir, duplicate]) (dirs=subdirs, [name=script, exports, variant_dir, duplicate]) This tells &scons; to execute one or more subsidiary SConscript (configuration) files. Any variables returned by a called script using &f-link-Return; will be returned by the call to &f-SConscript;. There are two ways to call the &f-SConscript; function. The first way you can call &f-SConscript; is to explicitly specify one or more scripts as the first argument. A single script may be specified as a string; multiple scripts must be specified as a list (either explicitly or as created by a function like &f-Split;). Examples: SConscript('SConscript') # run SConscript in the current directory SConscript('src/SConscript') # run SConscript in the src directory SConscript(['src/SConscript', 'doc/SConscript']) config = SConscript('MyConfig.py') The second way you can call &f-SConscript; is to specify a list of (sub)directory names as a dirs=subdirs keyword argument. In this case, &scons; will, by default, execute a subsidiary configuration file named &SConscript; in each of the specified directories. You may specify a name other than &SConscript; by supplying an optional name=script keyword argument. The first three examples below have the same effect as the first three examples above: SConscript(dirs='.') # run SConscript in the current directory SConscript(dirs='src') # run SConscript in the src directory SConscript(dirs=['src', 'doc']) SConscript(dirs=['sub1', 'sub2'], name='MySConscript') The optional exports argument provides a list of variable names or a dictionary of named values to export to the script(s). These variables are locally exported only to the specified script(s), and do not affect the global pool of variables used by the &f-Export; function. The subsidiary script(s) must use the &f-link-Import; function to import the variables. Examples: foo = SConscript('sub/SConscript', exports='env') SConscript('dir/SConscript', exports=['env', 'variable']) SConscript(dirs='subdir', exports='env variable') SConscript(dirs=['one', 'two', 'three'], exports='shared_info') If the optional variant_dir argument is present, it causes an effect equivalent to the &f-link-VariantDir; method described below. (If variant_dir is not present, the duplicate argument is ignored.) The variant_dir argument is interpreted relative to the directory of the calling &SConscript; file. See the description of the &f-VariantDir; function below for additional details and restrictions. If variant_dir is present, the source directory is the directory in which the &SConscript; file resides and the &SConscript; file is evaluated as if it were in the variant_dir directory: SConscript('src/SConscript', variant_dir = 'build') is equivalent to VariantDir('build', 'src') SConscript('build/SConscript') This later paradigm is often used when the sources are in the same directory as the &SConstruct;: SConscript('SConscript', variant_dir = 'build') is equivalent to VariantDir('build', '.') SConscript('build/SConscript') Here are some composite examples: # collect the configuration information and use it to build src and doc shared_info = SConscript('MyConfig.py') SConscript('src/SConscript', exports='shared_info') SConscript('doc/SConscript', exports='shared_info') # build debugging and production versions. SConscript # can use Dir('.').path to determine variant. SConscript('SConscript', variant_dir='debug', duplicate=0) SConscript('SConscript', variant_dir='prod', duplicate=0) # build debugging and production versions. SConscript # is passed flags to use. opts = { 'CPPDEFINES' : ['DEBUG'], 'CCFLAGS' : '-pgdb' } SConscript('SConscript', variant_dir='debug', duplicate=0, exports=opts) opts = { 'CPPDEFINES' : ['NODEBUG'], 'CCFLAGS' : '-O' } SConscript('SConscript', variant_dir='prod', duplicate=0, exports=opts) # build common documentation and compile for different architectures SConscript('doc/SConscript', variant_dir='build/doc', duplicate=0) SConscript('src/SConscript', variant_dir='build/x86', duplicate=0) SConscript('src/SConscript', variant_dir='build/ppc', duplicate=0) scons-doc-2.3.0/src/engine/SCons/Script/__init__.py0000644000175000017500000003352412114661557022667 0ustar dktrkranzdktrkranz"""SCons.Script This file implements the main() function used by the scons script. Architecturally, this *is* the scons script, and will likely only be called from the external "scons" wrapper. Consequently, anything here should not be, or be considered, part of the build engine. If it's something that we expect other software to want to use, it should go in some other module. If it's specific to the "scons" script invocation, it goes here. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Script/__init__.py 2013/03/03 09:48:35 garyo" import time start_time = time.time() import collections import os import sys # Special chicken-and-egg handling of the "--debug=memoizer" flag: # # SCons.Memoize contains a metaclass implementation that affects how # the other classes are instantiated. The Memoizer may add shim methods # to classes that have methods that cache computed values in order to # count and report the hits and misses. # # If we wait to enable the Memoization until after we've parsed the # command line options normally, it will be too late, because the Memoizer # will have already analyzed the classes that it's Memoizing and decided # to not add the shims. So we use a special-case, up-front check for # the "--debug=memoizer" flag and enable Memoizer before we import any # of the other modules that use it. _args = sys.argv + os.environ.get('SCONSFLAGS', '').split() if "--debug=memoizer" in _args: import SCons.Memoize import SCons.Warnings try: SCons.Memoize.EnableMemoization() except SCons.Warnings.Warning: # Some warning was thrown. Arrange for it to be displayed # or not after warnings are configured. import Main exc_type, exc_value, tb = sys.exc_info() Main.delayed_warnings.append((exc_type, exc_value)) del _args import SCons.Action import SCons.Builder import SCons.Environment import SCons.Node.FS import SCons.Options import SCons.Platform import SCons.Scanner import SCons.SConf import SCons.Subst import SCons.Tool import SCons.Util import SCons.Variables import SCons.Defaults import Main main = Main.main # The following are global class definitions and variables that used to # live directly in this module back before 0.96.90, when it contained # a lot of code. Some SConscript files in widely-distributed packages # (Blender is the specific example) actually reached into SCons.Script # directly to use some of these. Rather than break those SConscript # files, we're going to propagate these names into the SCons.Script # namespace here. # # Some of these are commented out because it's *really* unlikely anyone # used them, but we're going to leave the comment here to try to make # it obvious what to do if the situation arises. BuildTask = Main.BuildTask CleanTask = Main.CleanTask QuestionTask = Main.QuestionTask #PrintHelp = Main.PrintHelp #SConscriptSettableOptions = Main.SConscriptSettableOptions AddOption = Main.AddOption GetOption = Main.GetOption SetOption = Main.SetOption Progress = Main.Progress GetBuildFailures = Main.GetBuildFailures #keep_going_on_error = Main.keep_going_on_error #print_dtree = Main.print_dtree #print_explanations = Main.print_explanations #print_includes = Main.print_includes #print_objects = Main.print_objects #print_time = Main.print_time #print_tree = Main.print_tree #memory_stats = Main.memory_stats #ignore_errors = Main.ignore_errors #sconscript_time = Main.sconscript_time #command_time = Main.command_time #exit_status = Main.exit_status #profiling = Main.profiling #repositories = Main.repositories # import SConscript _SConscript = SConscript call_stack = _SConscript.call_stack # Action = SCons.Action.Action AddMethod = SCons.Util.AddMethod AllowSubstExceptions = SCons.Subst.SetAllowableExceptions Builder = SCons.Builder.Builder Configure = _SConscript.Configure Environment = SCons.Environment.Environment #OptParser = SCons.SConsOptions.OptParser FindPathDirs = SCons.Scanner.FindPathDirs Platform = SCons.Platform.Platform Return = _SConscript.Return Scanner = SCons.Scanner.Base Tool = SCons.Tool.Tool WhereIs = SCons.Util.WhereIs # BoolVariable = SCons.Variables.BoolVariable EnumVariable = SCons.Variables.EnumVariable ListVariable = SCons.Variables.ListVariable PackageVariable = SCons.Variables.PackageVariable PathVariable = SCons.Variables.PathVariable # Deprecated names that will go away some day. BoolOption = SCons.Options.BoolOption EnumOption = SCons.Options.EnumOption ListOption = SCons.Options.ListOption PackageOption = SCons.Options.PackageOption PathOption = SCons.Options.PathOption # Action factories. Chmod = SCons.Defaults.Chmod Copy = SCons.Defaults.Copy Delete = SCons.Defaults.Delete Mkdir = SCons.Defaults.Mkdir Move = SCons.Defaults.Move Touch = SCons.Defaults.Touch # Pre-made, public scanners. CScanner = SCons.Tool.CScanner DScanner = SCons.Tool.DScanner DirScanner = SCons.Defaults.DirScanner ProgramScanner = SCons.Tool.ProgramScanner SourceFileScanner = SCons.Tool.SourceFileScanner # Functions we might still convert to Environment methods. CScan = SCons.Defaults.CScan DefaultEnvironment = SCons.Defaults.DefaultEnvironment # Other variables we provide. class TargetList(collections.UserList): def _do_nothing(self, *args, **kw): pass def _add_Default(self, list): self.extend(list) def _clear(self): del self[:] ARGUMENTS = {} ARGLIST = [] BUILD_TARGETS = TargetList() COMMAND_LINE_TARGETS = [] DEFAULT_TARGETS = [] # BUILD_TARGETS can be modified in the SConscript files. If so, we # want to treat the modified BUILD_TARGETS list as if they specified # targets on the command line. To do that, though, we need to know if # BUILD_TARGETS was modified through "official" APIs or by hand. We do # this by updating two lists in parallel, the documented BUILD_TARGETS # list, above, and this internal _build_plus_default targets list which # should only have "official" API changes. Then Script/Main.py can # compare these two afterwards to figure out if the user added their # own targets to BUILD_TARGETS. _build_plus_default = TargetList() def _Add_Arguments(alist): for arg in alist: a, b = arg.split('=', 1) ARGUMENTS[a] = b ARGLIST.append((a, b)) def _Add_Targets(tlist): if tlist: COMMAND_LINE_TARGETS.extend(tlist) BUILD_TARGETS.extend(tlist) BUILD_TARGETS._add_Default = BUILD_TARGETS._do_nothing BUILD_TARGETS._clear = BUILD_TARGETS._do_nothing _build_plus_default.extend(tlist) _build_plus_default._add_Default = _build_plus_default._do_nothing _build_plus_default._clear = _build_plus_default._do_nothing def _Set_Default_Targets_Has_Been_Called(d, fs): return DEFAULT_TARGETS def _Set_Default_Targets_Has_Not_Been_Called(d, fs): if d is None: d = [fs.Dir('.')] return d _Get_Default_Targets = _Set_Default_Targets_Has_Not_Been_Called def _Set_Default_Targets(env, tlist): global DEFAULT_TARGETS global _Get_Default_Targets _Get_Default_Targets = _Set_Default_Targets_Has_Been_Called for t in tlist: if t is None: # Delete the elements from the list in-place, don't # reassign an empty list to DEFAULT_TARGETS, so that the # variables will still point to the same object we point to. del DEFAULT_TARGETS[:] BUILD_TARGETS._clear() _build_plus_default._clear() elif isinstance(t, SCons.Node.Node): DEFAULT_TARGETS.append(t) BUILD_TARGETS._add_Default([t]) _build_plus_default._add_Default([t]) else: nodes = env.arg2nodes(t, env.fs.Entry) DEFAULT_TARGETS.extend(nodes) BUILD_TARGETS._add_Default(nodes) _build_plus_default._add_Default(nodes) # help_text = None def HelpFunction(text): global help_text if SCons.Script.help_text is None: SCons.Script.help_text = text else: help_text = help_text + text # # Will be non-zero if we are reading an SConscript file. sconscript_reading = 0 # def Variables(files=[], args=ARGUMENTS): return SCons.Variables.Variables(files, args) def Options(files=[], args=ARGUMENTS): return SCons.Options.Options(files, args) # The list of global functions to add to the SConscript name space # that end up calling corresponding methods or Builders in the # DefaultEnvironment(). GlobalDefaultEnvironmentFunctions = [ # Methods from the SConsEnvironment class, above. 'Default', 'EnsurePythonVersion', 'EnsureSConsVersion', 'Exit', 'Export', 'GetLaunchDir', 'Help', 'Import', #'SConscript', is handled separately, below. 'SConscriptChdir', # Methods from the Environment.Base class. 'AddPostAction', 'AddPreAction', 'Alias', 'AlwaysBuild', 'BuildDir', 'CacheDir', 'Clean', #The Command() method is handled separately, below. 'Decider', 'Depends', 'Dir', 'NoClean', 'NoCache', 'Entry', 'Execute', 'File', 'FindFile', 'FindInstalledFiles', 'FindSourceFiles', 'Flatten', 'GetBuildPath', 'Glob', 'Ignore', 'Install', 'InstallAs', 'Literal', 'Local', 'ParseDepends', 'Precious', 'Repository', 'Requires', 'SConsignFile', 'SideEffect', 'SourceCode', 'SourceSignatures', 'Split', 'Tag', 'TargetSignatures', 'Value', 'VariantDir', ] GlobalDefaultBuilders = [ # Supported builders. 'CFile', 'CXXFile', 'DVI', 'Jar', 'Java', 'JavaH', 'Library', 'M4', 'MSVSProject', 'Object', 'PCH', 'PDF', 'PostScript', 'Program', 'RES', 'RMIC', 'SharedLibrary', 'SharedObject', 'StaticLibrary', 'StaticObject', 'Tar', 'TypeLibrary', 'Zip', 'Package', ] for name in GlobalDefaultEnvironmentFunctions + GlobalDefaultBuilders: exec "%s = _SConscript.DefaultEnvironmentCall(%s)" % (name, repr(name)) del name # There are a handful of variables that used to live in the # Script/SConscript.py module that some SConscript files out there were # accessing directly as SCons.Script.SConscript.*. The problem is that # "SConscript" in this namespace is no longer a module, it's a global # function call--or more precisely, an object that implements a global # function call through the default Environment. Nevertheless, we can # maintain backwards compatibility for SConscripts that were reaching in # this way by hanging some attributes off the "SConscript" object here. SConscript = _SConscript.DefaultEnvironmentCall('SConscript') # Make SConscript look enough like the module it used to be so # that pychecker doesn't barf. SConscript.__name__ = 'SConscript' SConscript.Arguments = ARGUMENTS SConscript.ArgList = ARGLIST SConscript.BuildTargets = BUILD_TARGETS SConscript.CommandLineTargets = COMMAND_LINE_TARGETS SConscript.DefaultTargets = DEFAULT_TARGETS # The global Command() function must be handled differently than the # global functions for other construction environment methods because # we want people to be able to use Actions that must expand $TARGET # and $SOURCE later, when (and if) the Action is invoked to build # the target(s). We do this with the subst=1 argument, which creates # a DefaultEnvironmentCall instance that wraps up a normal default # construction environment that performs variable substitution, not a # proxy that doesn't. # # There's a flaw here, though, because any other $-variables on a command # line will *also* be expanded, each to a null string, but that should # only be a problem in the unusual case where someone was passing a '$' # on a command line and *expected* the $ to get through to the shell # because they were calling Command() and not env.Command()... This is # unlikely enough that we're going to leave this as is and cross that # bridge if someone actually comes to it. Command = _SConscript.DefaultEnvironmentCall('Command', subst=1) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Script/Interactive.py0000644000175000017500000003341712114661557023406 0ustar dktrkranzdktrkranz# # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Script/Interactive.py 2013/03/03 09:48:35 garyo" __doc__ = """ SCons interactive mode """ # TODO: # # This has the potential to grow into something with a really big life # of its own, which might or might not be a good thing. Nevertheless, # here are some enhancements that will probably be requested some day # and are worth keeping in mind (assuming this takes off): # # - A command to re-read / re-load the SConscript files. This may # involve allowing people to specify command-line options (e.g. -f, # -I, --no-site-dir) that affect how the SConscript files are read. # # - Additional command-line options on the "build" command. # # Of the supported options that seemed to make sense (after a quick # pass through the list), the ones that seemed likely enough to be # used are listed in the man page and have explicit test scripts. # # These had code changed in Script/Main.py to support them, but didn't # seem likely to be used regularly, so had no test scripts added: # # build --diskcheck=* # build --implicit-cache=* # build --implicit-deps-changed=* # build --implicit-deps-unchanged=* # # These look like they should "just work" with no changes to the # existing code, but like those above, look unlikely to be used and # therefore had no test scripts added: # # build --random # # These I'm not sure about. They might be useful for individual # "build" commands, and may even work, but they seem unlikely enough # that we'll wait until they're requested before spending any time on # writing test scripts for them, or investigating whether they work. # # build -q [??? is there a useful analog to the exit status?] # build --duplicate= # build --profile= # build --max-drift= # build --warn=* # build --Y # # - Most of the SCons command-line options that the "build" command # supports should be settable as default options that apply to all # subsequent "build" commands. Maybe a "set {option}" command that # maps to "SetOption('{option}')". # # - Need something in the 'help' command that prints the -h output. # # - A command to run the configure subsystem separately (must see how # this interacts with the new automake model). # # - Command-line completion of target names; maybe even of SCons options? # Completion is something that's supported by the Python cmd module, # so this should be doable without too much trouble. # import cmd import copy import os import re import shlex import sys try: import readline except ImportError: pass class SConsInteractiveCmd(cmd.Cmd): """\ build [TARGETS] Build the specified TARGETS and their dependencies. 'b' is a synonym. clean [TARGETS] Clean (remove) the specified TARGETS and their dependencies. 'c' is a synonym. exit Exit SCons interactive mode. help [COMMAND] Prints help for the specified COMMAND. 'h' and '?' are synonyms. shell [COMMANDLINE] Execute COMMANDLINE in a subshell. 'sh' and '!' are synonyms. version Prints SCons version information. """ synonyms = { 'b' : 'build', 'c' : 'clean', 'h' : 'help', 'scons' : 'build', 'sh' : 'shell', } def __init__(self, **kw): cmd.Cmd.__init__(self) for key, val in kw.items(): setattr(self, key, val) if sys.platform == 'win32': self.shell_variable = 'COMSPEC' else: self.shell_variable = 'SHELL' def default(self, argv): print "*** Unknown command: %s" % argv[0] def onecmd(self, line): line = line.strip() if not line: print self.lastcmd return self.emptyline() self.lastcmd = line if line[0] == '!': line = 'shell ' + line[1:] elif line[0] == '?': line = 'help ' + line[1:] if os.sep == '\\': line = line.replace('\\', '\\\\') argv = shlex.split(line) argv[0] = self.synonyms.get(argv[0], argv[0]) if not argv[0]: return self.default(line) else: try: func = getattr(self, 'do_' + argv[0]) except AttributeError: return self.default(argv) return func(argv) def do_build(self, argv): """\ build [TARGETS] Build the specified TARGETS and their dependencies. 'b' is a synonym. """ import SCons.Node import SCons.SConsign import SCons.Script.Main options = copy.deepcopy(self.options) options, targets = self.parser.parse_args(argv[1:], values=options) SCons.Script.COMMAND_LINE_TARGETS = targets if targets: SCons.Script.BUILD_TARGETS = targets else: # If the user didn't specify any targets on the command line, # use the list of default targets. SCons.Script.BUILD_TARGETS = SCons.Script._build_plus_default nodes = SCons.Script.Main._build_targets(self.fs, options, targets, self.target_top) if not nodes: return # Call each of the Node's alter_targets() methods, which may # provide additional targets that ended up as part of the build # (the canonical example being a VariantDir() when we're building # from a source directory) and which we therefore need their # state cleared, too. x = [] for n in nodes: x.extend(n.alter_targets()[0]) nodes.extend(x) # Clean up so that we can perform the next build correctly. # # We do this by walking over all the children of the targets, # and clearing their state. # # We currently have to re-scan each node to find their # children, because built nodes have already been partially # cleared and don't remember their children. (In scons # 0.96.1 and earlier, this wasn't the case, and we didn't # have to re-scan the nodes.) # # Because we have to re-scan each node, we can't clear the # nodes as we walk over them, because we may end up rescanning # a cleared node as we scan a later node. Therefore, only # store the list of nodes that need to be cleared as we walk # the tree, and clear them in a separate pass. # # XXX: Someone more familiar with the inner workings of scons # may be able to point out a more efficient way to do this. SCons.Script.Main.progress_display("scons: Clearing cached node information ...") seen_nodes = {} def get_unseen_children(node, parent, seen_nodes=seen_nodes): def is_unseen(node, seen_nodes=seen_nodes): return node not in seen_nodes return list(filter(is_unseen, node.children(scan=1))) def add_to_seen_nodes(node, parent, seen_nodes=seen_nodes): seen_nodes[node] = 1 # If this file is in a VariantDir and has a # corresponding source file in the source tree, remember the # node in the source tree, too. This is needed in # particular to clear cached implicit dependencies on the # source file, since the scanner will scan it if the # VariantDir was created with duplicate=0. try: rfile_method = node.rfile except AttributeError: return else: rfile = rfile_method() if rfile != node: seen_nodes[rfile] = 1 for node in nodes: walker = SCons.Node.Walker(node, kids_func=get_unseen_children, eval_func=add_to_seen_nodes) n = walker.get_next() while n: n = walker.get_next() for node in seen_nodes.keys(): # Call node.clear() to clear most of the state node.clear() # node.clear() doesn't reset node.state, so call # node.set_state() to reset it manually node.set_state(SCons.Node.no_state) node.implicit = None # Debug: Uncomment to verify that all Taskmaster reference # counts have been reset to zero. #if node.ref_count != 0: # from SCons.Debug import Trace # Trace('node %s, ref_count %s !!!\n' % (node, node.ref_count)) SCons.SConsign.Reset() SCons.Script.Main.progress_display("scons: done clearing node information.") def do_clean(self, argv): """\ clean [TARGETS] Clean (remove) the specified TARGETS and their dependencies. 'c' is a synonym. """ return self.do_build(['build', '--clean'] + argv[1:]) def do_EOF(self, argv): print self.do_exit(argv) def _do_one_help(self, arg): try: # If help_() exists, then call it. func = getattr(self, 'help_' + arg) except AttributeError: try: func = getattr(self, 'do_' + arg) except AttributeError: doc = None else: doc = self._doc_to_help(func) if doc: sys.stdout.write(doc + '\n') sys.stdout.flush() else: doc = self.strip_initial_spaces(func()) if doc: sys.stdout.write(doc + '\n') sys.stdout.flush() def _doc_to_help(self, obj): doc = obj.__doc__ if doc is None: return '' return self._strip_initial_spaces(doc) def _strip_initial_spaces(self, s): #lines = s.split('\n') lines = s.split('\n') spaces = re.match(' *', lines[0]).group(0) #def strip_spaces(l): # if l.startswith(spaces): # l = l[len(spaces):] # return l #return '\n'.join([ strip_spaces(l) for l in lines ]) def strip_spaces(l, spaces=spaces): if l[:len(spaces)] == spaces: l = l[len(spaces):] return l lines = list(map(strip_spaces, lines)) return '\n'.join(lines) def do_exit(self, argv): """\ exit Exit SCons interactive mode. """ sys.exit(0) def do_help(self, argv): """\ help [COMMAND] Prints help for the specified COMMAND. 'h' and '?' are synonyms. """ if argv[1:]: for arg in argv[1:]: if self._do_one_help(arg): break else: # If bare 'help' is called, print this class's doc # string (if it has one). doc = self._doc_to_help(self.__class__) if doc: sys.stdout.write(doc + '\n') sys.stdout.flush() def do_shell(self, argv): """\ shell [COMMANDLINE] Execute COMMANDLINE in a subshell. 'sh' and '!' are synonyms. """ import subprocess argv = argv[1:] if not argv: argv = os.environ[self.shell_variable] try: # Per "[Python-Dev] subprocess insufficiently platform-independent?" # http://mail.python.org/pipermail/python-dev/2008-August/081979.html "+ # Doing the right thing with an argument list currently # requires different shell= values on Windows and Linux. p = subprocess.Popen(argv, shell=(sys.platform=='win32')) except EnvironmentError, e: sys.stderr.write('scons: %s: %s\n' % (argv[0], e.strerror)) else: p.wait() def do_version(self, argv): """\ version Prints SCons version information. """ sys.stdout.write(self.parser.version + '\n') def interact(fs, parser, options, targets, target_top): c = SConsInteractiveCmd(prompt = 'scons>>> ', fs = fs, parser = parser, options = options, targets = targets, target_top = target_top) c.cmdloop() # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Script/Main.xml0000644000175000017500000003633012114661557022162 0ustar dktrkranzdktrkranz (arguments) This function adds a new command-line option to be recognized. The specified arguments are the same as supported by the standard Python optparse.add_option() method (with a few additional capabilities noted below); see the documentation for optparse for a thorough discussion of its option-processing capabities. In addition to the arguments and values supported by the optparse.add_option() method, the SCons &f-AddOption; function allows you to set the nargs keyword value to '?' (a string with just the question mark) to indicate that the specified long option(s) take(s) an optional argument. When nargs = '?' is passed to the &f-AddOption; function, the const keyword argument may be used to supply the "default" value that should be used when the option is specified on the command line without an explicit argument. If no default= keyword argument is supplied when calling &f-AddOption;, the option will have a default value of None. Once a new command-line option has been added with &f-AddOption;, the option value may be accessed using &f-GetOption; or env.GetOption(). The value may also be set, using &f-SetOption; or env.SetOption(), if conditions in a &SConscript; require overriding any default value. Note, however, that a value specified on the command line will always override a value set by any SConscript file. Any specified help= strings for the new option(s) will be displayed by the or options (the latter only if no other help text is specified in the SConscript files). The help text for the local options specified by &f-AddOption; will appear below the SCons options themselves, under a separate Local Options heading. The options will appear in the help text in the order in which the &f-AddOption; calls occur. Example: AddOption('--prefix', dest='prefix', nargs=1, type='string', action='store', metavar='DIR', help='installation prefix') env = Environment(PREFIX = GetOption('prefix')) () Returns a list of exceptions for the actions that failed while attempting to build targets. Each element in the returned list is a BuildError object with the following attributes that record various aspects of the build failure: .node The node that was being built when the build failure occurred. .status The numeric exit status returned by the command or Python function that failed when trying to build the specified Node. .errstr The SCons error string describing the build failure. (This is often a generic message like "Error 2" to indicate that an executed command exited with a status of 2.) .filename The name of the file or directory that actually caused the failure. This may be different from the .node attribute. For example, if an attempt to build a target named sub/dir/target fails because the sub/dir directory could not be created, then the .node attribute will be sub/dir/target but the .filename attribute will be sub/dir. .executor The SCons Executor object for the target Node being built. This can be used to retrieve the construction environment used for the failed action. .action The actual SCons Action object that failed. This will be one specific action out of the possible list of actions that would have been executed to build the target. .command The actual expanded command that was executed and failed, after expansion of &cv-link-TARGET;, &cv-link-SOURCE;, and other construction variables. Note that the &f-GetBuildFailures; function will always return an empty list until any build failure has occurred, which means that &f-GetBuildFailures; will always return an empty list while the &SConscript; files are being read. Its primary intended use is for functions that will be executed before SCons exits by passing them to the standard Python atexit.register() function. Example: import atexit def print_build_failures(): from SCons.Script import GetBuildFailures for bf in GetBuildFailures(): print "%s failed: %s" % (bf.node, bf.errstr) atexit.register(print_build_failures) (name) This function provides a way to query the value of SCons options set on scons command line (or set using the &f-link-SetOption; function). The options supported are: cache_debug which corresponds to --cache-debug; cache_disable which corresponds to --cache-disable; cache_force which corresponds to --cache-force; cache_show which corresponds to --cache-show; clean which corresponds to -c, --clean and --remove; config which corresponds to --config; directory which corresponds to -C and --directory; diskcheck which corresponds to --diskcheck duplicate which corresponds to --duplicate; file which corresponds to -f, --file, --makefile and --sconstruct; help which corresponds to -h and --help; ignore_errors which corresponds to --ignore-errors; implicit_cache which corresponds to --implicit-cache; implicit_deps_changed which corresponds to --implicit-deps-changed; implicit_deps_unchanged which corresponds to --implicit-deps-unchanged; interactive which corresponds to --interact and --interactive; keep_going which corresponds to -k and --keep-going; max_drift which corresponds to --max-drift; no_exec which corresponds to -n, --no-exec, --just-print, --dry-run and --recon; no_site_dir which corresponds to --no-site-dir; num_jobs which corresponds to -j and --jobs; profile_file which corresponds to --profile; question which corresponds to -q and --question; random which corresponds to --random; repository which corresponds to -Y, --repository and --srcdir; silent which corresponds to -s, --silent and --quiet; site_dir which corresponds to --site-dir; stack_size which corresponds to --stack-size; taskmastertrace_file which corresponds to --taskmastertrace; and warn which corresponds to --warn and --warning. See the documentation for the corresponding command line object for information about each specific option. (callable, [interval]) (string, [interval, file, overwrite]) (list_of_strings, [interval, file, overwrite]) Allows SCons to show progress made during the build by displaying a string or calling a function while evaluating Nodes (e.g. files). If the first specified argument is a Python callable (a function or an object that has a __call__() method), the function will be called once every interval times a Node is evaluated. The callable will be passed the evaluated Node as its only argument. (For future compatibility, it's a good idea to also add *args and **kw as arguments to your function or method. This will prevent the code from breaking if SCons ever changes the interface to call the function with additional arguments in the future.) An example of a simple custom progress function that prints a string containing the Node name every 10 Nodes: def my_progress_function(node, *args, **kw): print 'Evaluating node %s!' % node Progress(my_progress_function, interval=10) A more complicated example of a custom progress display object that prints a string containing a count every 100 evaluated Nodes. Note the use of \r (a carriage return) at the end so that the string will overwrite itself on a display: import sys class ProgressCounter(object): count = 0 def __call__(self, node, *args, **kw): self.count += 100 sys.stderr.write('Evaluated %s nodes\r' % self.count) Progress(ProgressCounter(), interval=100) If the first argument &f-link-Progress; is a string, the string will be displayed every interval evaluated Nodes. The default is to print the string on standard output; an alternate output stream may be specified with the file= argument. The following will print a series of dots on the error output, one dot for every 100 evaluated Nodes: import sys Progress('.', interval=100, file=sys.stderr) If the string contains the verbatim substring &cv-TARGET;, it will be replaced with the Node. Note that, for performance reasons, this is not a regular SCons variable substition, so you can not use other variables or use curly braces. The following example will print the name of every evaluated Node, using a \r (carriage return) to cause each line to overwritten by the next line, and the overwrite= keyword argument to make sure the previously-printed file name is overwritten with blank spaces: import sys Progress('$TARGET\r', overwrite=True) If the first argument to &f-Progress; is a list of strings, then each string in the list will be displayed in rotating fashion every interval evaluated Nodes. This can be used to implement a "spinner" on the user's screen as follows: Progress(['-\r', '\\\r', '|\r', '/\r'], interval=5) (target, ...) Marks each given target as precious so it is not deleted before it is rebuilt. Normally &scons; deletes a target before building it. Multiple targets can be passed in to a single call to &f-Precious;. (name, value) This function provides a way to set a select subset of the scons command line options from a SConscript file. The options supported are: clean which corresponds to -c, --clean and --remove; duplicate which corresponds to --duplicate; help which corresponds to -h and --help; implicit_cache which corresponds to --implicit-cache; max_drift which corresponds to --max-drift; no_exec which corresponds to -n, --no-exec, --just-print, --dry-run and --recon; num_jobs which corresponds to -j and --jobs; random which corresponds to --random; and stack_size which corresponds to --stack-size. See the documentation for the corresponding command line object for information about each specific option. Example: SetOption('max_drift', 1) scons-doc-2.3.0/src/engine/SCons/Script/SConsOptions.py0000644000175000017500000011171512114661557023530 0ustar dktrkranzdktrkranz# # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Script/SConsOptions.py 2013/03/03 09:48:35 garyo" import optparse import re import sys import textwrap no_hyphen_re = re.compile(r'(\s+|(?<=[\w\!\"\'\&\.\,\?])-{2,}(?=\w))') try: from gettext import gettext except ImportError: def gettext(message): return message _ = gettext import SCons.Node.FS import SCons.Warnings OptionValueError = optparse.OptionValueError SUPPRESS_HELP = optparse.SUPPRESS_HELP diskcheck_all = SCons.Node.FS.diskcheck_types() def diskcheck_convert(value): if value is None: return [] if not SCons.Util.is_List(value): value = value.split(',') result = [] for v in value: v = v.lower() if v == 'all': result = diskcheck_all elif v == 'none': result = [] elif v in diskcheck_all: result.append(v) else: raise ValueError(v) return result class SConsValues(optparse.Values): """ Holder class for uniform access to SCons options, regardless of whether or not they can be set on the command line or in the SConscript files (using the SetOption() function). A SCons option value can originate three different ways: 1) set on the command line; 2) set in an SConscript file; 3) the default setting (from the the op.add_option() calls in the Parser() function, below). The command line always overrides a value set in a SConscript file, which in turn always overrides default settings. Because we want to support user-specified options in the SConscript file itself, though, we may not know about all of the options when the command line is first parsed, so we can't make all the necessary precedence decisions at the time the option is configured. The solution implemented in this class is to keep these different sets of settings separate (command line, SConscript file, and default) and to override the __getattr__() method to check them in turn. This should allow the rest of the code to just fetch values as attributes of an instance of this class, without having to worry about where they came from. Note that not all command line options are settable from SConscript files, and the ones that are must be explicitly added to the "settable" list in this class, and optionally validated and coerced in the set_option() method. """ def __init__(self, defaults): self.__dict__['__defaults__'] = defaults self.__dict__['__SConscript_settings__'] = {} def __getattr__(self, attr): """ Fetches an options value, checking first for explicit settings from the command line (which are direct attributes), then the SConscript file settings, then the default values. """ try: return self.__dict__[attr] except KeyError: try: return self.__dict__['__SConscript_settings__'][attr] except KeyError: return getattr(self.__dict__['__defaults__'], attr) settable = [ 'clean', 'diskcheck', 'duplicate', 'help', 'implicit_cache', 'max_drift', 'md5_chunksize', 'no_exec', 'num_jobs', 'random', 'stack_size', 'warn', ] def set_option(self, name, value): """ Sets an option from an SConscript file. """ if not name in self.settable: raise SCons.Errors.UserError("This option is not settable from a SConscript file: %s"%name) if name == 'num_jobs': try: value = int(value) if value < 1: raise ValueError except ValueError: raise SCons.Errors.UserError("A positive integer is required: %s"%repr(value)) elif name == 'max_drift': try: value = int(value) except ValueError: raise SCons.Errors.UserError("An integer is required: %s"%repr(value)) elif name == 'duplicate': try: value = str(value) except ValueError: raise SCons.Errors.UserError("A string is required: %s"%repr(value)) if not value in SCons.Node.FS.Valid_Duplicates: raise SCons.Errors.UserError("Not a valid duplication style: %s" % value) # Set the duplicate style right away so it can affect linking # of SConscript files. SCons.Node.FS.set_duplicate(value) elif name == 'diskcheck': try: value = diskcheck_convert(value) except ValueError, v: raise SCons.Errors.UserError("Not a valid diskcheck value: %s"%v) if 'diskcheck' not in self.__dict__: # No --diskcheck= option was specified on the command line. # Set this right away so it can affect the rest of the # file/Node lookups while processing the SConscript files. SCons.Node.FS.set_diskcheck(value) elif name == 'stack_size': try: value = int(value) except ValueError: raise SCons.Errors.UserError("An integer is required: %s"%repr(value)) elif name == 'md5_chunksize': try: value = int(value) except ValueError: raise SCons.Errors.UserError("An integer is required: %s"%repr(value)) elif name == 'warn': if SCons.Util.is_String(value): value = [value] value = self.__SConscript_settings__.get(name, []) + value SCons.Warnings.process_warn_strings(value) self.__SConscript_settings__[name] = value class SConsOption(optparse.Option): def convert_value(self, opt, value): if value is not None: if self.nargs in (1, '?'): return self.check_value(opt, value) else: return tuple([self.check_value(opt, v) for v in value]) def process(self, opt, value, values, parser): # First, convert the value(s) to the right type. Howl if any # value(s) are bogus. value = self.convert_value(opt, value) # And then take whatever action is expected of us. # This is a separate method to make life easier for # subclasses to add new actions. return self.take_action( self.action, self.dest, opt, value, values, parser) def _check_nargs_optional(self): if self.nargs == '?' and self._short_opts: fmt = "option %s: nargs='?' is incompatible with short options" raise SCons.Errors.UserError(fmt % self._short_opts[0]) try: _orig_CONST_ACTIONS = optparse.Option.CONST_ACTIONS _orig_CHECK_METHODS = optparse.Option.CHECK_METHODS except AttributeError: # optparse.Option had no CONST_ACTIONS before Python 2.5. _orig_CONST_ACTIONS = ("store_const",) def _check_const(self): if self.action not in self.CONST_ACTIONS and self.const is not None: raise OptionError( "'const' must not be supplied for action %r" % self.action, self) # optparse.Option collects its list of unbound check functions # up front. This sucks because it means we can't just override # the _check_const() function like a normal method, we have to # actually replace it in the list. This seems to be the most # straightforward way to do that. _orig_CHECK_METHODS = [optparse.Option._check_action, optparse.Option._check_type, optparse.Option._check_choice, optparse.Option._check_dest, _check_const, optparse.Option._check_nargs, optparse.Option._check_callback] CHECK_METHODS = _orig_CHECK_METHODS + [_check_nargs_optional] CONST_ACTIONS = _orig_CONST_ACTIONS + optparse.Option.TYPED_ACTIONS class SConsOptionGroup(optparse.OptionGroup): """ A subclass for SCons-specific option groups. The only difference between this and the base class is that we print the group's help text flush left, underneath their own title but lined up with the normal "SCons Options". """ def format_help(self, formatter): """ Format an option group's help text, outdenting the title so it's flush with the "SCons Options" title we print at the top. """ formatter.dedent() result = formatter.format_heading(self.title) formatter.indent() result = result + optparse.OptionContainer.format_help(self, formatter) return result class SConsOptionParser(optparse.OptionParser): preserve_unknown_options = False def error(self, msg): # overriden OptionValueError exception handler self.print_usage(sys.stderr) sys.stderr.write("SCons Error: %s\n" % msg) sys.exit(2) def _process_long_opt(self, rargs, values): """ SCons-specific processing of long options. This is copied directly from the normal optparse._process_long_opt() method, except that, if configured to do so, we catch the exception thrown when an unknown option is encountered and just stick it back on the "leftover" arguments for later (re-)processing. """ arg = rargs.pop(0) # Value explicitly attached to arg? Pretend it's the next # argument. if "=" in arg: (opt, next_arg) = arg.split("=", 1) rargs.insert(0, next_arg) had_explicit_value = True else: opt = arg had_explicit_value = False try: opt = self._match_long_opt(opt) except optparse.BadOptionError: if self.preserve_unknown_options: # SCons-specific: if requested, add unknown options to # the "leftover arguments" list for later processing. self.largs.append(arg) if had_explicit_value: # The unknown option will be re-processed later, # so undo the insertion of the explicit value. rargs.pop(0) return raise option = self._long_opt[opt] if option.takes_value(): nargs = option.nargs if nargs == '?': if had_explicit_value: value = rargs.pop(0) else: value = option.const elif len(rargs) < nargs: if nargs == 1: self.error(_("%s option requires an argument") % opt) else: self.error(_("%s option requires %d arguments") % (opt, nargs)) elif nargs == 1: value = rargs.pop(0) else: value = tuple(rargs[0:nargs]) del rargs[0:nargs] elif had_explicit_value: self.error(_("%s option does not take a value") % opt) else: value = None option.process(opt, value, values, self) def add_local_option(self, *args, **kw): """ Adds a local option to the parser. This is initiated by a SetOption() call to add a user-defined command-line option. We add the option to a separate option group for the local options, creating the group if necessary. """ try: group = self.local_option_group except AttributeError: group = SConsOptionGroup(self, 'Local Options') group = self.add_option_group(group) self.local_option_group = group result = group.add_option(*args, **kw) if result: # The option was added succesfully. We now have to add the # default value to our object that holds the default values # (so that an attempt to fetch the option's attribute will # yield the default value when not overridden) and then # we re-parse the leftover command-line options, so that # any value overridden on the command line is immediately # available if the user turns around and does a GetOption() # right away. setattr(self.values.__defaults__, result.dest, result.default) self.parse_args(self.largs, self.values) return result class SConsIndentedHelpFormatter(optparse.IndentedHelpFormatter): def format_usage(self, usage): return "usage: %s\n" % usage def format_heading(self, heading): """ This translates any heading of "options" or "Options" into "SCons Options." Unfortunately, we have to do this here, because those titles are hard-coded in the optparse calls. """ if heading == 'options': # The versions of optparse.py shipped with Pythons 2.3 and # 2.4 pass this in uncapitalized; override that so we get # consistent output on all versions. heading = "Options" if heading == 'Options': heading = "SCons Options" return optparse.IndentedHelpFormatter.format_heading(self, heading) def format_option(self, option): """ A copy of the normal optparse.IndentedHelpFormatter.format_option() method. This has been snarfed so we can modify text wrapping to out liking: -- add our own regular expression that doesn't break on hyphens (so things like --no-print-directory don't get broken); -- wrap the list of options themselves when it's too long (the wrapper.fill(opts) call below); -- set the subsequent_indent when wrapping the help_text. """ # The help for each option consists of two parts: # * the opt strings and metavars # eg. ("-x", or "-fFILENAME, --file=FILENAME") # * the user-supplied help string # eg. ("turn on expert mode", "read data from FILENAME") # # If possible, we write both of these on the same line: # -x turn on expert mode # # But if the opt string list is too long, we put the help # string on a second line, indented to the same column it would # start in if it fit on the first line. # -fFILENAME, --file=FILENAME # read data from FILENAME result = [] try: opts = self.option_strings[option] except AttributeError: # The Python 2.3 version of optparse attaches this to # to the option argument, not to this object. opts = option.option_strings opt_width = self.help_position - self.current_indent - 2 if len(opts) > opt_width: wrapper = textwrap.TextWrapper(width=self.width, initial_indent = ' ', subsequent_indent = ' ') wrapper.wordsep_re = no_hyphen_re opts = wrapper.fill(opts) + '\n' indent_first = self.help_position else: # start help on same line as opts opts = "%*s%-*s " % (self.current_indent, "", opt_width, opts) indent_first = 0 result.append(opts) if option.help: try: expand_default = self.expand_default except AttributeError: # The HelpFormatter base class in the Python 2.3 version # of optparse has no expand_default() method. help_text = option.help else: help_text = expand_default(option) # SCons: indent every line of the help text but the first. wrapper = textwrap.TextWrapper(width=self.help_width, subsequent_indent = ' ') wrapper.wordsep_re = no_hyphen_re help_lines = wrapper.wrap(help_text) result.append("%*s%s\n" % (indent_first, "", help_lines[0])) for line in help_lines[1:]: result.append("%*s%s\n" % (self.help_position, "", line)) elif opts[-1] != "\n": result.append("\n") return "".join(result) # For consistent help output across Python versions, we provide a # subclass copy of format_option_strings() and these two variables. # This is necessary (?) for Python2.3, which otherwise concatenates # a short option with its metavar. _short_opt_fmt = "%s %s" _long_opt_fmt = "%s=%s" def format_option_strings(self, option): """Return a comma-separated list of option strings & metavariables.""" if option.takes_value(): metavar = option.metavar or option.dest.upper() short_opts = [] for sopt in option._short_opts: short_opts.append(self._short_opt_fmt % (sopt, metavar)) long_opts = [] for lopt in option._long_opts: long_opts.append(self._long_opt_fmt % (lopt, metavar)) else: short_opts = option._short_opts long_opts = option._long_opts if self.short_first: opts = short_opts + long_opts else: opts = long_opts + short_opts return ", ".join(opts) def Parser(version): """ Returns an options parser object initialized with the standard SCons options. """ formatter = SConsIndentedHelpFormatter(max_help_position=30) op = SConsOptionParser(option_class=SConsOption, add_help_option=False, formatter=formatter, usage="usage: scons [OPTION] [TARGET] ...",) op.preserve_unknown_options = True op.version = version # Add the options to the parser we just created. # # These are in the order we want them to show up in the -H help # text, basically alphabetical. Each op.add_option() call below # should have a consistent format: # # op.add_option("-L", "--long-option-name", # nargs=1, type="string", # dest="long_option_name", default='foo', # action="callback", callback=opt_long_option, # help="help text goes here", # metavar="VAR") # # Even though the optparse module constructs reasonable default # destination names from the long option names, we're going to be # explicit about each one for easier readability and so this code # will at least show up when grepping the source for option attribute # names, or otherwise browsing the source code. # options ignored for compatibility def opt_ignore(option, opt, value, parser): sys.stderr.write("Warning: ignoring %s option\n" % opt) op.add_option("-b", "-d", "-e", "-m", "-S", "-t", "-w", "--environment-overrides", "--no-keep-going", "--no-print-directory", "--print-directory", "--stop", "--touch", action="callback", callback=opt_ignore, help="Ignored for compatibility.") op.add_option('-c', '--clean', '--remove', dest="clean", default=False, action="store_true", help="Remove specified targets and dependencies.") op.add_option('-C', '--directory', nargs=1, type="string", dest="directory", default=[], action="append", help="Change to DIR before doing anything.", metavar="DIR") op.add_option('--cache-debug', nargs=1, dest="cache_debug", default=None, action="store", help="Print CacheDir debug info to FILE.", metavar="FILE") op.add_option('--cache-disable', '--no-cache', dest='cache_disable', default=False, action="store_true", help="Do not retrieve built targets from CacheDir.") op.add_option('--cache-force', '--cache-populate', dest='cache_force', default=False, action="store_true", help="Copy already-built targets into the CacheDir.") op.add_option('--cache-show', dest='cache_show', default=False, action="store_true", help="Print build actions for files from CacheDir.") def opt_invalid(group, value, options): errmsg = "`%s' is not a valid %s option type, try:\n" % (value, group) return errmsg + " %s" % ", ".join(options) config_options = ["auto", "force" ,"cache"] def opt_config(option, opt, value, parser, c_options=config_options): if not value in c_options: raise OptionValueError(opt_invalid('config', value, c_options)) setattr(parser.values, option.dest, value) opt_config_help = "Controls Configure subsystem: %s." \ % ", ".join(config_options) op.add_option('--config', nargs=1, type="string", dest="config", default="auto", action="callback", callback=opt_config, help = opt_config_help, metavar="MODE") op.add_option('-D', dest="climb_up", default=None, action="store_const", const=2, help="Search up directory tree for SConstruct, " "build all Default() targets.") deprecated_debug_options = { "dtree" : '; please use --tree=derived instead', "nomemoizer" : ' and has no effect', "stree" : '; please use --tree=all,status instead', "tree" : '; please use --tree=all instead', } debug_options = ["count", "duplicate", "explain", "findlibs", "includes", "memoizer", "memory", "objects", "pdb", "prepare", "presub", "stacktrace", "time"] def opt_debug(option, opt, value, parser, debug_options=debug_options, deprecated_debug_options=deprecated_debug_options): if value in debug_options: parser.values.debug.append(value) elif value in deprecated_debug_options.keys(): parser.values.debug.append(value) try: parser.values.delayed_warnings except AttributeError: parser.values.delayed_warnings = [] msg = deprecated_debug_options[value] w = "The --debug=%s option is deprecated%s." % (value, msg) t = (SCons.Warnings.DeprecatedDebugOptionsWarning, w) parser.values.delayed_warnings.append(t) else: raise OptionValueError(opt_invalid('debug', value, debug_options)) opt_debug_help = "Print various types of debugging information: %s." \ % ", ".join(debug_options) op.add_option('--debug', nargs=1, type="string", dest="debug", default=[], action="callback", callback=opt_debug, help=opt_debug_help, metavar="TYPE") def opt_diskcheck(option, opt, value, parser): try: diskcheck_value = diskcheck_convert(value) except ValueError, e: raise OptionValueError("`%s' is not a valid diskcheck type" % e) setattr(parser.values, option.dest, diskcheck_value) op.add_option('--diskcheck', nargs=1, type="string", dest='diskcheck', default=None, action="callback", callback=opt_diskcheck, help="Enable specific on-disk checks.", metavar="TYPE") def opt_duplicate(option, opt, value, parser): if not value in SCons.Node.FS.Valid_Duplicates: raise OptionValueError(opt_invalid('duplication', value, SCons.Node.FS.Valid_Duplicates)) setattr(parser.values, option.dest, value) # Set the duplicate style right away so it can affect linking # of SConscript files. SCons.Node.FS.set_duplicate(value) opt_duplicate_help = "Set the preferred duplication methods. Must be one of " \ + ", ".join(SCons.Node.FS.Valid_Duplicates) op.add_option('--duplicate', nargs=1, type="string", dest="duplicate", default='hard-soft-copy', action="callback", callback=opt_duplicate, help=opt_duplicate_help) op.add_option('-f', '--file', '--makefile', '--sconstruct', nargs=1, type="string", dest="file", default=[], action="append", help="Read FILE as the top-level SConstruct file.") op.add_option('-h', '--help', dest="help", default=False, action="store_true", help="Print defined help message, or this one.") op.add_option("-H", "--help-options", action="help", help="Print this message and exit.") op.add_option('-i', '--ignore-errors', dest='ignore_errors', default=False, action="store_true", help="Ignore errors from build actions.") op.add_option('-I', '--include-dir', nargs=1, dest='include_dir', default=[], action="append", help="Search DIR for imported Python modules.", metavar="DIR") op.add_option('--implicit-cache', dest='implicit_cache', default=False, action="store_true", help="Cache implicit dependencies") def opt_implicit_deps(option, opt, value, parser): setattr(parser.values, 'implicit_cache', True) setattr(parser.values, option.dest, True) op.add_option('--implicit-deps-changed', dest="implicit_deps_changed", default=False, action="callback", callback=opt_implicit_deps, help="Ignore cached implicit dependencies.") op.add_option('--implicit-deps-unchanged', dest="implicit_deps_unchanged", default=False, action="callback", callback=opt_implicit_deps, help="Ignore changes in implicit dependencies.") op.add_option('--interact', '--interactive', dest='interactive', default=False, action="store_true", help="Run in interactive mode.") op.add_option('-j', '--jobs', nargs=1, type="int", dest="num_jobs", default=1, action="store", help="Allow N jobs at once.", metavar="N") op.add_option('-k', '--keep-going', dest='keep_going', default=False, action="store_true", help="Keep going when a target can't be made.") op.add_option('--max-drift', nargs=1, type="int", dest='max_drift', default=SCons.Node.FS.default_max_drift, action="store", help="Set maximum system clock drift to N seconds.", metavar="N") op.add_option('--md5-chunksize', nargs=1, type="int", dest='md5_chunksize', default=SCons.Node.FS.File.md5_chunksize, action="store", help="Set chunk-size for MD5 signature computation to N kilobytes.", metavar="N") op.add_option('-n', '--no-exec', '--just-print', '--dry-run', '--recon', dest='no_exec', default=False, action="store_true", help="Don't build; just print commands.") op.add_option('--no-site-dir', dest='no_site_dir', default=False, action="store_true", help="Don't search or use the usual site_scons dir.") op.add_option('--profile', nargs=1, dest="profile_file", default=None, action="store", help="Profile SCons and put results in FILE.", metavar="FILE") op.add_option('-q', '--question', dest="question", default=False, action="store_true", help="Don't build; exit status says if up to date.") op.add_option('-Q', dest='no_progress', default=False, action="store_true", help="Suppress \"Reading/Building\" progress messages.") op.add_option('--random', dest="random", default=False, action="store_true", help="Build dependencies in random order.") op.add_option('-s', '--silent', '--quiet', dest="silent", default=False, action="store_true", help="Don't print commands.") op.add_option('--site-dir', nargs=1, dest='site_dir', default=None, action="store", help="Use DIR instead of the usual site_scons dir.", metavar="DIR") op.add_option('--stack-size', nargs=1, type="int", dest='stack_size', action="store", help="Set the stack size of the threads used to run jobs to N kilobytes.", metavar="N") op.add_option('--taskmastertrace', nargs=1, dest="taskmastertrace_file", default=None, action="store", help="Trace Node evaluation to FILE.", metavar="FILE") tree_options = ["all", "derived", "prune", "status"] def opt_tree(option, opt, value, parser, tree_options=tree_options): import Main tp = Main.TreePrinter() for o in value.split(','): if o == 'all': tp.derived = False elif o == 'derived': tp.derived = True elif o == 'prune': tp.prune = True elif o == 'status': tp.status = True else: raise OptionValueError(opt_invalid('--tree', o, tree_options)) parser.values.tree_printers.append(tp) opt_tree_help = "Print a dependency tree in various formats: %s." \ % ", ".join(tree_options) op.add_option('--tree', nargs=1, type="string", dest="tree_printers", default=[], action="callback", callback=opt_tree, help=opt_tree_help, metavar="OPTIONS") op.add_option('-u', '--up', '--search-up', dest="climb_up", default=0, action="store_const", const=1, help="Search up directory tree for SConstruct, " "build targets at or below current directory.") op.add_option('-U', dest="climb_up", default=0, action="store_const", const=3, help="Search up directory tree for SConstruct, " "build Default() targets from local SConscript.") def opt_version(option, opt, value, parser): sys.stdout.write(parser.version + '\n') sys.exit(0) op.add_option("-v", "--version", action="callback", callback=opt_version, help="Print the SCons version number and exit.") def opt_warn(option, opt, value, parser, tree_options=tree_options): if SCons.Util.is_String(value): value = value.split(',') parser.values.warn.extend(value) op.add_option('--warn', '--warning', nargs=1, type="string", dest="warn", default=[], action="callback", callback=opt_warn, help="Enable or disable warnings.", metavar="WARNING-SPEC") op.add_option('-Y', '--repository', '--srcdir', nargs=1, dest="repository", default=[], action="append", help="Search REPOSITORY for source and target files.") # Options from Make and Cons classic that we do not yet support, # but which we may support someday and whose (potential) meanings # we don't want to change. These all get a "the -X option is not # yet implemented" message and don't show up in the help output. def opt_not_yet(option, opt, value, parser): msg = "Warning: the %s option is not yet implemented\n" % opt sys.stderr.write(msg) op.add_option('-l', '--load-average', '--max-load', nargs=1, type="float", dest="load_average", default=0, action="callback", callback=opt_not_yet, # action="store", # help="Don't start multiple jobs unless load is below " # "LOAD-AVERAGE." help=SUPPRESS_HELP) op.add_option('--list-actions', dest="list_actions", action="callback", callback=opt_not_yet, # help="Don't build; list files and build actions." help=SUPPRESS_HELP) op.add_option('--list-derived', dest="list_derived", action="callback", callback=opt_not_yet, # help="Don't build; list files that would be built." help=SUPPRESS_HELP) op.add_option('--list-where', dest="list_where", action="callback", callback=opt_not_yet, # help="Don't build; list files and where defined." help=SUPPRESS_HELP) op.add_option('-o', '--old-file', '--assume-old', nargs=1, type="string", dest="old_file", default=[], action="callback", callback=opt_not_yet, # action="append", # help = "Consider FILE to be old; don't rebuild it." help=SUPPRESS_HELP) op.add_option('--override', nargs=1, type="string", action="callback", callback=opt_not_yet, dest="override", # help="Override variables as specified in FILE." help=SUPPRESS_HELP) op.add_option('-p', action="callback", callback=opt_not_yet, dest="p", # help="Print internal environments/objects." help=SUPPRESS_HELP) op.add_option('-r', '-R', '--no-builtin-rules', '--no-builtin-variables', action="callback", callback=opt_not_yet, dest="no_builtin_rules", # help="Clear default environments and variables." help=SUPPRESS_HELP) op.add_option('--write-filenames', nargs=1, type="string", dest="write_filenames", action="callback", callback=opt_not_yet, # help="Write all filenames examined into FILE." help=SUPPRESS_HELP) op.add_option('-W', '--new-file', '--assume-new', '--what-if', nargs=1, type="string", dest="new_file", action="callback", callback=opt_not_yet, # help="Consider FILE to be changed." help=SUPPRESS_HELP) op.add_option('--warn-undefined-variables', dest="warn_undefined_variables", action="callback", callback=opt_not_yet, # help="Warn when an undefined variable is referenced." help=SUPPRESS_HELP) return op # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/dblite.py0000644000175000017500000001652512114661560021123 0ustar dktrkranzdktrkranz# dblite.py module contributed by Ralf W. Grosse-Kunstleve. # Extended for Unicode by Steven Knight. import SCons.compat import builtins import os # compat layer imports "cPickle" for us if it's available. import pickle import shutil import time keep_all_files = 00000 ignore_corrupt_dbfiles = 0 def corruption_warning(filename): print "Warning: Discarding corrupt database:", filename try: unicode except NameError: def is_string(s): return isinstance(s, str) else: def is_string(s): return type(s) in (str, unicode) try: unicode('a') except NameError: def unicode(s): return s dblite_suffix = '.dblite' tmp_suffix = '.tmp' class dblite(object): # Squirrel away references to the functions in various modules # that we'll use when our __del__() method calls our sync() method # during shutdown. We might get destroyed when Python is in the midst # of tearing down the different modules we import in an essentially # arbitrary order, and some of the various modules's global attributes # may already be wiped out from under us. # # See the discussion at: # http://mail.python.org/pipermail/python-bugs-list/2003-March/016877.html _open = builtins.open _pickle_dump = staticmethod(pickle.dump) _os_chmod = os.chmod try: _os_chown = os.chown except AttributeError: _os_chown = None _os_rename = os.rename _os_unlink = os.unlink _shutil_copyfile = shutil.copyfile _time_time = time.time def __init__(self, file_base_name, flag, mode): assert flag in (None, "r", "w", "c", "n") if (flag is None): flag = "r" base, ext = os.path.splitext(file_base_name) if ext == dblite_suffix: # There's already a suffix on the file name, don't add one. self._file_name = file_base_name self._tmp_name = base + tmp_suffix else: self._file_name = file_base_name + dblite_suffix self._tmp_name = file_base_name + tmp_suffix self._flag = flag self._mode = mode self._dict = {} self._needs_sync = 00000 if self._os_chown is not None and (os.geteuid()==0 or os.getuid()==0): # running as root; chown back to current owner/group when done try: statinfo = os.stat(self._file_name) self._chown_to = statinfo.st_uid self._chgrp_to = statinfo.st_gid except OSError, e: # db file doesn't exist yet. # Check os.environ for SUDO_UID, use if set self._chown_to = int(os.environ.get('SUDO_UID', -1)) self._chgrp_to = int(os.environ.get('SUDO_GID', -1)) else: self._chown_to = -1 # don't chown self._chgrp_to = -1 # don't chgrp if (self._flag == "n"): self._open(self._file_name, "wb", self._mode) else: try: f = self._open(self._file_name, "rb") except IOError, e: if (self._flag != "c"): raise e self._open(self._file_name, "wb", self._mode) else: p = f.read() if (len(p) > 0): try: self._dict = pickle.loads(p) except (pickle.UnpicklingError, EOFError): if (ignore_corrupt_dbfiles == 0): raise if (ignore_corrupt_dbfiles == 1): corruption_warning(self._file_name) def close(self): if (self._needs_sync): self.sync() def __del__(self): self.close() def sync(self): self._check_writable() f = self._open(self._tmp_name, "wb", self._mode) self._pickle_dump(self._dict, f, 1) f.close() # Windows doesn't allow renaming if the file exists, so unlink # it first, chmod'ing it to make sure we can do so. On UNIX, we # may not be able to chmod the file if it's owned by someone else # (e.g. from a previous run as root). We should still be able to # unlink() the file if the directory's writable, though, so ignore # any OSError exception thrown by the chmod() call. try: self._os_chmod(self._file_name, 0777) except OSError: pass self._os_unlink(self._file_name) self._os_rename(self._tmp_name, self._file_name) if self._os_chown is not None and self._chown_to > 0: # don't chown to root or -1 try: self._os_chown(self._file_name, self._chown_to, self._chgrp_to) except OSError: pass self._needs_sync = 00000 if (keep_all_files): self._shutil_copyfile( self._file_name, self._file_name + "_" + str(int(self._time_time()))) def _check_writable(self): if (self._flag == "r"): raise IOError("Read-only database: %s" % self._file_name) def __getitem__(self, key): return self._dict[key] def __setitem__(self, key, value): self._check_writable() if (not is_string(key)): raise TypeError("key `%s' must be a string but is %s" % (key, type(key))) if (not is_string(value)): raise TypeError("value `%s' must be a string but is %s" % (value, type(value))) self._dict[key] = value self._needs_sync = 0001 def keys(self): return list(self._dict.keys()) def has_key(self, key): return key in self._dict def __contains__(self, key): return key in self._dict def iterkeys(self): # Wrapping name in () prevents fixer from "fixing" this return (self._dict.iterkeys)() __iter__ = iterkeys def __len__(self): return len(self._dict) def open(file, flag=None, mode=0666): return dblite(file, flag, mode) def _exercise(): db = open("tmp", "n") assert len(db) == 0 db["foo"] = "bar" assert db["foo"] == "bar" db[unicode("ufoo")] = unicode("ubar") assert db[unicode("ufoo")] == unicode("ubar") db.sync() db = open("tmp", "c") assert len(db) == 2, len(db) assert db["foo"] == "bar" db["bar"] = "foo" assert db["bar"] == "foo" db[unicode("ubar")] = unicode("ufoo") assert db[unicode("ubar")] == unicode("ufoo") db.sync() db = open("tmp", "r") assert len(db) == 4, len(db) assert db["foo"] == "bar" assert db["bar"] == "foo" assert db[unicode("ufoo")] == unicode("ubar") assert db[unicode("ubar")] == unicode("ufoo") try: db.sync() except IOError, e: assert str(e) == "Read-only database: tmp.dblite" else: raise RuntimeError("IOError expected.") db = open("tmp", "w") assert len(db) == 4 db["ping"] = "pong" db.sync() try: db[(1,2)] = "tuple" except TypeError, e: assert str(e) == "key `(1, 2)' must be a string but is ", str(e) else: raise RuntimeError("TypeError exception expected") try: db["list"] = [1,2] except TypeError, e: assert str(e) == "value `[1, 2]' must be a string but is ", str(e) else: raise RuntimeError("TypeError exception expected") db = open("tmp", "r") assert len(db) == 5 db = open("tmp", "n") assert len(db) == 0 dblite._open("tmp.dblite", "w") db = open("tmp", "r") dblite._open("tmp.dblite", "w").write("x") try: db = open("tmp", "r") except pickle.UnpicklingError: pass else: raise RuntimeError("pickle exception expected.") global ignore_corrupt_dbfiles ignore_corrupt_dbfiles = 2 db = open("tmp", "r") assert len(db) == 0 os.unlink("tmp.dblite") try: db = open("tmp", "w") except IOError, e: assert str(e) == "[Errno 2] No such file or directory: 'tmp.dblite'", str(e) else: raise RuntimeError("IOError expected.") print "OK" if (__name__ == "__main__"): _exercise() # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Action.py0000644000175000017500000013475412114661557021110 0ustar dktrkranzdktrkranz"""SCons.Action This encapsulates information about executing any sort of action that can build one or more target Nodes (typically files) from one or more source Nodes (also typically files) given a specific Environment. The base class here is ActionBase. The base class supplies just a few OO utility methods and some generic methods for displaying information about an Action in response to the various commands that control printing. A second-level base class is _ActionAction. This extends ActionBase by providing the methods that can be used to show and perform an action. True Action objects will subclass _ActionAction; Action factory class objects will subclass ActionBase. The heavy lifting is handled by subclasses for the different types of actions we might execute: CommandAction CommandGeneratorAction FunctionAction ListAction The subclasses supply the following public interface methods used by other modules: __call__() THE public interface, "calling" an Action object executes the command or Python function. This also takes care of printing a pre-substitution command for debugging purposes. get_contents() Fetches the "contents" of an Action for signature calculation plus the varlist. This is what gets MD5 checksummed to decide if a target needs to be rebuilt because its action changed. genstring() Returns a string representation of the Action *without* command substitution, but allows a CommandGeneratorAction to generate the right action based on the specified target, source and env. This is used by the Signature subsystem (through the Executor) to obtain an (imprecise) representation of the Action operation for informative purposes. Subclasses also supply the following methods for internal use within this module: __str__() Returns a string approximation of the Action; no variable substitution is performed. execute() The internal method that really, truly, actually handles the execution of a command or Python function. This is used so that the __call__() methods can take care of displaying any pre-substitution representations, and *then* execute an action without worrying about the specific Actions involved. get_presig() Fetches the "contents" of a subclass for signature calculation. The varlist is added to this to produce the Action's contents. strfunction() Returns a substituted string representation of the Action. This is used by the _ActionAction.show() command to display the command/function that will be executed to generate the target(s). There is a related independent ActionCaller class that looks like a regular Action, and which serves as a wrapper for arbitrary functions that we want to let the user specify the arguments to now, but actually execute later (when an out-of-date check determines that it's needed to be executed, for example). Objects of this class are returned by an ActionFactory class that provides a __call__() method as a convenient way for wrapping up the functions. """ # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Action.py 2013/03/03 09:48:35 garyo" import SCons.compat import dis import os # compat layer imports "cPickle" for us if it's available. import pickle import re import sys import subprocess from SCons.Debug import logInstanceCreation import SCons.Errors import SCons.Executor import SCons.Util import SCons.Subst # we use these a lot, so try to optimize them is_String = SCons.Util.is_String is_List = SCons.Util.is_List class _null(object): pass print_actions = 1 execute_actions = 1 print_actions_presub = 0 def rfile(n): try: return n.rfile() except AttributeError: return n def default_exitstatfunc(s): return s try: SET_LINENO = dis.SET_LINENO HAVE_ARGUMENT = dis.HAVE_ARGUMENT except AttributeError: remove_set_lineno_codes = lambda x: x else: def remove_set_lineno_codes(code): result = [] n = len(code) i = 0 while i < n: c = code[i] op = ord(c) if op >= HAVE_ARGUMENT: if op != SET_LINENO: result.append(code[i:i+3]) i = i+3 else: result.append(c) i = i+1 return ''.join(result) strip_quotes = re.compile('^[\'"](.*)[\'"]$') def _callable_contents(obj): """Return the signature contents of a callable Python object. """ try: # Test if obj is a method. return _function_contents(obj.im_func) except AttributeError: try: # Test if obj is a callable object. return _function_contents(obj.__call__.im_func) except AttributeError: try: # Test if obj is a code object. return _code_contents(obj) except AttributeError: # Test if obj is a function object. return _function_contents(obj) def _object_contents(obj): """Return the signature contents of any Python object. We have to handle the case where object contains a code object since it can be pickled directly. """ try: # Test if obj is a method. return _function_contents(obj.im_func) except AttributeError: try: # Test if obj is a callable object. return _function_contents(obj.__call__.im_func) except AttributeError: try: # Test if obj is a code object. return _code_contents(obj) except AttributeError: try: # Test if obj is a function object. return _function_contents(obj) except AttributeError: # Should be a pickable Python object. try: return pickle.dumps(obj) except (pickle.PicklingError, TypeError): # This is weird, but it seems that nested classes # are unpickable. The Python docs say it should # always be a PicklingError, but some Python # versions seem to return TypeError. Just do # the best we can. return str(obj) def _code_contents(code): """Return the signature contents of a code object. By providing direct access to the code object of the function, Python makes this extremely easy. Hooray! Unfortunately, older versions of Python include line number indications in the compiled byte code. Boo! So we remove the line number byte codes to prevent recompilations from moving a Python function. """ contents = [] # The code contents depends on the number of local variables # but not their actual names. contents.append("%s,%s" % (code.co_argcount, len(code.co_varnames))) try: contents.append(",%s,%s" % (len(code.co_cellvars), len(code.co_freevars))) except AttributeError: # Older versions of Python do not support closures. contents.append(",0,0") # The code contents depends on any constants accessed by the # function. Note that we have to call _object_contents on each # constants because the code object of nested functions can # show-up among the constants. # # Note that we also always ignore the first entry of co_consts # which contains the function doc string. We assume that the # function does not access its doc string. contents.append(',(' + ','.join(map(_object_contents,code.co_consts[1:])) + ')') # The code contents depends on the variable names used to # accessed global variable, as changing the variable name changes # the variable actually accessed and therefore changes the # function result. contents.append(',(' + ','.join(map(_object_contents,code.co_names)) + ')') # The code contents depends on its actual code!!! contents.append(',(' + str(remove_set_lineno_codes(code.co_code)) + ')') return ''.join(contents) def _function_contents(func): """Return the signature contents of a function.""" contents = [_code_contents(func.func_code)] # The function contents depends on the value of defaults arguments if func.func_defaults: contents.append(',(' + ','.join(map(_object_contents,func.func_defaults)) + ')') else: contents.append(',()') # The function contents depends on the closure captured cell values. try: closure = func.func_closure or [] except AttributeError: # Older versions of Python do not support closures. closure = [] #xxx = [_object_contents(x.cell_contents) for x in closure] try: xxx = [_object_contents(x.cell_contents) for x in closure] except AttributeError: xxx = [] contents.append(',(' + ','.join(xxx) + ')') return ''.join(contents) def _actionAppend(act1, act2): # This function knows how to slap two actions together. # Mainly, it handles ListActions by concatenating into # a single ListAction. a1 = Action(act1) a2 = Action(act2) if a1 is None: return a2 if a2 is None: return a1 if isinstance(a1, ListAction): if isinstance(a2, ListAction): return ListAction(a1.list + a2.list) else: return ListAction(a1.list + [ a2 ]) else: if isinstance(a2, ListAction): return ListAction([ a1 ] + a2.list) else: return ListAction([ a1, a2 ]) def _do_create_keywords(args, kw): """This converts any arguments after the action argument into their equivalent keywords and adds them to the kw argument. """ v = kw.get('varlist', ()) # prevent varlist="FOO" from being interpreted as ['F', 'O', 'O'] if is_String(v): v = (v,) kw['varlist'] = tuple(v) if args: # turn positional args into equivalent keywords cmdstrfunc = args[0] if cmdstrfunc is None or is_String(cmdstrfunc): kw['cmdstr'] = cmdstrfunc elif callable(cmdstrfunc): kw['strfunction'] = cmdstrfunc else: raise SCons.Errors.UserError( 'Invalid command display variable type. ' 'You must either pass a string or a callback which ' 'accepts (target, source, env) as parameters.') if len(args) > 1: kw['varlist'] = args[1:] + kw['varlist'] if kw.get('strfunction', _null) is not _null \ and kw.get('cmdstr', _null) is not _null: raise SCons.Errors.UserError( 'Cannot have both strfunction and cmdstr args to Action()') def _do_create_action(act, kw): """This is the actual "implementation" for the Action factory method, below. This handles the fact that passing lists to Action() itself has different semantics than passing lists as elements of lists. The former will create a ListAction, the latter will create a CommandAction by converting the inner list elements to strings.""" if isinstance(act, ActionBase): return act if is_List(act): return CommandAction(act, **kw) if callable(act): try: gen = kw['generator'] del kw['generator'] except KeyError: gen = 0 if gen: action_type = CommandGeneratorAction else: action_type = FunctionAction return action_type(act, kw) if is_String(act): var=SCons.Util.get_environment_var(act) if var: # This looks like a string that is purely an Environment # variable reference, like "$FOO" or "${FOO}". We do # something special here...we lazily evaluate the contents # of that Environment variable, so a user could put something # like a function or a CommandGenerator in that variable # instead of a string. return LazyAction(var, kw) commands = str(act).split('\n') if len(commands) == 1: return CommandAction(commands[0], **kw) # The list of string commands may include a LazyAction, so we # reprocess them via _do_create_list_action. return _do_create_list_action(commands, kw) # Catch a common error case with a nice message: if isinstance(act, int) or isinstance(act, float): raise TypeError("Don't know how to create an Action from a number (%s)"%act) # Else fail silently (???) return None def _do_create_list_action(act, kw): """A factory for list actions. Convert the input list into Actions and then wrap them in a ListAction.""" acts = [] for a in act: aa = _do_create_action(a, kw) if aa is not None: acts.append(aa) if not acts: return ListAction([]) elif len(acts) == 1: return acts[0] else: return ListAction(acts) def Action(act, *args, **kw): """A factory for action objects.""" # Really simple: the _do_create_* routines do the heavy lifting. _do_create_keywords(args, kw) if is_List(act): return _do_create_list_action(act, kw) return _do_create_action(act, kw) class ActionBase(object): """Base class for all types of action objects that can be held by other objects (Builders, Executors, etc.) This provides the common methods for manipulating and combining those actions.""" def __cmp__(self, other): return cmp(self.__dict__, other) def no_batch_key(self, env, target, source): return None batch_key = no_batch_key def genstring(self, target, source, env): return str(self) def get_contents(self, target, source, env): result = [ self.get_presig(target, source, env) ] # This should never happen, as the Action() factory should wrap # the varlist, but just in case an action is created directly, # we duplicate this check here. vl = self.get_varlist(target, source, env) if is_String(vl): vl = (vl,) for v in vl: result.append(env.subst('${'+v+'}')) return ''.join(result) def __add__(self, other): return _actionAppend(self, other) def __radd__(self, other): return _actionAppend(other, self) def presub_lines(self, env): # CommandGeneratorAction needs a real environment # in order to return the proper string here, since # it may call LazyAction, which looks up a key # in that env. So we temporarily remember the env here, # and CommandGeneratorAction will use this env # when it calls its _generate method. self.presub_env = env lines = str(self).split('\n') self.presub_env = None # don't need this any more return lines def get_varlist(self, target, source, env, executor=None): return self.varlist def get_targets(self, env, executor): """ Returns the type of targets ($TARGETS, $CHANGED_TARGETS) used by this action. """ return self.targets class _ActionAction(ActionBase): """Base class for actions that create output objects.""" def __init__(self, cmdstr=_null, strfunction=_null, varlist=(), presub=_null, chdir=None, exitstatfunc=None, batch_key=None, targets='$TARGETS', **kw): self.cmdstr = cmdstr if strfunction is not _null: if strfunction is None: self.cmdstr = None else: self.strfunction = strfunction self.varlist = varlist self.presub = presub self.chdir = chdir if not exitstatfunc: exitstatfunc = default_exitstatfunc self.exitstatfunc = exitstatfunc self.targets = targets if batch_key: if not callable(batch_key): # They have set batch_key, but not to their own # callable. The default behavior here will batch # *all* targets+sources using this action, separated # for each construction environment. def default_batch_key(self, env, target, source): return (id(self), id(env)) batch_key = default_batch_key SCons.Util.AddMethod(self, batch_key, 'batch_key') def print_cmd_line(self, s, target, source, env): # In python 3, and in some of our tests, sys.stdout is # a String io object, and it takes unicode strings only # In other cases it's a regular Python 2.x file object # which takes strings (bytes), and if you pass those a # unicode object they try to decode with 'ascii' codec # which fails if the cmd line has any hi-bit-set chars. # This code assumes s is a regular string, but should # work if it's unicode too. try: sys.stdout.write(unicode(s + "\n")) except UnicodeDecodeError: sys.stdout.write(s + "\n") def __call__(self, target, source, env, exitstatfunc=_null, presub=_null, show=_null, execute=_null, chdir=_null, executor=None): if not is_List(target): target = [target] if not is_List(source): source = [source] if presub is _null: presub = self.presub if presub is _null: presub = print_actions_presub if exitstatfunc is _null: exitstatfunc = self.exitstatfunc if show is _null: show = print_actions if execute is _null: execute = execute_actions if chdir is _null: chdir = self.chdir save_cwd = None if chdir: save_cwd = os.getcwd() try: chdir = str(chdir.abspath) except AttributeError: if not is_String(chdir): if executor: chdir = str(executor.batches[0].targets[0].dir) else: chdir = str(target[0].dir) if presub: if executor: target = executor.get_all_targets() source = executor.get_all_sources() t = ' and '.join(map(str, target)) l = '\n '.join(self.presub_lines(env)) out = u"Building %s with action:\n %s\n" % (t, l) sys.stdout.write(out) cmd = None if show and self.strfunction: if executor: target = executor.get_all_targets() source = executor.get_all_sources() try: cmd = self.strfunction(target, source, env, executor) except TypeError: cmd = self.strfunction(target, source, env) if cmd: if chdir: cmd = ('os.chdir(%s)\n' % repr(chdir)) + cmd try: get = env.get except AttributeError: print_func = self.print_cmd_line else: print_func = get('PRINT_CMD_LINE_FUNC') if not print_func: print_func = self.print_cmd_line print_func(cmd, target, source, env) stat = 0 if execute: if chdir: os.chdir(chdir) try: stat = self.execute(target, source, env, executor=executor) if isinstance(stat, SCons.Errors.BuildError): s = exitstatfunc(stat.status) if s: stat.status = s else: stat = s else: stat = exitstatfunc(stat) finally: if save_cwd: os.chdir(save_cwd) if cmd and save_cwd: print_func('os.chdir(%s)' % repr(save_cwd), target, source, env) return stat def _string_from_cmd_list(cmd_list): """Takes a list of command line arguments and returns a pretty representation for printing.""" cl = [] for arg in map(str, cmd_list): if ' ' in arg or '\t' in arg: arg = '"' + arg + '"' cl.append(arg) return ' '.join(cl) # A fiddlin' little function that has an 'import SCons.Environment' which # can't be moved to the top level without creating an import loop. Since # this import creates a local variable named 'SCons', it blocks access to # the global variable, so we move it here to prevent complaints about local # variables being used uninitialized. default_ENV = None def get_default_ENV(env): global default_ENV try: return env['ENV'] except KeyError: if not default_ENV: import SCons.Environment # This is a hideously expensive way to get a default shell # environment. What it really should do is run the platform # setup to get the default ENV. Fortunately, it's incredibly # rare for an Environment not to have a shell environment, so # we're not going to worry about it overmuch. default_ENV = SCons.Environment.Environment()['ENV'] return default_ENV # This function is still in draft mode. We're going to need something like # it in the long run as more and more places use subprocess, but I'm sure # it'll have to be tweaked to get the full desired functionality. # one special arg (so far?), 'error', to tell what to do with exceptions. def _subproc(scons_env, cmd, error = 'ignore', **kw): """Do common setup for a subprocess.Popen() call""" # allow std{in,out,err} to be "'devnull'" io = kw.get('stdin') if is_String(io) and io == 'devnull': kw['stdin'] = open(os.devnull) io = kw.get('stdout') if is_String(io) and io == 'devnull': kw['stdout'] = open(os.devnull, 'w') io = kw.get('stderr') if is_String(io) and io == 'devnull': kw['stderr'] = open(os.devnull, 'w') # Figure out what shell environment to use ENV = kw.get('env', None) if ENV is None: ENV = get_default_ENV(scons_env) # Ensure that the ENV values are all strings: new_env = {} for key, value in ENV.items(): if is_List(value): # If the value is a list, then we assume it is a path list, # because that's a pretty common list-like value to stick # in an environment variable: value = SCons.Util.flatten_sequence(value) new_env[key] = os.pathsep.join(map(str, value)) else: # It's either a string or something else. If it's a string, # we still want to call str() because it might be a *Unicode* # string, which makes subprocess.Popen() gag. If it isn't a # string or a list, then we just coerce it to a string, which # is the proper way to handle Dir and File instances and will # produce something reasonable for just about everything else: new_env[key] = str(value) kw['env'] = new_env try: return subprocess.Popen(cmd, **kw) except EnvironmentError, e: if error == 'raise': raise # return a dummy Popen instance that only returns error class dummyPopen(object): def __init__(self, e): self.exception = e def communicate(self): return ('','') def wait(self): return -self.exception.errno stdin = None class f(object): def read(self): return '' def readline(self): return '' stdout = stderr = f() return dummyPopen(e) class CommandAction(_ActionAction): """Class for command-execution actions.""" def __init__(self, cmd, **kw): # Cmd can actually be a list or a single item; if it's a # single item it should be the command string to execute; if a # list then it should be the words of the command string to # execute. Only a single command should be executed by this # object; lists of commands should be handled by embedding # these objects in a ListAction object (which the Action() # factory above does). cmd will be passed to # Environment.subst_list() for substituting environment # variables. if __debug__: logInstanceCreation(self, 'Action.CommandAction') _ActionAction.__init__(self, **kw) if is_List(cmd): if list(filter(is_List, cmd)): raise TypeError("CommandAction should be given only " \ "a single command") self.cmd_list = cmd def __str__(self): if is_List(self.cmd_list): return ' '.join(map(str, self.cmd_list)) return str(self.cmd_list) def process(self, target, source, env, executor=None): if executor: result = env.subst_list(self.cmd_list, 0, executor=executor) else: result = env.subst_list(self.cmd_list, 0, target, source) silent = None ignore = None while True: try: c = result[0][0][0] except IndexError: c = None if c == '@': silent = 1 elif c == '-': ignore = 1 else: break result[0][0] = result[0][0][1:] try: if not result[0][0]: result[0] = result[0][1:] except IndexError: pass return result, ignore, silent def strfunction(self, target, source, env, executor=None): if self.cmdstr is None: return None if self.cmdstr is not _null: from SCons.Subst import SUBST_RAW if executor: c = env.subst(self.cmdstr, SUBST_RAW, executor=executor) else: c = env.subst(self.cmdstr, SUBST_RAW, target, source) if c: return c cmd_list, ignore, silent = self.process(target, source, env, executor) if silent: return '' return _string_from_cmd_list(cmd_list[0]) def execute(self, target, source, env, executor=None): """Execute a command action. This will handle lists of commands as well as individual commands, because construction variable substitution may turn a single "command" into a list. This means that this class can actually handle lists of commands, even though that's not how we use it externally. """ escape_list = SCons.Subst.escape_list flatten_sequence = SCons.Util.flatten_sequence try: shell = env['SHELL'] except KeyError: raise SCons.Errors.UserError('Missing SHELL construction variable.') try: spawn = env['SPAWN'] except KeyError: raise SCons.Errors.UserError('Missing SPAWN construction variable.') else: if is_String(spawn): spawn = env.subst(spawn, raw=1, conv=lambda x: x) escape = env.get('ESCAPE', lambda x: x) ENV = get_default_ENV(env) # Ensure that the ENV values are all strings: for key, value in ENV.items(): if not is_String(value): if is_List(value): # If the value is a list, then we assume it is a # path list, because that's a pretty common list-like # value to stick in an environment variable: value = flatten_sequence(value) ENV[key] = os.pathsep.join(map(str, value)) else: # If it isn't a string or a list, then we just coerce # it to a string, which is the proper way to handle # Dir and File instances and will produce something # reasonable for just about everything else: ENV[key] = str(value) if executor: target = executor.get_all_targets() source = executor.get_all_sources() cmd_list, ignore, silent = self.process(target, list(map(rfile, source)), env, executor) # Use len() to filter out any "command" that's zero-length. for cmd_line in filter(len, cmd_list): # Escape the command line for the interpreter we are using. cmd_line = escape_list(cmd_line, escape) result = spawn(shell, escape, cmd_line[0], cmd_line, ENV) if not ignore and result: msg = "Error %s" % result return SCons.Errors.BuildError(errstr=msg, status=result, action=self, command=cmd_line) return 0 def get_presig(self, target, source, env, executor=None): """Return the signature contents of this action's command line. This strips $(-$) and everything in between the string, since those parts don't affect signatures. """ from SCons.Subst import SUBST_SIG cmd = self.cmd_list if is_List(cmd): cmd = ' '.join(map(str, cmd)) else: cmd = str(cmd) if executor: return env.subst_target_source(cmd, SUBST_SIG, executor=executor) else: return env.subst_target_source(cmd, SUBST_SIG, target, source) def get_implicit_deps(self, target, source, env, executor=None): icd = env.get('IMPLICIT_COMMAND_DEPENDENCIES', True) if is_String(icd) and icd[:1] == '$': icd = env.subst(icd) if not icd or icd in ('0', 'None'): return [] from SCons.Subst import SUBST_SIG if executor: cmd_list = env.subst_list(self.cmd_list, SUBST_SIG, executor=executor) else: cmd_list = env.subst_list(self.cmd_list, SUBST_SIG, target, source) res = [] for cmd_line in cmd_list: if cmd_line: d = str(cmd_line[0]) m = strip_quotes.match(d) if m: d = m.group(1) d = env.WhereIs(d) if d: res.append(env.fs.File(d)) return res class CommandGeneratorAction(ActionBase): """Class for command-generator actions.""" def __init__(self, generator, kw): if __debug__: logInstanceCreation(self, 'Action.CommandGeneratorAction') self.generator = generator self.gen_kw = kw self.varlist = kw.get('varlist', ()) self.targets = kw.get('targets', '$TARGETS') def _generate(self, target, source, env, for_signature, executor=None): # ensure that target is a list, to make it easier to write # generator functions: if not is_List(target): target = [target] if executor: target = executor.get_all_targets() source = executor.get_all_sources() ret = self.generator(target=target, source=source, env=env, for_signature=for_signature) gen_cmd = Action(ret, **self.gen_kw) if not gen_cmd: raise SCons.Errors.UserError("Object returned from command generator: %s cannot be used to create an Action." % repr(ret)) return gen_cmd def __str__(self): try: env = self.presub_env except AttributeError: env = None if env is None: env = SCons.Defaults.DefaultEnvironment() act = self._generate([], [], env, 1) return str(act) def batch_key(self, env, target, source): return self._generate(target, source, env, 1).batch_key(env, target, source) def genstring(self, target, source, env, executor=None): return self._generate(target, source, env, 1, executor).genstring(target, source, env) def __call__(self, target, source, env, exitstatfunc=_null, presub=_null, show=_null, execute=_null, chdir=_null, executor=None): act = self._generate(target, source, env, 0, executor) if act is None: raise SCons.Errors.UserError("While building `%s': " "Cannot deduce file extension from source files: %s" % (repr(list(map(str, target))), repr(list(map(str, source))))) return act(target, source, env, exitstatfunc, presub, show, execute, chdir, executor) def get_presig(self, target, source, env, executor=None): """Return the signature contents of this action's command line. This strips $(-$) and everything in between the string, since those parts don't affect signatures. """ return self._generate(target, source, env, 1, executor).get_presig(target, source, env) def get_implicit_deps(self, target, source, env, executor=None): return self._generate(target, source, env, 1, executor).get_implicit_deps(target, source, env) def get_varlist(self, target, source, env, executor=None): return self._generate(target, source, env, 1, executor).get_varlist(target, source, env, executor) def get_targets(self, env, executor): return self._generate(None, None, env, 1, executor).get_targets(env, executor) # A LazyAction is a kind of hybrid generator and command action for # strings of the form "$VAR". These strings normally expand to other # strings (think "$CCCOM" to "$CC -c -o $TARGET $SOURCE"), but we also # want to be able to replace them with functions in the construction # environment. Consequently, we want lazy evaluation and creation of # an Action in the case of the function, but that's overkill in the more # normal case of expansion to other strings. # # So we do this with a subclass that's both a generator *and* # a command action. The overridden methods all do a quick check # of the construction variable, and if it's a string we just call # the corresponding CommandAction method to do the heavy lifting. # If not, then we call the same-named CommandGeneratorAction method. # The CommandGeneratorAction methods work by using the overridden # _generate() method, that is, our own way of handling "generation" of # an action based on what's in the construction variable. class LazyAction(CommandGeneratorAction, CommandAction): def __init__(self, var, kw): if __debug__: logInstanceCreation(self, 'Action.LazyAction') #FUTURE CommandAction.__init__(self, '${'+var+'}', **kw) CommandAction.__init__(self, '${'+var+'}', **kw) self.var = SCons.Util.to_String(var) self.gen_kw = kw def get_parent_class(self, env): c = env.get(self.var) if is_String(c) and not '\n' in c: return CommandAction return CommandGeneratorAction def _generate_cache(self, env): if env: c = env.get(self.var, '') else: c = '' gen_cmd = Action(c, **self.gen_kw) if not gen_cmd: raise SCons.Errors.UserError("$%s value %s cannot be used to create an Action." % (self.var, repr(c))) return gen_cmd def _generate(self, target, source, env, for_signature, executor=None): return self._generate_cache(env) def __call__(self, target, source, env, *args, **kw): c = self.get_parent_class(env) return c.__call__(self, target, source, env, *args, **kw) def get_presig(self, target, source, env): c = self.get_parent_class(env) return c.get_presig(self, target, source, env) def get_varlist(self, target, source, env, executor=None): c = self.get_parent_class(env) return c.get_varlist(self, target, source, env, executor) class FunctionAction(_ActionAction): """Class for Python function actions.""" def __init__(self, execfunction, kw): if __debug__: logInstanceCreation(self, 'Action.FunctionAction') self.execfunction = execfunction try: self.funccontents = _callable_contents(execfunction) except AttributeError: try: # See if execfunction will do the heavy lifting for us. self.gc = execfunction.get_contents except AttributeError: # This is weird, just do the best we can. self.funccontents = _object_contents(execfunction) _ActionAction.__init__(self, **kw) def function_name(self): try: return self.execfunction.__name__ except AttributeError: try: return self.execfunction.__class__.__name__ except AttributeError: return "unknown_python_function" def strfunction(self, target, source, env, executor=None): if self.cmdstr is None: return None if self.cmdstr is not _null: from SCons.Subst import SUBST_RAW if executor: c = env.subst(self.cmdstr, SUBST_RAW, executor=executor) else: c = env.subst(self.cmdstr, SUBST_RAW, target, source) if c: return c def array(a): def quote(s): try: str_for_display = s.str_for_display except AttributeError: s = repr(s) else: s = str_for_display() return s return '[' + ", ".join(map(quote, a)) + ']' try: strfunc = self.execfunction.strfunction except AttributeError: pass else: if strfunc is None: return None if callable(strfunc): return strfunc(target, source, env) name = self.function_name() tstr = array(target) sstr = array(source) return "%s(%s, %s)" % (name, tstr, sstr) def __str__(self): name = self.function_name() if name == 'ActionCaller': return str(self.execfunction) return "%s(target, source, env)" % name def execute(self, target, source, env, executor=None): exc_info = (None,None,None) try: if executor: target = executor.get_all_targets() source = executor.get_all_sources() rsources = list(map(rfile, source)) try: result = self.execfunction(target=target, source=rsources, env=env) except KeyboardInterrupt, e: raise except SystemExit, e: raise except Exception, e: result = e exc_info = sys.exc_info() if result: result = SCons.Errors.convert_to_BuildError(result, exc_info) result.node=target result.action=self try: result.command=self.strfunction(target, source, env, executor) except TypeError: result.command=self.strfunction(target, source, env) # FIXME: This maintains backward compatibility with respect to # which type of exceptions were returned by raising an # exception and which ones were returned by value. It would # probably be best to always return them by value here, but # some codes do not check the return value of Actions and I do # not have the time to modify them at this point. if (exc_info[1] and not isinstance(exc_info[1],EnvironmentError)): raise result return result finally: # Break the cycle between the traceback object and this # function stack frame. See the sys.exc_info() doc info for # more information about this issue. del exc_info def get_presig(self, target, source, env): """Return the signature contents of this callable action.""" try: return self.gc(target, source, env) except AttributeError: return self.funccontents def get_implicit_deps(self, target, source, env): return [] class ListAction(ActionBase): """Class for lists of other actions.""" def __init__(self, actionlist): if __debug__: logInstanceCreation(self, 'Action.ListAction') def list_of_actions(x): if isinstance(x, ActionBase): return x return Action(x) self.list = list(map(list_of_actions, actionlist)) # our children will have had any varlist # applied; we don't need to do it again self.varlist = () self.targets = '$TARGETS' def genstring(self, target, source, env): return '\n'.join([a.genstring(target, source, env) for a in self.list]) def __str__(self): return '\n'.join(map(str, self.list)) def presub_lines(self, env): return SCons.Util.flatten_sequence( [a.presub_lines(env) for a in self.list]) def get_presig(self, target, source, env): """Return the signature contents of this action list. Simple concatenation of the signatures of the elements. """ return "".join([x.get_contents(target, source, env) for x in self.list]) def __call__(self, target, source, env, exitstatfunc=_null, presub=_null, show=_null, execute=_null, chdir=_null, executor=None): if executor: target = executor.get_all_targets() source = executor.get_all_sources() for act in self.list: stat = act(target, source, env, exitstatfunc, presub, show, execute, chdir, executor) if stat: return stat return 0 def get_implicit_deps(self, target, source, env): result = [] for act in self.list: result.extend(act.get_implicit_deps(target, source, env)) return result def get_varlist(self, target, source, env, executor=None): result = SCons.Util.OrderedDict() for act in self.list: for var in act.get_varlist(target, source, env, executor): result[var] = True return list(result.keys()) class ActionCaller(object): """A class for delaying calling an Action function with specific (positional and keyword) arguments until the Action is actually executed. This class looks to the rest of the world like a normal Action object, but what it's really doing is hanging on to the arguments until we have a target, source and env to use for the expansion. """ def __init__(self, parent, args, kw): self.parent = parent self.args = args self.kw = kw def get_contents(self, target, source, env): actfunc = self.parent.actfunc try: # "self.actfunc" is a function. contents = str(actfunc.func_code.co_code) except AttributeError: # "self.actfunc" is a callable object. try: contents = str(actfunc.__call__.im_func.func_code.co_code) except AttributeError: # No __call__() method, so it might be a builtin # or something like that. Do the best we can. contents = str(actfunc) contents = remove_set_lineno_codes(contents) return contents def subst(self, s, target, source, env): # If s is a list, recursively apply subst() # to every element in the list if is_List(s): result = [] for elem in s: result.append(self.subst(elem, target, source, env)) return self.parent.convert(result) # Special-case hack: Let a custom function wrapped in an # ActionCaller get at the environment through which the action # was called by using this hard-coded value as a special return. if s == '$__env__': return env elif is_String(s): return env.subst(s, 1, target, source) return self.parent.convert(s) def subst_args(self, target, source, env): return [self.subst(x, target, source, env) for x in self.args] def subst_kw(self, target, source, env): kw = {} for key in self.kw.keys(): kw[key] = self.subst(self.kw[key], target, source, env) return kw def __call__(self, target, source, env, executor=None): args = self.subst_args(target, source, env) kw = self.subst_kw(target, source, env) return self.parent.actfunc(*args, **kw) def strfunction(self, target, source, env): args = self.subst_args(target, source, env) kw = self.subst_kw(target, source, env) return self.parent.strfunc(*args, **kw) def __str__(self): return self.parent.strfunc(*self.args, **self.kw) class ActionFactory(object): """A factory class that will wrap up an arbitrary function as an SCons-executable Action object. The real heavy lifting here is done by the ActionCaller class. We just collect the (positional and keyword) arguments that we're called with and give them to the ActionCaller object we create, so it can hang onto them until it needs them. """ def __init__(self, actfunc, strfunc, convert=lambda x: x): self.actfunc = actfunc self.strfunc = strfunc self.convert = convert def __call__(self, *args, **kw): ac = ActionCaller(self, args, kw) action = Action(ac, strfunction=ac.strfunction) return action # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/CacheDirTests.py0000644000175000017500000002266512114661557022355 0ustar dktrkranzdktrkranz# # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/CacheDirTests.py 2013/03/03 09:48:35 garyo" import os.path import shutil import sys import unittest from TestCmd import TestCmd import SCons.CacheDir built_it = None class Action(object): def __call__(self, targets, sources, env, **kw): global built_it if kw.get('execute', 1): built_it = 1 return 0 def genstring(self, target, source, env): return str(self) def get_contents(self, target, source, env): return '' class Builder(object): def __init__(self, environment, action): self.env = environment self.action = action self.overrides = {} self.source_scanner = None self.target_scanner = None class Environment(object): def __init__(self, cachedir): self.cachedir = cachedir def Override(self, overrides): return self def get_CacheDir(self): return self.cachedir class BaseTestCase(unittest.TestCase): """ Base fixtures common to our other unittest classes. """ def setUp(self): self.test = TestCmd(workdir='') import SCons.Node.FS self.fs = SCons.Node.FS.FS() self._CacheDir = SCons.CacheDir.CacheDir('cache') def File(self, name, bsig=None, action=Action()): node = self.fs.File(name) node.builder_set(Builder(Environment(self._CacheDir), action)) if bsig: node.cachesig = bsig #node.binfo = node.BuildInfo(node) #node.binfo.ninfo.bsig = bsig return node class CacheDirTestCase(BaseTestCase): """ Test calling CacheDir code directly. """ def test_cachepath(self): """Test the cachepath() method""" # Verify how the cachepath() method determines the name # of the file in cache. def my_collect(list): return list[0] save_collect = SCons.Util.MD5collect SCons.Util.MD5collect = my_collect try: f5 = self.File("cd.f5", 'a_fake_bsig') result = self._CacheDir.cachepath(f5) dirname = os.path.join('cache', 'A') filename = os.path.join(dirname, 'a_fake_bsig') assert result == (dirname, filename), result finally: SCons.Util.MD5collect = save_collect class FileTestCase(BaseTestCase): """ Test calling CacheDir code through Node.FS.File interfaces. """ # These tests were originally in Nodes/FSTests.py and got moved # when the CacheDir support was refactored into its own module. # Look in the history for Node/FSTests.py if any of this needs # to be re-examined. def retrieve_succeed(self, target, source, env, execute=1): self.retrieved.append(target) return 0 def retrieve_fail(self, target, source, env, execute=1): self.retrieved.append(target) return 1 def push(self, target, source, env): self.pushed.append(target) return 0 def test_CacheRetrieve(self): """Test the CacheRetrieve() function""" save_CacheRetrieve = SCons.CacheDir.CacheRetrieve self.retrieved = [] f1 = self.File("cd.f1") try: SCons.CacheDir.CacheRetrieve = self.retrieve_succeed self.retrieved = [] built_it = None r = f1.retrieve_from_cache() assert r == 1, r assert self.retrieved == [f1], self.retrieved assert built_it is None, built_it SCons.CacheDir.CacheRetrieve = self.retrieve_fail self.retrieved = [] built_it = None r = f1.retrieve_from_cache() assert not r, r assert self.retrieved == [f1], self.retrieved assert built_it is None, built_it finally: SCons.CacheDir.CacheRetrieve = save_CacheRetrieve def test_CacheRetrieveSilent(self): """Test the CacheRetrieveSilent() function""" save_CacheRetrieveSilent = SCons.CacheDir.CacheRetrieveSilent SCons.CacheDir.cache_show = 1 f2 = self.File("cd.f2", 'f2_bsig') try: SCons.CacheDir.CacheRetrieveSilent = self.retrieve_succeed self.retrieved = [] built_it = None r = f2.retrieve_from_cache() assert r == 1, r assert self.retrieved == [f2], self.retrieved assert built_it is None, built_it SCons.CacheDir.CacheRetrieveSilent = self.retrieve_fail self.retrieved = [] built_it = None r = f2.retrieve_from_cache() assert r is False, r assert self.retrieved == [f2], self.retrieved assert built_it is None, built_it finally: SCons.CacheDir.CacheRetrieveSilent = save_CacheRetrieveSilent def test_CachePush(self): """Test the CachePush() function""" save_CachePush = SCons.CacheDir.CachePush SCons.CacheDir.CachePush = self.push try: self.pushed = [] cd_f3 = self.test.workpath("cd.f3") f3 = self.File(cd_f3) f3.push_to_cache() assert self.pushed == [], self.pushed self.test.write(cd_f3, "cd.f3\n") f3.push_to_cache() assert self.pushed == [f3], self.pushed self.pushed = [] cd_f4 = self.test.workpath("cd.f4") f4 = self.File(cd_f4) f4.visited() assert self.pushed == [], self.pushed self.test.write(cd_f4, "cd.f4\n") f4.clear() f4.visited() assert self.pushed == [], self.pushed SCons.CacheDir.cache_force = 1 f4.clear() f4.visited() assert self.pushed == [f4], self.pushed finally: SCons.CacheDir.CachePush = save_CachePush def test_warning(self): """Test raising a warning if we can't copy a file to cache.""" test = TestCmd(workdir='') save_copy2 = shutil.copy2 def copy2(src, dst): raise OSError shutil.copy2 = copy2 save_mkdir = os.mkdir def mkdir(dir, mode=0): pass os.mkdir = mkdir old_warn_exceptions = SCons.Warnings.warningAsException(1) SCons.Warnings.enableWarningClass(SCons.Warnings.CacheWriteErrorWarning) try: cd_f7 = self.test.workpath("cd.f7") self.test.write(cd_f7, "cd.f7\n") f7 = self.File(cd_f7, 'f7_bsig') warn_caught = 0 try: f7.push_to_cache() except SCons.Errors.BuildError, e: assert e.exc_info[0] == SCons.Warnings.CacheWriteErrorWarning warn_caught = 1 assert warn_caught finally: shutil.copy2 = save_copy2 os.mkdir = save_mkdir SCons.Warnings.warningAsException(old_warn_exceptions) SCons.Warnings.suppressWarningClass(SCons.Warnings.CacheWriteErrorWarning) def test_no_strfunction(self): """Test handling no strfunction() for an action.""" save_CacheRetrieveSilent = SCons.CacheDir.CacheRetrieveSilent f8 = self.File("cd.f8", 'f8_bsig') try: SCons.CacheDir.CacheRetrieveSilent = self.retrieve_succeed self.retrieved = [] built_it = None r = f8.retrieve_from_cache() assert r == 1, r assert self.retrieved == [f8], self.retrieved assert built_it is None, built_it SCons.CacheDir.CacheRetrieveSilent = self.retrieve_fail self.retrieved = [] built_it = None r = f8.retrieve_from_cache() assert r is False, r assert self.retrieved == [f8], self.retrieved assert built_it is None, built_it finally: SCons.CacheDir.CacheRetrieveSilent = save_CacheRetrieveSilent if __name__ == "__main__": suite = unittest.TestSuite() tclasses = [ CacheDirTestCase, FileTestCase, ] for tclass in tclasses: names = unittest.getTestCaseNames(tclass, 'test_') suite.addTests(list(map(tclass, names))) if not unittest.TextTestRunner().run(suite).wasSuccessful(): sys.exit(1) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/SConf.py0000644000175000017500000011423212114661557020670 0ustar dktrkranzdktrkranz"""SCons.SConf Autoconf-like configuration support. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/SConf.py 2013/03/03 09:48:35 garyo" import SCons.compat import io import os import re import sys import traceback import SCons.Action import SCons.Builder import SCons.Errors import SCons.Job import SCons.Node.FS import SCons.Taskmaster import SCons.Util import SCons.Warnings import SCons.Conftest from SCons.Debug import Trace # Turn off the Conftest error logging SCons.Conftest.LogInputFiles = 0 SCons.Conftest.LogErrorMessages = 0 # Set build_type = None build_types = ['clean', 'help'] def SetBuildType(type): global build_type build_type = type # to be set, if we are in dry-run mode dryrun = 0 AUTO=0 # use SCons dependency scanning for up-to-date checks FORCE=1 # force all tests to be rebuilt CACHE=2 # force all tests to be taken from cache (raise an error, if necessary) cache_mode = AUTO def SetCacheMode(mode): """Set the Configure cache mode. mode must be one of "auto", "force", or "cache".""" global cache_mode if mode == "auto": cache_mode = AUTO elif mode == "force": cache_mode = FORCE elif mode == "cache": cache_mode = CACHE else: raise ValueError("SCons.SConf.SetCacheMode: Unknown mode " + mode) progress_display = SCons.Util.display # will be overwritten by SCons.Script def SetProgressDisplay(display): """Set the progress display to use (called from SCons.Script)""" global progress_display progress_display = display SConfFS = None _ac_build_counter = 0 # incremented, whenever TryBuild is called _ac_config_logs = {} # all config.log files created in this build _ac_config_hs = {} # all config.h files created in this build sconf_global = None # current sconf object def _createConfigH(target, source, env): t = open(str(target[0]), "w") defname = re.sub('[^A-Za-z0-9_]', '_', str(target[0]).upper()) t.write("""#ifndef %(DEFNAME)s_SEEN #define %(DEFNAME)s_SEEN """ % {'DEFNAME' : defname}) t.write(source[0].get_contents()) t.write(""" #endif /* %(DEFNAME)s_SEEN */ """ % {'DEFNAME' : defname}) t.close() def _stringConfigH(target, source, env): return "scons: Configure: creating " + str(target[0]) def CreateConfigHBuilder(env): """Called just before the building targets phase begins.""" if len(_ac_config_hs) == 0: return action = SCons.Action.Action(_createConfigH, _stringConfigH) sconfigHBld = SCons.Builder.Builder(action=action) env.Append( BUILDERS={'SConfigHBuilder':sconfigHBld} ) for k in _ac_config_hs.keys(): env.SConfigHBuilder(k, env.Value(_ac_config_hs[k])) class SConfWarning(SCons.Warnings.Warning): pass SCons.Warnings.enableWarningClass(SConfWarning) # some error definitions class SConfError(SCons.Errors.UserError): def __init__(self,msg): SCons.Errors.UserError.__init__(self,msg) class ConfigureDryRunError(SConfError): """Raised when a file or directory needs to be updated during a Configure process, but the user requested a dry-run""" def __init__(self,target): if not isinstance(target, SCons.Node.FS.File): msg = 'Cannot create configure directory "%s" within a dry-run.' % str(target) else: msg = 'Cannot update configure test "%s" within a dry-run.' % str(target) SConfError.__init__(self,msg) class ConfigureCacheError(SConfError): """Raised when a use explicitely requested the cache feature, but the test is run the first time.""" def __init__(self,target): SConfError.__init__(self, '"%s" is not yet built and cache is forced.' % str(target)) # define actions for building text files def _createSource( target, source, env ): fd = open(str(target[0]), "w") fd.write(source[0].get_contents()) fd.close() def _stringSource( target, source, env ): return (str(target[0]) + ' <-\n |' + source[0].get_contents().replace( '\n', "\n |" ) ) class SConfBuildInfo(SCons.Node.FS.FileBuildInfo): """ Special build info for targets of configure tests. Additional members are result (did the builder succeed last time?) and string, which contains messages of the original build phase. """ result = None # -> 0/None -> no error, != 0 error string = None # the stdout / stderr output when building the target def set_build_result(self, result, string): self.result = result self.string = string class Streamer(object): """ 'Sniffer' for a file-like writable object. Similar to the unix tool tee. """ def __init__(self, orig): self.orig = orig self.s = io.StringIO() def write(self, str): if self.orig: self.orig.write(str) self.s.write(str) def writelines(self, lines): for l in lines: self.write(l + '\n') def getvalue(self): """ Return everything written to orig since the Streamer was created. """ return self.s.getvalue() def flush(self): if self.orig: self.orig.flush() self.s.flush() class SConfBuildTask(SCons.Taskmaster.AlwaysTask): """ This is almost the same as SCons.Script.BuildTask. Handles SConfErrors correctly and knows about the current cache_mode. """ def display(self, message): if sconf_global.logstream: sconf_global.logstream.write("scons: Configure: " + message + "\n") def display_cached_string(self, bi): """ Logs the original builder messages, given the SConfBuildInfo instance bi. """ if not isinstance(bi, SConfBuildInfo): SCons.Warnings.warn(SConfWarning, "The stored build information has an unexpected class: %s" % bi.__class__) else: self.display("The original builder output was:\n" + (" |" + str(bi.string)).replace("\n", "\n |")) def failed(self): # check, if the reason was a ConfigureDryRunError or a # ConfigureCacheError and if yes, reraise the exception exc_type = self.exc_info()[0] if issubclass(exc_type, SConfError): raise elif issubclass(exc_type, SCons.Errors.BuildError): # we ignore Build Errors (occurs, when a test doesn't pass) # Clear the exception to prevent the contained traceback # to build a reference cycle. self.exc_clear() else: self.display('Caught exception while building "%s":\n' % self.targets[0]) try: excepthook = sys.excepthook except AttributeError: # Earlier versions of Python don't have sys.excepthook... def excepthook(type, value, tb): traceback.print_tb(tb) print type, value excepthook(*self.exc_info()) return SCons.Taskmaster.Task.failed(self) def collect_node_states(self): # returns (is_up_to_date, cached_error, cachable) # where is_up_to_date is 1, if the node(s) are up_to_date # cached_error is 1, if the node(s) are up_to_date, but the # build will fail # cachable is 0, if some nodes are not in our cache T = 0 changed = False cached_error = False cachable = True for t in self.targets: if T: Trace('%s' % (t)) bi = t.get_stored_info().binfo if isinstance(bi, SConfBuildInfo): if T: Trace(': SConfBuildInfo') if cache_mode == CACHE: t.set_state(SCons.Node.up_to_date) if T: Trace(': set_state(up_to-date)') else: if T: Trace(': get_state() %s' % t.get_state()) if T: Trace(': changed() %s' % t.changed()) if (t.get_state() != SCons.Node.up_to_date and t.changed()): changed = True if T: Trace(': changed %s' % changed) cached_error = cached_error or bi.result else: if T: Trace(': else') # the node hasn't been built in a SConf context or doesn't # exist cachable = False changed = ( t.get_state() != SCons.Node.up_to_date ) if T: Trace(': changed %s' % changed) if T: Trace('\n') return (not changed, cached_error, cachable) def execute(self): if not self.targets[0].has_builder(): return sconf = sconf_global is_up_to_date, cached_error, cachable = self.collect_node_states() if cache_mode == CACHE and not cachable: raise ConfigureCacheError(self.targets[0]) elif cache_mode == FORCE: is_up_to_date = 0 if cached_error and is_up_to_date: self.display("Building \"%s\" failed in a previous run and all " "its sources are up to date." % str(self.targets[0])) binfo = self.targets[0].get_stored_info().binfo self.display_cached_string(binfo) raise SCons.Errors.BuildError # will be 'caught' in self.failed elif is_up_to_date: self.display("\"%s\" is up to date." % str(self.targets[0])) binfo = self.targets[0].get_stored_info().binfo self.display_cached_string(binfo) elif dryrun: raise ConfigureDryRunError(self.targets[0]) else: # note stdout and stderr are the same here s = sys.stdout = sys.stderr = Streamer(sys.stdout) try: env = self.targets[0].get_build_env() if cache_mode == FORCE: # Set up the Decider() to force rebuilds by saying # that every source has changed. Note that we still # call the environment's underlying source decider so # that the correct .sconsign info will get calculated # and keep the build state consistent. def force_build(dependency, target, prev_ni, env_decider=env.decide_source): env_decider(dependency, target, prev_ni) return True if env.decide_source.func_code is not force_build.func_code: env.Decider(force_build) env['PSTDOUT'] = env['PSTDERR'] = s try: sconf.cached = 0 self.targets[0].build() finally: sys.stdout = sys.stderr = env['PSTDOUT'] = \ env['PSTDERR'] = sconf.logstream except KeyboardInterrupt: raise except SystemExit: exc_value = sys.exc_info()[1] raise SCons.Errors.ExplicitExit(self.targets[0],exc_value.code) except Exception, e: for t in self.targets: binfo = t.get_binfo() binfo.__class__ = SConfBuildInfo binfo.set_build_result(1, s.getvalue()) sconsign_entry = SCons.SConsign.SConsignEntry() sconsign_entry.binfo = binfo #sconsign_entry.ninfo = self.get_ninfo() # We'd like to do this as follows: # t.store_info(binfo) # However, we need to store it as an SConfBuildInfo # object, and store_info() will turn it into a # regular FileNodeInfo if the target is itself a # regular File. sconsign = t.dir.sconsign() sconsign.set_entry(t.name, sconsign_entry) sconsign.merge() raise e else: for t in self.targets: binfo = t.get_binfo() binfo.__class__ = SConfBuildInfo binfo.set_build_result(0, s.getvalue()) sconsign_entry = SCons.SConsign.SConsignEntry() sconsign_entry.binfo = binfo #sconsign_entry.ninfo = self.get_ninfo() # We'd like to do this as follows: # t.store_info(binfo) # However, we need to store it as an SConfBuildInfo # object, and store_info() will turn it into a # regular FileNodeInfo if the target is itself a # regular File. sconsign = t.dir.sconsign() sconsign.set_entry(t.name, sconsign_entry) sconsign.merge() class SConfBase(object): """This is simply a class to represent a configure context. After creating a SConf object, you can call any tests. After finished with your tests, be sure to call the Finish() method, which returns the modified environment. Some words about caching: In most cases, it is not necessary to cache Test results explicitely. Instead, we use the scons dependency checking mechanism. For example, if one wants to compile a test program (SConf.TryLink), the compiler is only called, if the program dependencies have changed. However, if the program could not be compiled in a former SConf run, we need to explicitely cache this error. """ def __init__(self, env, custom_tests = {}, conf_dir='$CONFIGUREDIR', log_file='$CONFIGURELOG', config_h = None, _depth = 0): """Constructor. Pass additional tests in the custom_tests-dictinary, e.g. custom_tests={'CheckPrivate':MyPrivateTest}, where MyPrivateTest defines a custom test. Note also the conf_dir and log_file arguments (you may want to build tests in the VariantDir, not in the SourceDir) """ global SConfFS if not SConfFS: SConfFS = SCons.Node.FS.default_fs or \ SCons.Node.FS.FS(env.fs.pathTop) if sconf_global is not None: raise SCons.Errors.UserError self.env = env if log_file is not None: log_file = SConfFS.File(env.subst(log_file)) self.logfile = log_file self.logstream = None self.lastTarget = None self.depth = _depth self.cached = 0 # will be set, if all test results are cached # add default tests default_tests = { 'CheckCC' : CheckCC, 'CheckCXX' : CheckCXX, 'CheckSHCC' : CheckSHCC, 'CheckSHCXX' : CheckSHCXX, 'CheckFunc' : CheckFunc, 'CheckType' : CheckType, 'CheckTypeSize' : CheckTypeSize, 'CheckDeclaration' : CheckDeclaration, 'CheckHeader' : CheckHeader, 'CheckCHeader' : CheckCHeader, 'CheckCXXHeader' : CheckCXXHeader, 'CheckLib' : CheckLib, 'CheckLibWithHeader' : CheckLibWithHeader, } self.AddTests(default_tests) self.AddTests(custom_tests) self.confdir = SConfFS.Dir(env.subst(conf_dir)) if config_h is not None: config_h = SConfFS.File(config_h) self.config_h = config_h self._startup() def Finish(self): """Call this method after finished with your tests: env = sconf.Finish() """ self._shutdown() return self.env def Define(self, name, value = None, comment = None): """ Define a pre processor symbol name, with the optional given value in the current config header. If value is None (default), then #define name is written. If value is not none, then #define name value is written. comment is a string which will be put as a C comment in the header, to explain the meaning of the value (appropriate C comments /* and */ will be put automatically.""" lines = [] if comment: comment_str = "/* %s */" % comment lines.append(comment_str) if value is not None: define_str = "#define %s %s" % (name, value) else: define_str = "#define %s" % name lines.append(define_str) lines.append('') self.config_h_text = self.config_h_text + '\n'.join(lines) def BuildNodes(self, nodes): """ Tries to build the given nodes immediately. Returns 1 on success, 0 on error. """ if self.logstream is not None: # override stdout / stderr to write in log file oldStdout = sys.stdout sys.stdout = self.logstream oldStderr = sys.stderr sys.stderr = self.logstream # the engine assumes the current path is the SConstruct directory ... old_fs_dir = SConfFS.getcwd() old_os_dir = os.getcwd() SConfFS.chdir(SConfFS.Top, change_os_dir=1) # Because we take responsibility here for writing out our # own .sconsign info (see SConfBuildTask.execute(), above), # we override the store_info() method with a null place-holder # so we really control how it gets written. for n in nodes: n.store_info = n.do_not_store_info ret = 1 try: # ToDo: use user options for calc save_max_drift = SConfFS.get_max_drift() SConfFS.set_max_drift(0) tm = SCons.Taskmaster.Taskmaster(nodes, SConfBuildTask) # we don't want to build tests in parallel jobs = SCons.Job.Jobs(1, tm ) jobs.run() for n in nodes: state = n.get_state() if (state != SCons.Node.executed and state != SCons.Node.up_to_date): # the node could not be built. we return 0 in this case ret = 0 finally: SConfFS.set_max_drift(save_max_drift) os.chdir(old_os_dir) SConfFS.chdir(old_fs_dir, change_os_dir=0) if self.logstream is not None: # restore stdout / stderr sys.stdout = oldStdout sys.stderr = oldStderr return ret def pspawn_wrapper(self, sh, escape, cmd, args, env): """Wrapper function for handling piped spawns. This looks to the calling interface (in Action.py) like a "normal" spawn, but associates the call with the PSPAWN variable from the construction environment and with the streams to which we want the output logged. This gets slid into the construction environment as the SPAWN variable so Action.py doesn't have to know or care whether it's spawning a piped command or not. """ return self.pspawn(sh, escape, cmd, args, env, self.logstream, self.logstream) def TryBuild(self, builder, text = None, extension = ""): """Low level TryBuild implementation. Normally you don't need to call that - you can use TryCompile / TryLink / TryRun instead """ global _ac_build_counter # Make sure we have a PSPAWN value, and save the current # SPAWN value. try: self.pspawn = self.env['PSPAWN'] except KeyError: raise SCons.Errors.UserError('Missing PSPAWN construction variable.') try: save_spawn = self.env['SPAWN'] except KeyError: raise SCons.Errors.UserError('Missing SPAWN construction variable.') nodesToBeBuilt = [] f = "conftest_" + str(_ac_build_counter) pref = self.env.subst( builder.builder.prefix ) suff = self.env.subst( builder.builder.suffix ) target = self.confdir.File(pref + f + suff) try: # Slide our wrapper into the construction environment as # the SPAWN function. self.env['SPAWN'] = self.pspawn_wrapper sourcetext = self.env.Value(text) if text is not None: textFile = self.confdir.File(f + extension) textFileNode = self.env.SConfSourceBuilder(target=textFile, source=sourcetext) nodesToBeBuilt.extend(textFileNode) source = textFileNode else: source = None nodes = builder(target = target, source = source) if not SCons.Util.is_List(nodes): nodes = [nodes] nodesToBeBuilt.extend(nodes) result = self.BuildNodes(nodesToBeBuilt) finally: self.env['SPAWN'] = save_spawn _ac_build_counter = _ac_build_counter + 1 if result: self.lastTarget = nodes[0] else: self.lastTarget = None return result def TryAction(self, action, text = None, extension = ""): """Tries to execute the given action with optional source file contents and optional source file extension , Returns the status (0 : failed, 1 : ok) and the contents of the output file. """ builder = SCons.Builder.Builder(action=action) self.env.Append( BUILDERS = {'SConfActionBuilder' : builder} ) ok = self.TryBuild(self.env.SConfActionBuilder, text, extension) del self.env['BUILDERS']['SConfActionBuilder'] if ok: outputStr = self.lastTarget.get_contents() return (1, outputStr) return (0, "") def TryCompile( self, text, extension): """Compiles the program given in text to an env.Object, using extension as file extension (e.g. '.c'). Returns 1, if compilation was successful, 0 otherwise. The target is saved in self.lastTarget (for further processing). """ return self.TryBuild(self.env.Object, text, extension) def TryLink( self, text, extension ): """Compiles the program given in text to an executable env.Program, using extension as file extension (e.g. '.c'). Returns 1, if compilation was successful, 0 otherwise. The target is saved in self.lastTarget (for further processing). """ return self.TryBuild(self.env.Program, text, extension ) def TryRun(self, text, extension ): """Compiles and runs the program given in text, using extension as file extension (e.g. '.c'). Returns (1, outputStr) on success, (0, '') otherwise. The target (a file containing the program's stdout) is saved in self.lastTarget (for further processing). """ ok = self.TryLink(text, extension) if( ok ): prog = self.lastTarget pname = prog.path output = self.confdir.File(os.path.basename(pname)+'.out') node = self.env.Command(output, prog, [ [ pname, ">", "${TARGET}"] ]) ok = self.BuildNodes(node) if ok: outputStr = output.get_contents() return( 1, outputStr) return (0, "") class TestWrapper(object): """A wrapper around Tests (to ensure sanity)""" def __init__(self, test, sconf): self.test = test self.sconf = sconf def __call__(self, *args, **kw): if not self.sconf.active: raise SCons.Errors.UserError context = CheckContext(self.sconf) ret = self.test(context, *args, **kw) if self.sconf.config_h is not None: self.sconf.config_h_text = self.sconf.config_h_text + context.config_h context.Result("error: no result") return ret def AddTest(self, test_name, test_instance): """Adds test_class to this SConf instance. It can be called with self.test_name(...)""" setattr(self, test_name, SConfBase.TestWrapper(test_instance, self)) def AddTests(self, tests): """Adds all the tests given in the tests dictionary to this SConf instance """ for name in tests.keys(): self.AddTest(name, tests[name]) def _createDir( self, node ): dirName = str(node) if dryrun: if not os.path.isdir( dirName ): raise ConfigureDryRunError(dirName) else: if not os.path.isdir( dirName ): os.makedirs( dirName ) node._exists = 1 def _startup(self): """Private method. Set up logstream, and set the environment variables necessary for a piped build """ global _ac_config_logs global sconf_global global SConfFS self.lastEnvFs = self.env.fs self.env.fs = SConfFS self._createDir(self.confdir) self.confdir.up().add_ignore( [self.confdir] ) if self.logfile is not None and not dryrun: # truncate logfile, if SConf.Configure is called for the first time # in a build if self.logfile in _ac_config_logs: log_mode = "a" else: _ac_config_logs[self.logfile] = None log_mode = "w" fp = open(str(self.logfile), log_mode) self.logstream = SCons.Util.Unbuffered(fp) # logfile may stay in a build directory, so we tell # the build system not to override it with a eventually # existing file with the same name in the source directory self.logfile.dir.add_ignore( [self.logfile] ) tb = traceback.extract_stack()[-3-self.depth] old_fs_dir = SConfFS.getcwd() SConfFS.chdir(SConfFS.Top, change_os_dir=0) self.logstream.write('file %s,line %d:\n\tConfigure(confdir = %s)\n' % (tb[0], tb[1], str(self.confdir)) ) SConfFS.chdir(old_fs_dir) else: self.logstream = None # we use a special builder to create source files from TEXT action = SCons.Action.Action(_createSource, _stringSource) sconfSrcBld = SCons.Builder.Builder(action=action) self.env.Append( BUILDERS={'SConfSourceBuilder':sconfSrcBld} ) self.config_h_text = _ac_config_hs.get(self.config_h, "") self.active = 1 # only one SConf instance should be active at a time ... sconf_global = self def _shutdown(self): """Private method. Reset to non-piped spawn""" global sconf_global, _ac_config_hs if not self.active: raise SCons.Errors.UserError("Finish may be called only once!") if self.logstream is not None and not dryrun: self.logstream.write("\n") self.logstream.close() self.logstream = None # remove the SConfSourceBuilder from the environment blds = self.env['BUILDERS'] del blds['SConfSourceBuilder'] self.env.Replace( BUILDERS=blds ) self.active = 0 sconf_global = None if not self.config_h is None: _ac_config_hs[self.config_h] = self.config_h_text self.env.fs = self.lastEnvFs class CheckContext(object): """Provides a context for configure tests. Defines how a test writes to the screen and log file. A typical test is just a callable with an instance of CheckContext as first argument: def CheckCustom(context, ...) context.Message('Checking my weird test ... ') ret = myWeirdTestFunction(...) context.Result(ret) Often, myWeirdTestFunction will be one of context.TryCompile/context.TryLink/context.TryRun. The results of those are cached, for they are only rebuild, if the dependencies have changed. """ def __init__(self, sconf): """Constructor. Pass the corresponding SConf instance.""" self.sconf = sconf self.did_show_result = 0 # for Conftest.py: self.vardict = {} self.havedict = {} self.headerfilename = None self.config_h = "" # config_h text will be stored here # we don't regenerate the config.h file after each test. That means, # that tests won't be able to include the config.h file, and so # they can't do an #ifdef HAVE_XXX_H. This shouldn't be a major # issue, though. If it turns out, that we need to include config.h # in tests, we must ensure, that the dependencies are worked out # correctly. Note that we can't use Conftest.py's support for config.h, # cause we will need to specify a builder for the config.h file ... def Message(self, text): """Inform about what we are doing right now, e.g. 'Checking for SOMETHING ... ' """ self.Display(text) self.sconf.cached = 1 self.did_show_result = 0 def Result(self, res): """Inform about the result of the test. res may be an integer or a string. In case of an integer, the written text will be 'yes' or 'no'. The result is only displayed when self.did_show_result is not set. """ if isinstance(res, (int, bool)): if res: text = "yes" else: text = "no" elif isinstance(res, str): text = res else: raise TypeError("Expected string, int or bool, got " + str(type(res))) if self.did_show_result == 0: # Didn't show result yet, do it now. self.Display(text + "\n") self.did_show_result = 1 def TryBuild(self, *args, **kw): return self.sconf.TryBuild(*args, **kw) def TryAction(self, *args, **kw): return self.sconf.TryAction(*args, **kw) def TryCompile(self, *args, **kw): return self.sconf.TryCompile(*args, **kw) def TryLink(self, *args, **kw): return self.sconf.TryLink(*args, **kw) def TryRun(self, *args, **kw): return self.sconf.TryRun(*args, **kw) def __getattr__( self, attr ): if( attr == 'env' ): return self.sconf.env elif( attr == 'lastTarget' ): return self.sconf.lastTarget else: raise AttributeError("CheckContext instance has no attribute '%s'" % attr) #### Stuff used by Conftest.py (look there for explanations). def BuildProg(self, text, ext): self.sconf.cached = 1 # TODO: should use self.vardict for $CC, $CPPFLAGS, etc. return not self.TryBuild(self.env.Program, text, ext) def CompileProg(self, text, ext): self.sconf.cached = 1 # TODO: should use self.vardict for $CC, $CPPFLAGS, etc. return not self.TryBuild(self.env.Object, text, ext) def CompileSharedObject(self, text, ext): self.sconf.cached = 1 # TODO: should use self.vardict for $SHCC, $CPPFLAGS, etc. return not self.TryBuild(self.env.SharedObject, text, ext) def RunProg(self, text, ext): self.sconf.cached = 1 # TODO: should use self.vardict for $CC, $CPPFLAGS, etc. st, out = self.TryRun(text, ext) return not st, out def AppendLIBS(self, lib_name_list): oldLIBS = self.env.get( 'LIBS', [] ) self.env.Append(LIBS = lib_name_list) return oldLIBS def PrependLIBS(self, lib_name_list): oldLIBS = self.env.get( 'LIBS', [] ) self.env.Prepend(LIBS = lib_name_list) return oldLIBS def SetLIBS(self, val): oldLIBS = self.env.get( 'LIBS', [] ) self.env.Replace(LIBS = val) return oldLIBS def Display(self, msg): if self.sconf.cached: # We assume that Display is called twice for each test here # once for the Checking for ... message and once for the result. # The self.sconf.cached flag can only be set between those calls msg = "(cached) " + msg self.sconf.cached = 0 progress_display(msg, append_newline=0) self.Log("scons: Configure: " + msg + "\n") def Log(self, msg): if self.sconf.logstream is not None: self.sconf.logstream.write(msg) #### End of stuff used by Conftest.py. def SConf(*args, **kw): if kw.get(build_type, True): kw['_depth'] = kw.get('_depth', 0) + 1 for bt in build_types: try: del kw[bt] except KeyError: pass return SConfBase(*args, **kw) else: return SCons.Util.Null() def CheckFunc(context, function_name, header = None, language = None): res = SCons.Conftest.CheckFunc(context, function_name, header = header, language = language) context.did_show_result = 1 return not res def CheckType(context, type_name, includes = "", language = None): res = SCons.Conftest.CheckType(context, type_name, header = includes, language = language) context.did_show_result = 1 return not res def CheckTypeSize(context, type_name, includes = "", language = None, expect = None): res = SCons.Conftest.CheckTypeSize(context, type_name, header = includes, language = language, expect = expect) context.did_show_result = 1 return res def CheckDeclaration(context, declaration, includes = "", language = None): res = SCons.Conftest.CheckDeclaration(context, declaration, includes = includes, language = language) context.did_show_result = 1 return not res def createIncludesFromHeaders(headers, leaveLast, include_quotes = '""'): # used by CheckHeader and CheckLibWithHeader to produce C - #include # statements from the specified header (list) if not SCons.Util.is_List(headers): headers = [headers] l = [] if leaveLast: lastHeader = headers[-1] headers = headers[:-1] else: lastHeader = None for s in headers: l.append("#include %s%s%s\n" % (include_quotes[0], s, include_quotes[1])) return ''.join(l), lastHeader def CheckHeader(context, header, include_quotes = '<>', language = None): """ A test for a C or C++ header file. """ prog_prefix, hdr_to_check = \ createIncludesFromHeaders(header, 1, include_quotes) res = SCons.Conftest.CheckHeader(context, hdr_to_check, prog_prefix, language = language, include_quotes = include_quotes) context.did_show_result = 1 return not res def CheckCC(context): res = SCons.Conftest.CheckCC(context) context.did_show_result = 1 return not res def CheckCXX(context): res = SCons.Conftest.CheckCXX(context) context.did_show_result = 1 return not res def CheckSHCC(context): res = SCons.Conftest.CheckSHCC(context) context.did_show_result = 1 return not res def CheckSHCXX(context): res = SCons.Conftest.CheckSHCXX(context) context.did_show_result = 1 return not res # Bram: Make this function obsolete? CheckHeader() is more generic. def CheckCHeader(context, header, include_quotes = '""'): """ A test for a C header file. """ return CheckHeader(context, header, include_quotes, language = "C") # Bram: Make this function obsolete? CheckHeader() is more generic. def CheckCXXHeader(context, header, include_quotes = '""'): """ A test for a C++ header file. """ return CheckHeader(context, header, include_quotes, language = "C++") def CheckLib(context, library = None, symbol = "main", header = None, language = None, autoadd = 1): """ A test for a library. See also CheckLibWithHeader. Note that library may also be None to test whether the given symbol compiles without flags. """ if library == []: library = [None] if not SCons.Util.is_List(library): library = [library] # ToDo: accept path for the library res = SCons.Conftest.CheckLib(context, library, symbol, header = header, language = language, autoadd = autoadd) context.did_show_result = 1 return not res # XXX # Bram: Can only include one header and can't use #ifdef HAVE_HEADER_H. def CheckLibWithHeader(context, libs, header, language, call = None, autoadd = 1): # ToDo: accept path for library. Support system header files. """ Another (more sophisticated) test for a library. Checks, if library and header is available for language (may be 'C' or 'CXX'). Call maybe be a valid expression _with_ a trailing ';'. As in CheckLib, we support library=None, to test if the call compiles without extra link flags. """ prog_prefix, dummy = \ createIncludesFromHeaders(header, 0) if libs == []: libs = [None] if not SCons.Util.is_List(libs): libs = [libs] res = SCons.Conftest.CheckLib(context, libs, None, prog_prefix, call = call, language = language, autoadd = autoadd) context.did_show_result = 1 return not res # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Conftest.py0000644000175000017500000006607112114661557021454 0ustar dktrkranzdktrkranz"""SCons.Conftest Autoconf-like configuration support; low level implementation of tests. """ # # Copyright (c) 2003 Stichting NLnet Labs # Copyright (c) 2001, 2002, 2003 Steven Knight # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # # The above copyright notice and this permission notice shall be included # in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # # # The purpose of this module is to define how a check is to be performed. # Use one of the Check...() functions below. # # # A context class is used that defines functions for carrying out the tests, # logging and messages. The following methods and members must be present: # # context.Display(msg) Function called to print messages that are normally # displayed for the user. Newlines are explicitly used. # The text should also be written to the logfile! # # context.Log(msg) Function called to write to a log file. # # context.BuildProg(text, ext) # Function called to build a program, using "ext" for the # file extention. Must return an empty string for # success, an error message for failure. # For reliable test results building should be done just # like an actual program would be build, using the same # command and arguments (including configure results so # far). # # context.CompileProg(text, ext) # Function called to compile a program, using "ext" for # the file extention. Must return an empty string for # success, an error message for failure. # For reliable test results compiling should be done just # like an actual source file would be compiled, using the # same command and arguments (including configure results # so far). # # context.AppendLIBS(lib_name_list) # Append "lib_name_list" to the value of LIBS. # "lib_namelist" is a list of strings. # Return the value of LIBS before changing it (any type # can be used, it is passed to SetLIBS() later.) # # context.PrependLIBS(lib_name_list) # Prepend "lib_name_list" to the value of LIBS. # "lib_namelist" is a list of strings. # Return the value of LIBS before changing it (any type # can be used, it is passed to SetLIBS() later.) # # context.SetLIBS(value) # Set LIBS to "value". The type of "value" is what # AppendLIBS() returned. # Return the value of LIBS before changing it (any type # can be used, it is passed to SetLIBS() later.) # # context.headerfilename # Name of file to append configure results to, usually # "confdefs.h". # The file must not exist or be empty when starting. # Empty or None to skip this (some tests will not work!). # # context.config_h (may be missing). If present, must be a string, which # will be filled with the contents of a config_h file. # # context.vardict Dictionary holding variables used for the tests and # stores results from the tests, used for the build # commands. # Normally contains "CC", "LIBS", "CPPFLAGS", etc. # # context.havedict Dictionary holding results from the tests that are to # be used inside a program. # Names often start with "HAVE_". These are zero # (feature not present) or one (feature present). Other # variables may have any value, e.g., "PERLVERSION" can # be a number and "SYSTEMNAME" a string. # import re from types import IntType # # PUBLIC VARIABLES # LogInputFiles = 1 # Set that to log the input files in case of a failed test LogErrorMessages = 1 # Set that to log Conftest-generated error messages # # PUBLIC FUNCTIONS # # Generic remarks: # - When a language is specified which is not supported the test fails. The # message is a bit different, because not all the arguments for the normal # message are available yet (chicken-egg problem). def CheckBuilder(context, text = None, language = None): """ Configure check to see if the compiler works. Note that this uses the current value of compiler and linker flags, make sure $CFLAGS, $CPPFLAGS and $LIBS are set correctly. "language" should be "C" or "C++" and is used to select the compiler. Default is "C". "text" may be used to specify the code to be build. Returns an empty string for success, an error message for failure. """ lang, suffix, msg = _lang2suffix(language) if msg: context.Display("%s\n" % msg) return msg if not text: text = """ int main() { return 0; } """ context.Display("Checking if building a %s file works... " % lang) ret = context.BuildProg(text, suffix) _YesNoResult(context, ret, None, text) return ret def CheckCC(context): """ Configure check for a working C compiler. This checks whether the C compiler, as defined in the $CC construction variable, can compile a C source file. It uses the current $CCCOM value too, so that it can test against non working flags. """ context.Display("Checking whether the C compiler works") text = """ int main() { return 0; } """ ret = _check_empty_program(context, 'CC', text, 'C') _YesNoResult(context, ret, None, text) return ret def CheckSHCC(context): """ Configure check for a working shared C compiler. This checks whether the C compiler, as defined in the $SHCC construction variable, can compile a C source file. It uses the current $SHCCCOM value too, so that it can test against non working flags. """ context.Display("Checking whether the (shared) C compiler works") text = """ int foo() { return 0; } """ ret = _check_empty_program(context, 'SHCC', text, 'C', use_shared = True) _YesNoResult(context, ret, None, text) return ret def CheckCXX(context): """ Configure check for a working CXX compiler. This checks whether the CXX compiler, as defined in the $CXX construction variable, can compile a CXX source file. It uses the current $CXXCOM value too, so that it can test against non working flags. """ context.Display("Checking whether the C++ compiler works") text = """ int main() { return 0; } """ ret = _check_empty_program(context, 'CXX', text, 'C++') _YesNoResult(context, ret, None, text) return ret def CheckSHCXX(context): """ Configure check for a working shared CXX compiler. This checks whether the CXX compiler, as defined in the $SHCXX construction variable, can compile a CXX source file. It uses the current $SHCXXCOM value too, so that it can test against non working flags. """ context.Display("Checking whether the (shared) C++ compiler works") text = """ int main() { return 0; } """ ret = _check_empty_program(context, 'SHCXX', text, 'C++', use_shared = True) _YesNoResult(context, ret, None, text) return ret def _check_empty_program(context, comp, text, language, use_shared = False): """Return 0 on success, 1 otherwise.""" if comp not in context.env or not context.env[comp]: # The compiler construction variable is not set or empty return 1 lang, suffix, msg = _lang2suffix(language) if msg: return 1 if use_shared: return context.CompileSharedObject(text, suffix) else: return context.CompileProg(text, suffix) def CheckFunc(context, function_name, header = None, language = None): """ Configure check for a function "function_name". "language" should be "C" or "C++" and is used to select the compiler. Default is "C". Optional "header" can be defined to define a function prototype, include a header file or anything else that comes before main(). Sets HAVE_function_name in context.havedict according to the result. Note that this uses the current value of compiler and linker flags, make sure $CFLAGS, $CPPFLAGS and $LIBS are set correctly. Returns an empty string for success, an error message for failure. """ # Remarks from autoconf: # - Don't include because on OSF/1 3.0 it includes # which includes which contains a prototype for select. # Similarly for bzero. # - assert.h is included to define __stub macros and hopefully few # prototypes, which can conflict with char $1(); below. # - Override any gcc2 internal prototype to avoid an error. # - We use char for the function declaration because int might match the # return type of a gcc2 builtin and then its argument prototype would # still apply. # - The GNU C library defines this for functions which it implements to # always fail with ENOSYS. Some functions are actually named something # starting with __ and the normal name is an alias. if context.headerfilename: includetext = '#include "%s"' % context.headerfilename else: includetext = '' if not header: header = """ #ifdef __cplusplus extern "C" #endif char %s();""" % function_name lang, suffix, msg = _lang2suffix(language) if msg: context.Display("Cannot check for %s(): %s\n" % (function_name, msg)) return msg text = """ %(include)s #include %(hdr)s int main() { #if defined (__stub_%(name)s) || defined (__stub___%(name)s) fail fail fail #else %(name)s(); #endif return 0; } """ % { 'name': function_name, 'include': includetext, 'hdr': header } context.Display("Checking for %s function %s()... " % (lang, function_name)) ret = context.BuildProg(text, suffix) _YesNoResult(context, ret, "HAVE_" + function_name, text, "Define to 1 if the system has the function `%s'." %\ function_name) return ret def CheckHeader(context, header_name, header = None, language = None, include_quotes = None): """ Configure check for a C or C++ header file "header_name". Optional "header" can be defined to do something before including the header file (unusual, supported for consistency). "language" should be "C" or "C++" and is used to select the compiler. Default is "C". Sets HAVE_header_name in context.havedict according to the result. Note that this uses the current value of compiler and linker flags, make sure $CFLAGS and $CPPFLAGS are set correctly. Returns an empty string for success, an error message for failure. """ # Why compile the program instead of just running the preprocessor? # It is possible that the header file exists, but actually using it may # fail (e.g., because it depends on other header files). Thus this test is # more strict. It may require using the "header" argument. # # Use <> by default, because the check is normally used for system header # files. SCons passes '""' to overrule this. # Include "confdefs.h" first, so that the header can use HAVE_HEADER_H. if context.headerfilename: includetext = '#include "%s"\n' % context.headerfilename else: includetext = '' if not header: header = "" lang, suffix, msg = _lang2suffix(language) if msg: context.Display("Cannot check for header file %s: %s\n" % (header_name, msg)) return msg if not include_quotes: include_quotes = "<>" text = "%s%s\n#include %s%s%s\n\n" % (includetext, header, include_quotes[0], header_name, include_quotes[1]) context.Display("Checking for %s header file %s... " % (lang, header_name)) ret = context.CompileProg(text, suffix) _YesNoResult(context, ret, "HAVE_" + header_name, text, "Define to 1 if you have the <%s> header file." % header_name) return ret def CheckType(context, type_name, fallback = None, header = None, language = None): """ Configure check for a C or C++ type "type_name". Optional "header" can be defined to include a header file. "language" should be "C" or "C++" and is used to select the compiler. Default is "C". Sets HAVE_type_name in context.havedict according to the result. Note that this uses the current value of compiler and linker flags, make sure $CFLAGS, $CPPFLAGS and $LIBS are set correctly. Returns an empty string for success, an error message for failure. """ # Include "confdefs.h" first, so that the header can use HAVE_HEADER_H. if context.headerfilename: includetext = '#include "%s"' % context.headerfilename else: includetext = '' if not header: header = "" lang, suffix, msg = _lang2suffix(language) if msg: context.Display("Cannot check for %s type: %s\n" % (type_name, msg)) return msg # Remarks from autoconf about this test: # - Grepping for the type in include files is not reliable (grep isn't # portable anyway). # - Using "TYPE my_var;" doesn't work for const qualified types in C++. # Adding an initializer is not valid for some C++ classes. # - Using the type as parameter to a function either fails for K&$ C or for # C++. # - Using "TYPE *my_var;" is valid in C for some types that are not # declared (struct something). # - Using "sizeof(TYPE)" is valid when TYPE is actually a variable. # - Using the previous two together works reliably. text = """ %(include)s %(header)s int main() { if ((%(name)s *) 0) return 0; if (sizeof (%(name)s)) return 0; } """ % { 'include': includetext, 'header': header, 'name': type_name } context.Display("Checking for %s type %s... " % (lang, type_name)) ret = context.BuildProg(text, suffix) _YesNoResult(context, ret, "HAVE_" + type_name, text, "Define to 1 if the system has the type `%s'." % type_name) if ret and fallback and context.headerfilename: f = open(context.headerfilename, "a") f.write("typedef %s %s;\n" % (fallback, type_name)) f.close() return ret def CheckTypeSize(context, type_name, header = None, language = None, expect = None): """This check can be used to get the size of a given type, or to check whether the type is of expected size. Arguments: - type : str the type to check - includes : sequence list of headers to include in the test code before testing the type - language : str 'C' or 'C++' - expect : int if given, will test wether the type has the given number of bytes. If not given, will automatically find the size. Returns: status : int 0 if the check failed, or the found size of the type if the check succeeded.""" # Include "confdefs.h" first, so that the header can use HAVE_HEADER_H. if context.headerfilename: includetext = '#include "%s"' % context.headerfilename else: includetext = '' if not header: header = "" lang, suffix, msg = _lang2suffix(language) if msg: context.Display("Cannot check for %s type: %s\n" % (type_name, msg)) return msg src = includetext + header if not expect is None: # Only check if the given size is the right one context.Display('Checking %s is %d bytes... ' % (type_name, expect)) # test code taken from autoconf: this is a pretty clever hack to find that # a type is of a given size using only compilation. This speeds things up # quite a bit compared to straightforward code using TryRun src = src + r""" typedef %s scons_check_type; int main() { static int test_array[1 - 2 * !(((long int) (sizeof(scons_check_type))) == %d)]; test_array[0] = 0; return 0; } """ st = context.CompileProg(src % (type_name, expect), suffix) if not st: context.Display("yes\n") _Have(context, "SIZEOF_%s" % type_name, expect, "The size of `%s', as computed by sizeof." % type_name) return expect else: context.Display("no\n") _LogFailed(context, src, st) return 0 else: # Only check if the given size is the right one context.Message('Checking size of %s ... ' % type_name) # We have to be careful with the program we wish to test here since # compilation will be attempted using the current environment's flags. # So make sure that the program will compile without any warning. For # example using: 'int main(int argc, char** argv)' will fail with the # '-Wall -Werror' flags since the variables argc and argv would not be # used in the program... # src = src + """ #include #include int main() { printf("%d", (int)sizeof(""" + type_name + """)); return 0; } """ st, out = context.RunProg(src, suffix) try: size = int(out) except ValueError: # If cannot convert output of test prog to an integer (the size), # something went wront, so just fail st = 1 size = 0 if not st: context.Display("yes\n") _Have(context, "SIZEOF_%s" % type_name, size, "The size of `%s', as computed by sizeof." % type_name) return size else: context.Display("no\n") _LogFailed(context, src, st) return 0 return 0 def CheckDeclaration(context, symbol, includes = None, language = None): """Checks whether symbol is declared. Use the same test as autoconf, that is test whether the symbol is defined as a macro or can be used as an r-value. Arguments: symbol : str the symbol to check includes : str Optional "header" can be defined to include a header file. language : str only C and C++ supported. Returns: status : bool True if the check failed, False if succeeded.""" # Include "confdefs.h" first, so that the header can use HAVE_HEADER_H. if context.headerfilename: includetext = '#include "%s"' % context.headerfilename else: includetext = '' if not includes: includes = "" lang, suffix, msg = _lang2suffix(language) if msg: context.Display("Cannot check for declaration %s: %s\n" % (symbol, msg)) return msg src = includetext + includes context.Display('Checking whether %s is declared... ' % symbol) src = src + r""" int main() { #ifndef %s (void) %s; #endif ; return 0; } """ % (symbol, symbol) st = context.CompileProg(src, suffix) _YesNoResult(context, st, "HAVE_DECL_" + symbol, src, "Set to 1 if %s is defined." % symbol) return st def CheckLib(context, libs, func_name = None, header = None, extra_libs = None, call = None, language = None, autoadd = 1, append = True): """ Configure check for a C or C++ libraries "libs". Searches through the list of libraries, until one is found where the test succeeds. Tests if "func_name" or "call" exists in the library. Note: if it exists in another library the test succeeds anyway! Optional "header" can be defined to include a header file. If not given a default prototype for "func_name" is added. Optional "extra_libs" is a list of library names to be added after "lib_name" in the build command. To be used for libraries that "lib_name" depends on. Optional "call" replaces the call to "func_name" in the test code. It must consist of complete C statements, including a trailing ";". Both "func_name" and "call" arguments are optional, and in that case, just linking against the libs is tested. "language" should be "C" or "C++" and is used to select the compiler. Default is "C". Note that this uses the current value of compiler and linker flags, make sure $CFLAGS, $CPPFLAGS and $LIBS are set correctly. Returns an empty string for success, an error message for failure. """ # Include "confdefs.h" first, so that the header can use HAVE_HEADER_H. if context.headerfilename: includetext = '#include "%s"' % context.headerfilename else: includetext = '' if not header: header = "" text = """ %s %s""" % (includetext, header) # Add a function declaration if needed. if func_name and func_name != "main": if not header: text = text + """ #ifdef __cplusplus extern "C" #endif char %s(); """ % func_name # The actual test code. if not call: call = "%s();" % func_name # if no function to test, leave main() blank text = text + """ int main() { %s return 0; } """ % (call or "") if call: i = call.find("\n") if i > 0: calltext = call[:i] + ".." elif call[-1] == ';': calltext = call[:-1] else: calltext = call for lib_name in libs: lang, suffix, msg = _lang2suffix(language) if msg: context.Display("Cannot check for library %s: %s\n" % (lib_name, msg)) return msg # if a function was specified to run in main(), say it if call: context.Display("Checking for %s in %s library %s... " % (calltext, lang, lib_name)) # otherwise, just say the name of library and language else: context.Display("Checking for %s library %s... " % (lang, lib_name)) if lib_name: l = [ lib_name ] if extra_libs: l.extend(extra_libs) if append: oldLIBS = context.AppendLIBS(l) else: oldLIBS = context.PrependLIBS(l) sym = "HAVE_LIB" + lib_name else: oldLIBS = -1 sym = None ret = context.BuildProg(text, suffix) _YesNoResult(context, ret, sym, text, "Define to 1 if you have the `%s' library." % lib_name) if oldLIBS != -1 and (ret or not autoadd): context.SetLIBS(oldLIBS) if not ret: return ret return ret # # END OF PUBLIC FUNCTIONS # def _YesNoResult(context, ret, key, text, comment = None): """ Handle the result of a test with a "yes" or "no" result. "ret" is the return value: empty if OK, error message when not. "key" is the name of the symbol to be defined (HAVE_foo). "text" is the source code of the program used for testing. "comment" is the C comment to add above the line defining the symbol (the comment is automatically put inside a /* */). If None, no comment is added. """ if key: _Have(context, key, not ret, comment) if ret: context.Display("no\n") _LogFailed(context, text, ret) else: context.Display("yes\n") def _Have(context, key, have, comment = None): """ Store result of a test in context.havedict and context.headerfilename. "key" is a "HAVE_abc" name. It is turned into all CAPITALS and non- alphanumerics are replaced by an underscore. The value of "have" can be: 1 - Feature is defined, add "#define key". 0 - Feature is not defined, add "/* #undef key */". Adding "undef" is what autoconf does. Not useful for the compiler, but it shows that the test was done. number - Feature is defined to this number "#define key have". Doesn't work for 0 or 1, use a string then. string - Feature is defined to this string "#define key have". Give "have" as is should appear in the header file, include quotes when desired and escape special characters! """ key_up = key.upper() key_up = re.sub('[^A-Z0-9_]', '_', key_up) context.havedict[key_up] = have if have == 1: line = "#define %s 1\n" % key_up elif have == 0: line = "/* #undef %s */\n" % key_up elif isinstance(have, IntType): line = "#define %s %d\n" % (key_up, have) else: line = "#define %s %s\n" % (key_up, str(have)) if comment is not None: lines = "\n/* %s */\n" % comment + line else: lines = "\n" + line if context.headerfilename: f = open(context.headerfilename, "a") f.write(lines) f.close() elif hasattr(context,'config_h'): context.config_h = context.config_h + lines def _LogFailed(context, text, msg): """ Write to the log about a failed program. Add line numbers, so that error messages can be understood. """ if LogInputFiles: context.Log("Failed program was:\n") lines = text.split('\n') if len(lines) and lines[-1] == '': lines = lines[:-1] # remove trailing empty line n = 1 for line in lines: context.Log("%d: %s\n" % (n, line)) n = n + 1 if LogErrorMessages: context.Log("Error message: %s\n" % msg) def _lang2suffix(lang): """ Convert a language name to a suffix. When "lang" is empty or None C is assumed. Returns a tuple (lang, suffix, None) when it works. For an unrecognized language returns (None, None, msg). Where: lang = the unified language name suffix = the suffix, including the leading dot msg = an error message """ if not lang or lang in ["C", "c"]: return ("C", ".c", None) if lang in ["c++", "C++", "cpp", "CXX", "cxx"]: return ("C++", ".cpp", None) return None, None, "Unsupported language: %s" % lang # vim: set sw=4 et sts=4 tw=79 fo+=l: # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/MemoizeTests.py0000644000175000017500000001105112114661557022303 0ustar dktrkranzdktrkranz# # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/MemoizeTests.py 2013/03/03 09:48:35 garyo" import sys import unittest import SCons.Memoize class FakeObject(object): __metaclass__ = SCons.Memoize.Memoized_Metaclass memoizer_counters = [] def __init__(self): self._memo = {} def _dict_key(self, argument): return argument memoizer_counters.append(SCons.Memoize.CountDict('dict', _dict_key)) def dict(self, argument): memo_key = argument try: memo_dict = self._memo['dict'] except KeyError: memo_dict = {} self._memo['dict'] = memo_dict else: try: return memo_dict[memo_key] except KeyError: pass result = self.compute_dict(argument) memo_dict[memo_key] = result return result memoizer_counters.append(SCons.Memoize.CountValue('value')) def value(self): try: return self._memo['value'] except KeyError: pass result = self.compute_value() self._memo['value'] = result return result def get_memoizer_counter(self, name): for mc in self.memoizer_counters: if mc.method_name == name: return mc return None class Returner(object): def __init__(self, result): self.result = result self.calls = 0 def __call__(self, *args, **kw): self.calls = self.calls + 1 return self.result class CountDictTestCase(unittest.TestCase): def test___call__(self): """Calling a Memoized dict method """ obj = FakeObject() called = [] fd1 = Returner(1) fd2 = Returner(2) obj.compute_dict = fd1 r = obj.dict(11) assert r == 1, r obj.compute_dict = fd2 r = obj.dict(12) assert r == 2, r r = obj.dict(11) assert r == 1, r obj.compute_dict = fd1 r = obj.dict(11) assert r == 1, r r = obj.dict(12) assert r == 2, r assert fd1.calls == 1, fd1.calls assert fd2.calls == 1, fd2.calls c = obj.get_memoizer_counter('dict') assert c.hit == 3, c.hit assert c.miss == 2, c.miss class CountValueTestCase(unittest.TestCase): def test___call__(self): """Calling a Memoized value method """ obj = FakeObject() called = [] fv1 = Returner(1) fv2 = Returner(2) obj.compute_value = fv1 r = obj.value() assert r == 1, r r = obj.value() assert r == 1, r obj.compute_value = fv2 r = obj.value() assert r == 1, r r = obj.value() assert r == 1, r assert fv1.calls == 1, fv1.calls assert fv2.calls == 0, fv2.calls c = obj.get_memoizer_counter('value') assert c.hit == 3, c.hit assert c.miss == 1, c.miss if __name__ == "__main__": suite = unittest.TestSuite() tclasses = [ CountDictTestCase, CountValueTestCase, ] for tclass in tclasses: names = unittest.getTestCaseNames(tclass, 'test_') suite.addTests(list(map(tclass, names))) if not unittest.TextTestRunner().run(suite).wasSuccessful(): sys.exit(1) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Debug.py0000644000175000017500000001517212114661557020711 0ustar dktrkranzdktrkranz"""SCons.Debug Code for debugging SCons internal things. Shouldn't be needed by most users. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Debug.py 2013/03/03 09:48:35 garyo" import os import sys import time import weakref tracked_classes = {} def logInstanceCreation(instance, name=None): if name is None: name = instance.__class__.__name__ if name not in tracked_classes: tracked_classes[name] = [] tracked_classes[name].append(weakref.ref(instance)) def string_to_classes(s): if s == '*': return sorted(tracked_classes.keys()) else: return s.split() def fetchLoggedInstances(classes="*"): classnames = string_to_classes(classes) return [(cn, len(tracked_classes[cn])) for cn in classnames] def countLoggedInstances(classes, file=sys.stdout): for classname in string_to_classes(classes): file.write("%s: %d\n" % (classname, len(tracked_classes[classname]))) def listLoggedInstances(classes, file=sys.stdout): for classname in string_to_classes(classes): file.write('\n%s:\n' % classname) for ref in tracked_classes[classname]: obj = ref() if obj is not None: file.write(' %s\n' % repr(obj)) def dumpLoggedInstances(classes, file=sys.stdout): for classname in string_to_classes(classes): file.write('\n%s:\n' % classname) for ref in tracked_classes[classname]: obj = ref() if obj is not None: file.write(' %s:\n' % obj) for key, value in obj.__dict__.items(): file.write(' %20s : %s\n' % (key, value)) if sys.platform[:5] == "linux": # Linux doesn't actually support memory usage stats from getrusage(). def memory(): mstr = open('/proc/self/stat').read() mstr = mstr.split()[22] return int(mstr) elif sys.platform[:6] == 'darwin': #TODO really get memory stats for OS X def memory(): return 0 else: try: import resource except ImportError: try: import win32process import win32api except ImportError: def memory(): return 0 else: def memory(): process_handle = win32api.GetCurrentProcess() memory_info = win32process.GetProcessMemoryInfo( process_handle ) return memory_info['PeakWorkingSetSize'] else: def memory(): res = resource.getrusage(resource.RUSAGE_SELF) return res[4] # returns caller's stack def caller_stack(*backlist): import traceback if not backlist: backlist = [0] result = [] for back in backlist: tb = traceback.extract_stack(limit=3+back) key = tb[0][:3] result.append('%s:%d(%s)' % func_shorten(key)) return result caller_bases = {} caller_dicts = {} # trace a caller's stack def caller_trace(back=0): import traceback tb = traceback.extract_stack(limit=3+back) tb.reverse() callee = tb[1][:3] caller_bases[callee] = caller_bases.get(callee, 0) + 1 for caller in tb[2:]: caller = callee + caller[:3] try: entry = caller_dicts[callee] except KeyError: caller_dicts[callee] = entry = {} entry[caller] = entry.get(caller, 0) + 1 callee = caller # print a single caller and its callers, if any def _dump_one_caller(key, file, level=0): leader = ' '*level for v,c in sorted([(-v,c) for c,v in caller_dicts[key].items()]): file.write("%s %6d %s:%d(%s)\n" % ((leader,-v) + func_shorten(c[-3:]))) if c in caller_dicts: _dump_one_caller(c, file, level+1) # print each call tree def dump_caller_counts(file=sys.stdout): for k in sorted(caller_bases.keys()): file.write("Callers of %s:%d(%s), %d calls:\n" % (func_shorten(k) + (caller_bases[k],))) _dump_one_caller(k, file) shorten_list = [ ( '/scons/SCons/', 1), ( '/src/engine/SCons/', 1), ( '/usr/lib/python', 0), ] if os.sep != '/': shorten_list = [(t[0].replace('/', os.sep), t[1]) for t in shorten_list] def func_shorten(func_tuple): f = func_tuple[0] for t in shorten_list: i = f.find(t[0]) if i >= 0: if t[1]: i = i + len(t[0]) return (f[i:],)+func_tuple[1:] return func_tuple TraceFP = {} if sys.platform == 'win32': TraceDefault = 'con' else: TraceDefault = '/dev/tty' TimeStampDefault = None StartTime = time.time() PreviousTime = StartTime def Trace(msg, file=None, mode='w', tstamp=None): """Write a trace message to a file. Whenever a file is specified, it becomes the default for the next call to Trace().""" global TraceDefault global TimeStampDefault global PreviousTime if file is None: file = TraceDefault else: TraceDefault = file if tstamp is None: tstamp = TimeStampDefault else: TimeStampDefault = tstamp try: fp = TraceFP[file] except KeyError: try: fp = TraceFP[file] = open(file, mode) except TypeError: # Assume we were passed an open file pointer. fp = file if tstamp: now = time.time() fp.write('%8.4f %8.4f: ' % (now - StartTime, now - PreviousTime)) PreviousTime = now fp.write(msg) fp.flush() # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Defaults.xml0000644000175000017500000003140612114661557021600 0ustar dktrkranzdktrkranz A function used to produce variables like &cv-_CPPINCFLAGS;. It takes four or five arguments: a prefix to concatenate onto each element, a list of elements, a suffix to concatenate onto each element, an environment for variable interpolation, and an optional function that will be called to transform the list before concatenation. env['_CPPINCFLAGS'] = '$( ${_concat(INCPREFIX, CPPPATH, INCSUFFIX, __env__, RDirs)} $)', The name of the directory in which Configure context test files are written. The default is .sconf_temp in the top-level directory containing the SConstruct file. The name of the Configure context log file. The default is config.log in the top-level directory containing the SConstruct file. An automatically-generated construction variable containing the C preprocessor command-line options to define values. The value of &cv-_CPPDEFFLAGS; is created by appending &cv-CPPDEFPREFIX; and &cv-CPPDEFSUFFIX; to the beginning and end of each definition in &cv-CPPDEFINES;. A platform independent specification of C preprocessor definitions. The definitions will be added to command lines through the automatically-generated &cv-_CPPDEFFLAGS; construction variable (see above), which is constructed according to the type of value of &cv-CPPDEFINES;: If &cv-CPPDEFINES; is a string, the values of the &cv-CPPDEFPREFIX; and &cv-CPPDEFSUFFIX; construction variables will be added to the beginning and end. # Will add -Dxyz to POSIX compiler command lines, # and /Dxyz to Microsoft Visual C++ command lines. env = Environment(CPPDEFINES='xyz') If &cv-CPPDEFINES; is a list, the values of the &cv-CPPDEFPREFIX; and &cv-CPPDEFSUFFIX; construction variables will be appended to the beginning and end of each element in the list. If any element is a list or tuple, then the first item is the name being defined and the second item is its value: # Will add -DB=2 -DA to POSIX compiler command lines, # and /DB=2 /DA to Microsoft Visual C++ command lines. env = Environment(CPPDEFINES=[('B', 2), 'A']) If &cv-CPPDEFINES; is a dictionary, the values of the &cv-CPPDEFPREFIX; and &cv-CPPDEFSUFFIX; construction variables will be appended to the beginning and end of each item from the dictionary. The key of each dictionary item is a name being defined to the dictionary item's corresponding value; if the value is None, then the name is defined without an explicit value. Note that the resulting flags are sorted by keyword to ensure that the order of the options on the command line is consistent each time &scons; is run. # Will add -DA -DB=2 to POSIX compiler command lines, # and /DA /DB=2 to Microsoft Visual C++ command lines. env = Environment(CPPDEFINES={'B':2, 'A':None}) The prefix used to specify preprocessor definitions on the C compiler command line. This will be appended to the beginning of each definition in the &cv-CPPDEFINES; construction variable when the &cv-_CPPDEFFLAGS; variable is automatically generated. The suffix used to specify preprocessor definitions on the C compiler command line. This will be appended to the end of each definition in the &cv-CPPDEFINES; construction variable when the &cv-_CPPDEFFLAGS; variable is automatically generated. An automatically-generated construction variable containing the C preprocessor command-line options for specifying directories to be searched for include files. The value of &cv-_CPPINCFLAGS; is created by appending &cv-INCPREFIX; and &cv-INCSUFFIX; to the beginning and end of each directory in &cv-CPPPATH;. The list of directories that the C preprocessor will search for include directories. The C/C++ implicit dependency scanner will search these directories for include files. Don't explicitly put include directory arguments in CCFLAGS or CXXFLAGS because the result will be non-portable and the directories will not be searched by the dependency scanner. Note: directory names in CPPPATH will be looked-up relative to the SConscript directory when they are used in a command. To force &scons; to look-up a directory relative to the root of the source tree use #: env = Environment(CPPPATH='#/include') The directory look-up can also be forced using the &Dir;() function: include = Dir('include') env = Environment(CPPPATH=include) The directory list will be added to command lines through the automatically-generated &cv-_CPPINCFLAGS; construction variable, which is constructed by appending the values of the &cv-INCPREFIX; and &cv-INCSUFFIX; construction variables to the beginning and end of each directory in &cv-CPPPATH;. Any command lines you define that need the CPPPATH directory list should include &cv-_CPPINCFLAGS;: env = Environment(CCCOM="my_compiler $_CPPINCFLAGS -c -o $TARGET $SOURCE") A function that converts a string into a Dir instance relative to the target being built. A function that converts a list of strings into a list of Dir instances relative to the target being built. The list of suffixes of files that will be scanned for imported D package files. The default list is: ['.d'] A function that converts a string into a File instance relative to the target being built. The list of suffixes of files that will be scanned for IDL implicit dependencies (#include or import lines). The default list is: [".idl", ".IDL"] The prefix used to specify an include directory on the C compiler command line. This will be appended to the beginning of each directory in the &cv-CPPPATH; and &cv-FORTRANPATH; construction variables when the &cv-_CPPINCFLAGS; and &cv-_FORTRANINCFLAGS; variables are automatically generated. The suffix used to specify an include directory on the C compiler command line. This will be appended to the end of each directory in the &cv-CPPPATH; and &cv-FORTRANPATH; construction variables when the &cv-_CPPINCFLAGS; and &cv-_FORTRANINCFLAGS; variables are automatically generated. A function to be called to install a file into a destination file name. The default function copies the file into the destination (and sets the destination file's mode and permission bits to match the source file's). The function takes the following arguments: def install(dest, source, env): dest is the path name of the destination file. source is the path name of the source file. env is the construction environment (a dictionary of construction values) in force for this file installation. The string displayed when a file is installed into a destination file name. The default is: Install file: "$SOURCE" as "$TARGET" The list of suffixes of files that will be scanned for LaTeX implicit dependencies (\include or \import files). The default list is: [".tex", ".ltx", ".latex"] An automatically-generated construction variable containing the linker command-line options for specifying directories to be searched for library. The value of &cv-_LIBDIRFLAGS; is created by appending &cv-LIBDIRPREFIX; and &cv-LIBDIRSUFFIX; to the beginning and end of each directory in &cv-LIBPATH;. The prefix used to specify a library directory on the linker command line. This will be appended to the beginning of each directory in the &cv-LIBPATH; construction variable when the &cv-_LIBDIRFLAGS; variable is automatically generated. The suffix used to specify a library directory on the linker command line. This will be appended to the end of each directory in the &cv-LIBPATH; construction variable when the &cv-_LIBDIRFLAGS; variable is automatically generated. An automatically-generated construction variable containing the linker command-line options for specifying libraries to be linked with the resulting target. The value of &cv-_LIBFLAGS; is created by appending &cv-LIBLINKPREFIX; and &cv-LIBLINKSUFFIX; to the beginning and end of each filename in &cv-LIBS;. The prefix used to specify a library to link on the linker command line. This will be appended to the beginning of each library in the &cv-LIBS; construction variable when the &cv-_LIBFLAGS; variable is automatically generated. The suffix used to specify a library to link on the linker command line. This will be appended to the end of each library in the &cv-LIBS; construction variable when the &cv-_LIBFLAGS; variable is automatically generated. The list of directories that will be searched for libraries. The implicit dependency scanner will search these directories for include files. Don't explicitly put include directory arguments in &cv-LINKFLAGS; or &cv-SHLINKFLAGS; because the result will be non-portable and the directories will not be searched by the dependency scanner. Note: directory names in LIBPATH will be looked-up relative to the SConscript directory when they are used in a command. To force &scons; to look-up a directory relative to the root of the source tree use #: env = Environment(LIBPATH='#/libs') The directory look-up can also be forced using the &Dir;() function: libs = Dir('libs') env = Environment(LIBPATH=libs) The directory list will be added to command lines through the automatically-generated &cv-_LIBDIRFLAGS; construction variable, which is constructed by appending the values of the &cv-LIBDIRPREFIX; and &cv-LIBDIRSUFFIX; construction variables to the beginning and end of each directory in &cv-LIBPATH;. Any command lines you define that need the LIBPATH directory list should include &cv-_LIBDIRFLAGS;: env = Environment(LINKCOM="my_linker $_LIBDIRFLAGS $_LIBFLAGS -o $TARGET $SOURCE") A list of one or more libraries that will be linked with any executable programs created by this environment. The library list will be added to command lines through the automatically-generated &cv-_LIBFLAGS; construction variable, which is constructed by appending the values of the &cv-LIBLINKPREFIX; and &cv-LIBLINKSUFFIX; construction variables to the beginning and end of each filename in &cv-LIBS;. Any command lines you define that need the LIBS library list should include &cv-_LIBFLAGS;: env = Environment(LINKCOM="my_linker $_LIBDIRFLAGS $_LIBFLAGS -o $TARGET $SOURCE") If you add a File object to the &cv-LIBS; list, the name of that file will be added to &cv-_LIBFLAGS;, and thus the link line, as is, without &cv-LIBLINKPREFIX; or &cv-LIBLINKSUFFIX;. For example: env.Append(LIBS=File('/tmp/mylib.so')) In all cases, scons will add dependencies from the executable program to all the libraries in this list. A function that converts a string into a list of Dir instances by searching the repositories. ([args]) Creates and returns a default construction environment object. This construction environment is used internally by SCons in order to execute many of the global functions in this list, and to fetch source files transparently from source code management systems. scons-doc-2.3.0/src/engine/SCons/PathList.py0000644000175000017500000002053012114661557021405 0ustar dktrkranzdktrkranz# # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/PathList.py 2013/03/03 09:48:35 garyo" __doc__ = """SCons.PathList A module for handling lists of directory paths (the sort of things that get set as CPPPATH, LIBPATH, etc.) with as much caching of data and efficiency as we can while still keeping the evaluation delayed so that we Do the Right Thing (almost) regardless of how the variable is specified. """ import os import SCons.Memoize import SCons.Node import SCons.Util # # Variables to specify the different types of entries in a PathList object: # TYPE_STRING_NO_SUBST = 0 # string with no '$' TYPE_STRING_SUBST = 1 # string containing '$' TYPE_OBJECT = 2 # other object def node_conv(obj): """ This is the "string conversion" routine that we have our substitutions use to return Nodes, not strings. This relies on the fact that an EntryProxy object has a get() method that returns the underlying Node that it wraps, which is a bit of architectural dependence that we might need to break or modify in the future in response to additional requirements. """ try: get = obj.get except AttributeError: if isinstance(obj, SCons.Node.Node) or SCons.Util.is_Sequence( obj ): result = obj else: result = str(obj) else: result = get() return result class _PathList(object): """ An actual PathList object. """ def __init__(self, pathlist): """ Initializes a PathList object, canonicalizing the input and pre-processing it for quicker substitution later. The stored representation of the PathList is a list of tuples containing (type, value), where the "type" is one of the TYPE_* variables defined above. We distinguish between: strings that contain no '$' and therefore need no delayed-evaluation string substitution (we expect that there will be many of these and that we therefore get a pretty big win from avoiding string substitution) strings that contain '$' and therefore need substitution (the hard case is things like '${TARGET.dir}/include', which require re-evaluation for every target + source) other objects (which may be something like an EntryProxy that needs a method called to return a Node) Pre-identifying the type of each element in the PathList up-front and storing the type in the list of tuples is intended to reduce the amount of calculation when we actually do the substitution over and over for each target. """ if SCons.Util.is_String(pathlist): pathlist = pathlist.split(os.pathsep) elif not SCons.Util.is_Sequence(pathlist): pathlist = [pathlist] pl = [] for p in pathlist: try: index = p.find('$') except (AttributeError, TypeError): type = TYPE_OBJECT else: if index == -1: type = TYPE_STRING_NO_SUBST else: type = TYPE_STRING_SUBST pl.append((type, p)) self.pathlist = tuple(pl) def __len__(self): return len(self.pathlist) def __getitem__(self, i): return self.pathlist[i] def subst_path(self, env, target, source): """ Performs construction variable substitution on a pre-digested PathList for a specific target and source. """ result = [] for type, value in self.pathlist: if type == TYPE_STRING_SUBST: value = env.subst(value, target=target, source=source, conv=node_conv) if SCons.Util.is_Sequence(value): result.extend(SCons.Util.flatten(value)) elif value: result.append(value) elif type == TYPE_OBJECT: value = node_conv(value) if value: result.append(value) elif value: result.append(value) return tuple(result) class PathListCache(object): """ A class to handle caching of PathList lookups. This class gets instantiated once and then deleted from the namespace, so it's used as a Singleton (although we don't enforce that in the usual Pythonic ways). We could have just made the cache a dictionary in the module namespace, but putting it in this class allows us to use the same Memoizer pattern that we use elsewhere to count cache hits and misses, which is very valuable. Lookup keys in the cache are computed by the _PathList_key() method. Cache lookup should be quick, so we don't spend cycles canonicalizing all forms of the same lookup key. For example, 'x:y' and ['x', 'y'] logically represent the same list, but we don't bother to split string representations and treat those two equivalently. (Note, however, that we do, treat lists and tuples the same.) The main type of duplication we're trying to catch will come from looking up the same path list from two different clones of the same construction environment. That is, given env2 = env1.Clone() both env1 and env2 will have the same CPPPATH value, and we can cheaply avoid re-parsing both values of CPPPATH by using the common value from this cache. """ if SCons.Memoize.use_memoizer: __metaclass__ = SCons.Memoize.Memoized_Metaclass memoizer_counters = [] def __init__(self): self._memo = {} def _PathList_key(self, pathlist): """ Returns the key for memoization of PathLists. Note that we want this to be pretty quick, so we don't completely canonicalize all forms of the same list. For example, 'dir1:$ROOT/dir2' and ['$ROOT/dir1', 'dir'] may logically represent the same list if you're executing from $ROOT, but we're not going to bother splitting strings into path elements, or massaging strings into Nodes, to identify that equivalence. We just want to eliminate obvious redundancy from the normal case of re-using exactly the same cloned value for a path. """ if SCons.Util.is_Sequence(pathlist): pathlist = tuple(SCons.Util.flatten(pathlist)) return pathlist memoizer_counters.append(SCons.Memoize.CountDict('PathList', _PathList_key)) def PathList(self, pathlist): """ Returns the cached _PathList object for the specified pathlist, creating and caching a new object as necessary. """ pathlist = self._PathList_key(pathlist) try: memo_dict = self._memo['PathList'] except KeyError: memo_dict = {} self._memo['PathList'] = memo_dict else: try: return memo_dict[pathlist] except KeyError: pass result = _PathList(pathlist) memo_dict[pathlist] = result return result PathList = PathListCache().PathList del PathListCache # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/SConfTests.py0000644000175000017500000007502712114661557021723 0ustar dktrkranzdktrkranz# # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/SConfTests.py 2013/03/03 09:48:35 garyo" import SCons.compat import io import os import re import sys from types import * import unittest import TestCmd sys.stdout = io.StringIO() if sys.platform == 'win32': existing_lib = "msvcrt" else: existing_lib = "m" class SConfTestCase(unittest.TestCase): def setUp(self): # we always want to start with a clean directory self.save_cwd = os.getcwd() self.test = TestCmd.TestCmd(workdir = '') os.chdir(self.test.workpath('')) def tearDown(self): self.test.cleanup() import SCons.SConsign SCons.SConsign.Reset() os.chdir(self.save_cwd) def _resetSConfState(self): # Ok, this is tricky, and i do not know, if everything is sane. # We try to reset scons' state (including all global variables) import SCons.SConsign SCons.SConsign.write() # simulate normal scons-finish for n in sys.modules.keys(): if n.split('.')[0] == 'SCons' and n[:12] != 'SCons.compat': m = sys.modules[n] if isinstance(m, ModuleType): # if this is really a scons module, clear its namespace del sys.modules[n] m.__dict__.clear() # we only use SCons.Environment and SCons.SConf for these tests. import SCons.Environment import SCons.SConf self.Environment = SCons.Environment self.SConf = SCons.SConf # and we need a new environment, cause references may point to # old modules (well, at least this is safe ...) self.scons_env = self.Environment.Environment() self.scons_env.AppendENVPath('PATH', os.environ['PATH']) # we want to do some autodetection here # this stuff works with # - cygwin on Windows (using cmd.exe, not bash) # - posix # - msvc on Windows (hopefully) if (not self.scons_env.Detect( self.scons_env.subst('$CXX') ) or not self.scons_env.Detect( self.scons_env.subst('$CC') ) or not self.scons_env.Detect( self.scons_env.subst('$LINK') )): raise Exception("This test needs an installed compiler!") if self.scons_env['CXX'] == 'g++': global existing_lib existing_lib = 'm' if sys.platform in ['cygwin', 'win32']: # On Windows, SCons.Platform.win32 redefines the builtin # file() and open() functions to close the file handles. # This interferes with the unittest.py infrastructure in # some way. Just sidestep the issue by restoring the # original builtin functions whenever we have to reset # all of our global state. import builtins import SCons.Platform.win32 builtins.file = SCons.Platform.win32._builtin_file builtins.open = SCons.Platform.win32._builtin_open def _baseTryXXX(self, TryFunc): # TryCompile and TryLink are much the same, so we can test them # in one method, we pass the function as a string ('TryCompile', # 'TryLink'), so we are aware of reloading modules. def checks(self, sconf, TryFuncString): TryFunc = self.SConf.SConfBase.__dict__[TryFuncString] res1 = TryFunc( sconf, "int main() { return 0; }\n", ".c" ) res2 = TryFunc( sconf, '#include "no_std_header.h"\nint main() {return 0; }\n', '.c' ) return (res1,res2) # 1. test initial behaviour (check ok / failed) self._resetSConfState() sconf = self.SConf.SConf(self.scons_env, conf_dir=self.test.workpath('config.tests'), log_file=self.test.workpath('config.log')) try: res = checks( self, sconf, TryFunc ) assert res[0] and not res[1], res finally: sconf.Finish() # 2.1 test the error caching mechanism (no dependencies have changed) self._resetSConfState() sconf = self.SConf.SConf(self.scons_env, conf_dir=self.test.workpath('config.tests'), log_file=self.test.workpath('config.log')) try: res = checks( self, sconf, TryFunc ) assert res[0] and not res[1], res finally: sconf.Finish() # we should have exactly one one error cached log = self.test.read( self.test.workpath('config.log') ) expr = re.compile( ".*failed in a previous run and all", re.DOTALL ) firstOcc = expr.match( log ) assert firstOcc is not None, log secondOcc = expr.match( log, firstOcc.end(0) ) assert secondOcc is None, log # 2.2 test the error caching mechanism (dependencies have changed) self._resetSConfState() sconf = self.SConf.SConf(self.scons_env, conf_dir=self.test.workpath('config.tests'), log_file=self.test.workpath('config.log')) no_std_header_h = self.test.workpath('config.tests', 'no_std_header.h') test_h = self.test.write( no_std_header_h, "/* we are changing a dependency now */\n" ); try: res = checks( self, sconf, TryFunc ) log = self.test.read( self.test.workpath('config.log') ) assert res[0] and res[1], res finally: sconf.Finish() def test_TryBuild(self): """Test SConf.TryBuild """ # 1 test that we can try a builder that returns a list of nodes self._resetSConfState() sconf = self.SConf.SConf(self.scons_env, conf_dir=self.test.workpath('config.tests'), log_file=self.test.workpath('config.log')) import SCons.Builder class MyBuilder(SCons.Builder.BuilderBase): def __init__(self): self.prefix = '' self.suffix = '' def __call__(self, env, target, source): class MyNode(object): def __init__(self, name): self.name = name self.state = None self.waiting_parents = set() self.side_effects = [] self.builder = None self.prerequisites = [] def disambiguate(self): return self def has_builder(self): return 1 def add_pre_action(self, *actions): pass def add_post_action(self, *actions): pass def children(self): return [] def get_state(self): return self.state def set_state(self, state): self.state = state def alter_targets(self): return [], None def depends_on(self, nodes): return None def postprocess(self): pass def clear(self): pass def is_up_to_date(self): return None def prepare(self): pass def push_to_cache(self): pass def retrieve_from_cache(self): return 0 def build(self, **kw): return def built(self): pass def get_stored_info(self): pass def do_not_store_info(self): pass def get_executor(self): class Executor(object): def __init__(self, targets): self.targets = targets def get_all_targets(self): return self.targets return Executor([self]) return [MyNode('n1'), MyNode('n2')] try: self.scons_env.Append(BUILDERS = {'SConfActionBuilder' : MyBuilder()}) sconf.TryBuild(self.scons_env.SConfActionBuilder) finally: sconf.Finish() def test_TryCompile(self): """Test SConf.TryCompile """ self._baseTryXXX( "TryCompile" ) #self.SConf.SConf.TryCompile ) def test_TryLink(self): """Test SConf.TryLink """ self._baseTryXXX( "TryLink" ) #self.SConf.SConf.TryLink ) def test_TryRun(self): """Test SConf.TryRun """ def checks(sconf): prog = """ #include int main() { printf( "Hello" ); return 0; } """ res1 = sconf.TryRun( prog, ".c" ) res2 = sconf.TryRun( "not a c program\n", ".c" ) return (res1, res2) self._resetSConfState() sconf = self.SConf.SConf(self.scons_env, conf_dir=self.test.workpath('config.tests'), log_file=self.test.workpath('config.log')) try: res = checks(sconf) assert res[0][0] and res[0][1] == "Hello", res assert not res[1][0] and res[1][1] == "", res finally: sconf.Finish() log = self.test.read( self.test.workpath('config.log') ) # test the caching mechanism self._resetSConfState() sconf = self.SConf.SConf(self.scons_env, conf_dir=self.test.workpath('config.tests'), log_file=self.test.workpath('config.log')) try: res = checks(sconf) assert res[0][0] and res[0][1] == "Hello", res assert not res[1][0] and res[1][1] == "", res finally: sconf.Finish() # we should have exactly one error cached log = self.test.read( self.test.workpath('config.log') ) expr = re.compile( ".*failed in a previous run and all", re.DOTALL ) firstOcc = expr.match( log ) assert firstOcc is not None, log secondOcc = expr.match( log, firstOcc.end(0) ) assert secondOcc is None, log def test_TryAction(self): """Test SConf.TryAction """ def actionOK(target, source, env): open(str(target[0]), "w").write( "RUN OK\n" ) return None def actionFAIL(target, source, env): return 1 self._resetSConfState() sconf = self.SConf.SConf(self.scons_env, conf_dir=self.test.workpath('config.tests'), log_file=self.test.workpath('config.log')) try: (ret, output) = sconf.TryAction(action=actionOK) assert ret and output == "RUN OK" + os.linesep, (ret, output) (ret, output) = sconf.TryAction(action=actionFAIL) assert not ret and output == "", (ret, output) finally: sconf.Finish() def _test_check_compilers(self, comp, func, name): """This is the implementation for CheckCC and CheckCXX tests.""" from copy import deepcopy # Check that Check* works r = func() assert r, "could not find %s ?" % comp # Check that Check* does fail if comp is not available in env oldcomp = deepcopy(self.scons_env[comp]) del self.scons_env[comp] r = func() assert not r, "%s worked wo comp ?" % name # Check that Check* does fail if comp is set but empty self.scons_env[comp] = '' r = func() assert not r, "%s worked with comp = '' ?" % name # Check that Check* does fail if comp is set to buggy executable self.scons_env[comp] = 'thiscccompilerdoesnotexist' r = func() assert not r, "%s worked with comp = thiscompilerdoesnotexist ?" % name # Check that Check* does fail if CFLAGS is buggy self.scons_env[comp] = oldcomp self.scons_env['%sFLAGS' % comp] = '/WX qwertyuiop.c' r = func() assert not r, "%s worked with %sFLAGS = qwertyuiop ?" % (name, comp) def test_CheckCC(self): """Test SConf.CheckCC() """ self._resetSConfState() sconf = self.SConf.SConf(self.scons_env, conf_dir=self.test.workpath('config.tests'), log_file=self.test.workpath('config.log')) try: try: self._test_check_compilers('CC', sconf.CheckCC, 'CheckCC') except AssertionError: sys.stderr.write(self.test.read('config.log')) raise finally: sconf.Finish() def test_CheckSHCC(self): """Test SConf.CheckSHCC() """ self._resetSConfState() sconf = self.SConf.SConf(self.scons_env, conf_dir=self.test.workpath('config.tests'), log_file=self.test.workpath('config.log')) try: try: self._test_check_compilers('SHCC', sconf.CheckSHCC, 'CheckSHCC') except AssertionError: sys.stderr.write(self.test.read('config.log')) raise finally: sconf.Finish() def test_CheckCXX(self): """Test SConf.CheckCXX() """ self._resetSConfState() sconf = self.SConf.SConf(self.scons_env, conf_dir=self.test.workpath('config.tests'), log_file=self.test.workpath('config.log')) try: try: self._test_check_compilers('CXX', sconf.CheckCXX, 'CheckCXX') except AssertionError: sys.stderr.write(self.test.read('config.log')) raise finally: sconf.Finish() def test_CheckSHCXX(self): """Test SConf.CheckSHCXX() """ self._resetSConfState() sconf = self.SConf.SConf(self.scons_env, conf_dir=self.test.workpath('config.tests'), log_file=self.test.workpath('config.log')) try: try: self._test_check_compilers('SHCXX', sconf.CheckSHCXX, 'CheckSHCXX') except AssertionError: sys.stderr.write(self.test.read('config.log')) raise finally: sconf.Finish() def test_CheckHeader(self): """Test SConf.CheckHeader() """ self._resetSConfState() sconf = self.SConf.SConf(self.scons_env, conf_dir=self.test.workpath('config.tests'), log_file=self.test.workpath('config.log')) try: # CheckHeader() r = sconf.CheckHeader( "stdio.h", include_quotes="<>", language="C" ) assert r, "did not find stdio.h" r = sconf.CheckHeader( "HopefullyNoHeader.noh", language="C" ) assert not r, "unexpectedly found HopefullyNoHeader.noh" r = sconf.CheckHeader( "vector", include_quotes="<>", language="C++" ) assert r, "did not find vector" r = sconf.CheckHeader( "HopefullyNoHeader.noh", language="C++" ) assert not r, "unexpectedly found HopefullyNoHeader.noh" finally: sconf.Finish() def test_CheckCHeader(self): """Test SConf.CheckCHeader() """ self._resetSConfState() sconf = self.SConf.SConf(self.scons_env, conf_dir=self.test.workpath('config.tests'), log_file=self.test.workpath('config.log')) try: # CheckCHeader() r = sconf.CheckCHeader( "stdio.h", include_quotes="<>" ) assert r, "did not find stdio.h" r = sconf.CheckCHeader( ["math.h", "stdio.h"], include_quotes="<>" ) assert r, "did not find stdio.h, #include math.h first" r = sconf.CheckCHeader( "HopefullyNoCHeader.noh" ) assert not r, "unexpectedly found HopefullyNoCHeader.noh" finally: sconf.Finish() def test_CheckCXXHeader(self): """Test SConf.CheckCXXHeader() """ self._resetSConfState() sconf = self.SConf.SConf(self.scons_env, conf_dir=self.test.workpath('config.tests'), log_file=self.test.workpath('config.log')) try: # CheckCXXHeader() r = sconf.CheckCXXHeader( "vector", include_quotes="<>" ) assert r, "did not find vector" r = sconf.CheckCXXHeader( ["stdio.h", "vector"], include_quotes="<>" ) assert r, "did not find vector, #include stdio.h first" r = sconf.CheckCXXHeader( "HopefullyNoCXXHeader.noh" ) assert not r, "unexpectedly found HopefullyNoCXXHeader.noh" finally: sconf.Finish() def test_CheckLib(self): """Test SConf.CheckLib() """ self._resetSConfState() sconf = self.SConf.SConf(self.scons_env, conf_dir=self.test.workpath('config.tests'), log_file=self.test.workpath('config.log')) try: # CheckLib() r = sconf.CheckLib( existing_lib, "main", autoadd=0 ) assert r, "did not find %s" % existing_lib r = sconf.CheckLib( "hopefullynolib", "main", autoadd=0 ) assert not r, "unexpectedly found hopefullynolib" # CheckLib() with list of libs r = sconf.CheckLib( [existing_lib], "main", autoadd=0 ) assert r, "did not find %s" % existing_lib r = sconf.CheckLib( ["hopefullynolib"], "main", autoadd=0 ) assert not r, "unexpectedly found hopefullynolib" # This is a check that a null list doesn't find functions # that are in libraries that must be explicitly named. # This works on POSIX systems where you have to -lm to # get the math functions, but it fails on Visual Studio # where you apparently get all those functions for free. # Comment out this check until someone who understands # Visual Studio better can come up with a corresponding # test (if that ever really becomes necessary). #r = sconf.CheckLib( [], "sin", autoadd=0 ) #assert not r, "unexpectedly found nonexistent library" r = sconf.CheckLib( [existing_lib,"hopefullynolib"], "main", autoadd=0 ) assert r, "did not find %s,%s " % (existing_lib,r) r = sconf.CheckLib( ["hopefullynolib",existing_lib], "main", autoadd=0 ) assert r, "did not find %s " % existing_lib # CheckLib() with autoadd def libs(env): return env.get('LIBS', []) env = sconf.env.Clone() try: r = sconf.CheckLib( existing_lib, "main", autoadd=1 ) assert r, "did not find main in %s" % existing_lib expect = libs(env) + [existing_lib] got = libs(sconf.env) assert got == expect, "LIBS: expected %s, got %s" % (expect, got) sconf.env = env.Clone() r = sconf.CheckLib( existing_lib, "main", autoadd=0 ) assert r, "did not find main in %s" % existing_lib expect = libs(env) got = libs(sconf.env) assert got == expect, "before and after LIBS were not the same" finally: sconf.env = env finally: sconf.Finish() def test_CheckLibWithHeader(self): """Test SConf.CheckLibWithHeader() """ self._resetSConfState() sconf = self.SConf.SConf(self.scons_env, conf_dir=self.test.workpath('config.tests'), log_file=self.test.workpath('config.log')) try: # CheckLibWithHeader() r = sconf.CheckLibWithHeader( existing_lib, "math.h", "C", autoadd=0 ) assert r, "did not find %s" % existing_lib r = sconf.CheckLibWithHeader( existing_lib, ["stdio.h", "math.h"], "C", autoadd=0 ) assert r, "did not find %s, #include stdio.h first" % existing_lib r = sconf.CheckLibWithHeader( "hopefullynolib", "math.h", "C", autoadd=0 ) assert not r, "unexpectedly found hopefullynolib" # CheckLibWithHeader() with lists of libs r = sconf.CheckLibWithHeader( [existing_lib], "math.h", "C", autoadd=0 ) assert r, "did not find %s" % existing_lib r = sconf.CheckLibWithHeader( [existing_lib], ["stdio.h", "math.h"], "C", autoadd=0 ) assert r, "did not find %s, #include stdio.h first" % existing_lib # This is a check that a null list doesn't find functions # that are in libraries that must be explicitly named. # This works on POSIX systems where you have to -lm to # get the math functions, but it fails on Visual Studio # where you apparently get all those functions for free. # Comment out this check until someone who understands # Visual Studio better can come up with a corresponding # test (if that ever really becomes necessary). #r = sconf.CheckLibWithHeader( [], "math.h", "C", call="sin(3);", autoadd=0 ) #assert not r, "unexpectedly found non-existent library" r = sconf.CheckLibWithHeader( ["hopefullynolib"], "math.h", "C", autoadd=0 ) assert not r, "unexpectedly found hopefullynolib" r = sconf.CheckLibWithHeader( ["hopefullynolib",existing_lib], ["stdio.h", "math.h"], "C", autoadd=0 ) assert r, "did not find %s, #include stdio.h first" % existing_lib r = sconf.CheckLibWithHeader( [existing_lib,"hopefullynolib"], ["stdio.h", "math.h"], "C", autoadd=0 ) assert r, "did not find %s, #include stdio.h first" % existing_lib # CheckLibWithHeader with autoadd def libs(env): return env.get('LIBS', []) env = sconf.env.Clone() try: r = sconf.CheckLibWithHeader( existing_lib, "math.h", "C", autoadd=1 ) assert r, "did not find math.h with %s" % existing_lib expect = libs(env) + [existing_lib] got = libs(sconf.env) assert got == expect, "LIBS: expected %s, got %s" % (expect, got) sconf.env = env.Clone() r = sconf.CheckLibWithHeader( existing_lib, "math.h", "C", autoadd=0 ) assert r, "did not find math.h with %s" % existing_lib expect = libs(env) got = libs(sconf.env) assert got == expect, "before and after LIBS were not the same" finally: sconf.env = env finally: sconf.Finish() def test_CheckFunc(self): """Test SConf.CheckFunc() """ self._resetSConfState() sconf = self.SConf.SConf(self.scons_env, conf_dir=self.test.workpath('config.tests'), log_file=self.test.workpath('config.log')) try: # CheckFunc() r = sconf.CheckFunc('strcpy') assert r, "did not find strcpy" r = sconf.CheckFunc('strcpy', '/* header */ char strcpy();') assert r, "did not find strcpy" r = sconf.CheckFunc('hopefullynofunction') assert not r, "unexpectedly found hopefullynofunction" finally: sconf.Finish() def test_Define(self): """Test SConf.Define() """ self._resetSConfState() sconf = self.SConf.SConf(self.scons_env, conf_dir=self.test.workpath('config.tests'), log_file=self.test.workpath('config.log'), config_h = self.test.workpath('config.h')) try: # XXX: we test the generated config.h string. This is not so good, # ideally, we would like to test if the generated file included in # a test program does what we want. # Test defining one symbol wo value sconf.config_h_text = '' sconf.Define('YOP') assert sconf.config_h_text == '#define YOP\n' # Test defining one symbol with integer value sconf.config_h_text = '' sconf.Define('YOP', 1) assert sconf.config_h_text == '#define YOP 1\n' # Test defining one symbol with string value sconf.config_h_text = '' sconf.Define('YOP', '"YIP"') assert sconf.config_h_text == '#define YOP "YIP"\n' # Test defining one symbol with string value sconf.config_h_text = '' sconf.Define('YOP', "YIP") assert sconf.config_h_text == '#define YOP YIP\n' finally: sconf.Finish() def test_CheckTypeSize(self): """Test SConf.CheckTypeSize() """ self._resetSConfState() sconf = self.SConf.SConf(self.scons_env, conf_dir=self.test.workpath('config.tests'), log_file=self.test.workpath('config.log')) try: # CheckTypeSize() # In ANSI C, sizeof(char) == 1. r = sconf.CheckTypeSize('char', expect = 1) assert r == 1, "sizeof(char) != 1 ??" r = sconf.CheckTypeSize('char', expect = 0) assert r == 0, "sizeof(char) == 0 ??" r = sconf.CheckTypeSize('char', expect = 2) assert r == 0, "sizeof(char) == 2 ??" r = sconf.CheckTypeSize('char') assert r == 1, "sizeof(char) != 1 ??" r = sconf.CheckTypeSize('const unsigned char') assert r == 1, "sizeof(const unsigned char) != 1 ??" # Checking C++ r = sconf.CheckTypeSize('const unsigned char', language = 'C++') assert r == 1, "sizeof(const unsigned char) != 1 ??" # Checking Non-existing type r = sconf.CheckTypeSize('thistypedefhasnotchancetosexist_scons') assert r == 0, \ "Checking size of thistypedefhasnotchancetosexist_scons succeeded ?" finally: sconf.Finish() def test_CheckDeclaration(self): """Test SConf.CheckDeclaration() """ self._resetSConfState() sconf = self.SConf.SConf(self.scons_env, conf_dir=self.test.workpath('config.tests'), log_file=self.test.workpath('config.log')) try: # In ANSI C, malloc should be available in stdlib r = sconf.CheckDeclaration('malloc', includes = "#include ") assert r, "malloc not declared ??" # For C++, __cplusplus should be declared r = sconf.CheckDeclaration('__cplusplus', language = 'C++') assert r, "__cplusplus not declared in C++ ??" r = sconf.CheckDeclaration('__cplusplus', language = 'C') assert not r, "__cplusplus declared in C ??" r = sconf.CheckDeclaration('unknown', language = 'Unknown') assert not r, "unknown language was supported ??" finally: sconf.Finish() def test_(self): """Test SConf.CheckType() """ self._resetSConfState() sconf = self.SConf.SConf(self.scons_env, conf_dir=self.test.workpath('config.tests'), log_file=self.test.workpath('config.log')) try: # CheckType() r = sconf.CheckType('off_t', '#include \n') assert r, "did not find off_t" r = sconf.CheckType('hopefullynotypedef_not') assert not r, "unexpectedly found hopefullynotypedef_not" finally: sconf.Finish() def test_CustomChecks(self): """Test Custom Checks """ def CheckCustom(test): test.Message( "Checking UserTest ... " ) prog = """ #include int main() { printf( "Hello" ); return 0; } """ (ret, output) = test.TryRun( prog, ".c" ) test.Result( ret ) assert ret and output == "Hello", (ret, output) return ret self._resetSConfState() sconf = self.SConf.SConf(self.scons_env, custom_tests={'CheckCustom': CheckCustom}, conf_dir=self.test.workpath('config.tests'), log_file=self.test.workpath('config.log')) try: ret = sconf.CheckCustom() assert ret, ret finally: sconf.Finish() if __name__ == "__main__": suite = unittest.makeSuite(SConfTestCase, 'test_') res = unittest.TextTestRunner().run(suite) if not res.wasSuccessful(): sys.exit(1) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Job.py0000644000175000017500000003737512114661557020406 0ustar dktrkranzdktrkranz"""SCons.Job This module defines the Serial and Parallel classes that execute tasks to complete a build. The Jobs class provides a higher level interface to start, stop, and wait on jobs. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Job.py 2013/03/03 09:48:35 garyo" import SCons.compat import os import signal import SCons.Errors # The default stack size (in kilobytes) of the threads used to execute # jobs in parallel. # # We use a stack size of 256 kilobytes. The default on some platforms # is too large and prevents us from creating enough threads to fully # parallelized the build. For example, the default stack size on linux # is 8 MBytes. explicit_stack_size = None default_stack_size = 256 interrupt_msg = 'Build interrupted.' class InterruptState(object): def __init__(self): self.interrupted = False def set(self): self.interrupted = True def __call__(self): return self.interrupted class Jobs(object): """An instance of this class initializes N jobs, and provides methods for starting, stopping, and waiting on all N jobs. """ def __init__(self, num, taskmaster): """ create 'num' jobs using the given taskmaster. If 'num' is 1 or less, then a serial job will be used, otherwise a parallel job with 'num' worker threads will be used. The 'num_jobs' attribute will be set to the actual number of jobs allocated. If more than one job is requested but the Parallel class can't do it, it gets reset to 1. Wrapping interfaces that care should check the value of 'num_jobs' after initialization. """ self.job = None if num > 1: stack_size = explicit_stack_size if stack_size is None: stack_size = default_stack_size try: self.job = Parallel(taskmaster, num, stack_size) self.num_jobs = num except NameError: pass if self.job is None: self.job = Serial(taskmaster) self.num_jobs = 1 def run(self, postfunc=lambda: None): """Run the jobs. postfunc() will be invoked after the jobs has run. It will be invoked even if the jobs are interrupted by a keyboard interrupt (well, in fact by a signal such as either SIGINT, SIGTERM or SIGHUP). The execution of postfunc() is protected against keyboard interrupts and is guaranteed to run to completion.""" self._setup_sig_handler() try: self.job.start() finally: postfunc() self._reset_sig_handler() def were_interrupted(self): """Returns whether the jobs were interrupted by a signal.""" return self.job.interrupted() def _setup_sig_handler(self): """Setup an interrupt handler so that SCons can shutdown cleanly in various conditions: a) SIGINT: Keyboard interrupt b) SIGTERM: kill or system shutdown c) SIGHUP: Controlling shell exiting We handle all of these cases by stopping the taskmaster. It turns out that it very difficult to stop the build process by throwing asynchronously an exception such as KeyboardInterrupt. For example, the python Condition variables (threading.Condition) and queue's do not seem to asynchronous-exception-safe. It would require adding a whole bunch of try/finally block and except KeyboardInterrupt all over the place. Note also that we have to be careful to handle the case when SCons forks before executing another process. In that case, we want the child to exit immediately. """ def handler(signum, stack, self=self, parentpid=os.getpid()): if os.getpid() == parentpid: self.job.taskmaster.stop() self.job.interrupted.set() else: os._exit(2) self.old_sigint = signal.signal(signal.SIGINT, handler) self.old_sigterm = signal.signal(signal.SIGTERM, handler) try: self.old_sighup = signal.signal(signal.SIGHUP, handler) except AttributeError: pass def _reset_sig_handler(self): """Restore the signal handlers to their previous state (before the call to _setup_sig_handler().""" signal.signal(signal.SIGINT, self.old_sigint) signal.signal(signal.SIGTERM, self.old_sigterm) try: signal.signal(signal.SIGHUP, self.old_sighup) except AttributeError: pass class Serial(object): """This class is used to execute tasks in series, and is more efficient than Parallel, but is only appropriate for non-parallel builds. Only one instance of this class should be in existence at a time. This class is not thread safe. """ def __init__(self, taskmaster): """Create a new serial job given a taskmaster. The taskmaster's next_task() method should return the next task that needs to be executed, or None if there are no more tasks. The taskmaster's executed() method will be called for each task when it is successfully executed or failed() will be called if it failed to execute (e.g. execute() raised an exception).""" self.taskmaster = taskmaster self.interrupted = InterruptState() def start(self): """Start the job. This will begin pulling tasks from the taskmaster and executing them, and return when there are no more tasks. If a task fails to execute (i.e. execute() raises an exception), then the job will stop.""" while True: task = self.taskmaster.next_task() if task is None: break try: task.prepare() if task.needs_execute(): task.execute() except: if self.interrupted(): try: raise SCons.Errors.BuildError( task.targets[0], errstr=interrupt_msg) except: task.exception_set() else: task.exception_set() # Let the failed() callback function arrange for the # build to stop if that's appropriate. task.failed() else: task.executed() task.postprocess() self.taskmaster.cleanup() # Trap import failure so that everything in the Job module but the # Parallel class (and its dependent classes) will work if the interpreter # doesn't support threads. try: import queue import threading except ImportError: pass else: class Worker(threading.Thread): """A worker thread waits on a task to be posted to its request queue, dequeues the task, executes it, and posts a tuple including the task and a boolean indicating whether the task executed successfully. """ def __init__(self, requestQueue, resultsQueue, interrupted): threading.Thread.__init__(self) self.setDaemon(1) self.requestQueue = requestQueue self.resultsQueue = resultsQueue self.interrupted = interrupted self.start() def run(self): while True: task = self.requestQueue.get() if task is None: # The "None" value is used as a sentinel by # ThreadPool.cleanup(). This indicates that there # are no more tasks, so we should quit. break try: if self.interrupted(): raise SCons.Errors.BuildError( task.targets[0], errstr=interrupt_msg) task.execute() except: task.exception_set() ok = False else: ok = True self.resultsQueue.put((task, ok)) class ThreadPool(object): """This class is responsible for spawning and managing worker threads.""" def __init__(self, num, stack_size, interrupted): """Create the request and reply queues, and 'num' worker threads. One must specify the stack size of the worker threads. The stack size is specified in kilobytes. """ self.requestQueue = queue.Queue(0) self.resultsQueue = queue.Queue(0) try: prev_size = threading.stack_size(stack_size*1024) except AttributeError, e: # Only print a warning if the stack size has been # explicitly set. if not explicit_stack_size is None: msg = "Setting stack size is unsupported by this version of Python:\n " + \ e.args[0] SCons.Warnings.warn(SCons.Warnings.StackSizeWarning, msg) except ValueError, e: msg = "Setting stack size failed:\n " + str(e) SCons.Warnings.warn(SCons.Warnings.StackSizeWarning, msg) # Create worker threads self.workers = [] for _ in range(num): worker = Worker(self.requestQueue, self.resultsQueue, interrupted) self.workers.append(worker) if 'prev_size' in locals(): threading.stack_size(prev_size) def put(self, task): """Put task into request queue.""" self.requestQueue.put(task) def get(self): """Remove and return a result tuple from the results queue.""" return self.resultsQueue.get() def preparation_failed(self, task): self.resultsQueue.put((task, False)) def cleanup(self): """ Shuts down the thread pool, giving each worker thread a chance to shut down gracefully. """ # For each worker thread, put a sentinel "None" value # on the requestQueue (indicating that there's no work # to be done) so that each worker thread will get one and # terminate gracefully. for _ in self.workers: self.requestQueue.put(None) # Wait for all of the workers to terminate. # # If we don't do this, later Python versions (2.4, 2.5) often # seem to raise exceptions during shutdown. This happens # in requestQueue.get(), as an assertion failure that # requestQueue.not_full is notified while not acquired, # seemingly because the main thread has shut down (or is # in the process of doing so) while the workers are still # trying to pull sentinels off the requestQueue. # # Normally these terminations should happen fairly quickly, # but we'll stick a one-second timeout on here just in case # someone gets hung. for worker in self.workers: worker.join(1.0) self.workers = [] class Parallel(object): """This class is used to execute tasks in parallel, and is somewhat less efficient than Serial, but is appropriate for parallel builds. This class is thread safe. """ def __init__(self, taskmaster, num, stack_size): """Create a new parallel job given a taskmaster. The taskmaster's next_task() method should return the next task that needs to be executed, or None if there are no more tasks. The taskmaster's executed() method will be called for each task when it is successfully executed or failed() will be called if the task failed to execute (i.e. execute() raised an exception). Note: calls to taskmaster are serialized, but calls to execute() on distinct tasks are not serialized, because that is the whole point of parallel jobs: they can execute multiple tasks simultaneously. """ self.taskmaster = taskmaster self.interrupted = InterruptState() self.tp = ThreadPool(num, stack_size, self.interrupted) self.maxjobs = num def start(self): """Start the job. This will begin pulling tasks from the taskmaster and executing them, and return when there are no more tasks. If a task fails to execute (i.e. execute() raises an exception), then the job will stop.""" jobs = 0 while True: # Start up as many available tasks as we're # allowed to. while jobs < self.maxjobs: task = self.taskmaster.next_task() if task is None: break try: # prepare task for execution task.prepare() except: task.exception_set() task.failed() task.postprocess() else: if task.needs_execute(): # dispatch task self.tp.put(task) jobs = jobs + 1 else: task.executed() task.postprocess() if not task and not jobs: break # Let any/all completed tasks finish up before we go # back and put the next batch of tasks on the queue. while True: task, ok = self.tp.get() jobs = jobs - 1 if ok: task.executed() else: if self.interrupted(): try: raise SCons.Errors.BuildError( task.targets[0], errstr=interrupt_msg) except: task.exception_set() # Let the failed() callback function arrange # for the build to stop if that's appropriate. task.failed() task.postprocess() if self.tp.resultsQueue.empty(): break self.tp.cleanup() self.taskmaster.cleanup() # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/CacheDir.py0000644000175000017500000001751612114661557021331 0ustar dktrkranzdktrkranz# # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/CacheDir.py 2013/03/03 09:48:35 garyo" __doc__ = """ CacheDir support """ import os.path import stat import sys import SCons.Action cache_enabled = True cache_debug = False cache_force = False cache_show = False def CacheRetrieveFunc(target, source, env): t = target[0] fs = t.fs cd = env.get_CacheDir() cachedir, cachefile = cd.cachepath(t) if not fs.exists(cachefile): cd.CacheDebug('CacheRetrieve(%s): %s not in cache\n', t, cachefile) return 1 cd.CacheDebug('CacheRetrieve(%s): retrieving from %s\n', t, cachefile) if SCons.Action.execute_actions: if fs.islink(cachefile): fs.symlink(fs.readlink(cachefile), t.path) else: env.copy_from_cache(cachefile, t.path) st = fs.stat(cachefile) fs.chmod(t.path, stat.S_IMODE(st[stat.ST_MODE]) | stat.S_IWRITE) return 0 def CacheRetrieveString(target, source, env): t = target[0] fs = t.fs cd = env.get_CacheDir() cachedir, cachefile = cd.cachepath(t) if t.fs.exists(cachefile): return "Retrieved `%s' from cache" % t.path return None CacheRetrieve = SCons.Action.Action(CacheRetrieveFunc, CacheRetrieveString) CacheRetrieveSilent = SCons.Action.Action(CacheRetrieveFunc, None) def CachePushFunc(target, source, env): t = target[0] if t.nocache: return fs = t.fs cd = env.get_CacheDir() cachedir, cachefile = cd.cachepath(t) if fs.exists(cachefile): # Don't bother copying it if it's already there. Note that # usually this "shouldn't happen" because if the file already # existed in cache, we'd have retrieved the file from there, # not built it. This can happen, though, in a race, if some # other person running the same build pushes their copy to # the cache after we decide we need to build it but before our # build completes. cd.CacheDebug('CachePush(%s): %s already exists in cache\n', t, cachefile) return cd.CacheDebug('CachePush(%s): pushing to %s\n', t, cachefile) tempfile = cachefile+'.tmp'+str(os.getpid()) errfmt = "Unable to copy %s to cache. Cache file is %s" if not fs.isdir(cachedir): try: fs.makedirs(cachedir) except EnvironmentError: # We may have received an exception because another process # has beaten us creating the directory. if not fs.isdir(cachedir): msg = errfmt % (str(target), cachefile) raise SCons.Errors.EnvironmentError(msg) try: 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) except EnvironmentError: # It's possible someone else tried writing the file at the # same time we did, or else that there was some problem like # the CacheDir being on a separate file system that's full. # In any case, inability to push a file to cache doesn't affect # the correctness of the build, so just print a warning. msg = errfmt % (str(target), cachefile) SCons.Warnings.warn(SCons.Warnings.CacheWriteErrorWarning, msg) CachePush = SCons.Action.Action(CachePushFunc, None) class CacheDir(object): def __init__(self, path): try: import hashlib except ImportError: msg = "No hashlib or MD5 module available, CacheDir() not supported" SCons.Warnings.warn(SCons.Warnings.NoMD5ModuleWarning, msg) self.path = None else: self.path = path self.current_cache_debug = None self.debugFP = None def CacheDebug(self, fmt, target, cachefile): if cache_debug != self.current_cache_debug: if cache_debug == '-': self.debugFP = sys.stdout elif cache_debug: self.debugFP = open(cache_debug, 'w') else: self.debugFP = None self.current_cache_debug = cache_debug if self.debugFP: self.debugFP.write(fmt % (target, os.path.split(cachefile)[1])) def is_enabled(self): return (cache_enabled and not self.path is None) def cachepath(self, node): """ """ if not self.is_enabled(): return None, None sig = node.get_cachedir_bsig() subdir = sig[0].upper() dir = os.path.join(self.path, subdir) return dir, os.path.join(dir, sig) def retrieve(self, node): """ This method is called from multiple threads in a parallel build, so only do thread safe stuff here. Do thread unsafe stuff in built(). Note that there's a special trick here with the execute flag (one that's not normally done for other actions). Basically if the user requested a no_exec (-n) build, then SCons.Action.execute_actions is set to 0 and when any action is called, it does its showing but then just returns zero instead of actually calling the action execution operation. The problem for caching is that if the file does NOT exist in cache then the CacheRetrieveString won't return anything to show for the task, but the Action.__call__ won't call CacheRetrieveFunc; instead it just returns zero, which makes the code below think that the file *was* successfully retrieved from the cache, therefore it doesn't do any subsequent building. However, the CacheRetrieveString didn't print anything because it didn't actually exist in the cache, and no more build actions will be performed, so the user just sees nothing. The fix is to tell Action.__call__ to always execute the CacheRetrieveFunc and then have the latter explicitly check SCons.Action.execute_actions itself. """ if not self.is_enabled(): return False env = node.get_build_env() if cache_show: if CacheRetrieveSilent(node, [], env, execute=1) == 0: node.build(presub=0, execute=0) return True else: if CacheRetrieve(node, [], env, execute=1) == 0: return True return False def push(self, node): if not self.is_enabled(): return return CachePush(node, [], node.get_build_env()) def push_if_forced(self, node): if cache_force: return self.push(node) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Sig.py0000644000175000017500000000450212114661557020400 0ustar dktrkranzdktrkranz# # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Sig.py 2013/03/03 09:48:35 garyo" __doc__ = """Place-holder for the old SCons.Sig module hierarchy This is no longer used, but code out there (such as the NSIS module on the SCons wiki) may try to import SCons.Sig. If so, we generate a warning that points them to the line that caused the import, and don't die. If someone actually tried to use the sub-modules or functions within the package (for example, SCons.Sig.MD5.signature()), then they'll still get an AttributeError, but at least they'll know where to start looking. """ import SCons.Util import SCons.Warnings msg = 'The SCons.Sig module no longer exists.\n' \ ' Remove the following "import SCons.Sig" line to eliminate this warning:' SCons.Warnings.warn(SCons.Warnings.DeprecatedSigModuleWarning, msg) default_calc = None default_module = None class MD5Null(SCons.Util.Null): def __repr__(self): return "MD5Null()" class TimeStampNull(SCons.Util.Null): def __repr__(self): return "TimeStampNull()" MD5 = MD5Null() TimeStamp = TimeStampNull() # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Environment.py0000644000175000017500000027340412114661557022173 0ustar dktrkranzdktrkranz"""SCons.Environment Base class for construction Environments. These are the primary objects used to communicate dependency and construction information to the build engine. Keyword arguments supplied when the construction Environment is created are construction variables used to initialize the Environment """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Environment.py 2013/03/03 09:48:35 garyo" import copy import os import sys import re import shlex from collections import UserDict import SCons.Action import SCons.Builder from SCons.Debug import logInstanceCreation import SCons.Defaults import SCons.Errors import SCons.Memoize import SCons.Node import SCons.Node.Alias import SCons.Node.FS import SCons.Node.Python import SCons.Platform import SCons.SConf import SCons.SConsign import SCons.Subst import SCons.Tool import SCons.Util import SCons.Warnings class _Null(object): pass _null = _Null _warn_copy_deprecated = True _warn_source_signatures_deprecated = True _warn_target_signatures_deprecated = True CleanTargets = {} CalculatorArgs = {} semi_deepcopy = SCons.Util.semi_deepcopy semi_deepcopy_dict = SCons.Util.semi_deepcopy_dict # Pull UserError into the global name space for the benefit of # Environment().SourceSignatures(), which has some import statements # which seem to mess up its ability to reference SCons directly. UserError = SCons.Errors.UserError def alias_builder(env, target, source): pass AliasBuilder = SCons.Builder.Builder(action = alias_builder, target_factory = SCons.Node.Alias.default_ans.Alias, source_factory = SCons.Node.FS.Entry, multi = 1, is_explicit = None, name='AliasBuilder') def apply_tools(env, tools, toolpath): # Store the toolpath in the Environment. if toolpath is not None: env['toolpath'] = toolpath if not tools: return # Filter out null tools from the list. for tool in [_f for _f in tools if _f]: if SCons.Util.is_List(tool) or isinstance(tool, tuple): toolname = tool[0] toolargs = tool[1] # should be a dict of kw args tool = env.Tool(toolname, **toolargs) else: env.Tool(tool) # These names are (or will be) controlled by SCons; users should never # set or override them. This warning can optionally be turned off, # but scons will still ignore the illegal variable names even if it's off. reserved_construction_var_names = [ 'CHANGED_SOURCES', 'CHANGED_TARGETS', 'SOURCE', 'SOURCES', 'TARGET', 'TARGETS', 'UNCHANGED_SOURCES', 'UNCHANGED_TARGETS', ] future_reserved_construction_var_names = [ #'HOST_OS', #'HOST_ARCH', #'HOST_CPU', ] def copy_non_reserved_keywords(dict): result = semi_deepcopy(dict) for k in result.keys(): if k in reserved_construction_var_names: msg = "Ignoring attempt to set reserved variable `$%s'" SCons.Warnings.warn(SCons.Warnings.ReservedVariableWarning, msg % k) del result[k] return result def _set_reserved(env, key, value): msg = "Ignoring attempt to set reserved variable `$%s'" SCons.Warnings.warn(SCons.Warnings.ReservedVariableWarning, msg % key) def _set_future_reserved(env, key, value): env._dict[key] = value msg = "`$%s' will be reserved in a future release and setting it will become ignored" SCons.Warnings.warn(SCons.Warnings.FutureReservedVariableWarning, msg % key) def _set_BUILDERS(env, key, value): try: bd = env._dict[key] for k in bd.keys(): del bd[k] except KeyError: bd = BuilderDict(kwbd, env) env._dict[key] = bd for k, v in value.items(): if not SCons.Builder.is_a_Builder(v): raise SCons.Errors.UserError('%s is not a Builder.' % repr(v)) bd.update(value) def _del_SCANNERS(env, key): del env._dict[key] env.scanner_map_delete() def _set_SCANNERS(env, key, value): env._dict[key] = value env.scanner_map_delete() def _delete_duplicates(l, keep_last): """Delete duplicates from a sequence, keeping the first or last.""" seen={} result=[] if keep_last: # reverse in & out, then keep first l.reverse() for i in l: try: if i not in seen: result.append(i) seen[i]=1 except TypeError: # probably unhashable. Just keep it. result.append(i) if keep_last: result.reverse() return result # The following is partly based on code in a comment added by Peter # Shannon at the following page (there called the "transplant" class): # # ASPN : Python Cookbook : Dynamically added methods to a class # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/81732 # # We had independently been using the idiom as BuilderWrapper, but # factoring out the common parts into this base class, and making # BuilderWrapper a subclass that overrides __call__() to enforce specific # Builder calling conventions, simplified some of our higher-layer code. class MethodWrapper(object): """ A generic Wrapper class that associates a method (which can actually be any callable) with an object. As part of creating this MethodWrapper object an attribute with the specified (by default, the name of the supplied method) is added to the underlying object. When that new "method" is called, our __call__() method adds the object as the first argument, simulating the Python behavior of supplying "self" on method calls. We hang on to the name by which the method was added to the underlying base class so that we can provide a method to "clone" ourselves onto a new underlying object being copied (without which we wouldn't need to save that info). """ def __init__(self, object, method, name=None): if name is None: name = method.__name__ self.object = object self.method = method self.name = name setattr(self.object, name, self) def __call__(self, *args, **kwargs): nargs = (self.object,) + args return self.method(*nargs, **kwargs) def clone(self, new_object): """ Returns an object that re-binds the underlying "method" to the specified new object. """ return self.__class__(new_object, self.method, self.name) class BuilderWrapper(MethodWrapper): """ A MethodWrapper subclass that that associates an environment with a Builder. This mainly exists to wrap the __call__() function so that all calls to Builders can have their argument lists massaged in the same way (treat a lone argument as the source, treat two arguments as target then source, make sure both target and source are lists) without having to have cut-and-paste code to do it. As a bit of obsessive backwards compatibility, we also intercept attempts to get or set the "env" or "builder" attributes, which were the names we used before we put the common functionality into the MethodWrapper base class. We'll keep this around for a while in case people shipped Tool modules that reached into the wrapper (like the Tool/qt.py module does, or did). There shouldn't be a lot attribute fetching or setting on these, so a little extra work shouldn't hurt. """ def __call__(self, target=None, source=_null, *args, **kw): if source is _null: source = target target = None if target is not None and not SCons.Util.is_List(target): target = [target] if source is not None and not SCons.Util.is_List(source): source = [source] return MethodWrapper.__call__(self, target, source, *args, **kw) def __repr__(self): return '' % repr(self.name) def __str__(self): return self.__repr__() def __getattr__(self, name): if name == 'env': return self.object elif name == 'builder': return self.method else: raise AttributeError(name) def __setattr__(self, name, value): if name == 'env': self.object = value elif name == 'builder': self.method = value else: self.__dict__[name] = value # This allows a Builder to be executed directly # through the Environment to which it's attached. # In practice, we shouldn't need this, because # builders actually get executed through a Node. # But we do have a unit test for this, and can't # yet rule out that it would be useful in the # future, so leave it for now. #def execute(self, **kw): # kw['env'] = self.env # self.builder.execute(**kw) class BuilderDict(UserDict): """This is a dictionary-like class used by an Environment to hold the Builders. We need to do this because every time someone changes the Builders in the Environment's BUILDERS dictionary, we must update the Environment's attributes.""" def __init__(self, dict, env): # Set self.env before calling the superclass initialization, # because it will end up calling our other methods, which will # need to point the values in this dictionary to self.env. self.env = env UserDict.__init__(self, dict) def __semi_deepcopy__(self): # These cannot be copied since they would both modify the same builder object, and indeed # just copying would modify the original builder raise TypeError( 'cannot semi_deepcopy a BuilderDict' ) def __setitem__(self, item, val): try: method = getattr(self.env, item).method except AttributeError: pass else: self.env.RemoveMethod(method) UserDict.__setitem__(self, item, val) BuilderWrapper(self.env, val, item) def __delitem__(self, item): UserDict.__delitem__(self, item) delattr(self.env, item) def update(self, dict): for i, v in dict.items(): self.__setitem__(i, v) _is_valid_var = re.compile(r'[_a-zA-Z]\w*$') def is_valid_construction_var(varstr): """Return if the specified string is a legitimate construction variable. """ return _is_valid_var.match(varstr) class SubstitutionEnvironment(object): """Base class for different flavors of construction environments. This class contains a minimal set of methods that handle contruction variable expansion and conversion of strings to Nodes, which may or may not be actually useful as a stand-alone class. Which methods ended up in this class is pretty arbitrary right now. They're basically the ones which we've empirically determined are common to the different construction environment subclasses, and most of the others that use or touch the underlying dictionary of construction variables. Eventually, this class should contain all the methods that we determine are necessary for a "minimal" interface to the build engine. A full "native Python" SCons environment has gotten pretty heavyweight with all of the methods and Tools and construction variables we've jammed in there, so it would be nice to have a lighter weight alternative for interfaces that don't need all of the bells and whistles. (At some point, we'll also probably rename this class "Base," since that more reflects what we want this class to become, but because we've released comments that tell people to subclass Environment.Base to create their own flavors of construction environment, we'll save that for a future refactoring when this class actually becomes useful.) """ if SCons.Memoize.use_memoizer: __metaclass__ = SCons.Memoize.Memoized_Metaclass def __init__(self, **kw): """Initialization of an underlying SubstitutionEnvironment class. """ if __debug__: logInstanceCreation(self, 'Environment.SubstitutionEnvironment') self.fs = SCons.Node.FS.get_default_fs() self.ans = SCons.Node.Alias.default_ans self.lookup_list = SCons.Node.arg2nodes_lookups self._dict = kw.copy() self._init_special() self.added_methods = [] #self._memo = {} def _init_special(self): """Initial the dispatch tables for special handling of special construction variables.""" self._special_del = {} self._special_del['SCANNERS'] = _del_SCANNERS self._special_set = {} for key in reserved_construction_var_names: self._special_set[key] = _set_reserved for key in future_reserved_construction_var_names: self._special_set[key] = _set_future_reserved self._special_set['BUILDERS'] = _set_BUILDERS self._special_set['SCANNERS'] = _set_SCANNERS # Freeze the keys of self._special_set in a list for use by # methods that need to check. (Empirically, list scanning has # gotten better than dict.has_key() in Python 2.5.) self._special_set_keys = list(self._special_set.keys()) def __cmp__(self, other): return cmp(self._dict, other._dict) def __delitem__(self, key): special = self._special_del.get(key) if special: special(self, key) else: del self._dict[key] def __getitem__(self, key): return self._dict[key] def __setitem__(self, key, value): # This is heavily used. This implementation is the best we have # according to the timings in bench/env.__setitem__.py. # # The "key in self._special_set_keys" test here seems to perform # pretty well for the number of keys we have. A hard-coded # list works a little better in Python 2.5, but that has the # disadvantage of maybe getting out of sync if we ever add more # variable names. Using self._special_set.has_key() works a # little better in Python 2.4, but is worse than this test. # So right now it seems like a good trade-off, but feel free to # revisit this with bench/env.__setitem__.py as needed (and # as newer versions of Python come out). if key in self._special_set_keys: self._special_set[key](self, key, value) else: # If we already have the entry, then it's obviously a valid # key and we don't need to check. If we do check, using a # global, pre-compiled regular expression directly is more # efficient than calling another function or a method. if key not in self._dict \ and not _is_valid_var.match(key): raise SCons.Errors.UserError("Illegal construction variable `%s'" % key) self._dict[key] = value def get(self, key, default=None): """Emulates the get() method of dictionaries.""" return self._dict.get(key, default) def has_key(self, key): return key in self._dict def __contains__(self, key): return self._dict.__contains__(key) def items(self): return list(self._dict.items()) 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: lookup_list = self.lookup_list if not args: return [] args = SCons.Util.flatten(args) nodes = [] for v in args: if SCons.Util.is_String(v): n = None for l in lookup_list: n = l(v) if n is not None: break if n is not None: if SCons.Util.is_String(n): # n = self.subst(n, raw=1, **kw) kw['raw'] = 1 n = self.subst(n, **kw) if node_factory: n = node_factory(n) if SCons.Util.is_List(n): nodes.extend(n) else: nodes.append(n) elif node_factory: # v = node_factory(self.subst(v, raw=1, **kw)) kw['raw'] = 1 v = node_factory(self.subst(v, **kw)) if SCons.Util.is_List(v): nodes.extend(v) else: nodes.append(v) else: nodes.append(v) return nodes def gvars(self): return self._dict def lvars(self): return {} def subst(self, string, raw=0, target=None, source=None, conv=None, executor=None): """Recursively interpolates construction variables from the Environment into the specified string, returning the expanded result. Construction variables are specified by a $ prefix in the string and begin with an initial underscore or alphabetic character followed by any number of underscores or alphanumeric characters. The construction variable names may be surrounded by curly braces to separate the name from trailing characters. """ gvars = self.gvars() lvars = self.lvars() lvars['__env__'] = self if executor: lvars.update(executor.get_lvars()) return SCons.Subst.scons_subst(string, self, raw, target, source, gvars, lvars, conv) def subst_kw(self, kw, raw=0, target=None, source=None): nkw = {} for k, v in kw.items(): k = self.subst(k, raw, target, source) if SCons.Util.is_String(v): v = self.subst(v, raw, target, source) nkw[k] = v return nkw def subst_list(self, string, raw=0, target=None, source=None, conv=None, executor=None): """Calls through to SCons.Subst.scons_subst_list(). See the documentation for that function.""" gvars = self.gvars() lvars = self.lvars() lvars['__env__'] = self if executor: lvars.update(executor.get_lvars()) return SCons.Subst.scons_subst_list(string, self, raw, target, source, gvars, lvars, conv) def subst_path(self, path, target=None, source=None): """Substitute a path list, turning EntryProxies into Nodes and leaving Nodes (and other objects) as-is.""" if not SCons.Util.is_List(path): path = [path] def s(obj): """This is the "string conversion" routine that we have our substitutions use to return Nodes, not strings. This relies on the fact that an EntryProxy object has a get() method that returns the underlying Node that it wraps, which is a bit of architectural dependence that we might need to break or modify in the future in response to additional requirements.""" try: get = obj.get except AttributeError: obj = SCons.Util.to_String_for_subst(obj) else: obj = get() return obj r = [] for p in path: if SCons.Util.is_String(p): p = self.subst(p, target=target, source=source, conv=s) if SCons.Util.is_List(p): if len(p) == 1: p = p[0] else: # We have an object plus a string, or multiple # objects that we need to smush together. No choice # but to make them into a string. p = ''.join(map(SCons.Util.to_String_for_subst, p)) else: p = s(p) r.append(p) return r subst_target_source = subst def backtick(self, command): import subprocess # common arguments kw = { 'stdin' : 'devnull', 'stdout' : subprocess.PIPE, 'stderr' : subprocess.PIPE, 'universal_newlines' : True, } # if the command is a list, assume it's been quoted # othewise force a shell if not SCons.Util.is_List(command): kw['shell'] = True # run constructed command p = SCons.Action._subproc(self, command, **kw) out,err = p.communicate() status = p.wait() if err: sys.stderr.write(unicode(err)) if status: 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. """ method = MethodWrapper(self, function, name) self.added_methods.append(method) def RemoveMethod(self, function): """ Removes the specified function's MethodWrapper from the added_methods list, so we don't re-bind it when making a clone. """ self.added_methods = [dm for dm in self.added_methods if not dm.method is function] def Override(self, overrides): """ Produce a modified environment whose variables are overriden by the overrides dictionaries. "overrides" is a dictionary that will override the variables of this environment. This function is much more efficient than Clone() or creating a new Environment because it doesn't copy the construction environment dictionary, it just wraps the underlying construction environment, and doesn't even create a wrapper object if there are no overrides. """ if not overrides: return self o = copy_non_reserved_keywords(overrides) if not o: return self overrides = {} merges = None for key, value in o.items(): if key == 'parse_flags': merges = value else: overrides[key] = SCons.Subst.scons_subst_once(value, self, key) env = OverrideEnvironment(self, overrides) if merges: env.MergeFlags(merges) return env def ParseFlags(self, *flags): """ Parse the set of flags and return a dict with the flags placed in the appropriate entry. The flags are treated as a typical set of command-line flags for a GNU-like toolchain and used to populate the entries in the dict immediately below. If one of the flag strings begins with a bang (exclamation mark), it is assumed to be a command and the rest of the string is executed; the result of that evaluation is then added to the dict. """ dict = { 'ASFLAGS' : SCons.Util.CLVar(''), 'CFLAGS' : SCons.Util.CLVar(''), 'CCFLAGS' : SCons.Util.CLVar(''), 'CXXFLAGS' : SCons.Util.CLVar(''), 'CPPDEFINES' : [], 'CPPFLAGS' : SCons.Util.CLVar(''), 'CPPPATH' : [], 'FRAMEWORKPATH' : SCons.Util.CLVar(''), 'FRAMEWORKS' : SCons.Util.CLVar(''), 'LIBPATH' : [], 'LIBS' : [], 'LINKFLAGS' : SCons.Util.CLVar(''), 'RPATH' : [], } def do_parse(arg): # if arg is a sequence, recurse with each element if not arg: return if not SCons.Util.is_String(arg): for t in arg: do_parse(t) return # if arg is a command, execute it if arg[0] == '!': arg = self.backtick(arg[1:]) # utility function to deal with -D option def append_define(name, dict = dict): t = name.split('=') if len(t) == 1: dict['CPPDEFINES'].append(name) else: dict['CPPDEFINES'].append([t[0], '='.join(t[1:])]) # Loop through the flags and add them to the appropriate option. # This tries to strike a balance between checking for all possible # flags and keeping the logic to a finite size, so it doesn't # check for some that don't occur often. It particular, if the # flag is not known to occur in a config script and there's a way # of passing the flag to the right place (by wrapping it in a -W # flag, for example) we don't check for it. Note that most # preprocessor options are not handled, since unhandled options # are placed in CCFLAGS, so unless the preprocessor is invoked # separately, these flags will still get to the preprocessor. # Other options not currently handled: # -iqoutedir (preprocessor search path) # -u symbol (linker undefined symbol) # -s (linker strip files) # -static* (linker static binding) # -shared* (linker dynamic binding) # -symbolic (linker global binding) # -R dir (deprecated linker rpath) # IBM compilers may also accept -qframeworkdir=foo params = shlex.split(arg) append_next_arg_to = None # for multi-word args for arg in params: if append_next_arg_to: if append_next_arg_to == 'CPPDEFINES': append_define(arg) elif append_next_arg_to == '-include': t = ('-include', self.fs.File(arg)) dict['CCFLAGS'].append(t) elif append_next_arg_to == '-isysroot': t = ('-isysroot', arg) dict['CCFLAGS'].append(t) dict['LINKFLAGS'].append(t) elif append_next_arg_to == '-arch': t = ('-arch', arg) dict['CCFLAGS'].append(t) dict['LINKFLAGS'].append(t) else: dict[append_next_arg_to].append(arg) append_next_arg_to = None elif not arg[0] in ['-', '+']: dict['LIBS'].append(self.fs.File(arg)) elif arg == '-dylib_file': dict['LINKFLAGS'].append(arg) append_next_arg_to = 'LINKFLAGS' elif arg[:2] == '-L': if arg[2:]: dict['LIBPATH'].append(arg[2:]) else: append_next_arg_to = 'LIBPATH' elif arg[:2] == '-l': if arg[2:]: dict['LIBS'].append(arg[2:]) else: append_next_arg_to = 'LIBS' elif arg[:2] == '-I': if arg[2:]: dict['CPPPATH'].append(arg[2:]) else: append_next_arg_to = 'CPPPATH' elif arg[:4] == '-Wa,': dict['ASFLAGS'].append(arg[4:]) dict['CCFLAGS'].append(arg) elif arg[:4] == '-Wl,': if arg[:11] == '-Wl,-rpath=': dict['RPATH'].append(arg[11:]) elif arg[:7] == '-Wl,-R,': dict['RPATH'].append(arg[7:]) elif arg[:6] == '-Wl,-R': dict['RPATH'].append(arg[6:]) else: dict['LINKFLAGS'].append(arg) elif arg[:4] == '-Wp,': dict['CPPFLAGS'].append(arg) elif arg[:2] == '-D': if arg[2:]: append_define(arg[2:]) else: append_next_arg_to = 'CPPDEFINES' elif arg == '-framework': append_next_arg_to = 'FRAMEWORKS' elif arg[:14] == '-frameworkdir=': dict['FRAMEWORKPATH'].append(arg[14:]) elif arg[:2] == '-F': if arg[2:]: dict['FRAMEWORKPATH'].append(arg[2:]) else: append_next_arg_to = 'FRAMEWORKPATH' elif arg in ['-mno-cygwin', '-pthread', '-openmp', '-fopenmp']: dict['CCFLAGS'].append(arg) dict['LINKFLAGS'].append(arg) elif arg == '-mwindows': dict['LINKFLAGS'].append(arg) elif arg[:5] == '-std=': if arg[5:].find('++')!=-1: key='CXXFLAGS' else: key='CFLAGS' dict[key].append(arg) elif arg[0] == '+': dict['CCFLAGS'].append(arg) dict['LINKFLAGS'].append(arg) elif arg in ['-include', '-isysroot', '-arch']: append_next_arg_to = arg else: dict['CCFLAGS'].append(arg) for arg in flags: do_parse(arg) return dict def MergeFlags(self, args, unique=1, dict=None): """ Merge the dict in args into the construction variables of this env, or the passed-in dict. If args is not a dict, it is converted into a dict using ParseFlags. If unique is not set, the flags are appended rather than merged. """ if dict is None: dict = self if not SCons.Util.is_Dict(args): args = self.ParseFlags(args) if not unique: self.Append(**args) return self for key, value in args.items(): if not value: continue try: orig = self[key] except KeyError: orig = value else: if not orig: orig = value elif value: # Add orig and value. The logic here was lifted from # part of env.Append() (see there for a lot of comments # about the order in which things are tried) and is # used mainly to handle coercion of strings to CLVar to # "do the right thing" given (e.g.) an original CCFLAGS # string variable like '-pipe -Wall'. try: orig = orig + value except (KeyError, TypeError): try: add_to_orig = orig.append except AttributeError: value.insert(0, orig) orig = value else: add_to_orig(value) t = [] if key[-4:] == 'PATH': ### keep left-most occurence for v in orig: if v not in t: t.append(v) else: ### keep right-most occurence orig.reverse() for v in orig: if v not in t: t.insert(0, v) self[key] = t return self # def MergeShellPaths(self, args, prepend=1): # """ # Merge the dict in args into the shell environment in env['ENV']. # Shell path elements are appended or prepended according to prepend. # Uses Pre/AppendENVPath, so it always appends or prepends uniquely. # Example: env.MergeShellPaths({'LIBPATH': '/usr/local/lib'}) # prepends /usr/local/lib to env['ENV']['LIBPATH']. # """ # for pathname, pathval in args.items(): # if not pathval: # continue # if prepend: # self.PrependENVPath(pathname, pathval) # else: # self.AppendENVPath(pathname, pathval) def default_decide_source(dependency, target, prev_ni): f = SCons.Defaults.DefaultEnvironment().decide_source return f(dependency, target, prev_ni) def default_decide_target(dependency, target, prev_ni): f = SCons.Defaults.DefaultEnvironment().decide_target return f(dependency, target, prev_ni) def default_copy_from_cache(src, dst): f = SCons.Defaults.DefaultEnvironment().copy_from_cache return f(src, dst) class Base(SubstitutionEnvironment): """Base class for "real" construction Environments. These are the primary objects used to communicate dependency and construction information to the build engine. Keyword arguments supplied when the construction Environment is created are construction variables used to initialize the Environment. """ memoizer_counters = [] ####################################################################### # This is THE class for interacting with the SCons build engine, # and it contains a lot of stuff, so we're going to try to keep this # a little organized by grouping the methods. ####################################################################### ####################################################################### # Methods that make an Environment act like a dictionary. These have # the expected standard names for Python mapping objects. Note that # we don't actually make an Environment a subclass of UserDict for # performance reasons. Note also that we only supply methods for # dictionary functionality that we actually need and use. ####################################################################### def __init__(self, platform=None, tools=None, toolpath=None, variables=None, parse_flags = None, **kw): """ Initialization of a basic SCons construction environment, including setting up special construction variables like BUILDER, PLATFORM, etc., and searching for and applying available Tools. Note that we do *not* call the underlying base class (SubsitutionEnvironment) initialization, because we need to initialize things in a very specific order that doesn't work with the much simpler base class initialization. """ if __debug__: logInstanceCreation(self, 'Environment.Base') self._memo = {} self.fs = SCons.Node.FS.get_default_fs() self.ans = SCons.Node.Alias.default_ans self.lookup_list = SCons.Node.arg2nodes_lookups self._dict = semi_deepcopy(SCons.Defaults.ConstructionEnvironment) self._init_special() self.added_methods = [] # We don't use AddMethod, or define these as methods in this # class, because we *don't* want these functions to be bound # methods. They need to operate independently so that the # settings will work properly regardless of whether a given # target ends up being built with a Base environment or an # OverrideEnvironment or what have you. self.decide_target = default_decide_target self.decide_source = default_decide_source self.copy_from_cache = default_copy_from_cache self._dict['BUILDERS'] = BuilderDict(self._dict['BUILDERS'], self) if platform is None: platform = self._dict.get('PLATFORM', None) if platform is None: platform = SCons.Platform.Platform() if SCons.Util.is_String(platform): platform = SCons.Platform.Platform(platform) self._dict['PLATFORM'] = str(platform) platform(self) self._dict['HOST_OS'] = self._dict.get('HOST_OS',None) self._dict['HOST_ARCH'] = self._dict.get('HOST_ARCH',None) # Now set defaults for TARGET_{OS|ARCH} self._dict['TARGET_OS'] = self._dict.get('HOST_OS',None) self._dict['TARGET_ARCH'] = self._dict.get('HOST_ARCH',None) # Apply the passed-in and customizable variables to the # environment before calling the tools, because they may use # some of them during initialization. if 'options' in kw: # Backwards compatibility: they may stll be using the # old "options" keyword. variables = kw['options'] del kw['options'] self.Replace(**kw) keys = list(kw.keys()) if variables: keys = keys + list(variables.keys()) variables.Update(self) save = {} for k in keys: try: save[k] = self._dict[k] except KeyError: # No value may have been set if they tried to pass in a # reserved variable name like TARGETS. pass SCons.Tool.Initializers(self) if tools is None: tools = self._dict.get('TOOLS', None) if tools is None: tools = ['default'] apply_tools(self, tools, toolpath) # Now restore the passed-in and customized variables # to the environment, since the values the user set explicitly # should override any values set by the tools. for key, val in save.items(): self._dict[key] = val # Finally, apply any flags to be merged in if parse_flags: self.MergeFlags(parse_flags) ####################################################################### # Utility methods that are primarily for internal use by SCons. # These begin with lower-case letters. ####################################################################### def get_builder(self, name): """Fetch the builder with the specified name from the environment. """ try: return self._dict['BUILDERS'][name] except KeyError: return None def get_CacheDir(self): try: path = self._CacheDir_path except AttributeError: path = SCons.Defaults.DefaultEnvironment()._CacheDir_path try: if path == self._last_CacheDir_path: return self._last_CacheDir except AttributeError: pass cd = SCons.CacheDir.CacheDir(path) self._last_CacheDir_path = path self._last_CacheDir = cd return cd def get_factory(self, factory, default='File'): """Return a factory function for creating Nodes for this construction environment. """ name = default try: is_node = issubclass(factory, SCons.Node.FS.Base) except TypeError: # The specified factory isn't a Node itself--it's # most likely None, or possibly a callable. pass else: if is_node: # The specified factory is a Node (sub)class. Try to # return the FS method that corresponds to the Node's # name--that is, we return self.fs.Dir if they want a Dir, # self.fs.File for a File, etc. try: name = factory.__name__ except AttributeError: pass else: factory = None if not factory: # They passed us None, or we picked up a name from a specified # class, so return the FS method. (Note that we *don't* # use our own self.{Dir,File} methods because that would # cause env.subst() to be called twice on the file name, # interfering with files that have $$ in them.) factory = getattr(self.fs, name) return factory memoizer_counters.append(SCons.Memoize.CountValue('_gsm')) def _gsm(self): try: return self._memo['_gsm'] except KeyError: pass result = {} try: scanners = self._dict['SCANNERS'] except KeyError: pass else: # Reverse the scanner list so that, if multiple scanners # claim they can scan the same suffix, earlier scanners # in the list will overwrite later scanners, so that # the result looks like a "first match" to the user. if not SCons.Util.is_List(scanners): scanners = [scanners] else: scanners = scanners[:] # copy so reverse() doesn't mod original scanners.reverse() for scanner in scanners: for k in scanner.get_skeys(self): if k and self['PLATFORM'] == 'win32': k = k.lower() result[k] = scanner self._memo['_gsm'] = result return result def get_scanner(self, skey): """Find the appropriate scanner given a key (usually a file suffix). """ if skey and self['PLATFORM'] == 'win32': skey = skey.lower() return self._gsm().get(skey) def scanner_map_delete(self, kw=None): """Delete the cached scanner map (if we need to). """ try: del self._memo['_gsm'] except KeyError: pass def _update(self, dict): """Update an environment's values directly, bypassing the normal checks that occur when users try to set items. """ self._dict.update(dict) def get_src_sig_type(self): try: return self.src_sig_type except AttributeError: t = SCons.Defaults.DefaultEnvironment().src_sig_type self.src_sig_type = t return t def get_tgt_sig_type(self): try: return self.tgt_sig_type except AttributeError: t = SCons.Defaults.DefaultEnvironment().tgt_sig_type self.tgt_sig_type = t return t ####################################################################### # Public methods for manipulating an Environment. These begin with # upper-case letters. The essential characteristic of methods in # this section is that they do *not* have corresponding same-named # global functions. For example, a stand-alone Append() function # makes no sense, because Append() is all about appending values to # an Environment's construction variables. ####################################################################### def Append(self, **kw): """Append values to existing construction variables in an Environment. """ kw = copy_non_reserved_keywords(kw) for key, val in kw.items(): # It would be easier on the eyes to write this using # "continue" statements whenever we finish processing an item, # but Python 1.5.2 apparently doesn't let you use "continue" # within try:-except: blocks, so we have to nest our code. try: if key == 'CPPDEFINES' and SCons.Util.is_String(self._dict[key]): self._dict[key] = [self._dict[key]] orig = self._dict[key] except KeyError: # No existing variable in the environment, so just set # it to the new value. if key == 'CPPDEFINES' and SCons.Util.is_String(val): self._dict[key] = [val] else: self._dict[key] = val else: try: # Check if the original looks like a dictionary. # If it is, we can't just try adding the value because # dictionaries don't have __add__() methods, and # things like UserList will incorrectly coerce the # original dict to a list (which we don't want). update_dict = orig.update except AttributeError: try: # Most straightforward: just try to add them # together. This will work in most cases, when the # original and new values are of compatible types. self._dict[key] = orig + val except (KeyError, TypeError): try: # Check if the original is a list. add_to_orig = orig.append except AttributeError: # The original isn't a list, but the new # value is (by process of elimination), # so insert the original in the new value # (if there's one to insert) and replace # the variable with it. if orig: val.insert(0, orig) self._dict[key] = val else: # The original is a list, so append the new # value to it (if there's a value to append). if val: add_to_orig(val) else: # The original looks like a dictionary, so update it # based on what we think the value looks like. if SCons.Util.is_List(val): if key == 'CPPDEFINES': orig = orig.items() orig += val self._dict[key] = orig else: for v in val: orig[v] = None else: try: update_dict(val) except (AttributeError, TypeError, ValueError): if SCons.Util.is_Dict(val): for k, v in val.items(): orig[k] = v else: orig[val] = None self.scanner_map_delete(kw) # allow Dirs and strings beginning with # for top-relative # Note this uses the current env's fs (in self). def _canonicalize(self, path): if not SCons.Util.is_String(path): # typically a Dir path = str(path) if path and path[0] == '#': path = str(self.fs.Dir(path)) return path def AppendENVPath(self, name, newpath, envname = 'ENV', sep = os.pathsep, delete_existing=1): """Append path elements to the path 'name' in the 'ENV' dictionary for this environment. Will only add any particular path once, and will normpath and normcase all paths to help assure this. This can also handle the case where the env variable is a list instead of a string. If delete_existing is 0, a newpath which is already in the path will not be moved to the end (it will be left where it is). """ orig = '' if envname in self._dict and name in self._dict[envname]: orig = self._dict[envname][name] nv = SCons.Util.AppendPath(orig, newpath, sep, delete_existing, canonicalize=self._canonicalize) if envname not in self._dict: self._dict[envname] = {} self._dict[envname][name] = nv def AppendUnique(self, delete_existing=0, **kw): """Append values to existing construction variables in an Environment, if they're not already there. If delete_existing is 1, removes existing values first, so values move to end. """ kw = copy_non_reserved_keywords(kw) for key, val in kw.items(): if SCons.Util.is_List(val): val = _delete_duplicates(val, delete_existing) if key not in self._dict or self._dict[key] in ('', None): self._dict[key] = val elif SCons.Util.is_Dict(self._dict[key]) and \ SCons.Util.is_Dict(val): self._dict[key].update(val) elif SCons.Util.is_List(val): dk = self._dict[key] if key == 'CPPDEFINES': tmp = [] for i in val: if SCons.Util.is_List(i): if len(i) >= 2: tmp.append((i[0], i[1])) else: tmp.append((i[0],)) elif SCons.Util.is_Tuple(i): tmp.append(i) else: tmp.append((i,)) val = tmp if SCons.Util.is_Dict(dk): dk = dk.items() elif SCons.Util.is_String(dk): dk = [(dk,)] else: tmp = [] for i in dk: if SCons.Util.is_List(i): if len(i) >= 2: tmp.append((i[0], i[1])) else: tmp.append((i[0],)) elif SCons.Util.is_Tuple(i): tmp.append(i) else: tmp.append((i,)) dk = tmp else: if not SCons.Util.is_List(dk): dk = [dk] if delete_existing: dk = [x for x in dk if x not in val] else: val = [x for x in val if x not in dk] self._dict[key] = dk + val else: dk = self._dict[key] if SCons.Util.is_List(dk): if key == 'CPPDEFINES': tmp = [] for i in dk: if SCons.Util.is_List(i): if len(i) >= 2: tmp.append((i[0], i[1])) else: tmp.append((i[0],)) elif SCons.Util.is_Tuple(i): tmp.append(i) else: tmp.append((i,)) dk = tmp if SCons.Util.is_Dict(val): val = val.items() elif SCons.Util.is_String(val): val = [(val,)] if delete_existing: dk = filter(lambda x, val=val: x not in val, dk) self._dict[key] = dk + val else: dk = [x for x in dk if x not in val] self._dict[key] = dk + val else: # By elimination, val is not a list. Since dk is a # list, wrap val in a list first. if delete_existing: dk = filter(lambda x, val=val: x not in val, dk) self._dict[key] = dk + [val] else: if not val in dk: self._dict[key] = dk + [val] else: if key == 'CPPDEFINES': if SCons.Util.is_String(dk): dk = [dk] elif SCons.Util.is_Dict(dk): dk = dk.items() if SCons.Util.is_String(val): if val in dk: val = [] else: val = [val] elif SCons.Util.is_Dict(val): tmp = [] for i,j in val.iteritems(): if j is not None: tmp.append((i,j)) else: tmp.append(i) val = tmp if delete_existing: dk = [x for x in dk if x not in val] self._dict[key] = dk + val self.scanner_map_delete(kw) def Clone(self, tools=[], toolpath=None, parse_flags = None, **kw): """Return a copy of a construction Environment. The copy is like a Python "deep copy"--that is, independent copies are made recursively of each objects--except that a reference is copied when an object is not deep-copyable (like a function). There are no references to any mutable objects in the original Environment. """ try: builders = self._dict['BUILDERS'] except KeyError: pass clone = copy.copy(self) # BUILDERS is not safe to do a simple copy clone._dict = semi_deepcopy_dict(self._dict, ['BUILDERS']) clone._dict['BUILDERS'] = BuilderDict(builders, clone) # Check the methods added via AddMethod() and re-bind them to # the cloned environment. Only do this if the attribute hasn't # been overwritten by the user explicitly and still points to # the added method. clone.added_methods = [] for mw in self.added_methods: if mw == getattr(self, mw.name): clone.added_methods.append(mw.clone(clone)) clone._memo = {} # Apply passed-in variables before the tools # so the tools can use the new variables kw = copy_non_reserved_keywords(kw) new = {} for key, value in kw.items(): new[key] = SCons.Subst.scons_subst_once(value, self, key) clone.Replace(**new) apply_tools(clone, tools, toolpath) # apply them again in case the tools overwrote them clone.Replace(**new) # Finally, apply any flags to be merged in if parse_flags: clone.MergeFlags(parse_flags) if __debug__: logInstanceCreation(self, 'Environment.EnvironmentClone') return clone def Copy(self, *args, **kw): global _warn_copy_deprecated if _warn_copy_deprecated: msg = "The env.Copy() method is deprecated; use the env.Clone() method instead." SCons.Warnings.warn(SCons.Warnings.DeprecatedCopyWarning, msg) _warn_copy_deprecated = False return self.Clone(*args, **kw) def _changed_build(self, dependency, target, prev_ni): if dependency.changed_state(target, prev_ni): return 1 return self.decide_source(dependency, target, prev_ni) def _changed_content(self, dependency, target, prev_ni): return dependency.changed_content(target, prev_ni) def _changed_source(self, dependency, target, prev_ni): target_env = dependency.get_build_env() type = target_env.get_tgt_sig_type() if type == 'source': return target_env.decide_source(dependency, target, prev_ni) else: return target_env.decide_target(dependency, target, prev_ni) def _changed_timestamp_then_content(self, dependency, target, prev_ni): return dependency.changed_timestamp_then_content(target, prev_ni) def _changed_timestamp_newer(self, dependency, target, prev_ni): return dependency.changed_timestamp_newer(target, prev_ni) def _changed_timestamp_match(self, dependency, target, prev_ni): return dependency.changed_timestamp_match(target, prev_ni) def _copy_from_cache(self, src, dst): return self.fs.copy(src, dst) def _copy2_from_cache(self, src, dst): return self.fs.copy2(src, dst) def Decider(self, function): copy_function = self._copy2_from_cache if function in ('MD5', 'content'): if not SCons.Util.md5: raise UserError("MD5 signatures are not available in this version of Python.") function = self._changed_content elif function == 'MD5-timestamp': function = self._changed_timestamp_then_content elif function in ('timestamp-newer', 'make'): function = self._changed_timestamp_newer copy_function = self._copy_from_cache elif function == 'timestamp-match': function = self._changed_timestamp_match elif not callable(function): raise UserError("Unknown Decider value %s" % repr(function)) # We don't use AddMethod because we don't want to turn the # function, which only expects three arguments, into a bound # method, which would add self as an initial, fourth argument. self.decide_target = function self.decide_source = function self.copy_from_cache = copy_function def Detect(self, progs): """Return the first available program in progs. """ if not SCons.Util.is_List(progs): progs = [ progs ] for prog in progs: path = self.WhereIs(prog) if path: return prog return None def Dictionary(self, *args): if not args: return self._dict dlist = [self._dict[x] for x in args] if len(dlist) == 1: dlist = dlist[0] return dlist def Dump(self, key = None): """ Using the standard Python pretty printer, dump the contents of the scons build environment to stdout. If the key passed in is anything other than None, then that will be used as an index into the build environment dictionary and whatever is found there will be fed into the pretty printer. Note that this key is case sensitive. """ import pprint pp = pprint.PrettyPrinter(indent=2) if key: dict = self.Dictionary(key) else: dict = self.Dictionary() return pp.pformat(dict) def FindIxes(self, paths, prefix, suffix): """ Search a list of paths for something that matches the prefix and suffix. paths - the list of paths or nodes. prefix - construction variable for the prefix. suffix - construction variable for the suffix. """ suffix = self.subst('$'+suffix) prefix = self.subst('$'+prefix) for path in paths: dir,name = os.path.split(str(path)) if name[:len(prefix)] == prefix and name[-len(suffix):] == suffix: return path def ParseConfig(self, command, function=None, unique=1): """ Use the specified function to parse the output of the command in order to modify the current environment. The 'command' can be a string or a list of strings representing a command and its arguments. 'Function' is an optional argument that takes the environment, the output of the command, and the unique flag. If no function is specified, MergeFlags, which treats the output as the result of a typical 'X-config' command (i.e. gtk-config), will merge the output into the appropriate variables. """ if function is None: def parse_conf(env, cmd, unique=unique): return env.MergeFlags(cmd, unique) function = parse_conf if SCons.Util.is_List(command): command = ' '.join(command) command = self.subst(command) return function(self, self.backtick(command)) def ParseDepends(self, filename, must_exist=None, only_one=0): """ Parse a mkdep-style file for explicit dependencies. This is completely abusable, and should be unnecessary in the "normal" case of proper SCons configuration, but it may help make the transition from a Make hierarchy easier for some people to swallow. It can also be genuinely useful when using a tool that can write a .d file, but for which writing a scanner would be too complicated. """ filename = self.subst(filename) try: fp = open(filename, 'r') except IOError: if must_exist: raise return lines = SCons.Util.LogicalLines(fp).readlines() lines = [l for l in lines if l[0] != '#'] tdlist = [] for line in lines: try: target, depends = line.split(':', 1) except (AttributeError, ValueError): # Throws AttributeError if line isn't a string. Can throw # ValueError if line doesn't split into two or more elements. pass else: tdlist.append((target.split(), depends.split())) if only_one: targets = [] for td in tdlist: targets.extend(td[0]) if len(targets) > 1: raise SCons.Errors.UserError( "More than one dependency target found in `%s': %s" % (filename, targets)) for target, depends in tdlist: self.Depends(target, depends) def Platform(self, platform): platform = self.subst(platform) return SCons.Platform.Platform(platform)(self) def Prepend(self, **kw): """Prepend values to existing construction variables in an Environment. """ kw = copy_non_reserved_keywords(kw) for key, val in kw.items(): # It would be easier on the eyes to write this using # "continue" statements whenever we finish processing an item, # but Python 1.5.2 apparently doesn't let you use "continue" # within try:-except: blocks, so we have to nest our code. try: orig = self._dict[key] except KeyError: # No existing variable in the environment, so just set # it to the new value. self._dict[key] = val else: try: # Check if the original looks like a dictionary. # If it is, we can't just try adding the value because # dictionaries don't have __add__() methods, and # things like UserList will incorrectly coerce the # original dict to a list (which we don't want). update_dict = orig.update except AttributeError: try: # Most straightforward: just try to add them # together. This will work in most cases, when the # original and new values are of compatible types. self._dict[key] = val + orig except (KeyError, TypeError): try: # Check if the added value is a list. add_to_val = val.append except AttributeError: # The added value isn't a list, but the # original is (by process of elimination), # so insert the the new value in the original # (if there's one to insert). if val: orig.insert(0, val) else: # The added value is a list, so append # the original to it (if there's a value # to append). if orig: add_to_val(orig) self._dict[key] = val else: # The original looks like a dictionary, so update it # based on what we think the value looks like. if SCons.Util.is_List(val): for v in val: orig[v] = None else: try: update_dict(val) except (AttributeError, TypeError, ValueError): if SCons.Util.is_Dict(val): for k, v in val.items(): orig[k] = v else: orig[val] = None self.scanner_map_delete(kw) def PrependENVPath(self, name, newpath, envname = 'ENV', sep = os.pathsep, delete_existing=1): """Prepend path elements to the path 'name' in the 'ENV' dictionary for this environment. Will only add any particular path once, and will normpath and normcase all paths to help assure this. This can also handle the case where the env variable is a list instead of a string. If delete_existing is 0, a newpath which is already in the path will not be moved to the front (it will be left where it is). """ orig = '' if envname in self._dict and name in self._dict[envname]: orig = self._dict[envname][name] nv = SCons.Util.PrependPath(orig, newpath, sep, delete_existing, canonicalize=self._canonicalize) if envname not in self._dict: self._dict[envname] = {} self._dict[envname][name] = nv def PrependUnique(self, delete_existing=0, **kw): """Prepend values to existing construction variables in an Environment, if they're not already there. If delete_existing is 1, removes existing values first, so values move to front. """ kw = copy_non_reserved_keywords(kw) for key, val in kw.items(): if SCons.Util.is_List(val): val = _delete_duplicates(val, not delete_existing) if key not in self._dict or self._dict[key] in ('', None): self._dict[key] = val elif SCons.Util.is_Dict(self._dict[key]) and \ SCons.Util.is_Dict(val): self._dict[key].update(val) elif SCons.Util.is_List(val): dk = self._dict[key] if not SCons.Util.is_List(dk): dk = [dk] if delete_existing: dk = [x for x in dk if x not in val] else: val = [x for x in val if x not in dk] self._dict[key] = val + dk else: dk = self._dict[key] if SCons.Util.is_List(dk): # By elimination, val is not a list. Since dk is a # list, wrap val in a list first. if delete_existing: dk = [x for x in dk if x not in val] self._dict[key] = [val] + dk else: if not val in dk: self._dict[key] = [val] + dk else: if delete_existing: dk = [x for x in dk if x not in val] self._dict[key] = val + dk self.scanner_map_delete(kw) def Replace(self, **kw): """Replace existing construction variables in an Environment with new construction variables and/or values. """ try: kwbd = kw['BUILDERS'] except KeyError: pass else: kwbd = BuilderDict(kwbd,self) del kw['BUILDERS'] self.__setitem__('BUILDERS', kwbd) kw = copy_non_reserved_keywords(kw) self._update(semi_deepcopy(kw)) self.scanner_map_delete(kw) def ReplaceIxes(self, path, old_prefix, old_suffix, new_prefix, new_suffix): """ Replace old_prefix with new_prefix and old_suffix with new_suffix. env - Environment used to interpolate variables. path - the path that will be modified. old_prefix - construction variable for the old prefix. old_suffix - construction variable for the old suffix. new_prefix - construction variable for the new prefix. new_suffix - construction variable for the new suffix. """ old_prefix = self.subst('$'+old_prefix) old_suffix = self.subst('$'+old_suffix) new_prefix = self.subst('$'+new_prefix) new_suffix = self.subst('$'+new_suffix) dir,name = os.path.split(str(path)) if name[:len(old_prefix)] == old_prefix: name = name[len(old_prefix):] if name[-len(old_suffix):] == old_suffix: name = name[:-len(old_suffix)] return os.path.join(dir, new_prefix+name+new_suffix) def SetDefault(self, **kw): for k in kw.keys(): if k in self._dict: del kw[k] self.Replace(**kw) def _find_toolpath_dir(self, tp): return self.fs.Dir(self.subst(tp)).srcnode().abspath def Tool(self, tool, toolpath=None, **kw): if SCons.Util.is_String(tool): tool = self.subst(tool) if toolpath is None: toolpath = self.get('toolpath', []) toolpath = list(map(self._find_toolpath_dir, toolpath)) tool = SCons.Tool.Tool(tool, toolpath, **kw) tool(self) def WhereIs(self, prog, path=None, pathext=None, reject=[]): """Find prog in the path. """ if path is None: try: path = self['ENV']['PATH'] except KeyError: pass elif SCons.Util.is_String(path): path = self.subst(path) if pathext is None: try: pathext = self['ENV']['PATHEXT'] except KeyError: pass elif SCons.Util.is_String(pathext): pathext = self.subst(pathext) prog = self.subst(prog) path = SCons.Util.WhereIs(prog, path, pathext, reject) if path: return path return None ####################################################################### # Public methods for doing real "SCons stuff" (manipulating # dependencies, setting attributes on targets, etc.). These begin # with upper-case letters. The essential characteristic of methods # in this section is that they all *should* have corresponding # same-named global functions. ####################################################################### def Action(self, *args, **kw): def subst_string(a, self=self): if SCons.Util.is_String(a): a = self.subst(a) return a nargs = list(map(subst_string, args)) nkw = self.subst_kw(kw) return SCons.Action.Action(*nargs, **nkw) def AddPreAction(self, files, action): nodes = self.arg2nodes(files, self.fs.Entry) action = SCons.Action.Action(action) uniq = {} for executor in [n.get_executor() for n in nodes]: uniq[executor] = 1 for executor in uniq.keys(): executor.add_pre_action(action) return nodes def AddPostAction(self, files, action): nodes = self.arg2nodes(files, self.fs.Entry) action = SCons.Action.Action(action) uniq = {} for executor in [n.get_executor() for n in nodes]: uniq[executor] = 1 for executor in uniq.keys(): executor.add_post_action(action) return nodes def Alias(self, target, source=[], action=None, **kw): tlist = self.arg2nodes(target, self.ans.Alias) if not SCons.Util.is_List(source): source = [source] source = [_f for _f in source if _f] if not action: if not source: # There are no source files and no action, so just # return a target list of classic Alias Nodes, without # any builder. The externally visible effect is that # this will make the wrapping Script.BuildTask class # say that there's "Nothing to be done" for this Alias, # instead of that it's "up to date." return tlist # No action, but there are sources. Re-call all the target # builders to add the sources to each target. result = [] for t in tlist: bld = t.get_builder(AliasBuilder) result.extend(bld(self, t, source)) return result nkw = self.subst_kw(kw) nkw.update({ 'action' : SCons.Action.Action(action), 'source_factory' : self.fs.Entry, 'multi' : 1, 'is_explicit' : None, }) bld = SCons.Builder.Builder(**nkw) # Apply the Builder separately to each target so that the Aliases # stay separate. If we did one "normal" Builder call with the # whole target list, then all of the target Aliases would be # associated under a single Executor. result = [] for t in tlist: # Calling the convert() method will cause a new Executor to be # created from scratch, so we have to explicitly initialize # it with the target's existing sources, plus our new ones, # so nothing gets lost. b = t.get_builder() if b is None or b is AliasBuilder: b = bld else: nkw['action'] = b.action + action b = SCons.Builder.Builder(**nkw) t.convert() result.extend(b(self, t, t.sources + source)) return result def AlwaysBuild(self, *targets): tlist = [] for t in targets: tlist.extend(self.arg2nodes(t, self.fs.Entry)) for t in tlist: t.set_always_build() return tlist def BuildDir(self, *args, **kw): msg = """BuildDir() and the build_dir keyword have been deprecated;\n\tuse VariantDir() and the variant_dir keyword instead.""" SCons.Warnings.warn(SCons.Warnings.DeprecatedBuildDirWarning, msg) if 'build_dir' in kw: kw['variant_dir'] = kw['build_dir'] del kw['build_dir'] return self.VariantDir(*args, **kw) def Builder(self, **kw): nkw = self.subst_kw(kw) return SCons.Builder.Builder(**nkw) def CacheDir(self, path): import SCons.CacheDir if path is not None: path = self.subst(path) self._CacheDir_path = path def Clean(self, targets, files): global CleanTargets tlist = self.arg2nodes(targets, self.fs.Entry) flist = self.arg2nodes(files, self.fs.Entry) for t in tlist: try: CleanTargets[t].extend(flist) except KeyError: CleanTargets[t] = flist def Configure(self, *args, **kw): nargs = [self] if args: nargs = nargs + self.subst_list(args)[0] nkw = self.subst_kw(kw) nkw['_depth'] = kw.get('_depth', 0) + 1 try: nkw['custom_tests'] = self.subst_kw(nkw['custom_tests']) except KeyError: pass return SCons.SConf.SConf(*nargs, **nkw) def Command(self, target, source, action, **kw): """Builds the supplied target files from the supplied source files using the supplied action. Action may be any type that the Builder constructor will accept for an action.""" bkw = { 'action' : action, 'target_factory' : self.fs.Entry, 'source_factory' : self.fs.Entry, } try: bkw['source_scanner'] = kw['source_scanner'] except KeyError: pass else: del kw['source_scanner'] bld = SCons.Builder.Builder(**bkw) return bld(self, target, source, **kw) def Depends(self, target, dependency): """Explicity specify that 'target's depend on 'dependency'.""" tlist = self.arg2nodes(target, self.fs.Entry) dlist = self.arg2nodes(dependency, self.fs.Entry) for t in tlist: t.add_dependency(dlist) return tlist def Dir(self, name, *args, **kw): """ """ s = self.subst(name) if SCons.Util.is_Sequence(s): result=[] for e in s: result.append(self.fs.Dir(e, *args, **kw)) return result return self.fs.Dir(s, *args, **kw) def NoClean(self, *targets): """Tags a target so that it will not be cleaned by -c""" tlist = [] for t in targets: tlist.extend(self.arg2nodes(t, self.fs.Entry)) for t in tlist: t.set_noclean() return tlist def NoCache(self, *targets): """Tags a target so that it will not be cached""" tlist = [] for t in targets: tlist.extend(self.arg2nodes(t, self.fs.Entry)) for t in tlist: t.set_nocache() return tlist def Entry(self, name, *args, **kw): """ """ s = self.subst(name) if SCons.Util.is_Sequence(s): result=[] for e in s: result.append(self.fs.Entry(e, *args, **kw)) return result return self.fs.Entry(s, *args, **kw) def Environment(self, **kw): return SCons.Environment.Environment(**self.subst_kw(kw)) def Execute(self, action, *args, **kw): """Directly execute an action through an Environment """ action = self.Action(action, *args, **kw) result = action([], [], self) if isinstance(result, SCons.Errors.BuildError): errstr = result.errstr if result.filename: errstr = result.filename + ': ' + errstr sys.stderr.write("scons: *** %s\n" % errstr) return result.status else: return result def File(self, name, *args, **kw): """ """ s = self.subst(name) if SCons.Util.is_Sequence(s): result=[] for e in s: result.append(self.fs.File(e, *args, **kw)) return result return self.fs.File(s, *args, **kw) def FindFile(self, file, dirs): file = self.subst(file) nodes = self.arg2nodes(dirs, self.fs.Dir) return SCons.Node.FS.find_file(file, tuple(nodes)) def Flatten(self, sequence): return SCons.Util.flatten(sequence) def GetBuildPath(self, files): result = list(map(str, self.arg2nodes(files, self.fs.Entry))) if SCons.Util.is_List(files): return result else: return result[0] def Glob(self, pattern, ondisk=True, source=False, strings=False): return self.fs.Glob(self.subst(pattern), ondisk, source, strings) def Ignore(self, target, dependency): """Ignore a dependency.""" tlist = self.arg2nodes(target, self.fs.Entry) dlist = self.arg2nodes(dependency, self.fs.Entry) for t in tlist: t.add_ignore(dlist) return tlist def Literal(self, string): return SCons.Subst.Literal(string) def Local(self, *targets): ret = [] for targ in targets: if isinstance(targ, SCons.Node.Node): targ.set_local() ret.append(targ) else: for t in self.arg2nodes(targ, self.fs.Entry): t.set_local() ret.append(t) return ret def Precious(self, *targets): tlist = [] for t in targets: tlist.extend(self.arg2nodes(t, self.fs.Entry)) for t in tlist: t.set_precious() return tlist def Repository(self, *dirs, **kw): dirs = self.arg2nodes(list(dirs), self.fs.Dir) self.fs.Repository(*dirs, **kw) def Requires(self, target, prerequisite): """Specify that 'prerequisite' must be built before 'target', (but 'target' does not actually depend on 'prerequisite' and need not be rebuilt if it changes).""" tlist = self.arg2nodes(target, self.fs.Entry) plist = self.arg2nodes(prerequisite, self.fs.Entry) for t in tlist: t.add_prerequisite(plist) return tlist def Scanner(self, *args, **kw): nargs = [] for arg in args: if SCons.Util.is_String(arg): arg = self.subst(arg) nargs.append(arg) nkw = self.subst_kw(kw) return SCons.Scanner.Base(*nargs, **nkw) def SConsignFile(self, name=".sconsign", dbm_module=None): if name is not None: name = self.subst(name) if not os.path.isabs(name): name = os.path.join(str(self.fs.SConstruct_dir), name) if name: name = os.path.normpath(name) sconsign_dir = os.path.dirname(name) if sconsign_dir and not os.path.exists(sconsign_dir): self.Execute(SCons.Defaults.Mkdir(sconsign_dir)) SCons.SConsign.File(name, dbm_module) def SideEffect(self, side_effect, target): """Tell scons that side_effects are built as side effects of building targets.""" side_effects = self.arg2nodes(side_effect, self.fs.Entry) targets = self.arg2nodes(target, self.fs.Entry) for side_effect in side_effects: if side_effect.multiple_side_effect_has_builder(): raise SCons.Errors.UserError("Multiple ways to build the same target were specified for: %s" % str(side_effect)) side_effect.add_source(targets) side_effect.side_effect = 1 self.Precious(side_effect) for target in targets: target.side_effects.append(side_effect) return side_effects def SourceCode(self, entry, builder): """Arrange for a source code builder for (part of) a tree.""" msg = """SourceCode() has been deprecated and there is no replacement. \tIf you need this function, please contact dev@scons.tigris.org.""" SCons.Warnings.warn(SCons.Warnings.DeprecatedSourceCodeWarning, msg) entries = self.arg2nodes(entry, self.fs.Entry) for entry in entries: entry.set_src_builder(builder) return entries def SourceSignatures(self, type): global _warn_source_signatures_deprecated if _warn_source_signatures_deprecated: msg = "The env.SourceSignatures() method is deprecated;\n" + \ "\tconvert your build to use the env.Decider() method instead." SCons.Warnings.warn(SCons.Warnings.DeprecatedSourceSignaturesWarning, msg) _warn_source_signatures_deprecated = False type = self.subst(type) self.src_sig_type = type if type == 'MD5': if not SCons.Util.md5: raise UserError("MD5 signatures are not available in this version of Python.") self.decide_source = self._changed_content elif type == 'timestamp': self.decide_source = self._changed_timestamp_match else: raise UserError("Unknown source signature type '%s'" % type) def Split(self, arg): """This function converts a string or list into a list of strings or Nodes. This makes things easier for users by allowing files to be specified as a white-space separated list to be split. The input rules are: - A single string containing names separated by spaces. These will be split apart at the spaces. - A single Node instance - A list containing either strings or Node instances. Any strings in the list are not split at spaces. In all cases, the function returns a list of Nodes and strings.""" if SCons.Util.is_List(arg): return list(map(self.subst, arg)) elif SCons.Util.is_String(arg): return self.subst(arg).split() else: return [self.subst(arg)] def TargetSignatures(self, type): global _warn_target_signatures_deprecated if _warn_target_signatures_deprecated: msg = "The env.TargetSignatures() method is deprecated;\n" + \ "\tconvert your build to use the env.Decider() method instead." SCons.Warnings.warn(SCons.Warnings.DeprecatedTargetSignaturesWarning, msg) _warn_target_signatures_deprecated = False type = self.subst(type) self.tgt_sig_type = type if type in ('MD5', 'content'): if not SCons.Util.md5: raise UserError("MD5 signatures are not available in this version of Python.") self.decide_target = self._changed_content elif type == 'timestamp': self.decide_target = self._changed_timestamp_match elif type == 'build': self.decide_target = self._changed_build elif type == 'source': self.decide_target = self._changed_source else: raise UserError("Unknown target signature type '%s'"%type) def Value(self, value, built_value=None): """ """ return SCons.Node.Python.Value(value, built_value) def VariantDir(self, variant_dir, src_dir, duplicate=1): variant_dir = self.arg2nodes(variant_dir, self.fs.Dir)[0] src_dir = self.arg2nodes(src_dir, self.fs.Dir)[0] self.fs.VariantDir(variant_dir, src_dir, duplicate) def FindSourceFiles(self, node='.'): """ returns a list of all source files. """ node = self.arg2nodes(node, self.fs.Entry)[0] sources = [] def build_source(ss): for s in ss: if isinstance(s, SCons.Node.FS.Dir): build_source(s.all_children()) elif s.has_builder(): build_source(s.sources) elif isinstance(s.disambiguate(), SCons.Node.FS.File): sources.append(s) build_source(node.all_children()) def final_source(node): while (node != node.srcnode()): node = node.srcnode() return node sources = map( final_source, sources ); # remove duplicates return list(set(sources)) def FindInstalledFiles(self): """ returns the list of all targets of the Install and InstallAs Builder. """ from SCons.Tool import install if install._UNIQUE_INSTALLED_FILES is None: install._UNIQUE_INSTALLED_FILES = SCons.Util.uniquer_hashables(install._INSTALLED_FILES) return install._UNIQUE_INSTALLED_FILES class OverrideEnvironment(Base): """A proxy that overrides variables in a wrapped construction environment by returning values from an overrides dictionary in preference to values from the underlying subject environment. This is a lightweight (I hope) proxy that passes through most use of attributes to the underlying Environment.Base class, but has just enough additional methods defined to act like a real construction environment with overridden values. It can wrap either a Base construction environment, or another OverrideEnvironment, which can in turn nest arbitrary OverrideEnvironments... Note that we do *not* call the underlying base class (SubsitutionEnvironment) initialization, because we get most of those from proxying the attributes of the subject construction environment. But because we subclass SubstitutionEnvironment, this class also has inherited arg2nodes() and subst*() methods; those methods can't be proxied because they need *this* object's methods to fetch the values from the overrides dictionary. """ def __init__(self, subject, overrides={}): if __debug__: logInstanceCreation(self, 'Environment.OverrideEnvironment') self.__dict__['__subject'] = subject self.__dict__['overrides'] = overrides # Methods that make this class act like a proxy. def __getattr__(self, name): return getattr(self.__dict__['__subject'], name) def __setattr__(self, name, value): setattr(self.__dict__['__subject'], name, value) # Methods that make this class act like a dictionary. def __getitem__(self, key): try: return self.__dict__['overrides'][key] except KeyError: return self.__dict__['__subject'].__getitem__(key) def __setitem__(self, key, value): if not is_valid_construction_var(key): raise SCons.Errors.UserError("Illegal construction variable `%s'" % key) self.__dict__['overrides'][key] = value def __delitem__(self, key): try: del self.__dict__['overrides'][key] except KeyError: deleted = 0 else: deleted = 1 try: result = self.__dict__['__subject'].__delitem__(key) except KeyError: if not deleted: raise result = None return result def get(self, key, default=None): """Emulates the get() method of dictionaries.""" try: return self.__dict__['overrides'][key] except KeyError: return self.__dict__['__subject'].get(key, default) def has_key(self, key): try: self.__dict__['overrides'][key] return 1 except KeyError: return key in self.__dict__['__subject'] def __contains__(self, key): if self.__dict__['overrides'].__contains__(key): return 1 return self.__dict__['__subject'].__contains__(key) def Dictionary(self): """Emulates the items() method of dictionaries.""" d = self.__dict__['__subject'].Dictionary().copy() d.update(self.__dict__['overrides']) return d def items(self): """Emulates the items() method of dictionaries.""" return list(self.Dictionary().items()) # Overridden private construction environment methods. def _update(self, dict): """Update an environment's values directly, bypassing the normal checks that occur when users try to set items. """ self.__dict__['overrides'].update(dict) def gvars(self): return self.__dict__['__subject'].gvars() def lvars(self): lvars = self.__dict__['__subject'].lvars() lvars.update(self.__dict__['overrides']) return lvars # Overridden public construction environment methods. def Replace(self, **kw): kw = copy_non_reserved_keywords(kw) self.__dict__['overrides'].update(semi_deepcopy(kw)) # The entry point that will be used by the external world # to refer to a construction environment. This allows the wrapper # interface to extend a construction environment for its own purposes # by subclassing SCons.Environment.Base and then assigning the # class to SCons.Environment.Environment. Environment = Base # An entry point for returning a proxy subclass instance that overrides # the subst*() methods so they don't actually perform construction # variable substitution. This is specifically intended to be the shim # layer in between global function calls (which don't want construction # variable substitution) and the DefaultEnvironment() (which would # substitute variables if left to its own devices).""" # # We have to wrap this in a function that allows us to delay definition of # the class until it's necessary, so that when it subclasses Environment # it will pick up whatever Environment subclass the wrapper interface # might have assigned to SCons.Environment.Environment. def NoSubstitutionProxy(subject): class _NoSubstitutionProxy(Environment): def __init__(self, subject): self.__dict__['__subject'] = subject def __getattr__(self, name): return getattr(self.__dict__['__subject'], name) def __setattr__(self, name, value): return setattr(self.__dict__['__subject'], name, value) def executor_to_lvars(self, kwdict): if kwdict.has_key('executor'): kwdict['lvars'] = kwdict['executor'].get_lvars() del kwdict['executor'] else: kwdict['lvars'] = {} def raw_to_mode(self, dict): try: raw = dict['raw'] except KeyError: pass else: del dict['raw'] dict['mode'] = raw def subst(self, string, *args, **kwargs): return string def subst_kw(self, kw, *args, **kwargs): return kw def subst_list(self, string, *args, **kwargs): nargs = (string, self,) + args nkw = kwargs.copy() nkw['gvars'] = {} self.executor_to_lvars(nkw) self.raw_to_mode(nkw) return SCons.Subst.scons_subst_list(*nargs, **nkw) def subst_target_source(self, string, *args, **kwargs): nargs = (string, self,) + args nkw = kwargs.copy() nkw['gvars'] = {} self.executor_to_lvars(nkw) self.raw_to_mode(nkw) return SCons.Subst.scons_subst(*nargs, **nkw) return _NoSubstitutionProxy(subject) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Scanner/0000755000175000017500000000000012114661557020674 5ustar dktrkranzdktrkranzscons-doc-2.3.0/src/engine/SCons/Scanner/Dir.py0000644000175000017500000000735012114661557021771 0ustar dktrkranzdktrkranz# # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Scanner/Dir.py 2013/03/03 09:48:35 garyo" import SCons.Node.FS import SCons.Scanner def only_dirs(nodes): is_Dir = lambda n: isinstance(n.disambiguate(), SCons.Node.FS.Dir) return list(filter(is_Dir, nodes)) def DirScanner(**kw): """Return a prototype Scanner instance for scanning directories for on-disk files""" kw['node_factory'] = SCons.Node.FS.Entry kw['recursive'] = only_dirs return SCons.Scanner.Base(scan_on_disk, "DirScanner", **kw) def DirEntryScanner(**kw): """Return a prototype Scanner instance for "scanning" directory Nodes for their in-memory entries""" kw['node_factory'] = SCons.Node.FS.Entry kw['recursive'] = None return SCons.Scanner.Base(scan_in_memory, "DirEntryScanner", **kw) skip_entry = {} skip_entry_list = [ '.', '..', '.sconsign', # Used by the native dblite.py module. '.sconsign.dblite', # Used by dbm and dumbdbm. '.sconsign.dir', # Used by dbm. '.sconsign.pag', # Used by dumbdbm. '.sconsign.dat', '.sconsign.bak', # Used by some dbm emulations using Berkeley DB. '.sconsign.db', ] for skip in skip_entry_list: skip_entry[skip] = 1 skip_entry[SCons.Node.FS._my_normcase(skip)] = 1 do_not_scan = lambda k: k not in skip_entry def scan_on_disk(node, env, path=()): """ Scans a directory for on-disk files and directories therein. Looking up the entries will add these to the in-memory Node tree representation of the file system, so all we have to do is just that and then call the in-memory scanning function. """ try: flist = node.fs.listdir(node.abspath) except (IOError, OSError): return [] e = node.Entry for f in filter(do_not_scan, flist): # Add ./ to the beginning of the file name so if it begins with a # '#' we don't look it up relative to the top-level directory. e('./' + f) return scan_in_memory(node, env, path) def scan_in_memory(node, env, path=()): """ "Scans" a Node.FS.Dir for its in-memory entries. """ try: entries = node.entries except AttributeError: # It's not a Node.FS.Dir (or doesn't look enough like one for # our purposes), which can happen if a target list containing # mixed Node types (Dirs and Files, for example) has a Dir as # the first entry. return [] entry_list = sorted(filter(do_not_scan, list(entries.keys()))) return [entries[n] for n in entry_list] # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Scanner/Prog.py0000644000175000017500000000634212114661557022162 0ustar dktrkranzdktrkranz# # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Scanner/Prog.py 2013/03/03 09:48:35 garyo" import SCons.Node import SCons.Node.FS import SCons.Scanner import SCons.Util # global, set by --debug=findlibs print_find_libs = None def ProgramScanner(**kw): """Return a prototype Scanner instance for scanning executable files for static-lib dependencies""" kw['path_function'] = SCons.Scanner.FindPathDirs('LIBPATH') ps = SCons.Scanner.Base(scan, "ProgramScanner", **kw) return ps def scan(node, env, libpath = ()): """ This scanner scans program files for static-library dependencies. It will search the LIBPATH environment variable for libraries specified in the LIBS variable, returning any files it finds as dependencies. """ try: libs = env['LIBS'] except KeyError: # There are no LIBS in this environment, so just return a null list: return [] if SCons.Util.is_String(libs): libs = libs.split() else: libs = SCons.Util.flatten(libs) try: prefix = env['LIBPREFIXES'] if not SCons.Util.is_List(prefix): prefix = [ prefix ] except KeyError: prefix = [ '' ] try: suffix = env['LIBSUFFIXES'] if not SCons.Util.is_List(suffix): suffix = [ suffix ] except KeyError: suffix = [ '' ] pairs = [] for suf in map(env.subst, suffix): for pref in map(env.subst, prefix): pairs.append((pref, suf)) result = [] if callable(libpath): libpath = libpath() find_file = SCons.Node.FS.find_file adjustixes = SCons.Util.adjustixes for lib in libs: if SCons.Util.is_String(lib): lib = env.subst(lib) for pref, suf in pairs: l = adjustixes(lib, pref, suf) l = find_file(l, libpath, verbose=print_find_libs) if l: result.append(l) else: result.append(lib) return result # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Scanner/IDL.py0000644000175000017500000000352012114661557021656 0ustar dktrkranzdktrkranz"""SCons.Scanner.IDL This module implements the depenency scanner for IDL (Interface Definition Language) files. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Scanner/IDL.py 2013/03/03 09:48:35 garyo" import SCons.Node.FS import SCons.Scanner def IDLScan(): """Return a prototype Scanner instance for scanning IDL source files""" cs = SCons.Scanner.ClassicCPP("IDLScan", "$IDLSUFFIXES", "CPPPATH", '^[ \t]*(?:#[ \t]*include|[ \t]*import)[ \t]+(<|")([^>"]+)(>|")') return cs # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Scanner/DirTests.py0000644000175000017500000001162212114661557023011 0ustar dktrkranzdktrkranz# # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Scanner/DirTests.py 2013/03/03 09:48:35 garyo" import os.path import sys import unittest import TestCmd import SCons.Node.FS import SCons.Scanner.Dir #class DummyNode: # def __init__(self, name, fs): # self.name = name # self.abspath = test.workpath(name) # self.fs = fs # def __str__(self): # return self.name # def Entry(self, name): # return self.fs.Entry(name) class DummyEnvironment(object): def __init__(self, root): self.fs = SCons.Node.FS.FS(root) def Dir(self, name): return self.fs.Dir(name) def Entry(self, name): return self.fs.Entry(name) def File(self, name): return self.fs.File(name) def get_factory(self, factory): return factory or self.fs.Entry class DirScannerTestBase(unittest.TestCase): def setUp(self): self.test = TestCmd.TestCmd(workdir = '') self.test.subdir('dir', ['dir', 'sub']) self.test.write(['dir', 'f1'], "dir/f1\n") self.test.write(['dir', 'f2'], "dir/f2\n") self.test.write(['dir', '.sconsign'], "dir/.sconsign\n") self.test.write(['dir', '.sconsign.bak'], "dir/.sconsign.bak\n") self.test.write(['dir', '.sconsign.dat'], "dir/.sconsign.dat\n") self.test.write(['dir', '.sconsign.db'], "dir/.sconsign.db\n") self.test.write(['dir', '.sconsign.dblite'], "dir/.sconsign.dblite\n") self.test.write(['dir', '.sconsign.dir'], "dir/.sconsign.dir\n") self.test.write(['dir', '.sconsign.pag'], "dir/.sconsign.pag\n") self.test.write(['dir', 'sub', 'f3'], "dir/sub/f3\n") self.test.write(['dir', 'sub', 'f4'], "dir/sub/f4\n") self.test.write(['dir', 'sub', '.sconsign'], "dir/.sconsign\n") self.test.write(['dir', 'sub', '.sconsign.bak'], "dir/.sconsign.bak\n") self.test.write(['dir', 'sub', '.sconsign.dat'], "dir/.sconsign.dat\n") self.test.write(['dir', 'sub', '.sconsign.dblite'], "dir/.sconsign.dblite\n") self.test.write(['dir', 'sub', '.sconsign.dir'], "dir/.sconsign.dir\n") self.test.write(['dir', 'sub', '.sconsign.pag'], "dir/.sconsign.pag\n") class DirScannerTestCase(DirScannerTestBase): def runTest(self): env = DummyEnvironment(self.test.workpath()) s = SCons.Scanner.Dir.DirScanner() expect = [ os.path.join('dir', 'f1'), os.path.join('dir', 'f2'), os.path.join('dir', 'sub'), ] deps = s(env.Dir('dir'), env, ()) sss = list(map(str, deps)) assert sss == expect, sss expect = [ os.path.join('dir', 'sub', 'f3'), os.path.join('dir', 'sub', 'f4'), ] deps = s(env.Dir('dir/sub'), env, ()) sss = list(map(str, deps)) assert sss == expect, sss class DirEntryScannerTestCase(DirScannerTestBase): def runTest(self): env = DummyEnvironment(self.test.workpath()) s = SCons.Scanner.Dir.DirEntryScanner() deps = s(env.Dir('dir'), env, ()) sss = list(map(str, deps)) assert sss == [], sss deps = s(env.Dir('dir/sub'), env, ()) sss = list(map(str, deps)) assert sss == [], sss # Make sure we don't blow up if handed a non-Dir node. deps = s(env.File('dir/f1'), env, ()) sss = list(map(str, deps)) assert sss == [], sss def suite(): suite = unittest.TestSuite() suite.addTest(DirScannerTestCase()) suite.addTest(DirEntryScannerTestCase()) return suite if __name__ == "__main__": runner = unittest.TextTestRunner() result = runner.run(suite()) if not result.wasSuccessful(): sys.exit(1) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Scanner/RC.py0000644000175000017500000000404112114661557021551 0ustar dktrkranzdktrkranz"""SCons.Scanner.RC This module implements the depenency scanner for RC (Interface Definition Language) files. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Scanner/RC.py 2013/03/03 09:48:35 garyo" import SCons.Node.FS import SCons.Scanner import re def RCScan(): """Return a prototype Scanner instance for scanning RC source files""" res_re= r'^(?:\s*#\s*(?:include)|' \ '.*?\s+(?:ICON|BITMAP|CURSOR|HTML|FONT|MESSAGETABLE|TYPELIB|REGISTRY|D3DFX)' \ '\s*.*?)' \ '\s*(<|"| )([^>"\s]+)(?:[>"\s])*$' resScanner = SCons.Scanner.ClassicCPP( "ResourceScanner", "$RCSUFFIXES", "CPPPATH", res_re ) return resScanner # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Scanner/D.py0000644000175000017500000000477612114661557021447 0ustar dktrkranzdktrkranz"""SCons.Scanner.D Scanner for the Digital Mars "D" programming language. Coded by Andy Friesen 17 Nov 2003 """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Scanner/D.py 2013/03/03 09:48:35 garyo" import re import SCons.Scanner def DScanner(): """Return a prototype Scanner instance for scanning D source files""" ds = D() return ds class D(SCons.Scanner.Classic): def __init__ (self): SCons.Scanner.Classic.__init__ (self, name = "DScanner", suffixes = '$DSUFFIXES', path_variable = 'DPATH', regex = 'import\s+(?:[a-zA-Z0-9_.]+)\s*(?:,\s*(?:[a-zA-Z0-9_.]+)\s*)*;') self.cre2 = re.compile ('(?:import\s)?\s*([a-zA-Z0-9_.]+)\s*(?:,|;)', re.M) def find_include(self, include, source_dir, path): # translate dots (package separators) to slashes inc = include.replace('.', '/') i = SCons.Node.FS.find_file(inc + '.d', (source_dir,) + path) if i is None: i = SCons.Node.FS.find_file (inc + '.di', (source_dir,) + path) return i, include def find_include_names(self, node): includes = [] for i in self.cre.findall(node.get_text_contents()): includes = includes + self.cre2.findall(i) return includes # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Scanner/C.py0000644000175000017500000001141412114661557021431 0ustar dktrkranzdktrkranz"""SCons.Scanner.C This module implements the depenency scanner for C/C++ code. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Scanner/C.py 2013/03/03 09:48:35 garyo" import SCons.Node.FS import SCons.Scanner import SCons.Util import SCons.cpp class SConsCPPScanner(SCons.cpp.PreProcessor): """ SCons-specific subclass of the cpp.py module's processing. We subclass this so that: 1) we can deal with files represented by Nodes, not strings; 2) we can keep track of the files that are missing. """ def __init__(self, *args, **kw): SCons.cpp.PreProcessor.__init__(self, *args, **kw) self.missing = [] def initialize_result(self, fname): self.result = SCons.Util.UniqueList([fname]) def finalize_result(self, fname): return self.result[1:] def find_include_file(self, t): keyword, quote, fname = t result = SCons.Node.FS.find_file(fname, self.searchpath[quote]) if not result: self.missing.append((fname, self.current_file)) return result def read_file(self, file): try: fp = open(str(file.rfile())) except EnvironmentError, e: self.missing.append((file, self.current_file)) return '' else: return fp.read() def dictify_CPPDEFINES(env): cppdefines = env.get('CPPDEFINES', {}) if cppdefines is None: return {} if SCons.Util.is_Sequence(cppdefines): result = {} for c in cppdefines: if SCons.Util.is_Sequence(c): result[c[0]] = c[1] else: result[c] = None return result if not SCons.Util.is_Dict(cppdefines): return {cppdefines : None} return cppdefines class SConsCPPScannerWrapper(object): """ The SCons wrapper around a cpp.py scanner. This is the actual glue between the calling conventions of generic SCons scanners, and the (subclass of) cpp.py class that knows how to look for #include lines with reasonably real C-preprocessor-like evaluation of #if/#ifdef/#else/#elif lines. """ def __init__(self, name, variable): self.name = name self.path = SCons.Scanner.FindPathDirs(variable) def __call__(self, node, env, path = ()): cpp = SConsCPPScanner(current = node.get_dir(), cpppath = path, dict = dictify_CPPDEFINES(env)) result = cpp(node) for included, includer in cpp.missing: fmt = "No dependency generated for file: %s (included from: %s) -- file not found" SCons.Warnings.warn(SCons.Warnings.DependencyWarning, fmt % (included, includer)) return result def recurse_nodes(self, nodes): return nodes def select(self, node): return self def CScanner(): """Return a prototype Scanner instance for scanning source files that use the C pre-processor""" # Here's how we would (or might) use the CPP scanner code above that # knows how to evaluate #if/#ifdef/#else/#elif lines when searching # for #includes. This is commented out for now until we add the # right configurability to let users pick between the scanners. #return SConsCPPScannerWrapper("CScanner", "CPPPATH") cs = SCons.Scanner.ClassicCPP("CScanner", "$CPPSUFFIXES", "CPPPATH", '^[ \t]*#[ \t]*(?:include|import)[ \t]*(<|")([^>"]+)(>|")') return cs # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Scanner/CTests.py0000644000175000017500000003407512114661557022464 0ustar dktrkranzdktrkranz# # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Scanner/CTests.py 2013/03/03 09:48:35 garyo" import SCons.compat import collections import os import sys import TestCmd import unittest import SCons.Node.FS import SCons.Warnings import SCons.Scanner.C test = TestCmd.TestCmd(workdir = '') os.chdir(test.workpath('')) # create some source files and headers: test.write('f1.cpp',""" #include \"f1.h\" #include int main() { return 0; } """) test.write('f2.cpp',""" #include \"d1/f1.h\" #include #include \"f1.h\" #import int main() { return 0; } """) test.write('f3.cpp',""" #include \t "f1.h" \t #include "f2.h" # \t include "f3-test.h" #include \t \t #include # \t include // #include "never.h" const char* x = "#include " int main() { return 0; } """) # for Emacs -> " test.subdir('d1', ['d1', 'd2']) headers = ['f1.h','f2.h', 'f3-test.h', 'fi.h', 'fj.h', 'never.h', 'd1/f1.h', 'd1/f2.h', 'd1/f3-test.h', 'd1/fi.h', 'd1/fj.h', 'd1/d2/f1.h', 'd1/d2/f2.h', 'd1/d2/f3-test.h', 'd1/d2/f4.h', 'd1/d2/fi.h', 'd1/d2/fj.h'] for h in headers: test.write(h, " ") test.write('f2.h',""" #include "fi.h" """) test.write('f3-test.h',""" #include """) test.subdir('include', 'subdir', ['subdir', 'include']) test.write('fa.cpp',""" #include \"fa.h\" #include int main() { return 0; } """) test.write(['include', 'fa.h'], "\n") test.write(['include', 'fb.h'], "\n") test.write(['subdir', 'include', 'fa.h'], "\n") test.write(['subdir', 'include', 'fb.h'], "\n") test.subdir('repository', ['repository', 'include'], ['repository', 'src' ]) test.subdir('work', ['work', 'src']) test.write(['repository', 'include', 'iii.h'], "\n") test.write(['work', 'src', 'fff.c'], """ #include #include int main() { return 0; } """) test.write([ 'work', 'src', 'aaa.c'], """ #include "bbb.h" int main() { return 0; } """) test.write([ 'work', 'src', 'bbb.h'], "\n") test.write([ 'repository', 'src', 'ccc.c'], """ #include "ddd.h" int main() { return 0; } """) test.write([ 'repository', 'src', 'ddd.h'], "\n") test.write('f5.c', """\ #include\"f5a.h\" #include """) test.write("f5a.h", "\n") test.write("f5b.h", "\n") # define some helpers: class DummyEnvironment(collections.UserDict): def __init__(self, **kw): collections.UserDict.__init__(self) self.data.update(kw) self.fs = SCons.Node.FS.FS(test.workpath('')) def Dictionary(self, *args): return self.data def subst(self, strSubst, target=None, source=None, conv=None): if strSubst[0] == '$': return self.data[strSubst[1:]] return strSubst def subst_list(self, strSubst, target=None, source=None, conv=None): if strSubst[0] == '$': return [self.data[strSubst[1:]]] return [[strSubst]] def subst_path(self, path, target=None, source=None, conv=None): if not isinstance(path, list): path = [path] return list(map(self.subst, path)) def get_calculator(self): return None def get_factory(self, factory): return factory or self.fs.File def Dir(self, filename): return self.fs.Dir(filename) def File(self, filename): return self.fs.File(filename) if os.path.normcase('foo') == os.path.normcase('FOO'): my_normpath = os.path.normcase else: my_normpath = os.path.normpath def deps_match(self, deps, headers): global my_normpath scanned = list(map(my_normpath, list(map(str, deps)))) expect = list(map(my_normpath, headers)) self.failUnless(scanned == expect, "expect %s != scanned %s" % (expect, scanned)) # define some tests: class CScannerTestCase1(unittest.TestCase): def runTest(self): """Find local files with no CPPPATH""" env = DummyEnvironment(CPPPATH=[]) s = SCons.Scanner.C.CScanner() path = s.path(env) deps = s(env.File('f1.cpp'), env, path) headers = ['f1.h', 'f2.h'] deps_match(self, deps, headers) class CScannerTestCase2(unittest.TestCase): def runTest(self): """Find a file in a CPPPATH directory""" env = DummyEnvironment(CPPPATH=[test.workpath("d1")]) s = SCons.Scanner.C.CScanner() path = s.path(env) deps = s(env.File('f1.cpp'), env, path) headers = ['f1.h', 'd1/f2.h'] deps_match(self, deps, headers) class CScannerTestCase3(unittest.TestCase): def runTest(self): """Find files in explicit subdirectories, ignore missing file""" env = DummyEnvironment(CPPPATH=[test.workpath("d1")]) s = SCons.Scanner.C.CScanner() path = s.path(env) deps = s(env.File('f2.cpp'), env, path) headers = ['d1/f1.h', 'f1.h', 'd1/d2/f1.h'] deps_match(self, deps, headers) class CScannerTestCase4(unittest.TestCase): def runTest(self): """Find files in explicit subdirectories""" env = DummyEnvironment(CPPPATH=[test.workpath("d1"), test.workpath("d1/d2")]) s = SCons.Scanner.C.CScanner() path = s.path(env) deps = s(env.File('f2.cpp'), env, path) headers = ['d1/f1.h', 'f1.h', 'd1/d2/f1.h', 'd1/d2/f4.h'] deps_match(self, deps, headers) class CScannerTestCase5(unittest.TestCase): def runTest(self): """Make sure files in repositories will get scanned""" env = DummyEnvironment(CPPPATH=[]) s = SCons.Scanner.C.CScanner() path = s.path(env) n = env.File('f3.cpp') def my_rexists(s=n): s.rexists_called = 1 return s.old_rexists() setattr(n, 'old_rexists', n.rexists) setattr(n, 'rexists', my_rexists) deps = s(n, env, path) # Make sure rexists() got called on the file node being # scanned, essential for cooperation with VariantDir functionality. assert n.rexists_called headers = ['f1.h', 'f2.h', 'f3-test.h', 'd1/f1.h', 'd1/f2.h', 'd1/f3-test.h'] deps_match(self, deps, headers) class CScannerTestCase6(unittest.TestCase): def runTest(self): """Find a same-named file in different directories when CPPPATH changes""" env1 = DummyEnvironment(CPPPATH=[test.workpath("d1")]) env2 = DummyEnvironment(CPPPATH=[test.workpath("d1/d2")]) s = SCons.Scanner.C.CScanner() path1 = s.path(env1) path2 = s.path(env2) deps1 = s(env1.File('f1.cpp'), env1, path1) deps2 = s(env2.File('f1.cpp'), env2, path2) headers1 = ['f1.h', 'd1/f2.h'] headers2 = ['f1.h', 'd1/d2/f2.h'] deps_match(self, deps1, headers1) deps_match(self, deps2, headers2) class CScannerTestCase8(unittest.TestCase): def runTest(self): """Find files in a subdirectory relative to the current directory""" env = DummyEnvironment(CPPPATH=["include"]) s = SCons.Scanner.C.CScanner() path = s.path(env) deps1 = s(env.File('fa.cpp'), env, path) env.fs.chdir(env.Dir('subdir')) dir = env.fs.getcwd() env.fs.chdir(env.Dir('')) path = s.path(env, dir) deps2 = s(env.File('#fa.cpp'), env, path) headers1 = list(map(test.workpath, ['include/fa.h', 'include/fb.h'])) headers2 = ['include/fa.h', 'include/fb.h'] deps_match(self, deps1, headers1) deps_match(self, deps2, headers2) class CScannerTestCase9(unittest.TestCase): def runTest(self): """Generate a warning when we can't find a #included file""" SCons.Warnings.enableWarningClass(SCons.Warnings.DependencyWarning) class TestOut(object): def __call__(self, x): self.out = x to = TestOut() to.out = None SCons.Warnings._warningOut = to test.write('fa.h','\n') fs = SCons.Node.FS.FS(test.workpath('')) env = DummyEnvironment(CPPPATH=[]) env.fs = fs s = SCons.Scanner.C.CScanner() path = s.path(env) deps = s(fs.File('fa.cpp'), env, path) # Did we catch the warning associated with not finding fb.h? assert to.out deps_match(self, deps, [ 'fa.h' ]) test.unlink('fa.h') class CScannerTestCase10(unittest.TestCase): def runTest(self): """Find files in the local directory when the scanned file is elsewhere""" fs = SCons.Node.FS.FS(test.workpath('')) fs.chdir(fs.Dir('include')) env = DummyEnvironment(CPPPATH=[]) env.fs = fs s = SCons.Scanner.C.CScanner() path = s.path(env) test.write('include/fa.cpp', test.read('fa.cpp')) fs.chdir(fs.Dir('..')) deps = s(fs.File('#include/fa.cpp'), env, path) deps_match(self, deps, [ 'include/fa.h', 'include/fb.h' ]) test.unlink('include/fa.cpp') class CScannerTestCase11(unittest.TestCase): def runTest(self): """Handle dependencies on a derived .h file in a non-existent directory""" os.chdir(test.workpath('work')) fs = SCons.Node.FS.FS(test.workpath('work')) fs.Repository(test.workpath('repository')) # Create a derived file in a directory that does not exist yet. # This was a bug at one time. f1=fs.File('include2/jjj.h') f1.builder=1 env = DummyEnvironment(CPPPATH=['include', 'include2']) env.fs = fs s = SCons.Scanner.C.CScanner() path = s.path(env) deps = s(fs.File('src/fff.c'), env, path) deps_match(self, deps, [ test.workpath('repository/include/iii.h'), 'include2/jjj.h' ]) os.chdir(test.workpath('')) class CScannerTestCase12(unittest.TestCase): def runTest(self): """Find files in VariantDir() directories""" os.chdir(test.workpath('work')) fs = SCons.Node.FS.FS(test.workpath('work')) fs.VariantDir('build1', 'src', 1) fs.VariantDir('build2', 'src', 0) fs.Repository(test.workpath('repository')) env = DummyEnvironment(CPPPATH=[]) env.fs = fs s = SCons.Scanner.C.CScanner() path = s.path(env) deps1 = s(fs.File('build1/aaa.c'), env, path) deps_match(self, deps1, [ 'build1/bbb.h' ]) deps2 = s(fs.File('build2/aaa.c'), env, path) deps_match(self, deps2, [ 'src/bbb.h' ]) deps3 = s(fs.File('build1/ccc.c'), env, path) deps_match(self, deps3, [ 'build1/ddd.h' ]) deps4 = s(fs.File('build2/ccc.c'), env, path) deps_match(self, deps4, [ test.workpath('repository/src/ddd.h') ]) os.chdir(test.workpath('')) class CScannerTestCase13(unittest.TestCase): def runTest(self): """Find files in directories named in a substituted environment variable""" class SubstEnvironment(DummyEnvironment): def subst(self, arg, target=None, source=None, conv=None, test=test): if arg == "$blah": return test.workpath("d1") else: return arg env = SubstEnvironment(CPPPATH=["$blah"]) s = SCons.Scanner.C.CScanner() path = s.path(env) deps = s(env.File('f1.cpp'), env, path) headers = ['f1.h', 'd1/f2.h'] deps_match(self, deps, headers) class CScannerTestCase14(unittest.TestCase): def runTest(self): """Find files when there's no space between "#include" and the name""" env = DummyEnvironment(CPPPATH=[]) s = SCons.Scanner.C.CScanner() path = s.path(env) deps = s(env.File('f5.c'), env, path) headers = ['f5a.h', 'f5b.h'] deps_match(self, deps, headers) class CScannerTestCase15(unittest.TestCase): def runTest(self): """Verify scanner initialization with the suffixes in $CPPSUFFIXES""" suffixes = [".c", ".C", ".cxx", ".cpp", ".c++", ".cc", ".h", ".H", ".hxx", ".hpp", ".hh", ".F", ".fpp", ".FPP", ".S", ".spp", ".SPP"] env = DummyEnvironment(CPPSUFFIXES = suffixes) s = SCons.Scanner.C.CScanner() for suffix in suffixes: assert suffix in s.get_skeys(env), "%s not in skeys" % suffix def suite(): suite = unittest.TestSuite() suite.addTest(CScannerTestCase1()) suite.addTest(CScannerTestCase2()) suite.addTest(CScannerTestCase3()) suite.addTest(CScannerTestCase4()) suite.addTest(CScannerTestCase5()) suite.addTest(CScannerTestCase6()) suite.addTest(CScannerTestCase8()) suite.addTest(CScannerTestCase9()) suite.addTest(CScannerTestCase10()) suite.addTest(CScannerTestCase11()) suite.addTest(CScannerTestCase12()) suite.addTest(CScannerTestCase13()) suite.addTest(CScannerTestCase14()) suite.addTest(CScannerTestCase15()) return suite if __name__ == "__main__": runner = unittest.TextTestRunner() result = runner.run(suite()) if not result.wasSuccessful(): sys.exit(1) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Scanner/FortranTests.py0000644000175000017500000004252512114661557023714 0ustar dktrkranzdktrkranz# # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Scanner/FortranTests.py 2013/03/03 09:48:35 garyo" import os import os.path import sys import unittest import SCons.Scanner.Fortran import SCons.Node.FS import SCons.Warnings import TestCmd original = os.getcwd() test = TestCmd.TestCmd(workdir = '') os.chdir(test.workpath('')) # create some source files and headers: test.write('fff1.f',""" PROGRAM FOO INCLUDE 'f1.f' include 'f2.f' STOP END """) test.write('fff2.f',""" PROGRAM FOO INCLUDE 'f2.f' include 'd1/f2.f' INCLUDE 'd2/f2.f' STOP END """) test.write('fff3.f',""" PROGRAM FOO INCLUDE 'f3.f' ; INCLUDE\t'd1/f3.f' STOP END """) # for Emacs -> " test.subdir('d1', ['d1', 'd2']) headers = ['fi.f', 'never.f', 'd1/f1.f', 'd1/f2.f', 'd1/f3.f', 'd1/fi.f', 'd1/d2/f1.f', 'd1/d2/f2.f', 'd1/d2/f3.f', 'd1/d2/f4.f', 'd1/d2/fi.f'] for h in headers: test.write(h, "\n") test.subdir('include', 'subdir', ['subdir', 'include']) test.write('fff4.f',""" PROGRAM FOO INCLUDE 'f4.f' STOP END """) test.write('include/f4.f', "\n") test.write('subdir/include/f4.f', "\n") test.write('fff5.f',""" PROGRAM FOO INCLUDE 'f5.f' INCLUDE 'not_there.f' STOP END """) test.write('f5.f', "\n") test.subdir('repository', ['repository', 'include'], [ 'repository', 'src' ]) test.subdir('work', ['work', 'src']) test.write(['repository', 'include', 'iii.f'], "\n") test.write(['work', 'src', 'fff.f'], """ PROGRAM FOO INCLUDE 'iii.f' INCLUDE 'jjj.f' STOP END """) test.write([ 'work', 'src', 'aaa.f'], """ PROGRAM FOO INCLUDE 'bbb.f' STOP END """) test.write([ 'work', 'src', 'bbb.f'], "\n") test.write([ 'repository', 'src', 'ccc.f'], """ PROGRAM FOO INCLUDE 'ddd.f' STOP END """) test.write([ 'repository', 'src', 'ddd.f'], "\n") test.write('fff90a.f90',""" PROGRAM FOO ! Test comments - these includes should NOT be picked up C INCLUDE 'fi.f' # INCLUDE 'fi.f' ! INCLUDE 'fi.f' INCLUDE 'f1.f' ! in-line comments are valid syntax INCLUDE"fi.f" ! space is significant - this should be ignored INCLUDE ! Absoft compiler allows greater than/less than delimiters ! ! Allow kind type parameters INCLUDE kindType_"f3.f" INCLUDE kind_Type_"f4.f" ! ! Test multiple statements per line - use various spacings between semicolons incLUDE 'f5.f';include "f6.f" ; include ; include 'f8.f' ;include kindType_'f9.f' ! ! Test various USE statement syntaxes ! USE Mod01 use mod02 use use USE mOD03, ONLY : someVar USE MOD04 ,only:someVar USE Mod05 , ONLY: someVar ! in-line comment USE Mod06,ONLY :someVar,someOtherVar USE mod07;USE mod08; USE mod09 ;USE mod10 ; USE mod11 ! Test various semicolon placements use mod12 ;use mod13! Test comment at end of line ! USE modi ! USE modia ; use modib ! Scanner regexp will only ignore the first - this is a deficiency in the regexp ! USE modic ; ! use modid ! Scanner regexp should ignore both modules USE mod14 !; USE modi ! Only ignore the second USE mod15!;USE modi USE mod16 ! ; USE modi ! Test semicolon syntax - use various spacings USE :: mod17 USE::mod18 USE ::mod19 ; USE:: mod20 use, non_intrinsic :: mod21, ONLY : someVar ; use,intrinsic:: mod22 USE, NON_INTRINSIC::mod23 ; USE ,INTRINSIC ::mod24 USE mod25 ! Test USE statement at the beginning of line ; USE modi ! Scanner should ignore this since it isn't valid syntax USEmodi ! No space in between USE and module name - ignore it USE mod01 ! This one is a duplicate - there should only be one dependency to it. STOP END """) modules = ['mod01.mod', 'mod02.mod', 'mod03.mod', 'mod04.mod', 'mod05.mod', 'mod06.mod', 'mod07.mod', 'mod08.mod', 'mod09.mod', 'mod10.mod', 'mod11.mod', 'mod12.mod', 'mod13.mod', 'mod14.mod', 'mod15.mod', 'mod16.mod', 'mod17.mod', 'mod18.mod', 'mod19.mod', 'mod20.mod', 'mod21.mod', 'mod22.mod', 'mod23.mod', 'mod24.mod', 'mod25.mod'] for m in modules: test.write(m, "\n") test.subdir('modules') test.write(['modules', 'use.mod'], "\n") # define some helpers: class DummyEnvironment(object): def __init__(self, listCppPath): self.path = listCppPath self.fs = SCons.Node.FS.FS(test.workpath('')) def Dictionary(self, *args): if not args: return { 'FORTRANPATH': self.path, 'FORTRANMODSUFFIX' : ".mod" } elif len(args) == 1 and args[0] == 'FORTRANPATH': return self.path else: raise KeyError("Dummy environment only has FORTRANPATH attribute.") def has_key(self, key): return key in self.Dictionary() def __getitem__(self,key): return self.Dictionary()[key] def __setitem__(self,key,value): self.Dictionary()[key] = value def __delitem__(self,key): del self.Dictionary()[key] def subst(self, arg, target=None, source=None, conv=None): if arg[0] == '$': return self[arg[1:]] return arg def subst_path(self, path, target=None, source=None, conv=None): if not isinstance(path, list): path = [path] return list(map(self.subst, path)) def get_calculator(self): return None def get_factory(self, factory): return factory or self.fs.File def Dir(self, filename): return self.fs.Dir(filename) def File(self, filename): return self.fs.File(filename) def deps_match(self, deps, headers): scanned = list(map(os.path.normpath, list(map(str, deps)))) expect = list(map(os.path.normpath, headers)) self.failUnless(scanned == expect, "expect %s != scanned %s" % (expect, scanned)) # define some tests: class FortranScannerTestCase1(unittest.TestCase): def runTest(self): test.write('f1.f', "\n") test.write('f2.f', " INCLUDE 'fi.f'\n") env = DummyEnvironment([]) s = SCons.Scanner.Fortran.FortranScan() path = s.path(env) deps = s(env.File('fff1.f'), env, path) headers = ['f1.f', 'f2.f'] deps_match(self, deps, headers) test.unlink('f1.f') test.unlink('f2.f') class FortranScannerTestCase2(unittest.TestCase): def runTest(self): test.write('f1.f', "\n") test.write('f2.f', " INCLUDE 'fi.f'\n") env = DummyEnvironment([test.workpath("d1")]) s = SCons.Scanner.Fortran.FortranScan() path = s.path(env) deps = s(env.File('fff1.f'), env, path) headers = ['f1.f', 'f2.f'] deps_match(self, deps, headers) test.unlink('f1.f') test.unlink('f2.f') class FortranScannerTestCase3(unittest.TestCase): def runTest(self): env = DummyEnvironment([test.workpath("d1")]) s = SCons.Scanner.Fortran.FortranScan() path = s.path(env) deps = s(env.File('fff1.f'), env, path) headers = ['d1/f1.f', 'd1/f2.f'] deps_match(self, deps, headers) class FortranScannerTestCase4(unittest.TestCase): def runTest(self): test.write(['d1', 'f2.f'], " INCLUDE 'fi.f'\n") env = DummyEnvironment([test.workpath("d1")]) s = SCons.Scanner.Fortran.FortranScan() path = s.path(env) deps = s(env.File('fff1.f'), env, path) headers = ['d1/f1.f', 'd1/f2.f'] deps_match(self, deps, headers) test.write(['d1', 'f2.f'], "\n") class FortranScannerTestCase5(unittest.TestCase): def runTest(self): env = DummyEnvironment([test.workpath("d1")]) s = SCons.Scanner.Fortran.FortranScan() path = s.path(env) deps = s(env.File('fff2.f'), env, path) headers = ['d1/f2.f', 'd1/d2/f2.f', 'd1/f2.f'] deps_match(self, deps, headers) class FortranScannerTestCase6(unittest.TestCase): def runTest(self): test.write('f2.f', "\n") env = DummyEnvironment([test.workpath("d1")]) s = SCons.Scanner.Fortran.FortranScan() path = s.path(env) deps = s(env.File('fff2.f'), env, path) headers = ['d1/f2.f', 'd1/d2/f2.f', 'f2.f'] deps_match(self, deps, headers) test.unlink('f2.f') class FortranScannerTestCase7(unittest.TestCase): def runTest(self): env = DummyEnvironment([test.workpath("d1/d2"), test.workpath("d1")]) s = SCons.Scanner.Fortran.FortranScan() path = s.path(env) deps = s(env.File('fff2.f'), env, path) headers = ['d1/f2.f', 'd1/d2/f2.f', 'd1/d2/f2.f'] deps_match(self, deps, headers) class FortranScannerTestCase8(unittest.TestCase): def runTest(self): test.write('f2.f', "\n") env = DummyEnvironment([test.workpath("d1/d2"), test.workpath("d1")]) s = SCons.Scanner.Fortran.FortranScan() path = s.path(env) deps = s(env.File('fff2.f'), env, path) headers = ['d1/f2.f', 'd1/d2/f2.f', 'f2.f'] deps_match(self, deps, headers) test.unlink('f2.f') class FortranScannerTestCase9(unittest.TestCase): def runTest(self): test.write('f3.f', "\n") env = DummyEnvironment([]) s = SCons.Scanner.Fortran.FortranScan() path = s.path(env) n = env.File('fff3.f') def my_rexists(s=n): s.rexists_called = 1 return s.old_rexists() setattr(n, 'old_rexists', n.rexists) setattr(n, 'rexists', my_rexists) deps = s(n, env, path) # Make sure rexists() got called on the file node being # scanned, essential for cooperation with VariantDir functionality. assert n.rexists_called headers = ['d1/f3.f', 'f3.f'] deps_match(self, deps, headers) test.unlink('f3.f') class FortranScannerTestCase10(unittest.TestCase): def runTest(self): env = DummyEnvironment(["include"]) s = SCons.Scanner.Fortran.FortranScan() path = s.path(env) deps1 = s(env.File('fff4.f'), env, path) env.fs.chdir(env.Dir('subdir')) dir = env.fs.getcwd() env.fs.chdir(env.Dir('')) path = s.path(env, dir) deps2 = s(env.File('#fff4.f'), env, path) headers1 = list(map(test.workpath, ['include/f4.f'])) headers2 = ['include/f4.f'] deps_match(self, deps1, headers1) deps_match(self, deps2, headers2) class FortranScannerTestCase11(unittest.TestCase): def runTest(self): SCons.Warnings.enableWarningClass(SCons.Warnings.DependencyWarning) class TestOut(object): def __call__(self, x): self.out = x to = TestOut() to.out = None SCons.Warnings._warningOut = to env = DummyEnvironment([]) s = SCons.Scanner.Fortran.FortranScan() path = s.path(env) deps = s(env.File('fff5.f'), env, path) # Did we catch the warning from not finding not_there.f? assert to.out deps_match(self, deps, [ 'f5.f' ]) class FortranScannerTestCase12(unittest.TestCase): def runTest(self): env = DummyEnvironment([]) env.fs.chdir(env.Dir('include')) s = SCons.Scanner.Fortran.FortranScan() path = s.path(env) test.write('include/fff4.f', test.read('fff4.f')) deps = s(env.File('#include/fff4.f'), env, path) env.fs.chdir(env.Dir('')) deps_match(self, deps, ['f4.f']) test.unlink('include/fff4.f') class FortranScannerTestCase13(unittest.TestCase): def runTest(self): os.chdir(test.workpath('work')) fs = SCons.Node.FS.FS(test.workpath('work')) fs.Repository(test.workpath('repository')) # Create a derived file in a directory that does not exist yet. # This was a bug at one time. f1=fs.File('include2/jjj.f') f1.builder=1 env = DummyEnvironment(['include','include2']) env.fs = fs s = SCons.Scanner.Fortran.FortranScan() path = s.path(env) deps = s(fs.File('src/fff.f'), env, path) deps_match(self, deps, [test.workpath('repository/include/iii.f'), 'include2/jjj.f']) os.chdir(test.workpath('')) class FortranScannerTestCase14(unittest.TestCase): def runTest(self): os.chdir(test.workpath('work')) fs = SCons.Node.FS.FS(test.workpath('work')) fs.VariantDir('build1', 'src', 1) fs.VariantDir('build2', 'src', 0) fs.Repository(test.workpath('repository')) env = DummyEnvironment([]) env.fs = fs s = SCons.Scanner.Fortran.FortranScan() path = s.path(env) deps1 = s(fs.File('build1/aaa.f'), env, path) deps_match(self, deps1, [ 'build1/bbb.f' ]) deps2 = s(fs.File('build2/aaa.f'), env, path) deps_match(self, deps2, [ 'src/bbb.f' ]) deps3 = s(fs.File('build1/ccc.f'), env, path) deps_match(self, deps3, [ 'build1/ddd.f' ]) deps4 = s(fs.File('build2/ccc.f'), env, path) deps_match(self, deps4, [ test.workpath('repository/src/ddd.f') ]) os.chdir(test.workpath('')) class FortranScannerTestCase15(unittest.TestCase): def runTest(self): class SubstEnvironment(DummyEnvironment): def subst(self, arg, target=None, source=None, conv=None, test=test): if arg == "$junk": return test.workpath("d1") else: return arg test.write(['d1', 'f2.f'], " INCLUDE 'fi.f'\n") env = SubstEnvironment(["$junk"]) s = SCons.Scanner.Fortran.FortranScan() path = s.path(env) deps = s(env.File('fff1.f'), env, path) headers = ['d1/f1.f', 'd1/f2.f'] deps_match(self, deps, headers) test.write(['d1', 'f2.f'], "\n") class FortranScannerTestCase16(unittest.TestCase): def runTest(self): test.write('f1.f', "\n") test.write('f2.f', "\n") test.write('f3.f', "\n") test.write('f4.f', "\n") test.write('f5.f', "\n") test.write('f6.f', "\n") test.write('f7.f', "\n") test.write('f8.f', "\n") test.write('f9.f', "\n") test.write('f10.f', "\n") env = DummyEnvironment([test.workpath('modules')]) s = SCons.Scanner.Fortran.FortranScan() path = s.path(env) deps = s(env.File('fff90a.f90'), env, path) headers = ['f1.f', 'f2.f', 'f3.f', 'f4.f', 'f5.f', 'f6.f', 'f7.f', 'f8.f', 'f9.f'] modules = ['mod01.mod', 'mod02.mod', 'mod03.mod', 'mod04.mod', 'mod05.mod', 'mod06.mod', 'mod07.mod', 'mod08.mod', 'mod09.mod', 'mod10.mod', 'mod11.mod', 'mod12.mod', 'mod13.mod', 'mod14.mod', 'mod15.mod', 'mod16.mod', 'mod17.mod', 'mod18.mod', 'mod19.mod', 'mod20.mod', 'mod21.mod', 'mod22.mod', 'mod23.mod', 'mod24.mod', 'mod25.mod', 'modules/use.mod'] deps_expected = headers + modules deps_match(self, deps, deps_expected) test.unlink('f1.f') test.unlink('f2.f') test.unlink('f3.f') test.unlink('f4.f') test.unlink('f5.f') test.unlink('f6.f') test.unlink('f7.f') test.unlink('f8.f') test.unlink('f9.f') test.unlink('f10.f') def suite(): suite = unittest.TestSuite() suite.addTest(FortranScannerTestCase1()) suite.addTest(FortranScannerTestCase2()) suite.addTest(FortranScannerTestCase3()) suite.addTest(FortranScannerTestCase4()) suite.addTest(FortranScannerTestCase5()) suite.addTest(FortranScannerTestCase6()) suite.addTest(FortranScannerTestCase7()) suite.addTest(FortranScannerTestCase8()) suite.addTest(FortranScannerTestCase9()) suite.addTest(FortranScannerTestCase10()) suite.addTest(FortranScannerTestCase11()) suite.addTest(FortranScannerTestCase12()) suite.addTest(FortranScannerTestCase13()) suite.addTest(FortranScannerTestCase14()) suite.addTest(FortranScannerTestCase15()) suite.addTest(FortranScannerTestCase16()) return suite if __name__ == "__main__": runner = unittest.TextTestRunner() result = runner.run(suite()) if not result.wasSuccessful(): sys.exit(1) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Scanner/LaTeX.py0000644000175000017500000003761412114661557022236 0ustar dktrkranzdktrkranz"""SCons.Scanner.LaTeX This module implements the dependency scanner for LaTeX code. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Scanner/LaTeX.py 2013/03/03 09:48:35 garyo" import os.path import re import SCons.Scanner import SCons.Util # list of graphics file extensions for TeX and LaTeX TexGraphics = ['.eps', '.ps'] LatexGraphics = ['.pdf', '.png', '.jpg', '.gif', '.tif'] # Used as a return value of modify_env_var if the variable is not set. class _Null(object): pass _null = _Null # The user specifies the paths in env[variable], similar to other builders. # They may be relative and must be converted to absolute, as expected # by LaTeX and Co. The environment may already have some paths in # env['ENV'][var]. These paths are honored, but the env[var] paths have # higher precedence. All changes are un-done on exit. def modify_env_var(env, var, abspath): try: save = env['ENV'][var] except KeyError: save = _null env.PrependENVPath(var, abspath) try: if SCons.Util.is_List(env[var]): env.PrependENVPath(var, [os.path.abspath(str(p)) for p in env[var]]) else: # Split at os.pathsep to convert into absolute path env.PrependENVPath(var, [os.path.abspath(p) for p in str(env[var]).split(os.pathsep)]) except KeyError: pass # Convert into a string explicitly to append ":" (without which it won't search system # paths as well). The problem is that env.AppendENVPath(var, ":") # does not work, refuses to append ":" (os.pathsep). if SCons.Util.is_List(env['ENV'][var]): env['ENV'][var] = os.pathsep.join(env['ENV'][var]) # Append the trailing os.pathsep character here to catch the case with no env[var] env['ENV'][var] = env['ENV'][var] + os.pathsep return save class FindENVPathDirs(object): """A class to bind a specific *PATH variable name to a function that will return all of the *path directories.""" def __init__(self, variable): self.variable = variable def __call__(self, env, dir=None, target=None, source=None, argument=None): import SCons.PathList try: path = env['ENV'][self.variable] except KeyError: return () dir = dir or env.fs._cwd path = SCons.PathList.PathList(path).subst_path(env, target, source) return tuple(dir.Rfindalldirs(path)) def LaTeXScanner(): """Return a prototype Scanner instance for scanning LaTeX source files when built with latex. """ ds = LaTeX(name = "LaTeXScanner", suffixes = '$LATEXSUFFIXES', # in the search order, see below in LaTeX class docstring graphics_extensions = TexGraphics, recursive = 0) return ds def PDFLaTeXScanner(): """Return a prototype Scanner instance for scanning LaTeX source files when built with pdflatex. """ ds = LaTeX(name = "PDFLaTeXScanner", suffixes = '$LATEXSUFFIXES', # in the search order, see below in LaTeX class docstring graphics_extensions = LatexGraphics, recursive = 0) return ds class LaTeX(SCons.Scanner.Base): """Class for scanning LaTeX files for included files. Unlike most scanners, which use regular expressions that just return the included file name, this returns a tuple consisting of the keyword for the inclusion ("include", "includegraphics", "input", or "bibliography"), and then the file name itself. Based on a quick look at LaTeX documentation, it seems that we should append .tex suffix for the "include" keywords, append .tex if there is no extension for the "input" keyword, and need to add .bib for the "bibliography" keyword that does not accept extensions by itself. Finally, if there is no extension for an "includegraphics" keyword latex will append .ps or .eps to find the file, while pdftex may use .pdf, .jpg, .tif, .mps, or .png. The actual subset and search order may be altered by DeclareGraphicsExtensions command. This complication is ignored. The default order corresponds to experimentation with teTeX $ latex --version pdfeTeX 3.141592-1.21a-2.2 (Web2C 7.5.4) kpathsea version 3.5.4 The order is: ['.eps', '.ps'] for latex ['.png', '.pdf', '.jpg', '.tif']. Another difference is that the search path is determined by the type of the file being searched: env['TEXINPUTS'] for "input" and "include" keywords env['TEXINPUTS'] for "includegraphics" keyword env['TEXINPUTS'] for "lstinputlisting" keyword env['BIBINPUTS'] for "bibliography" keyword env['BSTINPUTS'] for "bibliographystyle" keyword env['INDEXSTYLE'] for "makeindex" keyword, no scanning support needed just allows user to set it if needed. FIXME: also look for the class or style in document[class|style]{} FIXME: also look for the argument of bibliographystyle{} """ keyword_paths = {'include': 'TEXINPUTS', 'input': 'TEXINPUTS', 'includegraphics': 'TEXINPUTS', 'bibliography': 'BIBINPUTS', 'bibliographystyle': 'BSTINPUTS', 'addbibresource': 'BIBINPUTS', 'addglobalbib': 'BIBINPUTS', 'addsectionbib': 'BIBINPUTS', 'makeindex': 'INDEXSTYLE', 'usepackage': 'TEXINPUTS', 'lstinputlisting': 'TEXINPUTS'} env_variables = SCons.Util.unique(list(keyword_paths.values())) def __init__(self, name, suffixes, graphics_extensions, *args, **kw): # We have to include \n with the % we exclude from the first part # part of the regex because the expression is compiled with re.M. # Without the \n, the ^ could match the beginning of a *previous* # line followed by one or more newline characters (i.e. blank # lines), interfering with a match on the next line. # add option for whitespace before the '[options]' or the '{filename}' regex = r'^[^%\n]*\\(include|includegraphics(?:\s*\[[^\]]+\])?|lstinputlisting(?:\[[^\]]+\])?|input|bibliography|addbibresource|addglobalbib|addsectionbib|usepackage)\s*{([^}]*)}' self.cre = re.compile(regex, re.M) self.comment_re = re.compile(r'^((?:(?:\\%)|[^%\n])*)(.*)$', re.M) self.graphics_extensions = graphics_extensions def _scan(node, env, path=(), self=self): node = node.rfile() if not node.exists(): return [] return self.scan_recurse(node, path) class FindMultiPathDirs(object): """The stock FindPathDirs function has the wrong granularity: it is called once per target, while we need the path that depends on what kind of included files is being searched. This wrapper hides multiple instances of FindPathDirs, one per the LaTeX path variable in the environment. When invoked, the function calculates and returns all the required paths as a dictionary (converted into a tuple to become hashable). Then the scan function converts it back and uses a dictionary of tuples rather than a single tuple of paths. """ def __init__(self, dictionary): self.dictionary = {} for k,n in dictionary.items(): self.dictionary[k] = ( SCons.Scanner.FindPathDirs(n), FindENVPathDirs(n) ) def __call__(self, env, dir=None, target=None, source=None, argument=None): di = {} for k,(c,cENV) in self.dictionary.items(): di[k] = ( c(env, dir=None, target=None, source=None, argument=None) , cENV(env, dir=None, target=None, source=None, argument=None) ) # To prevent "dict is not hashable error" return tuple(di.items()) class LaTeXScanCheck(object): """Skip all but LaTeX source files, i.e., do not scan *.eps, *.pdf, *.jpg, etc. """ def __init__(self, suffixes): self.suffixes = suffixes def __call__(self, node, env): current = not node.has_builder() or node.is_up_to_date() scannable = node.get_suffix() in env.subst_list(self.suffixes)[0] # Returning false means that the file is not scanned. return scannable and current kw['function'] = _scan kw['path_function'] = FindMultiPathDirs(LaTeX.keyword_paths) kw['recursive'] = 0 kw['skeys'] = suffixes kw['scan_check'] = LaTeXScanCheck(suffixes) kw['name'] = name SCons.Scanner.Base.__init__(self, *args, **kw) def _latex_names(self, include): filename = include[1] if include[0] == 'input': base, ext = os.path.splitext( filename ) if ext == "": return [filename + '.tex'] if (include[0] == 'include'): return [filename + '.tex'] if include[0] == 'bibliography': base, ext = os.path.splitext( filename ) if ext == "": return [filename + '.bib'] if include[0] == 'usepackage': base, ext = os.path.splitext( filename ) if ext == "": return [filename + '.sty'] if include[0] == 'includegraphics': base, ext = os.path.splitext( filename ) if ext == "": #return [filename+e for e in self.graphics_extensions + TexGraphics] # use the line above to find dependencies for the PDF builder # when only an .eps figure is present. Since it will be found # if the user tells scons how to make the pdf figure, leave # it out for now. return [filename+e for e in self.graphics_extensions] return [filename] def sort_key(self, include): return SCons.Node.FS._my_normcase(str(include)) def find_include(self, include, source_dir, path): try: sub_path = path[include[0]] except (IndexError, KeyError): sub_path = () try_names = self._latex_names(include) for n in try_names: # see if we find it using the path in env[var] i = SCons.Node.FS.find_file(n, (source_dir,) + sub_path[0]) if i: return i, include # see if we find it using the path in env['ENV'][var] i = SCons.Node.FS.find_file(n, (source_dir,) + sub_path[1]) if i: return i, include return i, include def canonical_text(self, text): """Standardize an input TeX-file contents. Currently: * removes comments, unwrapping comment-wrapped lines. """ out = [] line_continues_a_comment = False for line in text.splitlines(): line,comment = self.comment_re.findall(line)[0] if line_continues_a_comment == True: out[-1] = out[-1] + line.lstrip() else: out.append(line) line_continues_a_comment = len(comment) > 0 return '\n'.join(out).rstrip()+'\n' def scan(self, node): # Modify the default scan function to allow for the regular # expression to return a comma separated list of file names # as can be the case with the bibliography keyword. # Cache the includes list in node so we only scan it once: # path_dict = dict(list(path)) # add option for whitespace (\s) before the '[' noopt_cre = re.compile('\s*\[.*$') if node.includes != None: includes = node.includes else: text = self.canonical_text(node.get_text_contents()) includes = self.cre.findall(text) # 1. Split comma-separated lines, e.g. # ('bibliography', 'phys,comp') # should become two entries # ('bibliography', 'phys') # ('bibliography', 'comp') # 2. Remove the options, e.g., such as # ('includegraphics[clip,width=0.7\\linewidth]', 'picture.eps') # should become # ('includegraphics', 'picture.eps') split_includes = [] for include in includes: inc_type = noopt_cre.sub('', include[0]) inc_list = include[1].split(',') for j in range(len(inc_list)): split_includes.append( (inc_type, inc_list[j]) ) # includes = split_includes node.includes = includes return includes def scan_recurse(self, node, path=()): """ do a recursive scan of the top level target file This lets us search for included files based on the directory of the main file just as latex does""" path_dict = dict(list(path)) queue = [] queue.extend( self.scan(node) ) seen = {} # This is a hand-coded DSU (decorate-sort-undecorate, or # Schwartzian transform) pattern. The sort key is the raw name # of the file as specifed on the \include, \input, etc. line. # TODO: what about the comment in the original Classic scanner: # """which lets # us keep the sort order constant regardless of whether the file # is actually found in a Repository or locally.""" nodes = [] source_dir = node.get_dir() #for include in includes: while queue: include = queue.pop() try: if seen[include[1]] == 1: continue except KeyError: seen[include[1]] = 1 # # Handle multiple filenames in include[1] # n, i = self.find_include(include, source_dir, path_dict) if n is None: # Do not bother with 'usepackage' warnings, as they most # likely refer to system-level files if include[0] != 'usepackage': SCons.Warnings.warn(SCons.Warnings.DependencyWarning, "No dependency generated for file: %s (included from: %s) -- file not found" % (i, node)) else: sortkey = self.sort_key(n) nodes.append((sortkey, n)) # recurse down queue.extend( self.scan(n) ) return [pair[1] for pair in sorted(nodes)] # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Scanner/ScannerTests.py0000644000175000017500000005244612114661557023675 0ustar dktrkranzdktrkranz# # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Scanner/ScannerTests.py 2013/03/03 09:48:35 garyo" import SCons.compat import collections import sys import unittest import SCons.Scanner class DummyFS(object): def File(self, name): return DummyNode(name) class DummyEnvironment(collections.UserDict): def __init__(self, dict=None, **kw): collections.UserDict.__init__(self, dict) self.data.update(kw) self.fs = DummyFS() def subst(self, strSubst, target=None, source=None, conv=None): if strSubst[0] == '$': return self.data[strSubst[1:]] return strSubst def subst_list(self, strSubst, target=None, source=None, conv=None): if strSubst[0] == '$': return [self.data[strSubst[1:]]] return [[strSubst]] def subst_path(self, path, target=None, source=None, conv=None): if not isinstance(path, list): path = [path] return list(map(self.subst, path)) def get_factory(self, factory): return factory or self.fs.File class DummyNode(object): def __init__(self, name, search_result=()): self.name = name self.search_result = tuple(search_result) def rexists(self): return 1 def __str__(self): return self.name def Rfindalldirs(self, pathlist): return self.search_result + pathlist class FindPathDirsTestCase(unittest.TestCase): def test_FindPathDirs(self): """Test the FindPathDirs callable class""" env = DummyEnvironment(LIBPATH = [ 'foo' ]) env.fs = DummyFS() env.fs._cwd = DummyNode('cwd') dir = DummyNode('dir', ['xxx']) fpd = SCons.Scanner.FindPathDirs('LIBPATH') result = fpd(env) assert str(result) == "('foo',)", result result = fpd(env, dir) assert str(result) == "('xxx', 'foo')", result class ScannerTestCase(unittest.TestCase): def test_creation(self): """Test creation of Scanner objects""" def func(self): pass s = SCons.Scanner.Base(func) assert isinstance(s, SCons.Scanner.Base), s s = SCons.Scanner.Base({}) assert isinstance(s, SCons.Scanner.Base), s s = SCons.Scanner.Base(func, name='fooscan') assert str(s) == 'fooscan', str(s) s = SCons.Scanner.Base({}, name='barscan') assert str(s) == 'barscan', str(s) s = SCons.Scanner.Base(func, name='fooscan', argument=9) assert str(s) == 'fooscan', str(s) assert s.argument == 9, s.argument s = SCons.Scanner.Base({}, name='fooscan', argument=888) assert str(s) == 'fooscan', str(s) assert s.argument == 888, s.argument class BaseTestCase(unittest.TestCase): class skey_node(object): def __init__(self, key): self.key = key def scanner_key(self): return self.key def rexists(self): return 1 def func(self, filename, env, target, *args): self.filename = filename self.env = env self.target = target if len(args) > 0: self.arg = args[0] return self.deps def test(self, scanner, env, filename, deps, *args): self.deps = deps path = scanner.path(env) scanned = scanner(filename, env, path) scanned_strs = [str(x) for x in scanned] self.failUnless(self.filename == filename, "the filename was passed incorrectly") self.failUnless(self.env == env, "the environment was passed incorrectly") self.failUnless(scanned_strs == deps, "the dependencies were returned incorrectly") for d in scanned: self.failUnless(not isinstance(d, str), "got a string in the dependencies") if len(args) > 0: self.failUnless(self.arg == args[0], "the argument was passed incorrectly") else: self.failIf(hasattr(self, "arg"), "an argument was given when it shouldn't have been") def test___call__dict(self): """Test calling Scanner.Base objects with a dictionary""" called = [] def s1func(node, env, path, called=called): called.append('s1func') called.append(node) return [] def s2func(node, env, path, called=called): called.append('s2func') called.append(node) return [] s1 = SCons.Scanner.Base(s1func) s2 = SCons.Scanner.Base(s2func) selector = SCons.Scanner.Base({'.x' : s1, '.y' : s2}) nx = self.skey_node('.x') env = DummyEnvironment() selector(nx, env, []) assert called == ['s1func', nx], called del called[:] ny = self.skey_node('.y') selector(ny, env, []) assert called == ['s2func', ny], called def test_path(self): """Test the Scanner.Base path() method""" def pf(env, cwd, target, source, argument=None): return "pf: %s %s %s %s %s" % \ (env.VARIABLE, cwd, target[0], source[0], argument) env = DummyEnvironment() env.VARIABLE = 'v1' target = DummyNode('target') source = DummyNode('source') s = SCons.Scanner.Base(self.func, path_function=pf) p = s.path(env, 'here', [target], [source]) assert p == "pf: v1 here target source None", p s = SCons.Scanner.Base(self.func, path_function=pf, argument="xyz") p = s.path(env, 'here', [target], [source]) assert p == "pf: v1 here target source xyz", p def test_positional(self): """Test the Scanner.Base class using positional arguments""" s = SCons.Scanner.Base(self.func, "Pos") env = DummyEnvironment() env.VARIABLE = "var1" self.test(s, env, DummyNode('f1.cpp'), ['f1.h', 'f1.hpp']) env = DummyEnvironment() env.VARIABLE = "i1" self.test(s, env, DummyNode('i1.cpp'), ['i1.h', 'i1.hpp']) def test_keywords(self): """Test the Scanner.Base class using keyword arguments""" s = SCons.Scanner.Base(function = self.func, name = "Key") env = DummyEnvironment() env.VARIABLE = "var2" self.test(s, env, DummyNode('f2.cpp'), ['f2.h', 'f2.hpp']) env = DummyEnvironment() env.VARIABLE = "i2" self.test(s, env, DummyNode('i2.cpp'), ['i2.h', 'i2.hpp']) def test_pos_opt(self): """Test the Scanner.Base class using both position and optional arguments""" arg = "this is the argument" s = SCons.Scanner.Base(self.func, "PosArg", arg) env = DummyEnvironment() env.VARIABLE = "var3" self.test(s, env, DummyNode('f3.cpp'), ['f3.h', 'f3.hpp'], arg) env = DummyEnvironment() env.VARIABLE = "i3" self.test(s, env, DummyNode('i3.cpp'), ['i3.h', 'i3.hpp'], arg) def test_key_opt(self): """Test the Scanner.Base class using both keyword and optional arguments""" arg = "this is another argument" s = SCons.Scanner.Base(function = self.func, name = "KeyArg", argument = arg) env = DummyEnvironment() env.VARIABLE = "var4" self.test(s, env, DummyNode('f4.cpp'), ['f4.h', 'f4.hpp'], arg) env = DummyEnvironment() env.VARIABLE = "i4" self.test(s, env, DummyNode('i4.cpp'), ['i4.h', 'i4.hpp'], arg) def test___cmp__(self): """Test the Scanner.Base class __cmp__() method""" s = SCons.Scanner.Base(self.func, "Cmp") assert cmp(s, None) def test_hash(self): """Test the Scanner.Base class __hash__() method""" s = SCons.Scanner.Base(self.func, "Hash") dict = {} dict[s] = 777 i = hash(id(s)) h = hash(list(dict.keys())[0]) self.failUnless(h == i, "hash Scanner base class expected %s, got %s" % (i, h)) def test_scan_check(self): """Test the Scanner.Base class scan_check() method""" def my_scan(filename, env, target, *args): return [] def check(node, env, s=self): s.checked[str(node)] = 1 return 1 env = DummyEnvironment() s = SCons.Scanner.Base(my_scan, "Check", scan_check = check) self.checked = {} path = s.path(env) scanned = s(DummyNode('x'), env, path) self.failUnless(self.checked['x'] == 1, "did not call check function") def test_recursive(self): """Test the Scanner.Base class recursive flag""" nodes = [1, 2, 3, 4] s = SCons.Scanner.Base(function = self.func) n = s.recurse_nodes(nodes) self.failUnless(n == [], "default behavior returned nodes: %s" % n) s = SCons.Scanner.Base(function = self.func, recursive = None) n = s.recurse_nodes(nodes) self.failUnless(n == [], "recursive = None returned nodes: %s" % n) s = SCons.Scanner.Base(function = self.func, recursive = 1) n = s.recurse_nodes(nodes) self.failUnless(n == n, "recursive = 1 didn't return all nodes: %s" % n) def odd_only(nodes): return [n for n in nodes if n % 2] s = SCons.Scanner.Base(function = self.func, recursive = odd_only) n = s.recurse_nodes(nodes) self.failUnless(n == [1, 3], "recursive = 1 didn't return all nodes: %s" % n) def test_get_skeys(self): """Test the Scanner.Base get_skeys() method""" s = SCons.Scanner.Base(function = self.func) sk = s.get_skeys() self.failUnless(sk == [], "did not initialize to expected []") s = SCons.Scanner.Base(function = self.func, skeys = ['.1', '.2']) sk = s.get_skeys() self.failUnless(sk == ['.1', '.2'], "sk was %s, not ['.1', '.2']") s = SCons.Scanner.Base(function = self.func, skeys = '$LIST') env = DummyEnvironment(LIST = ['.3', '.4']) sk = s.get_skeys(env) self.failUnless(sk == ['.3', '.4'], "sk was %s, not ['.3', '.4']") def test_select(self): """Test the Scanner.Base select() method""" scanner = SCons.Scanner.Base(function = self.func) s = scanner.select('.x') assert s is scanner, s selector = SCons.Scanner.Base({'.x' : 1, '.y' : 2}) s = selector.select(self.skey_node('.x')) assert s == 1, s s = selector.select(self.skey_node('.y')) assert s == 2, s s = selector.select(self.skey_node('.z')) assert s is None, s def test_add_scanner(self): """Test the Scanner.Base add_scanner() method""" selector = SCons.Scanner.Base({'.x' : 1, '.y' : 2}) s = selector.select(self.skey_node('.z')) assert s is None, s selector.add_scanner('.z', 3) s = selector.select(self.skey_node('.z')) assert s == 3, s def test___str__(self): """Test the Scanner.Base __str__() method""" scanner = SCons.Scanner.Base(function = self.func) s = str(scanner) assert s == 'NONE', s scanner = SCons.Scanner.Base(function = self.func, name = 'xyzzy') s = str(scanner) assert s == 'xyzzy', s class SelectorTestCase(unittest.TestCase): class skey_node(object): def __init__(self, key): self.key = key def scanner_key(self): return self.key def rexists(self): return 1 def test___init__(self): """Test creation of Scanner.Selector object""" s = SCons.Scanner.Selector({}) assert isinstance(s, SCons.Scanner.Selector), s assert s.dict == {}, s.dict def test___call__(self): """Test calling Scanner.Selector objects""" called = [] def s1func(node, env, path, called=called): called.append('s1func') called.append(node) return [] def s2func(node, env, path, called=called): called.append('s2func') called.append(node) return [] s1 = SCons.Scanner.Base(s1func) s2 = SCons.Scanner.Base(s2func) selector = SCons.Scanner.Selector({'.x' : s1, '.y' : s2}) nx = self.skey_node('.x') env = DummyEnvironment() selector(nx, env, []) assert called == ['s1func', nx], called del called[:] ny = self.skey_node('.y') selector(ny, env, []) assert called == ['s2func', ny], called def test_select(self): """Test the Scanner.Selector select() method""" selector = SCons.Scanner.Selector({'.x' : 1, '.y' : 2}) s = selector.select(self.skey_node('.x')) assert s == 1, s s = selector.select(self.skey_node('.y')) assert s == 2, s s = selector.select(self.skey_node('.z')) assert s is None, s def test_add_scanner(self): """Test the Scanner.Selector add_scanner() method""" selector = SCons.Scanner.Selector({'.x' : 1, '.y' : 2}) s = selector.select(self.skey_node('.z')) assert s is None, s selector.add_scanner('.z', 3) s = selector.select(self.skey_node('.z')) assert s == 3, s class CurrentTestCase(unittest.TestCase): def test_class(self): """Test the Scanner.Current class""" class MyNode(object): def __init__(self): self.called_has_builder = None self.called_is_up_to_date = None self.func_called = None def rexists(self): return 1 class HasNoBuilder(MyNode): def has_builder(self): self.called_has_builder = 1 return None class IsNotCurrent(MyNode): def has_builder(self): self.called_has_builder = 1 return 1 def is_up_to_date(self): self.called_is_up_to_date = 1 return None class IsCurrent(MyNode): def has_builder(self): self.called_has_builder = 1 return 1 def is_up_to_date(self): self.called_is_up_to_date = 1 return 1 def func(node, env, path): node.func_called = 1 return [] env = DummyEnvironment() s = SCons.Scanner.Current(func) path = s.path(env) hnb = HasNoBuilder() s(hnb, env, path) self.failUnless(hnb.called_has_builder, "did not call has_builder()") self.failUnless(not hnb.called_is_up_to_date, "did call is_up_to_date()") self.failUnless(hnb.func_called, "did not call func()") inc = IsNotCurrent() s(inc, env, path) self.failUnless(inc.called_has_builder, "did not call has_builder()") self.failUnless(inc.called_is_up_to_date, "did not call is_up_to_date()") self.failUnless(not inc.func_called, "did call func()") ic = IsCurrent() s(ic, env, path) self.failUnless(ic.called_has_builder, "did not call has_builder()") self.failUnless(ic.called_is_up_to_date, "did not call is_up_to_date()") self.failUnless(ic.func_called, "did not call func()") class ClassicTestCase(unittest.TestCase): def test_find_include(self): """Test the Scanner.Classic find_include() method""" env = DummyEnvironment() s = SCons.Scanner.Classic("t", ['.suf'], 'MYPATH', '^my_inc (\S+)') def _find_file(filename, paths): return paths[0]+'/'+filename save = SCons.Node.FS.find_file SCons.Node.FS.find_file = _find_file try: n, i = s.find_include('aaa', 'foo', ('path',)) assert n == 'foo/aaa', n assert i == 'aaa', i finally: SCons.Node.FS.find_file = save def test_name(self): """Test setting the Scanner.Classic name""" s = SCons.Scanner.Classic("my_name", ['.s'], 'MYPATH', '^my_inc (\S+)') assert s.name == "my_name", s.name def test_scan(self): """Test the Scanner.Classic scan() method""" class MyNode(object): def __init__(self, name): self.name = name self._rfile = self self.includes = None def rfile(self): return self._rfile def exists(self): return self._exists def get_contents(self): return self._contents def get_text_contents(self): return self._contents def get_dir(self): return self._dir class MyScanner(SCons.Scanner.Classic): def find_include(self, include, source_dir, path): return include, include env = DummyEnvironment() s = MyScanner("t", ['.suf'], 'MYPATH', '^my_inc (\S+)') # This set of tests is intended to test the scanning operation # of the Classic scanner. # Note that caching has been added for not just the includes # but the entire scan call. The caching is based on the # arguments, so we will fiddle with the path parameter to # defeat this caching for the purposes of these tests. # If the node doesn't exist, scanning turns up nothing. n1 = MyNode("n1") n1._exists = None ret = s.function(n1, env) assert ret == [], ret # Verify that it finds includes from the contents. n = MyNode("n") n._exists = 1 n._dir = MyNode("n._dir") n._contents = 'my_inc abc\n' ret = s.function(n, env, ('foo',)) assert ret == ['abc'], ret # Verify that it uses the cached include info. n._contents = 'my_inc def\n' ret = s.function(n, env, ('foo2',)) assert ret == ['abc'], ret # Verify that if we wipe the cache, it uses the new contents. n.includes = None ret = s.function(n, env, ('foo3',)) assert ret == ['def'], ret # We no longer cache overall scan results, which would be returned # if individual results are de-cached. If we ever restore that # functionality, this test goes back here. #ret = s.function(n, env, ('foo2',)) #assert ret == ['abc'], 'caching inactive; got: %s'%ret # Verify that it sorts what it finds. n.includes = ['xyz', 'uvw'] ret = s.function(n, env, ('foo4',)) assert ret == ['uvw', 'xyz'], ret # Verify that we use the rfile() node. nr = MyNode("nr") nr._exists = 1 nr._dir = MyNode("nr._dir") nr.includes = ['jkl', 'mno'] n._rfile = nr ret = s.function(n, env, ('foo5',)) assert ret == ['jkl', 'mno'], ret class ClassicCPPTestCase(unittest.TestCase): def test_find_include(self): """Test the Scanner.ClassicCPP find_include() method""" env = DummyEnvironment() s = SCons.Scanner.ClassicCPP("Test", [], None, "") def _find_file(filename, paths): return paths[0]+'/'+filename save = SCons.Node.FS.find_file SCons.Node.FS.find_file = _find_file try: n, i = s.find_include(('"', 'aaa'), 'foo', ('path',)) assert n == 'foo/aaa', n assert i == 'aaa', i n, i = s.find_include(('<', 'bbb'), 'foo', ('path',)) assert n == 'path/bbb', n assert i == 'bbb', i n, i = s.find_include(('<', u'ccc'), 'foo', ('path',)) assert n == 'path/ccc', n assert i == 'ccc', i finally: SCons.Node.FS.find_file = save def suite(): suite = unittest.TestSuite() tclasses = [ FindPathDirsTestCase, ScannerTestCase, BaseTestCase, SelectorTestCase, CurrentTestCase, ClassicTestCase, ClassicCPPTestCase, ] for tclass in tclasses: names = unittest.getTestCaseNames(tclass, 'test_') suite.addTests(list(map(tclass, names))) return suite if __name__ == "__main__": runner = unittest.TextTestRunner() result = runner.run(suite()) if not result.wasSuccessful(): sys.exit(1) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Scanner/LaTeXTests.py0000644000175000017500000001174312114661557023254 0ustar dktrkranzdktrkranz# # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Scanner/LaTeXTests.py 2013/03/03 09:48:35 garyo" import SCons.compat import collections import os import sys import unittest import TestCmd import SCons.Node.FS import SCons.Scanner.LaTeX test = TestCmd.TestCmd(workdir = '') test.write('test1.latex',""" \include{inc1} \input{inc2} include{incNO} %\include{incNO} xyzzy \include{inc6} """) test.write('test2.latex',""" \include{inc1} \include{inc3} """) test.write('test3.latex',""" \includegraphics{inc4.eps} \includegraphics[width=60mm]{inc5.xyz} """) test.subdir('subdir') test.write('inc1.tex',"\n") test.write('inc2.tex',"\n") test.write(['subdir', 'inc3.tex'], "\n") test.write(['subdir', 'inc4.eps'], "\n") test.write('inc5.xyz', "\n") test.write('inc6.tex', "\n") test.write('incNO.tex', "\n") # define some helpers: # copied from CTest.py class DummyEnvironment(collections.UserDict): def __init__(self, **kw): collections.UserDict.__init__(self) self.data.update(kw) self.fs = SCons.Node.FS.FS(test.workpath('')) def Dictionary(self, *args): return self.data def subst(self, strSubst, target=None, source=None, conv=None): if strSubst[0] == '$': return self.data[strSubst[1:]] return strSubst def subst_list(self, strSubst, target=None, source=None, conv=None): if strSubst[0] == '$': return [self.data[strSubst[1:]]] return [[strSubst]] def subst_path(self, path, target=None, source=None, conv=None): if not isinstance(path, list): path = [path] return list(map(self.subst, path)) def get_calculator(self): return None def get_factory(self, factory): return factory or self.fs.File def Dir(self, filename): return self.fs.Dir(filename) def File(self, filename): return self.fs.File(filename) if os.path.normcase('foo') == os.path.normcase('FOO'): my_normpath = os.path.normcase else: my_normpath = os.path.normpath def deps_match(self, deps, headers): global my_normpath scanned = list(map(my_normpath, list(map(str, deps)))) expect = list(map(my_normpath, headers)) self.failUnless(scanned == expect, "expect %s != scanned %s" % (expect, scanned)) class LaTeXScannerTestCase1(unittest.TestCase): def runTest(self): env = DummyEnvironment(LATEXSUFFIXES = [".tex", ".ltx", ".latex"]) s = SCons.Scanner.LaTeX.LaTeXScanner() path = s.path(env) deps = s(env.File('test1.latex'), env, path) headers = ['inc1.tex', 'inc2.tex', 'inc6.tex'] deps_match(self, deps, headers) class LaTeXScannerTestCase2(unittest.TestCase): def runTest(self): env = DummyEnvironment(TEXINPUTS=[test.workpath("subdir")],LATEXSUFFIXES = [".tex", ".ltx", ".latex"]) s = SCons.Scanner.LaTeX.LaTeXScanner() path = s.path(env) deps = s(env.File('test2.latex'), env, path) headers = ['inc1.tex', 'subdir/inc3.tex'] deps_match(self, deps, headers) class LaTeXScannerTestCase3(unittest.TestCase): def runTest(self): env = DummyEnvironment(TEXINPUTS=[test.workpath("subdir")],LATEXSUFFIXES = [".tex", ".ltx", ".latex"]) s = SCons.Scanner.LaTeX.LaTeXScanner() path = s.path(env) deps = s(env.File('test3.latex'), env, path) files = ['inc5.xyz', 'subdir/inc4.eps'] deps_match(self, deps, files) def suite(): suite = unittest.TestSuite() suite.addTest(LaTeXScannerTestCase1()) suite.addTest(LaTeXScannerTestCase2()) suite.addTest(LaTeXScannerTestCase3()) return suite if __name__ == "__main__": runner = unittest.TextTestRunner() result = runner.run(suite()) if not result.wasSuccessful(): sys.exit(1) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Scanner/RCTests.py0000644000175000017500000001253212114661557022600 0ustar dktrkranzdktrkranz# # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Scanner/RCTests.py 2013/03/03 09:48:35 garyo" import TestCmd import SCons.Scanner.RC import unittest import sys import collections import os import SCons.Node.FS import SCons.Warnings test = TestCmd.TestCmd(workdir = '') os.chdir(test.workpath('')) # create some source files and headers: test.write('t1.rc',''' #include "t1.h" ''') test.write('t2.rc',""" #include "t1.h" ICO_TEST ICON DISCARDABLE "abc.ico" BMP_TEST BITMAP DISCARDABLE "def.bmp" cursor1 CURSOR "bullseye.cur" ID_RESPONSE_ERROR_PAGE HTML "responseerrorpage.htm" 5 FONT "cmroman.fnt" 1 MESSAGETABLE "MSG00409.bin" 1 MESSAGETABLE MSG00410.bin 1 TYPELIB "testtypelib.tlb" TEST_REGIS REGISTRY MOVEABLE PURE "testregis.rgs" TEST_D3DFX D3DFX DISCARDABLE "testEffect.fx" """) test.write('t3.rc','#include "t1.h"\r\n') # Create dummy include files headers = ['t1.h', 'abc.ico','def.bmp','bullseye.cur','responseerrorpage.htm','cmroman.fnt', 'testEffect.fx', 'MSG00409.bin','MSG00410.bin','testtypelib.tlb','testregis.rgs'] for h in headers: test.write(h, " ") # define some helpers: class DummyEnvironment(collections.UserDict): def __init__(self,**kw): collections.UserDict.__init__(self) self.data.update(kw) self.fs = SCons.Node.FS.FS(test.workpath('')) def Dictionary(self, *args): return self.data def subst(self, arg, target=None, source=None, conv=None): if strSubst[0] == '$': return self.data[strSubst[1:]] return strSubst def subst_path(self, path, target=None, source=None, conv=None): if not isinstance(path, list): path = [path] return list(map(self.subst, path)) def has_key(self, key): return key in self.Dictionary() def get_calculator(self): return None def get_factory(self, factory): return factory or self.fs.File def Dir(self, filename): return self.fs.Dir(filename) def File(self, filename): return self.fs.File(filename) global my_normpath my_normpath = os.path.normpath if os.path.normcase('foo') == os.path.normcase('FOO'): my_normpath = os.path.normcase def deps_match(self, deps, headers): scanned = sorted(map(my_normpath, list(map(str, deps)))) expect = sorted(map(my_normpath, headers)) self.failUnless(scanned == expect, "expect %s != scanned %s" % (expect, scanned)) # define some tests: class RCScannerTestCase1(unittest.TestCase): def runTest(self): path = [] env = DummyEnvironment(RCSUFFIXES=['.rc','.rc2'], CPPPATH=path) s = SCons.Scanner.RC.RCScan() deps = s(env.File('t1.rc'), env, path) headers = ['t1.h'] deps_match(self, deps, headers) class RCScannerTestCase2(unittest.TestCase): def runTest(self): path = [] env = DummyEnvironment(RCSUFFIXES=['.rc','.rc2'], CPPPATH=path) s = SCons.Scanner.RC.RCScan() deps = s(env.File('t2.rc'), env, path) headers = ['MSG00410.bin', 'abc.ico','bullseye.cur', 'cmroman.fnt','def.bmp', 'MSG00409.bin', 'responseerrorpage.htm', 't1.h', 'testEffect.fx', 'testregis.rgs','testtypelib.tlb'] deps_match(self, deps, headers) class RCScannerTestCase3(unittest.TestCase): def runTest(self): path = [] env = DummyEnvironment(RCSUFFIXES=['.rc','.rc2'], CPPPATH=path) s = SCons.Scanner.RC.RCScan() deps = s(env.File('t3.rc'), env, path) headers = ['t1.h'] deps_match(self, deps, headers) def suite(): suite = unittest.TestSuite() suite.addTest(RCScannerTestCase1()) suite.addTest(RCScannerTestCase2()) suite.addTest(RCScannerTestCase3()) return suite if __name__ == "__main__": runner = unittest.TextTestRunner() result = runner.run(suite()) if not result.wasSuccessful(): sys.exit(1) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Scanner/__init__.xml0000644000175000017500000000305012114661557023153 0ustar dktrkranzdktrkranz (variable) Returns a function (actually a callable Python object) intended to be used as the path_function of a Scanner object. The returned object will look up the specified variable in a construction environment and treat the construction variable's value as a list of directory paths that should be searched (like &cv-link-CPPPATH;, &cv-link-LIBPATH;, etc.). Note that use of &f-FindPathDirs; is generally preferable to writing your own path_function for the following reasons: 1) The returned list will contain all appropriate directories found in source trees (when &f-link-VariantDir; is used) or in code repositories (when &f-Repository; or the option are used). 2) scons will identify expansions of variable that evaluate to the same list of directories as, in fact, the same list, and avoid re-scanning the directories for files, when possible. Example: def my_scan(node, env, path, arg): # Code to scan file contents goes here... return include_files scanner = Scanner(name = 'myscanner', function = my_scan, path_function = FindPathDirs('MYPATH')) scons-doc-2.3.0/src/engine/SCons/Scanner/ProgTests.py0000644000175000017500000002277212114661557023212 0ustar dktrkranzdktrkranz# # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Scanner/ProgTests.py 2013/03/03 09:48:35 garyo" import os.path import sys import unittest import TestCmd import SCons.Node.FS import SCons.Scanner.Prog test = TestCmd.TestCmd(workdir = '') test.subdir('d1', ['d1', 'd2'], 'dir', ['dir', 'sub']) libs = [ 'l1.lib', 'd1/l2.lib', 'd1/d2/l3.lib', 'dir/libfoo.a', 'dir/sub/libbar.a', 'dir/libxyz.other'] for h in libs: test.write(h, "\n") # define some helpers: class DummyEnvironment(object): def __init__(self, **kw): self._dict = {'LIBSUFFIXES' : '.lib'} self._dict.update(kw) self.fs = SCons.Node.FS.FS(test.workpath('')) def Dictionary(self, *args): if not args: return self._dict elif len(args) == 1: return self._dict[args[0]] else: return [self._dict[x] for x in args] def has_key(self, key): return key in self.Dictionary() def __getitem__(self,key): return self.Dictionary()[key] def __setitem__(self,key,value): self.Dictionary()[key] = value def __delitem__(self,key): del self.Dictionary()[key] def subst(self, s, target=None, source=None, conv=None): try: if s[0] == '$': return self._dict[s[1:]] except IndexError: return '' return s def subst_path(self, path, target=None, source=None, conv=None): if not isinstance(path, list): path = [path] return list(map(self.subst, path)) def get_factory(self, factory): return factory or self.fs.File def Dir(self, filename): return self.fs.Dir(test.workpath(filename)) def File(self, filename): return self.fs.File(test.workpath(filename)) class DummyNode(object): def __init__(self, name): self.name = name def rexists(self): return 1 def __str__(self): return self.name def deps_match(deps, libs): deps=sorted(map(str, deps)) libs.sort() return list(map(os.path.normpath, deps)) == list(map(os.path.normpath, libs)) # define some tests: class ProgramScannerTestCase1(unittest.TestCase): def runTest(self): env = DummyEnvironment(LIBPATH=[ test.workpath("") ], LIBS=[ 'l1', 'l2', 'l3' ]) s = SCons.Scanner.Prog.ProgramScanner() path = s.path(env) deps = s(DummyNode('dummy'), env, path) assert deps_match(deps, ['l1.lib']), list(map(str, deps)) env = DummyEnvironment(LIBPATH=[ test.workpath("") ], LIBS='l1') s = SCons.Scanner.Prog.ProgramScanner() path = s.path(env) deps = s(DummyNode('dummy'), env, path) assert deps_match(deps, ['l1.lib']), list(map(str, deps)) f1 = env.fs.File(test.workpath('f1')) env = DummyEnvironment(LIBPATH=[ test.workpath("") ], LIBS=[f1]) s = SCons.Scanner.Prog.ProgramScanner() path = s.path(env) deps = s(DummyNode('dummy'), env, path) assert deps[0] is f1, deps f2 = env.fs.File(test.workpath('f1')) env = DummyEnvironment(LIBPATH=[ test.workpath("") ], LIBS=f2) s = SCons.Scanner.Prog.ProgramScanner() path = s.path(env) deps = s(DummyNode('dummy'), env, path) assert deps[0] is f2, deps class ProgramScannerTestCase2(unittest.TestCase): def runTest(self): env = DummyEnvironment(LIBPATH=list(map(test.workpath, ["", "d1", "d1/d2" ])), LIBS=[ 'l1', 'l2', 'l3' ]) s = SCons.Scanner.Prog.ProgramScanner() path = s.path(env) deps = s(DummyNode('dummy'), env, path) assert deps_match(deps, ['l1.lib', 'd1/l2.lib', 'd1/d2/l3.lib' ]), list(map(str, deps)) class ProgramScannerTestCase3(unittest.TestCase): def runTest(self): env = DummyEnvironment(LIBPATH=[test.workpath("d1/d2"), test.workpath("d1")], LIBS='l2 l3'.split()) s = SCons.Scanner.Prog.ProgramScanner() path = s.path(env) deps = s(DummyNode('dummy'), env, path) assert deps_match(deps, ['d1/l2.lib', 'd1/d2/l3.lib']), list(map(str, deps)) class ProgramScannerTestCase5(unittest.TestCase): def runTest(self): class SubstEnvironment(DummyEnvironment): def subst(self, arg, target=None, source=None, conv=None, path=test.workpath("d1")): if arg == "$blah": return test.workpath("d1") else: return arg env = SubstEnvironment(LIBPATH=[ "$blah" ], LIBS='l2 l3'.split()) s = SCons.Scanner.Prog.ProgramScanner() path = s.path(env) deps = s(DummyNode('dummy'), env, path) assert deps_match(deps, [ 'd1/l2.lib' ]), list(map(str, deps)) class ProgramScannerTestCase6(unittest.TestCase): def runTest(self): env = DummyEnvironment(LIBPATH=[ test.workpath("dir") ], LIBS=['foo', 'sub/libbar', 'xyz.other'], LIBPREFIXES=['lib'], LIBSUFFIXES=['.a']) s = SCons.Scanner.Prog.ProgramScanner() path = s.path(env) deps = s(DummyNode('dummy'), env, path) assert deps_match(deps, ['dir/libfoo.a', 'dir/sub/libbar.a', 'dir/libxyz.other']), list(map(str, deps)) class ProgramScannerTestCase7(unittest.TestCase): def runTest(self): env = DummyEnvironment(LIBPATH=[ test.workpath("dir") ], LIBS=['foo', '$LIBBAR', '$XYZ'], LIBPREFIXES=['lib'], LIBSUFFIXES=['.a'], LIBBAR='sub/libbar', XYZ='xyz.other') s = SCons.Scanner.Prog.ProgramScanner() path = s.path(env) deps = s(DummyNode('dummy'), env, path) assert deps_match(deps, ['dir/libfoo.a', 'dir/sub/libbar.a', 'dir/libxyz.other']), list(map(str, deps)) class ProgramScannerTestCase8(unittest.TestCase): def runTest(self): n1 = DummyNode('n1') env = DummyEnvironment(LIBPATH=[ test.workpath("dir") ], LIBS=[n1], LIBPREFIXES=['p1-', 'p2-'], LIBSUFFIXES=['.1', '2']) s = SCons.Scanner.Prog.ProgramScanner(node_class = DummyNode) path = s.path(env) deps = s(DummyNode('dummy'), env, path) assert deps == [n1], deps n2 = DummyNode('n2') env = DummyEnvironment(LIBPATH=[ test.workpath("dir") ], LIBS=[n1, [n2]], LIBPREFIXES=['p1-', 'p2-'], LIBSUFFIXES=['.1', '2']) s = SCons.Scanner.Prog.ProgramScanner(node_class = DummyNode) path = s.path(env) deps = s(DummyNode('dummy'), env, path) assert deps == [n1, n2], deps def suite(): suite = unittest.TestSuite() suite.addTest(ProgramScannerTestCase1()) suite.addTest(ProgramScannerTestCase2()) suite.addTest(ProgramScannerTestCase3()) suite.addTest(ProgramScannerTestCase5()) suite.addTest(ProgramScannerTestCase6()) suite.addTest(ProgramScannerTestCase7()) suite.addTest(ProgramScannerTestCase8()) try: unicode except NameError: pass else: code = """if 1: class ProgramScannerTestCase4(unittest.TestCase): def runTest(self): env = DummyEnvironment(LIBPATH=[test.workpath("d1/d2"), test.workpath("d1")], LIBS=u'l2 l3'.split()) s = SCons.Scanner.Prog.ProgramScanner() path = s.path(env) deps = s(DummyNode('dummy'), env, path) assert deps_match(deps, ['d1/l2.lib', 'd1/d2/l3.lib']), map(str, deps) suite.addTest(ProgramScannerTestCase4()) \n""" exec code return suite if __name__ == "__main__": runner = unittest.TextTestRunner() result = runner.run(suite()) if not result.wasSuccessful(): sys.exit(1) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Scanner/IDLTests.py0000644000175000017500000003232312114661557022704 0ustar dktrkranzdktrkranz# # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Scanner/IDLTests.py 2013/03/03 09:48:35 garyo" import TestCmd import SCons.Scanner.IDL import unittest import sys import os import os.path import SCons.Node.FS import SCons.Warnings test = TestCmd.TestCmd(workdir = '') os.chdir(test.workpath('')) # create some source files and headers: test.write('t1.idl',''' #include "f1.idl" #include import "f3.idl"; [ object, uuid(22995106-CE26-4561-AF1B-C71C6934B840), dual, helpstring("IBarObject Interface"), pointer_default(unique) ] interface IBarObject : IDispatch { }; ''') test.write('t2.idl',""" #include \"d1/f1.idl\" #include #include \"f1.idl\" import ; [ object, uuid(22995106-CE26-4561-AF1B-C71C6934B840), dual, helpstring(\"IBarObject Interface\"), pointer_default(unique) ] interface IBarObject : IDispatch { }; """) test.write('t3.idl',""" #include \t \"f1.idl\" \t #include \"f2.idl\" # \t include \"f3-test.idl\" #include \t \t #include # \t include import \t \"d1/f1.idl\" \t import \"d1/f2.idl\" include \t \"never.idl\" \t include \"never.idl\" // #include \"never.idl\" const char* x = \"#include \" [ object, uuid(22995106-CE26-4561-AF1B-C71C6934B840), dual, helpstring(\"IBarObject Interface\"), pointer_default(unique) ] interface IBarObject : IDispatch { }; """) test.subdir('d1', ['d1', 'd2']) headers = ['f1.idl','f2.idl', 'f3.idl', 'f3-test.idl', 'fi.idl', 'fj.idl', 'never.idl', 'd1/f1.idl', 'd1/f2.idl', 'd1/f3-test.idl', 'd1/fi.idl', 'd1/fj.idl', 'd1/d2/f1.idl', 'd1/d2/f2.idl', 'd1/d2/f3-test.idl', 'd1/d2/f4.idl', 'd1/d2/fi.idl', 'd1/d2/fj.idl'] for h in headers: test.write(h, " ") test.write('f2.idl',""" #include "fi.idl" """) test.write('f3-test.idl',""" #include """) test.subdir('include', 'subdir', ['subdir', 'include']) test.write('t4.idl',""" #include \"fa.idl\" #include [ object, uuid(22995106-CE26-4561-AF1B-C71C6934B840), dual, helpstring(\"IBarObject Interface\"), pointer_default(unique) ] interface IBarObject : IDispatch { }; """) test.write(['include', 'fa.idl'], "\n") test.write(['include', 'fb.idl'], "\n") test.write(['subdir', 'include', 'fa.idl'], "\n") test.write(['subdir', 'include', 'fb.idl'], "\n") test.subdir('repository', ['repository', 'include'], ['repository', 'src' ]) test.subdir('work', ['work', 'src']) test.write(['repository', 'include', 'iii.idl'], "\n") test.write(['work', 'src', 'fff.c'], """ #include #include int main() { return 0; } """) test.write([ 'work', 'src', 'aaa.c'], """ #include "bbb.idl" int main() { return 0; } """) test.write([ 'work', 'src', 'bbb.idl'], "\n") test.write([ 'repository', 'src', 'ccc.c'], """ #include "ddd.idl" int main() { return 0; } """) test.write([ 'repository', 'src', 'ddd.idl'], "\n") # define some helpers: class DummyEnvironment(object): def __init__(self, listCppPath): self.path = listCppPath self.fs = SCons.Node.FS.FS(test.workpath('')) def Dictionary(self, *args): if not args: return { 'CPPPATH': self.path } elif len(args) == 1 and args[0] == 'CPPPATH': return self.path else: raise KeyError("Dummy environment only has CPPPATH attribute.") def subst(self, arg, target=None, source=None, conv=None): return arg def subst_path(self, path, target=None, source=None, conv=None): if not isinstance(path, list): path = [path] return list(map(self.subst, path)) def has_key(self, key): return key in self.Dictionary() def __getitem__(self,key): return self.Dictionary()[key] def __setitem__(self,key,value): self.Dictionary()[key] = value def __delitem__(self,key): del self.Dictionary()[key] def get_calculator(self): return None def get_factory(self, factory): return factory or self.fs.File def Dir(self, filename): return self.fs.Dir(filename) def File(self, filename): return self.fs.File(filename) global my_normpath my_normpath = os.path.normpath if os.path.normcase('foo') == os.path.normcase('FOO'): my_normpath = os.path.normcase def deps_match(self, deps, headers): scanned = list(map(my_normpath, list(map(str, deps)))) expect = list(map(my_normpath, headers)) self.failUnless(scanned == expect, "expect %s != scanned %s" % (expect, scanned)) # define some tests: class IDLScannerTestCase1(unittest.TestCase): def runTest(self): env = DummyEnvironment([]) s = SCons.Scanner.IDL.IDLScan() path = s.path(env) deps = s(env.File('t1.idl'), env, path) headers = ['f1.idl', 'f3.idl', 'f2.idl'] deps_match(self, deps, headers) class IDLScannerTestCase2(unittest.TestCase): def runTest(self): env = DummyEnvironment([test.workpath("d1")]) s = SCons.Scanner.IDL.IDLScan() path = s.path(env) deps = s(env.File('t1.idl'), env, path) headers = ['f1.idl', 'f3.idl', 'd1/f2.idl'] deps_match(self, deps, headers) class IDLScannerTestCase3(unittest.TestCase): def runTest(self): env = DummyEnvironment([test.workpath("d1")]) s = SCons.Scanner.IDL.IDLScan() path = s.path(env) deps = s(env.File('t2.idl'), env, path) headers = ['d1/f1.idl', 'f1.idl', 'd1/d2/f1.idl', 'f3.idl'] deps_match(self, deps, headers) class IDLScannerTestCase4(unittest.TestCase): def runTest(self): env = DummyEnvironment([test.workpath("d1"), test.workpath("d1/d2")]) s = SCons.Scanner.IDL.IDLScan() path = s.path(env) deps = s(env.File('t2.idl'), env, path) headers = ['d1/f1.idl', 'f1.idl', 'd1/d2/f1.idl', 'f3.idl'] deps_match(self, deps, headers) class IDLScannerTestCase5(unittest.TestCase): def runTest(self): env = DummyEnvironment([]) s = SCons.Scanner.IDL.IDLScan() path = s.path(env) n = env.File('t3.idl') def my_rexists(s=n): s.rexists_called = 1 return s.old_rexists() setattr(n, 'old_rexists', n.rexists) setattr(n, 'rexists', my_rexists) deps = s(n, env, path) # Make sure rexists() got called on the file node being # scanned, essential for cooperation with VariantDir functionality. assert n.rexists_called headers = ['d1/f1.idl', 'd1/f2.idl', 'f1.idl', 'f2.idl', 'f3-test.idl', 'd1/f1.idl', 'd1/f2.idl', 'd1/f3-test.idl'] deps_match(self, deps, headers) class IDLScannerTestCase6(unittest.TestCase): def runTest(self): env1 = DummyEnvironment([test.workpath("d1")]) env2 = DummyEnvironment([test.workpath("d1/d2")]) s = SCons.Scanner.IDL.IDLScan() path1 = s.path(env1) path2 = s.path(env2) deps1 = s(env1.File('t1.idl'), env1, path1) deps2 = s(env2.File('t1.idl'), env2, path2) headers1 = ['f1.idl', 'f3.idl', 'd1/f2.idl'] headers2 = ['f1.idl', 'f3.idl', 'd1/d2/f2.idl'] deps_match(self, deps1, headers1) deps_match(self, deps2, headers2) class IDLScannerTestCase7(unittest.TestCase): def runTest(self): env = DummyEnvironment(["include"]) s = SCons.Scanner.IDL.IDLScan() path = s.path(env) deps1 = s(env.File('t4.idl'), env, path) env.fs.chdir(env.Dir('subdir')) dir = env.fs.getcwd() env.fs.chdir(env.Dir('')) path = s.path(env, dir) deps2 = s(env.File('#t4.idl'), env, path) headers1 = list(map(test.workpath, ['include/fa.idl', 'include/fb.idl'])) headers2 = ['include/fa.idl', 'include/fb.idl'] deps_match(self, deps1, headers1) deps_match(self, deps2, headers2) class IDLScannerTestCase8(unittest.TestCase): def runTest(self): SCons.Warnings.enableWarningClass(SCons.Warnings.DependencyWarning) class TestOut(object): def __call__(self, x): self.out = x to = TestOut() to.out = None SCons.Warnings._warningOut = to test.write('fa.idl','\n') env = DummyEnvironment([]) s = SCons.Scanner.IDL.IDLScan() path = s.path(env) deps = s(env.File('t4.idl'), env, path) # Did we catch the warning associated with not finding fb.idl? assert to.out deps_match(self, deps, [ 'fa.idl' ]) test.unlink('fa.idl') class IDLScannerTestCase9(unittest.TestCase): def runTest(self): env = DummyEnvironment([]) env.fs.chdir(env.Dir('include')) s = SCons.Scanner.IDL.IDLScan() path = s.path(env) test.write('include/t4.idl', test.read('t4.idl')) deps = s(env.File('#include/t4.idl'), env, path) env.fs.chdir(env.Dir('')) deps_match(self, deps, [ 'fa.idl', 'fb.idl' ]) test.unlink('include/t4.idl') class IDLScannerTestCase10(unittest.TestCase): def runTest(self): os.chdir(test.workpath('work')) fs = SCons.Node.FS.FS(test.workpath('work')) fs.Repository(test.workpath('repository')) # Create a derived file in a directory that does not exist yet. # This was a bug at one time. env = DummyEnvironment(['include', 'include2']) env.fs = fs f1 = fs.File('include2/jjj.idl') f1.builder = 1 s = SCons.Scanner.IDL.IDLScan() path = s.path(env) deps = s(fs.File('src/fff.c'), env, path) deps_match(self, deps, [ test.workpath('repository/include/iii.idl'), 'include2/jjj.idl' ]) os.chdir(test.workpath('')) class IDLScannerTestCase11(unittest.TestCase): def runTest(self): os.chdir(test.workpath('work')) fs = SCons.Node.FS.FS(test.workpath('work')) fs.VariantDir('build1', 'src', 1) fs.VariantDir('build2', 'src', 0) fs.Repository(test.workpath('repository')) env = DummyEnvironment([]) env.fs = fs s = SCons.Scanner.IDL.IDLScan() path = s.path(env) deps1 = s(fs.File('build1/aaa.c'), env, path) deps_match(self, deps1, [ 'build1/bbb.idl' ]) deps2 = s(fs.File('build2/aaa.c'), env, path) deps_match(self, deps2, [ 'src/bbb.idl' ]) deps3 = s(fs.File('build1/ccc.c'), env, path) deps_match(self, deps3, [ 'build1/ddd.idl' ]) deps4 = s(fs.File('build2/ccc.c'), env, path) deps_match(self, deps4, [ test.workpath('repository/src/ddd.idl') ]) os.chdir(test.workpath('')) class IDLScannerTestCase12(unittest.TestCase): def runTest(self): class SubstEnvironment(DummyEnvironment): def subst(self, arg, target=None, source=None, conv=None, test=test): if arg == "$blah": return test.workpath("d1") else: return arg env = SubstEnvironment(["$blah"]) s = SCons.Scanner.IDL.IDLScan() path = s.path(env) deps = s(env.File('t1.idl'), env, path) headers = ['f1.idl', 'f3.idl', 'd1/f2.idl'] deps_match(self, deps, headers) def suite(): suite = unittest.TestSuite() suite.addTest(IDLScannerTestCase1()) suite.addTest(IDLScannerTestCase2()) suite.addTest(IDLScannerTestCase3()) suite.addTest(IDLScannerTestCase4()) suite.addTest(IDLScannerTestCase5()) suite.addTest(IDLScannerTestCase6()) suite.addTest(IDLScannerTestCase7()) suite.addTest(IDLScannerTestCase8()) suite.addTest(IDLScannerTestCase9()) suite.addTest(IDLScannerTestCase10()) suite.addTest(IDLScannerTestCase11()) suite.addTest(IDLScannerTestCase12()) return suite if __name__ == "__main__": runner = unittest.TextTestRunner() result = runner.run(suite()) if not result.wasSuccessful(): sys.exit(1) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Scanner/__init__.py0000644000175000017500000003461012114661557023011 0ustar dktrkranzdktrkranz"""SCons.Scanner The Scanner package for the SCons software construction utility. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Scanner/__init__.py 2013/03/03 09:48:35 garyo" import re import SCons.Node.FS import SCons.Util class _Null(object): pass # This is used instead of None as a default argument value so None can be # used as an actual argument value. _null = _Null def Scanner(function, *args, **kw): """ Public interface factory function for creating different types of Scanners based on the different types of "functions" that may be supplied. TODO: Deprecate this some day. We've moved the functionality inside the Base class and really don't need this factory function any more. It was, however, used by some of our Tool modules, so the call probably ended up in various people's custom modules patterned on SCons code. """ if SCons.Util.is_Dict(function): return Selector(function, *args, **kw) else: return Base(function, *args, **kw) class FindPathDirs(object): """A class to bind a specific *PATH variable name to a function that will return all of the *path directories.""" def __init__(self, variable): self.variable = variable def __call__(self, env, dir=None, target=None, source=None, argument=None): import SCons.PathList try: path = env[self.variable] except KeyError: return () dir = dir or env.fs._cwd path = SCons.PathList.PathList(path).subst_path(env, target, source) return tuple(dir.Rfindalldirs(path)) class Base(object): """ The base class for dependency scanners. This implements straightforward, single-pass scanning of a single file. """ def __init__(self, function, name = "NONE", argument = _null, skeys = _null, path_function = None, # Node.FS.Base so that, by default, it's okay for a # scanner to return a Dir, File or Entry. node_class = SCons.Node.FS.Base, node_factory = None, scan_check = None, recursive = None): """ Construct a new scanner object given a scanner function. 'function' - a scanner function taking two or three arguments and returning a list of strings. 'name' - a name for identifying this scanner object. 'argument' - an optional argument that, if specified, will be passed to both the scanner function and the path_function. 'skeys' - an optional list argument that can be used to determine which scanner should be used for a given Node. In the case of File nodes, for example, the 'skeys' would be file suffixes. 'path_function' - a function that takes four or five arguments (a construction environment, Node for the directory containing the SConscript file that defined the primary target, list of target nodes, list of source nodes, and optional argument for this instance) and returns a tuple of the directories that can be searched for implicit dependency files. May also return a callable() which is called with no args and returns the tuple (supporting Bindable class). 'node_class' - the class of Nodes which this scan will return. If node_class is None, then this scanner will not enforce any Node conversion and will return the raw results from the underlying scanner function. 'node_factory' - the factory function to be called to translate the raw results returned by the scanner function into the expected node_class objects. 'scan_check' - a function to be called to first check whether this node really needs to be scanned. 'recursive' - specifies that this scanner should be invoked recursively on all of the implicit dependencies it returns (the canonical example being #include lines in C source files). May be a callable, which will be called to filter the list of nodes found to select a subset for recursive scanning (the canonical example being only recursively scanning subdirectories within a directory). The scanner function's first argument will be a Node that should be scanned for dependencies, the second argument will be an Environment object, the third argument will be the tuple of paths returned by the path_function, and the fourth argument will be the value passed into 'argument', and the returned list should contain the Nodes for all the direct dependencies of the file. Examples: s = Scanner(my_scanner_function) s = Scanner(function = my_scanner_function) s = Scanner(function = my_scanner_function, argument = 'foo') """ # Note: this class could easily work with scanner functions that take # something other than a filename as an argument (e.g. a database # node) and a dependencies list that aren't file names. All that # would need to be changed is the documentation. self.function = function self.path_function = path_function self.name = name self.argument = argument if skeys is _null: if SCons.Util.is_Dict(function): skeys = list(function.keys()) else: skeys = [] self.skeys = skeys self.node_class = node_class self.node_factory = node_factory self.scan_check = scan_check if callable(recursive): self.recurse_nodes = recursive elif recursive: self.recurse_nodes = self._recurse_all_nodes else: self.recurse_nodes = self._recurse_no_nodes def path(self, env, dir=None, target=None, source=None): if not self.path_function: return () if not self.argument is _null: return self.path_function(env, dir, target, source, self.argument) else: return self.path_function(env, dir, target, source) def __call__(self, node, env, path = ()): """ This method scans a single object. 'node' is the node that will be passed to the scanner function, and 'env' is the environment that will be passed to the scanner function. A list of direct dependency nodes for the specified node will be returned. """ if self.scan_check and not self.scan_check(node, env): return [] self = self.select(node) if not self.argument is _null: list = self.function(node, env, path, self.argument) else: list = self.function(node, env, path) kw = {} if hasattr(node, 'dir'): kw['directory'] = node.dir node_factory = env.get_factory(self.node_factory) nodes = [] for l in list: if self.node_class and not isinstance(l, self.node_class): l = node_factory(l, **kw) nodes.append(l) return nodes def __cmp__(self, other): try: return cmp(self.__dict__, other.__dict__) except AttributeError: # other probably doesn't have a __dict__ return cmp(self.__dict__, other) def __hash__(self): return id(self) def __str__(self): return self.name def add_skey(self, skey): """Add a skey to the list of skeys""" self.skeys.append(skey) def get_skeys(self, env=None): if env and SCons.Util.is_String(self.skeys): return env.subst_list(self.skeys)[0] return self.skeys def select(self, node): if SCons.Util.is_Dict(self.function): key = node.scanner_key() try: return self.function[key] except KeyError: return None else: return self def _recurse_all_nodes(self, nodes): return nodes def _recurse_no_nodes(self, nodes): return [] recurse_nodes = _recurse_no_nodes def add_scanner(self, skey, scanner): self.function[skey] = scanner self.add_skey(skey) class Selector(Base): """ A class for selecting a more specific scanner based on the scanner_key() (suffix) for a specific Node. TODO: This functionality has been moved into the inner workings of the Base class, and this class will be deprecated at some point. (It was never exposed directly as part of the public interface, although it is used by the Scanner() factory function that was used by various Tool modules and therefore was likely a template for custom modules that may be out there.) """ def __init__(self, dict, *args, **kw): Base.__init__(self, None, *args, **kw) self.dict = dict self.skeys = list(dict.keys()) def __call__(self, node, env, path = ()): return self.select(node)(node, env, path) def select(self, node): try: return self.dict[node.scanner_key()] except KeyError: return None def add_scanner(self, skey, scanner): self.dict[skey] = scanner self.add_skey(skey) class Current(Base): """ A class for scanning files that are source files (have no builder) or are derived files and are current (which implies that they exist, either locally or in a repository). """ def __init__(self, *args, **kw): def current_check(node, env): return not node.has_builder() or node.is_up_to_date() kw['scan_check'] = current_check Base.__init__(self, *args, **kw) class Classic(Current): """ A Scanner subclass to contain the common logic for classic CPP-style include scanning, but which can be customized to use different regular expressions to find the includes. Note that in order for this to work "out of the box" (without overriding the find_include() and sort_key() methods), the regular expression passed to the constructor must return the name of the include file in group 0. """ def __init__(self, name, suffixes, path_variable, regex, *args, **kw): self.cre = re.compile(regex, re.M) def _scan(node, env, path=(), self=self): node = node.rfile() if not node.exists(): return [] return self.scan(node, path) kw['function'] = _scan kw['path_function'] = FindPathDirs(path_variable) kw['recursive'] = 1 kw['skeys'] = suffixes kw['name'] = name Current.__init__(self, *args, **kw) def find_include(self, include, source_dir, path): n = SCons.Node.FS.find_file(include, (source_dir,) + tuple(path)) return n, include def sort_key(self, include): return SCons.Node.FS._my_normcase(include) def find_include_names(self, node): return self.cre.findall(node.get_text_contents()) def scan(self, node, path=()): # cache the includes list in node so we only scan it once: if node.includes is not None: includes = node.includes else: includes = self.find_include_names (node) # Intern the names of the include files. Saves some memory # if the same header is included many times. node.includes = list(map(SCons.Util.silent_intern, includes)) # This is a hand-coded DSU (decorate-sort-undecorate, or # Schwartzian transform) pattern. The sort key is the raw name # of the file as specifed on the #include line (including the # " or <, since that may affect what file is found), which lets # us keep the sort order constant regardless of whether the file # is actually found in a Repository or locally. nodes = [] source_dir = node.get_dir() if callable(path): path = path() for include in includes: n, i = self.find_include(include, source_dir, path) if n is None: SCons.Warnings.warn(SCons.Warnings.DependencyWarning, "No dependency generated for file: %s (included from: %s) -- file not found" % (i, node)) else: nodes.append((self.sort_key(include), n)) return [pair[1] for pair in sorted(nodes)] class ClassicCPP(Classic): """ A Classic Scanner subclass which takes into account the type of bracketing used to include the file, and uses classic CPP rules for searching for the files based on the bracketing. Note that in order for this to work, the regular expression passed to the constructor must return the leading bracket in group 0, and the contained filename in group 1. """ def find_include(self, include, source_dir, path): if include[0] == '"': paths = (source_dir,) + tuple(path) else: paths = tuple(path) + (source_dir,) n = SCons.Node.FS.find_file(include[1], paths) i = SCons.Util.silent_intern(include[1]) return n, i def sort_key(self, include): return SCons.Node.FS._my_normcase(' '.join(include)) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Scanner/Fortran.py0000644000175000017500000003402712114661557022667 0ustar dktrkranzdktrkranz"""SCons.Scanner.Fortran This module implements the dependency scanner for Fortran code. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Scanner/Fortran.py 2013/03/03 09:48:35 garyo" import re import SCons.Node import SCons.Node.FS import SCons.Scanner import SCons.Util import SCons.Warnings class F90Scanner(SCons.Scanner.Classic): """ A Classic Scanner subclass for Fortran source files which takes into account both USE and INCLUDE statements. This scanner will work for both F77 and F90 (and beyond) compilers. Currently, this scanner assumes that the include files do not contain USE statements. To enable the ability to deal with USE statements in include files, add logic right after the module names are found to loop over each include file, search for and locate each USE statement, and append each module name to the list of dependencies. Caching the search results in a common dictionary somewhere so that the same include file is not searched multiple times would be a smart thing to do. """ def __init__(self, name, suffixes, path_variable, use_regex, incl_regex, def_regex, *args, **kw): self.cre_use = re.compile(use_regex, re.M) self.cre_incl = re.compile(incl_regex, re.M) self.cre_def = re.compile(def_regex, re.M) def _scan(node, env, path, self=self): node = node.rfile() if not node.exists(): return [] return self.scan(node, env, path) kw['function'] = _scan kw['path_function'] = SCons.Scanner.FindPathDirs(path_variable) kw['recursive'] = 1 kw['skeys'] = suffixes kw['name'] = name SCons.Scanner.Current.__init__(self, *args, **kw) def scan(self, node, env, path=()): # cache the includes list in node so we only scan it once: if node.includes != None: mods_and_includes = node.includes else: # retrieve all included filenames includes = self.cre_incl.findall(node.get_text_contents()) # retrieve all USE'd module names modules = self.cre_use.findall(node.get_text_contents()) # retrieve all defined module names defmodules = self.cre_def.findall(node.get_text_contents()) # Remove all USE'd module names that are defined in the same file # (case-insensitively) d = {} for m in defmodules: d[m.lower()] = 1 modules = [m for m in modules if m.lower() not in d] # Convert module name to a .mod filename suffix = env.subst('$FORTRANMODSUFFIX') modules = [x.lower() + suffix for x in modules] # Remove unique items from the list mods_and_includes = SCons.Util.unique(includes+modules) node.includes = mods_and_includes # This is a hand-coded DSU (decorate-sort-undecorate, or # Schwartzian transform) pattern. The sort key is the raw name # of the file as specifed on the USE or INCLUDE line, which lets # us keep the sort order constant regardless of whether the file # is actually found in a Repository or locally. nodes = [] source_dir = node.get_dir() if callable(path): path = path() for dep in mods_and_includes: n, i = self.find_include(dep, source_dir, path) if n is None: SCons.Warnings.warn(SCons.Warnings.DependencyWarning, "No dependency generated for file: %s (referenced by: %s) -- file not found" % (i, node)) else: sortkey = self.sort_key(dep) nodes.append((sortkey, n)) return [pair[1] for pair in sorted(nodes)] def FortranScan(path_variable="FORTRANPATH"): """Return a prototype Scanner instance for scanning source files for Fortran USE & INCLUDE statements""" # The USE statement regex matches the following: # # USE module_name # USE :: module_name # USE, INTRINSIC :: module_name # USE, NON_INTRINSIC :: module_name # # Limitations # # -- While the regex can handle multiple USE statements on one line, # it cannot properly handle them if they are commented out. # In either of the following cases: # # ! USE mod_a ; USE mod_b [entire line is commented out] # USE mod_a ! ; USE mod_b [in-line comment of second USE statement] # # the second module name (mod_b) will be picked up as a dependency # even though it should be ignored. The only way I can see # to rectify this would be to modify the scanner to eliminate # the call to re.findall, read in the contents of the file, # treating the comment character as an end-of-line character # in addition to the normal linefeed, loop over each line, # weeding out the comments, and looking for the USE statements. # One advantage to this is that the regex passed to the scanner # would no longer need to match a semicolon. # # -- I question whether or not we need to detect dependencies to # INTRINSIC modules because these are built-in to the compiler. # If we consider them a dependency, will SCons look for them, not # find them, and kill the build? Or will we there be standard # compiler-specific directories we will need to point to so the # compiler and SCons can locate the proper object and mod files? # Here is a breakdown of the regex: # # (?i) : regex is case insensitive # ^ : start of line # (?: : group a collection of regex symbols without saving the match as a "group" # ^|; : matches either the start of the line or a semicolon - semicolon # ) : end the unsaved grouping # \s* : any amount of white space # USE : match the string USE, case insensitive # (?: : group a collection of regex symbols without saving the match as a "group" # \s+| : match one or more whitespace OR .... (the next entire grouped set of regex symbols) # (?: : group a collection of regex symbols without saving the match as a "group" # (?: : establish another unsaved grouping of regex symbols # \s* : any amount of white space # , : match a comma # \s* : any amount of white space # (?:NON_)? : optionally match the prefix NON_, case insensitive # INTRINSIC : match the string INTRINSIC, case insensitive # )? : optionally match the ", INTRINSIC/NON_INTRINSIC" grouped expression # \s* : any amount of white space # :: : match a double colon that must appear after the INTRINSIC/NON_INTRINSIC attribute # ) : end the unsaved grouping # ) : end the unsaved grouping # \s* : match any amount of white space # (\w+) : match the module name that is being USE'd # # use_regex = "(?i)(?:^|;)\s*USE(?:\s+|(?:(?:\s*,\s*(?:NON_)?INTRINSIC)?\s*::))\s*(\w+)" # The INCLUDE statement regex matches the following: # # INCLUDE 'some_Text' # INCLUDE "some_Text" # INCLUDE "some_Text" ; INCLUDE "some_Text" # INCLUDE kind_"some_Text" # INCLUDE kind_'some_Text" # # where some_Text can include any alphanumeric and/or special character # as defined by the Fortran 2003 standard. # # Limitations: # # -- The Fortran standard dictates that a " or ' in the INCLUDE'd # string must be represented as a "" or '', if the quotes that wrap # the entire string are either a ' or ", respectively. While the # regular expression below can detect the ' or " characters just fine, # the scanning logic, presently is unable to detect them and reduce # them to a single instance. This probably isn't an issue since, # in practice, ' or " are not generally used in filenames. # # -- This regex will not properly deal with multiple INCLUDE statements # when the entire line has been commented out, ala # # ! INCLUDE 'some_file' ; INCLUDE 'some_file' # # In such cases, it will properly ignore the first INCLUDE file, # but will actually still pick up the second. Interestingly enough, # the regex will properly deal with these cases: # # INCLUDE 'some_file' # INCLUDE 'some_file' !; INCLUDE 'some_file' # # To get around the above limitation, the FORTRAN programmer could # simply comment each INCLUDE statement separately, like this # # ! INCLUDE 'some_file' !; INCLUDE 'some_file' # # The way I see it, the only way to get around this limitation would # be to modify the scanning logic to replace the calls to re.findall # with a custom loop that processes each line separately, throwing # away fully commented out lines before attempting to match against # the INCLUDE syntax. # # Here is a breakdown of the regex: # # (?i) : regex is case insensitive # (?: : begin a non-saving group that matches the following: # ^ : either the start of the line # | : or # ['">]\s*; : a semicolon that follows a single quote, # double quote or greater than symbol (with any # amount of whitespace in between). This will # allow the regex to match multiple INCLUDE # statements per line (although it also requires # the positive lookahead assertion that is # used below). It will even properly deal with # (i.e. ignore) cases in which the additional # INCLUDES are part of an in-line comment, ala # " INCLUDE 'someFile' ! ; INCLUDE 'someFile2' " # ) : end of non-saving group # \s* : any amount of white space # INCLUDE : match the string INCLUDE, case insensitive # \s+ : match one or more white space characters # (?\w+_)? : match the optional "kind-param _" prefix allowed by the standard # [<"'] : match the include delimiter - an apostrophe, double quote, or less than symbol # (.+?) : match one or more characters that make up # the included path and file name and save it # in a group. The Fortran standard allows for # any non-control character to be used. The dot # operator will pick up any character, including # control codes, but I can't conceive of anyone # putting control codes in their file names. # The question mark indicates it is non-greedy so # that regex will match only up to the next quote, # double quote, or greater than symbol # (?=["'>]) : positive lookahead assertion to match the include # delimiter - an apostrophe, double quote, or # greater than symbol. This level of complexity # is required so that the include delimiter is # not consumed by the match, thus allowing the # sub-regex discussed above to uniquely match a # set of semicolon-separated INCLUDE statements # (as allowed by the F2003 standard) include_regex = """(?i)(?:^|['">]\s*;)\s*INCLUDE\s+(?:\w+_)?[<"'](.+?)(?=["'>])""" # The MODULE statement regex finds module definitions by matching # the following: # # MODULE module_name # # but *not* the following: # # MODULE PROCEDURE procedure_name # # Here is a breakdown of the regex: # # (?i) : regex is case insensitive # ^\s* : any amount of white space # MODULE : match the string MODULE, case insensitive # \s+ : match one or more white space characters # (?!PROCEDURE) : but *don't* match if the next word matches # PROCEDURE (negative lookahead assertion), # case insensitive # (\w+) : match one or more alphanumeric characters # that make up the defined module name and # save it in a group def_regex = """(?i)^\s*MODULE\s+(?!PROCEDURE)(\w+)""" scanner = F90Scanner("FortranScan", "$FORTRANSUFFIXES", path_variable, use_regex, include_regex, def_regex) return scanner # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Options/0000755000175000017500000000000012114661557020736 5ustar dktrkranzdktrkranzscons-doc-2.3.0/src/engine/SCons/Options/BoolOption.py0000644000175000017500000000373712114661557023406 0ustar dktrkranzdktrkranz# # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Options/BoolOption.py 2013/03/03 09:48:35 garyo" __doc__ = """Place-holder for the old SCons.Options module hierarchy This is for backwards compatibility. The new equivalent is the Variables/ class hierarchy. These will have deprecation warnings added (some day), and will then be removed entirely (some day). """ import SCons.Variables import SCons.Warnings warned = False def BoolOption(*args, **kw): global warned if not warned: msg = "The BoolOption() function is deprecated; use the BoolVariable() function instead." SCons.Warnings.warn(SCons.Warnings.DeprecatedOptionsWarning, msg) warned = True return SCons.Variables.BoolVariable(*args, **kw) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Options/PackageOption.py0000644000175000017500000000375612114661557024047 0ustar dktrkranzdktrkranz# # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Options/PackageOption.py 2013/03/03 09:48:35 garyo" __doc__ = """Place-holder for the old SCons.Options module hierarchy This is for backwards compatibility. The new equivalent is the Variables/ class hierarchy. These will have deprecation warnings added (some day), and will then be removed entirely (some day). """ import SCons.Variables import SCons.Warnings warned = False def PackageOption(*args, **kw): global warned if not warned: msg = "The PackageOption() function is deprecated; use the PackageVariable() function instead." SCons.Warnings.warn(SCons.Warnings.DeprecatedOptionsWarning, msg) warned = True return SCons.Variables.PackageVariable(*args, **kw) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Options/ListOption.py0000644000175000017500000000373712114661557023426 0ustar dktrkranzdktrkranz# # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Options/ListOption.py 2013/03/03 09:48:35 garyo" __doc__ = """Place-holder for the old SCons.Options module hierarchy This is for backwards compatibility. The new equivalent is the Variables/ class hierarchy. These will have deprecation warnings added (some day), and will then be removed entirely (some day). """ import SCons.Variables import SCons.Warnings warned = False def ListOption(*args, **kw): global warned if not warned: msg = "The ListOption() function is deprecated; use the ListVariable() function instead." SCons.Warnings.warn(SCons.Warnings.DeprecatedOptionsWarning, msg) warned = True return SCons.Variables.ListVariable(*args, **kw) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Options/EnumOption.py0000644000175000017500000000373712114661557023417 0ustar dktrkranzdktrkranz# # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Options/EnumOption.py 2013/03/03 09:48:35 garyo" __doc__ = """Place-holder for the old SCons.Options module hierarchy This is for backwards compatibility. The new equivalent is the Variables/ class hierarchy. These will have deprecation warnings added (some day), and will then be removed entirely (some day). """ import SCons.Variables import SCons.Warnings warned = False def EnumOption(*args, **kw): global warned if not warned: msg = "The EnumOption() function is deprecated; use the EnumVariable() function instead." SCons.Warnings.warn(SCons.Warnings.DeprecatedOptionsWarning, msg) warned = True return SCons.Variables.EnumVariable(*args, **kw) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Options/__init__.py0000644000175000017500000000516712114661557023060 0ustar dktrkranzdktrkranz# # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Options/__init__.py 2013/03/03 09:48:35 garyo" __doc__ = """Place-holder for the old SCons.Options module hierarchy This is for backwards compatibility. The new equivalent is the Variables/ class hierarchy. These will have deprecation warnings added (some day), and will then be removed entirely (some day). """ import SCons.Variables import SCons.Warnings from BoolOption import BoolOption # okay from EnumOption import EnumOption # okay from ListOption import ListOption # naja from PackageOption import PackageOption # naja from PathOption import PathOption # okay warned = False class Options(SCons.Variables.Variables): def __init__(self, *args, **kw): global warned if not warned: msg = "The Options class is deprecated; use the Variables class instead." SCons.Warnings.warn(SCons.Warnings.DeprecatedOptionsWarning, msg) warned = True SCons.Variables.Variables.__init__(self, *args, **kw) def AddOptions(self, *args, **kw): return SCons.Variables.Variables.AddVariables(self, *args, **kw) def UnknownOptions(self, *args, **kw): return SCons.Variables.Variables.UnknownVariables(self, *args, **kw) def FormatOptionHelpText(self, *args, **kw): return SCons.Variables.Variables.FormatVariableHelpText(self, *args, **kw) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Options/PathOption.py0000644000175000017500000000536112114661557023402 0ustar dktrkranzdktrkranz# # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Options/PathOption.py 2013/03/03 09:48:35 garyo" __doc__ = """Place-holder for the old SCons.Options module hierarchy This is for backwards compatibility. The new equivalent is the Variables/ class hierarchy. These will have deprecation warnings added (some day), and will then be removed entirely (some day). """ import SCons.Variables import SCons.Warnings warned = False class _PathOptionClass(object): def warn(self): global warned if not warned: msg = "The PathOption() function is deprecated; use the PathVariable() function instead." SCons.Warnings.warn(SCons.Warnings.DeprecatedOptionsWarning, msg) warned = True def __call__(self, *args, **kw): self.warn() return SCons.Variables.PathVariable(*args, **kw) def PathAccept(self, *args, **kw): self.warn() return SCons.Variables.PathVariable.PathAccept(*args, **kw) def PathIsDir(self, *args, **kw): self.warn() return SCons.Variables.PathVariable.PathIsDir(*args, **kw) def PathIsDirCreate(self, *args, **kw): self.warn() return SCons.Variables.PathVariable.PathIsDirCreate(*args, **kw) def PathIsFile(self, *args, **kw): self.warn() return SCons.Variables.PathVariable.PathIsFile(*args, **kw) def PathExists(self, *args, **kw): self.warn() return SCons.Variables.PathVariable.PathExists(*args, **kw) PathOption = _PathOptionClass() # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Subst.xml0000644000175000017500000000242612114661557021131 0ustar dktrkranzdktrkranz ([exception, ...]) Specifies the exceptions that will be allowed when expanding construction variables. By default, any construction variable expansions that generate a NameError or IndexError exception will expand to a '' (a null string) and not cause scons to fail. All exceptions not in the specified list will generate an error message and terminate processing. If &f-AllowSubstExceptions; is called multiple times, each call completely overwrites the previous list of allowed exceptions. Example: # Requires that all construction variable names exist. # (You may wish to do this if you want to enforce strictly # that all construction variables must be defined before use.) AllowSubstExceptions() # Also allow a string containing a zero-division expansion # like '${1 / 0}' to evalute to ''. AllowSubstExceptions(IndexError, NameError, ZeroDivisionError) scons-doc-2.3.0/src/engine/SCons/BuilderTests.py0000644000175000017500000020066412114661557022276 0ustar dktrkranzdktrkranz# # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/BuilderTests.py 2013/03/03 09:48:35 garyo" import SCons.compat # Define a null function for use as a builder action. # Where this is defined in the file seems to affect its # byte-code contents, so try to minimize changes by # defining it here, before we even import anything. def Func(): pass import collections import io import os.path import re import sys import unittest import TestCmd import SCons.Action import SCons.Builder import SCons.Environment import SCons.Errors import SCons.Subst import SCons.Util sys.stdout = io.StringIO() # Initial setup of the common environment for all tests, # a temporary working directory containing a # script for writing arguments to an output file. # # We don't do this as a setUp() method because it's # unnecessary to create a separate directory and script # for each test, they can just use the one. test = TestCmd.TestCmd(workdir = '') outfile = test.workpath('outfile') outfile2 = test.workpath('outfile2') infile = test.workpath('infile') test.write(infile, "infile\n") show_string = None scons_env = SCons.Environment.Environment() env_arg2nodes_called = None class Environment(object): def __init__(self, **kw): self.d = {} self.d['SHELL'] = scons_env['SHELL'] self.d['SPAWN'] = scons_env['SPAWN'] self.d['ESCAPE'] = scons_env['ESCAPE'] for k, v in kw.items(): self.d[k] = v global env_arg2nodes_called env_arg2nodes_called = None self.scanner = None self.fs = SCons.Node.FS.FS() def subst(self, s): if not SCons.Util.is_String(s): return s def substitute(m, d=self.d): return d.get(m.group(1), '') return re.sub(r'\$(\w+)', substitute, s) def subst_target_source(self, string, raw=0, target=None, source=None, dict=None, conv=None): return SCons.Subst.scons_subst(string, self, raw, target, source, dict, conv) def subst_list(self, string, raw=0, target=None, source=None, conv=None): return SCons.Subst.scons_subst_list(string, self, raw, target, source, {}, {}, conv) def arg2nodes(self, args, factory, **kw): global env_arg2nodes_called env_arg2nodes_called = 1 if not SCons.Util.is_List(args): args = [args] list = [] for a in args: if SCons.Util.is_String(a): a = factory(self.subst(a)) list.append(a) return list def get_factory(self, factory): return factory or self.fs.File def get_scanner(self, ext): return self.scanner def Dictionary(self): return {} def autogenerate(self, dir=''): return {} def __setitem__(self, item, var): self.d[item] = var def __getitem__(self, item): return self.d[item] def __contains__(self, item): return self.d.__contains__(item) def has_key(self, item): return item in self.d def keys(self): return list(self.d.keys()) def get(self, key, value=None): return self.d.get(key, value) def Override(self, overrides): env = Environment(**self.d) env.d.update(overrides) env.scanner = self.scanner return env def _update(self, dict): self.d.update(dict) def items(self): return list(self.d.items()) def sig_dict(self): d = {} for k,v in self.items(): d[k] = v d['TARGETS'] = ['__t1__', '__t2__', '__t3__', '__t4__', '__t5__', '__t6__'] d['TARGET'] = d['TARGETS'][0] d['SOURCES'] = ['__s1__', '__s2__', '__s3__', '__s4__', '__s5__', '__s6__'] d['SOURCE'] = d['SOURCES'][0] return d def __cmp__(self, other): return cmp(self.scanner, other.scanner) or cmp(self.d, other.d) class MyAction(object): def __init__(self, action): self.action = action def __call__(self, *args, **kw): pass def get_executor(self, env, overrides, tlist, slist, executor_kw): return ['executor'] + [self.action] class MyNode_without_target_from_source(object): def __init__(self, name): self.name = name self.sources = [] self.builder = None self.is_explicit = None self.side_effect = 0 self.suffix = os.path.splitext(name)[1] def disambiguate(self): return self def __str__(self): return self.name def builder_set(self, builder): self.builder = builder def has_builder(self): return not self.builder is None def set_explicit(self, is_explicit): self.is_explicit = is_explicit def has_explicit_builder(self): return self.is_explicit def env_set(self, env, safe=0): self.env = env def add_source(self, source): self.sources.extend(source) def scanner_key(self): return self.name def is_derived(self): return self.has_builder() def generate_build_env(self, env): return env def get_build_env(self): return self.executor.get_build_env() def set_executor(self, executor): self.executor = executor def get_executor(self, create=1): return self.executor class MyNode(MyNode_without_target_from_source): def target_from_source(self, prefix, suffix, stripext): return MyNode(prefix + stripext(str(self))[0] + suffix) class BuilderTestCase(unittest.TestCase): def test__init__(self): """Test simple Builder creation """ builder = SCons.Builder.Builder(action="foo") assert not builder is None, builder builder = SCons.Builder.Builder(action="foo", OVERRIDE='x') x = builder.overrides['OVERRIDE'] assert x == 'x', x def test__nonzero__(self): """Test a builder raising an exception when __nonzero__ is called """ builder = SCons.Builder.Builder(action="foo") exc_caught = None try: builder.__nonzero__() except SCons.Errors.InternalError: exc_caught = 1 assert exc_caught, "did not catch expected InternalError exception" class Node(object): pass n = Node() n.builder = builder exc_caught = None try: if n.builder: pass except SCons.Errors.InternalError: exc_caught = 1 assert exc_caught, "did not catch expected InternalError exception" def test__call__(self): """Test calling a builder to establish source dependencies """ env = Environment() builder = SCons.Builder.Builder(action="foo", target_factory=MyNode, source_factory=MyNode) tgt = builder(env, source=[]) assert tgt == [], tgt n1 = MyNode("n1") n2 = MyNode("n2") builder(env, target = n1, source = n2) assert env_arg2nodes_called assert n1.env == env, n1.env assert n1.builder == builder, n1.builder assert n1.sources == [n2], n1.sources assert n1.executor, "no executor found" assert not hasattr(n2, 'env') l = [1] ul = collections.UserList([2]) try: l.extend(ul) except TypeError: def mystr(l): return str(list(map(str, l))) else: mystr = str nnn1 = MyNode("nnn1") nnn2 = MyNode("nnn2") tlist = builder(env, target = [nnn1, nnn2], source = []) s = mystr(tlist) assert s == "['nnn1', 'nnn2']", s l = list(map(str, tlist)) assert l == ['nnn1', 'nnn2'], l tlist = builder(env, target = 'n3', source = 'n4') s = mystr(tlist) assert s == "['n3']", s target = tlist[0] l = list(map(str, tlist)) assert l == ['n3'], l assert target.name == 'n3' assert target.sources[0].name == 'n4' tlist = builder(env, target = 'n4 n5', source = ['n6 n7']) s = mystr(tlist) assert s == "['n4 n5']", s l = list(map(str, tlist)) assert l == ['n4 n5'], l target = tlist[0] assert target.name == 'n4 n5' assert target.sources[0].name == 'n6 n7' tlist = builder(env, target = ['n8 n9'], source = 'n10 n11') s = mystr(tlist) assert s == "['n8 n9']", s l = list(map(str, tlist)) assert l == ['n8 n9'], l target = tlist[0] assert target.name == 'n8 n9' assert target.sources[0].name == 'n10 n11' # A test to be uncommented when we freeze the environment # as part of calling the builder. #env1 = Environment(VAR='foo') #target = builder(env1, target = 'n12', source = 'n13') #env1['VAR'] = 'bar' #be = target.get_build_env() #assert be['VAR'] == 'foo', be['VAR'] try: unicode except NameError: uni = str else: uni = unicode target = builder(env, target = uni('n12 n13'), source = [uni('n14 n15')])[0] assert target.name == uni('n12 n13') assert target.sources[0].name == uni('n14 n15') target = builder(env, target = [uni('n16 n17')], source = uni('n18 n19'))[0] assert target.name == uni('n16 n17') assert target.sources[0].name == uni('n18 n19') n20 = MyNode_without_target_from_source('n20') flag = 0 try: target = builder(env, None, source=n20) except SCons.Errors.UserError, e: flag = 1 assert flag, "UserError should be thrown if a source node can't create a target." builder = SCons.Builder.Builder(action="foo", target_factory=MyNode, source_factory=MyNode, prefix='p-', suffix='.s') target = builder(env, None, source='n21')[0] assert target.name == 'p-n21.s', target builder = SCons.Builder.Builder(misspelled_action="foo", suffix = '.s') try: builder(env, target = 'n22', source = 'n22') except SCons.Errors.UserError, e: pass else: raise Exception("Did not catch expected UserError.") builder = SCons.Builder.Builder(action="foo") target = builder(env, None, source='n22', srcdir='src_dir')[0] p = target.sources[0].path assert p == os.path.join('src_dir', 'n22'), p def test_mistaken_variables(self): """Test keyword arguments that are often mistakes """ import SCons.Warnings env = Environment() builder = SCons.Builder.Builder(action="foo") save_warn = SCons.Warnings.warn warned = [] def my_warn(exception, warning, warned=warned): warned.append(warning) SCons.Warnings.warn = my_warn try: target = builder(env, 'mistaken1', sources='mistaken1.c') assert warned == ["Did you mean to use `source' instead of `sources'?"], warned del warned[:] target = builder(env, 'mistaken2', targets='mistaken2.c') assert warned == ["Did you mean to use `target' instead of `targets'?"], warned del warned[:] target = builder(env, 'mistaken3', targets='mistaken3', sources='mistaken3.c') assert "Did you mean to use `source' instead of `sources'?" in warned, warned assert "Did you mean to use `target' instead of `targets'?" in warned, warned del warned[:] finally: SCons.Warnings.warn = save_warn def test_action(self): """Test Builder creation Verify that we can retrieve the supplied action attribute. """ builder = SCons.Builder.Builder(action="foo") assert builder.action.cmd_list == "foo" def func(): pass builder = SCons.Builder.Builder(action=func) assert isinstance(builder.action, SCons.Action.FunctionAction) # Preserve the following so that the baseline test will fail. # Remove it in favor of the previous test at some convenient # point in the future. assert builder.action.execfunction == func def test_generator(self): """Test Builder creation given a generator function.""" def generator(): pass builder = SCons.Builder.Builder(generator=generator) assert builder.action.generator == generator def test_get_name(self): """Test the get_name() method """ def test_cmp(self): """Test simple comparisons of Builder objects """ b1 = SCons.Builder.Builder(src_suffix = '.o') b2 = SCons.Builder.Builder(src_suffix = '.o') assert b1 == b2 b3 = SCons.Builder.Builder(src_suffix = '.x') assert b1 != b3 assert b2 != b3 def test_target_factory(self): """Test a Builder that creates target nodes of a specified class """ class Foo(object): pass def FooFactory(target): global Foo return Foo(target) builder = SCons.Builder.Builder(target_factory = FooFactory) assert builder.target_factory is FooFactory assert not builder.source_factory is FooFactory def test_source_factory(self): """Test a Builder that creates source nodes of a specified class """ class Foo(object): pass def FooFactory(source): global Foo return Foo(source) builder = SCons.Builder.Builder(source_factory = FooFactory) assert not builder.target_factory is FooFactory assert builder.source_factory is FooFactory def test_splitext(self): """Test the splitext() method attached to a Builder.""" b = SCons.Builder.Builder() assert b.splitext('foo') == ('foo','') assert b.splitext('foo.bar') == ('foo','.bar') assert b.splitext(os.path.join('foo.bar', 'blat')) == (os.path.join('foo.bar', 'blat'),'') class MyBuilder(SCons.Builder.BuilderBase): def splitext(self, path): return "called splitext()" b = MyBuilder() ret = b.splitext('xyz.c') assert ret == "called splitext()", ret def test_adjust_suffix(self): """Test how a Builder adjusts file suffixes """ b = SCons.Builder.Builder() assert b.adjust_suffix('.foo') == '.foo' assert b.adjust_suffix('foo') == '.foo' assert b.adjust_suffix('$foo') == '$foo' class MyBuilder(SCons.Builder.BuilderBase): def adjust_suffix(self, suff): return "called adjust_suffix()" b = MyBuilder() ret = b.adjust_suffix('.foo') assert ret == "called adjust_suffix()", ret def test_prefix(self): """Test Builder creation with a specified target prefix Make sure that there is no '.' separator appended. """ env = Environment() builder = SCons.Builder.Builder(prefix = 'lib.') assert builder.get_prefix(env) == 'lib.' builder = SCons.Builder.Builder(prefix = 'lib', action='') assert builder.get_prefix(env) == 'lib' tgt = builder(env, target = 'tgt1', source = 'src1')[0] assert tgt.path == 'libtgt1', \ "Target has unexpected name: %s" % tgt.path tgt = builder(env, target = 'tgt2a tgt2b', source = 'src2')[0] assert tgt.path == 'libtgt2a tgt2b', \ "Target has unexpected name: %s" % tgt.path tgt = builder(env, target = None, source = 'src3')[0] assert tgt.path == 'libsrc3', \ "Target has unexpected name: %s" % tgt.path tgt = builder(env, target = None, source = 'lib/src4')[0] assert tgt.path == os.path.join('lib', 'libsrc4'), \ "Target has unexpected name: %s" % tgt.path tgt = builder(env, target = 'lib/tgt5', source = 'lib/src5')[0] assert tgt.path == os.path.join('lib', 'libtgt5'), \ "Target has unexpected name: %s" % tgt.path def gen_prefix(env, sources): return "gen_prefix() says " + env['FOO'] my_env = Environment(FOO = 'xyzzy') builder = SCons.Builder.Builder(prefix = gen_prefix) assert builder.get_prefix(my_env) == "gen_prefix() says xyzzy" my_env['FOO'] = 'abracadabra' assert builder.get_prefix(my_env) == "gen_prefix() says abracadabra" def my_emit(env, sources): return env.subst('$EMIT') my_env = Environment(FOO = '.foo', EMIT = 'emit-') builder = SCons.Builder.Builder(prefix = {None : 'default-', '.in' : 'out-', '.x' : 'y-', '$FOO' : 'foo-', '.zzz' : my_emit}, action = '') tgt = builder(my_env, target = None, source = 'f1')[0] assert tgt.path == 'default-f1', tgt.path tgt = builder(my_env, target = None, source = 'f2.c')[0] assert tgt.path == 'default-f2', tgt.path tgt = builder(my_env, target = None, source = 'f3.in')[0] assert tgt.path == 'out-f3', tgt.path tgt = builder(my_env, target = None, source = 'f4.x')[0] assert tgt.path == 'y-f4', tgt.path tgt = builder(my_env, target = None, source = 'f5.foo')[0] assert tgt.path == 'foo-f5', tgt.path tgt = builder(my_env, target = None, source = 'f6.zzz')[0] assert tgt.path == 'emit-f6', tgt.path def test_set_suffix(self): """Test the set_suffix() method""" b = SCons.Builder.Builder(action='') env = Environment(XSUFFIX = '.x') s = b.get_suffix(env) assert s == '', s b.set_suffix('.foo') s = b.get_suffix(env) assert s == '.foo', s b.set_suffix('$XSUFFIX') s = b.get_suffix(env) assert s == '.x', s def test_src_suffix(self): """Test Builder creation with a specified source file suffix Make sure that the '.' separator is appended to the beginning if it isn't already present. """ env = Environment(XSUFFIX = '.x', YSUFFIX = '.y') b1 = SCons.Builder.Builder(src_suffix = '.c', action='') assert b1.src_suffixes(env) == ['.c'], b1.src_suffixes(env) tgt = b1(env, target = 'tgt2', source = 'src2')[0] assert tgt.sources[0].path == 'src2.c', \ "Source has unexpected name: %s" % tgt.sources[0].path tgt = b1(env, target = 'tgt3', source = 'src3a src3b')[0] assert len(tgt.sources) == 1 assert tgt.sources[0].path == 'src3a src3b.c', \ "Unexpected tgt.sources[0] name: %s" % tgt.sources[0].path b2 = SCons.Builder.Builder(src_suffix = '.2', src_builder = b1) r = sorted(b2.src_suffixes(env)) assert r == ['.2', '.c'], r b3 = SCons.Builder.Builder(action = {'.3a' : '', '.3b' : ''}) s = sorted(b3.src_suffixes(env)) assert s == ['.3a', '.3b'], s b4 = SCons.Builder.Builder(src_suffix = '$XSUFFIX') assert b4.src_suffixes(env) == ['.x'], b4.src_suffixes(env) b5 = SCons.Builder.Builder(action = { '.y' : ''}) assert b5.src_suffixes(env) == ['.y'], b5.src_suffixes(env) def test_srcsuffix_nonext(self): "Test target generation from non-extension source suffixes" env = Environment() b6 = SCons.Builder.Builder(action = '', src_suffix='_src.a', suffix='.b') tgt = b6(env, target=None, source='foo_src.a') assert str(tgt[0]) == 'foo.b', str(tgt[0]) b7 = SCons.Builder.Builder(action = '', src_suffix='_source.a', suffix='_obj.b') b8 = SCons.Builder.Builder(action = '', src_builder=b7, suffix='.c') tgt = b8(env, target=None, source='foo_source.a') assert str(tgt[0]) == 'foo_obj.c', str(tgt[0]) src = env.fs.File('foo_source.a') tgt = b8(env, target=None, source=src) assert str(tgt[0]) == 'foo_obj.c', str(tgt[0]) b9 = SCons.Builder.Builder(action={'_src.a' : 'srcaction'}, suffix='.c') b9.add_action('_altsrc.b', 'altaction') tgt = b9(env, target=None, source='foo_altsrc.b') assert str(tgt[0]) == 'foo.c', str(tgt[0]) def test_src_suffix_expansion(self): """Test handling source suffixes when an expansion is involved""" env = Environment(OBJSUFFIX = '.obj') b1 = SCons.Builder.Builder(action = '', src_suffix='.c', suffix='.obj') b2 = SCons.Builder.Builder(action = '', src_builder=b1, src_suffix='.obj', suffix='.exe') tgt = b2(env, target=None, source=['foo$OBJSUFFIX']) s = list(map(str, tgt[0].sources)) assert s == ['foo.obj'], s def test_suffix(self): """Test Builder creation with a specified target suffix Make sure that the '.' separator is appended to the beginning if it isn't already present. """ env = Environment() builder = SCons.Builder.Builder(suffix = '.o') assert builder.get_suffix(env) == '.o', builder.get_suffix(env) builder = SCons.Builder.Builder(suffix = 'o', action='') assert builder.get_suffix(env) == '.o', builder.get_suffix(env) tgt = builder(env, target = 'tgt3', source = 'src3')[0] assert tgt.path == 'tgt3.o', \ "Target has unexpected name: %s" % tgt.path tgt = builder(env, target = 'tgt4a tgt4b', source = 'src4')[0] assert tgt.path == 'tgt4a tgt4b.o', \ "Target has unexpected name: %s" % tgt.path tgt = builder(env, target = None, source = 'src5')[0] assert tgt.path == 'src5.o', \ "Target has unexpected name: %s" % tgt.path def gen_suffix(env, sources): return "gen_suffix() says " + env['BAR'] my_env = Environment(BAR = 'hocus pocus') builder = SCons.Builder.Builder(suffix = gen_suffix) assert builder.get_suffix(my_env) == "gen_suffix() says hocus pocus", builder.get_suffix(my_env) my_env['BAR'] = 'presto chango' assert builder.get_suffix(my_env) == "gen_suffix() says presto chango" def my_emit(env, sources): return env.subst('$EMIT') my_env = Environment(BAR = '.bar', EMIT = '.emit') builder = SCons.Builder.Builder(suffix = {None : '.default', '.in' : '.out', '.x' : '.y', '$BAR' : '.new', '.zzz' : my_emit}, action='') tgt = builder(my_env, target = None, source = 'f1')[0] assert tgt.path == 'f1.default', tgt.path tgt = builder(my_env, target = None, source = 'f2.c')[0] assert tgt.path == 'f2.default', tgt.path tgt = builder(my_env, target = None, source = 'f3.in')[0] assert tgt.path == 'f3.out', tgt.path tgt = builder(my_env, target = None, source = 'f4.x')[0] assert tgt.path == 'f4.y', tgt.path tgt = builder(my_env, target = None, source = 'f5.bar')[0] assert tgt.path == 'f5.new', tgt.path tgt = builder(my_env, target = None, source = 'f6.zzz')[0] assert tgt.path == 'f6.emit', tgt.path def test_single_source(self): """Test Builder with single_source flag set""" def func(target, source, env): open(str(target[0]), "w") if (len(source) == 1 and len(target) == 1): env['CNT'][0] = env['CNT'][0] + 1 env = Environment() infiles = [] outfiles = [] for i in range(10): infiles.append(test.workpath('%d.in' % i)) outfiles.append(test.workpath('%d.out' % i)) test.write(infiles[-1], "\n") builder = SCons.Builder.Builder(action=SCons.Action.Action(func,None), single_source = 1, suffix='.out') env['CNT'] = [0] tgt = builder(env, target=outfiles[0], source=infiles[0])[0] s = str(tgt) t = os.path.normcase(test.workpath('0.out')) assert os.path.normcase(s) == t, s tgt.prepare() tgt.build() assert env['CNT'][0] == 1, env['CNT'][0] tgt = builder(env, outfiles[1], infiles[1])[0] s = str(tgt) t = os.path.normcase(test.workpath('1.out')) assert os.path.normcase(s) == t, s tgt.prepare() tgt.build() assert env['CNT'][0] == 2 tgts = builder(env, None, infiles[2:4]) s = list(map(str, tgts)) expect = [test.workpath('2.out'), test.workpath('3.out')] expect = list(map(os.path.normcase, expect)) assert list(map(os.path.normcase, s)) == expect, s for t in tgts: t.prepare() tgts[0].build() tgts[1].build() assert env['CNT'][0] == 4 try: tgt = builder(env, outfiles[4], infiles[4:6]) except SCons.Errors.UserError: pass else: assert 0 try: # The builder may output more than one target per input file. tgt = builder(env, outfiles[4:6], infiles[4:6]) except SCons.Errors.UserError: pass else: assert 0 def test_lists(self): """Testing handling lists of targets and source""" def function2(target, source, env, tlist = [outfile, outfile2], **kw): for t in target: open(str(t), 'w').write("function2\n") for t in tlist: if not t in list(map(str, target)): open(t, 'w').write("function2\n") return 1 env = Environment() builder = SCons.Builder.Builder(action = function2) tgts = builder(env, source=[]) assert tgts == [], tgts tgts = builder(env, target = [outfile, outfile2], source = infile) for t in tgts: t.prepare() try: tgts[0].build() except SCons.Errors.BuildError: pass c = test.read(outfile, 'r') assert c == "function2\n", c c = test.read(outfile2, 'r') assert c == "function2\n", c sub1_out = test.workpath('sub1', 'out') sub2_out = test.workpath('sub2', 'out') def function3(target, source, env, tlist = [sub1_out, sub2_out]): for t in target: open(str(t), 'w').write("function3\n") for t in tlist: if not t in list(map(str, target)): open(t, 'w').write("function3\n") return 1 builder = SCons.Builder.Builder(action = function3) tgts = builder(env, target = [sub1_out, sub2_out], source = infile) for t in tgts: t.prepare() try: tgts[0].build() except SCons.Errors.BuildError: pass c = test.read(sub1_out, 'r') assert c == "function3\n", c c = test.read(sub2_out, 'r') assert c == "function3\n", c assert os.path.exists(test.workpath('sub1')) assert os.path.exists(test.workpath('sub2')) def test_src_builder(self): """Testing Builders with src_builder""" # These used to be MultiStepBuilder objects until we # eliminated it as a separate class env = Environment() builder1 = SCons.Builder.Builder(action='foo', src_suffix='.bar', suffix='.foo') builder2 = SCons.Builder.Builder(action=MyAction('act'), src_builder = builder1, src_suffix = '.foo') tgt = builder2(env, source=[]) assert tgt == [], tgt sources = ['test.bar', 'test2.foo', 'test3.txt', 'test4'] tgt = builder2(env, target='baz', source=sources)[0] s = str(tgt) assert s == 'baz', s s = list(map(str, tgt.sources)) assert s == ['test.foo', 'test2.foo', 'test3.txt', 'test4.foo'], s s = list(map(str, tgt.sources[0].sources)) assert s == ['test.bar'], s tgt = builder2(env, None, 'aaa.bar')[0] s = str(tgt) assert s == 'aaa', s s = list(map(str, tgt.sources)) assert s == ['aaa.foo'], s s = list(map(str, tgt.sources[0].sources)) assert s == ['aaa.bar'], s builder3 = SCons.Builder.Builder(action='bld3') assert not builder3.src_builder is builder1.src_builder builder4 = SCons.Builder.Builder(action='bld4', src_suffix='.i', suffix='_wrap.c') builder5 = SCons.Builder.Builder(action=MyAction('act'), src_builder=builder4, suffix='.obj', src_suffix='.c') builder6 = SCons.Builder.Builder(action=MyAction('act'), src_builder=builder5, suffix='.exe', src_suffix='.obj') tgt = builder6(env, 'test', 'test.i')[0] s = str(tgt) assert s == 'test.exe', s s = list(map(str, tgt.sources)) assert s == ['test_wrap.obj'], s s = list(map(str, tgt.sources[0].sources)) assert s == ['test_wrap.c'], s s = list(map(str, tgt.sources[0].sources[0].sources)) assert s == ['test.i'], s def test_target_scanner(self): """Testing ability to set target and source scanners through a builder.""" global instanced class TestScanner(object): pass tscan = TestScanner() sscan = TestScanner() env = Environment() builder = SCons.Builder.Builder(target_scanner=tscan, source_scanner=sscan, action='') tgt = builder(env, target='foo2', source='bar')[0] assert tgt.builder.target_scanner == tscan, tgt.builder.target_scanner assert tgt.builder.source_scanner == sscan, tgt.builder.source_scanner builder1 = SCons.Builder.Builder(action='foo', src_suffix='.bar', suffix='.foo') builder2 = SCons.Builder.Builder(action='foo', src_builder = builder1, target_scanner = tscan, source_scanner = tscan) tgt = builder2(env, target='baz2', source='test.bar test2.foo test3.txt')[0] assert tgt.builder.target_scanner == tscan, tgt.builder.target_scanner assert tgt.builder.source_scanner == tscan, tgt.builder.source_scanner def test_actual_scanner(self): """Test usage of actual Scanner objects.""" import SCons.Scanner def func(self): pass scanner = SCons.Scanner.Base(func, name='fooscan') b1 = SCons.Builder.Builder(action='bld', target_scanner=scanner) b2 = SCons.Builder.Builder(action='bld', target_scanner=scanner) b3 = SCons.Builder.Builder(action='bld') assert b1 == b2 assert b1 != b3 def test_src_scanner(slf): """Testing ability to set a source file scanner through a builder.""" class TestScanner(object): def key(self, env): return 'TestScannerkey' def instance(self, env): return self def select(self, node): return self name = 'TestScanner' def __str__(self): return self.name scanner = TestScanner() builder = SCons.Builder.Builder(action='action') # With no scanner specified, source_scanner and # backup_source_scanner are None. bar_y = MyNode('bar.y') env1 = Environment() tgt = builder(env1, target='foo1.x', source='bar.y')[0] src = tgt.sources[0] assert tgt.builder.target_scanner != scanner, tgt.builder.target_scanner assert tgt.builder.source_scanner is None, tgt.builder.source_scanner assert tgt.get_source_scanner(bar_y) is None, tgt.get_source_scanner(bar_y) assert not src.has_builder(), src.has_builder() s = src.get_source_scanner(bar_y) assert isinstance(s, SCons.Util.Null), repr(s) # An Environment that has suffix-specified SCANNERS should # provide a source scanner to the target. class EnvTestScanner(object): def key(self, env): return '.y' def instance(self, env): return self name = 'EnvTestScanner' def __str__(self): return self.name def select(self, node): return self def path(self, env, dir=None): return () def __call__(self, node, env, path): return [] env3 = Environment(SCANNERS = [EnvTestScanner()]) env3.scanner = EnvTestScanner() # test env's version of SCANNERS tgt = builder(env3, target='foo2.x', source='bar.y')[0] src = tgt.sources[0] assert tgt.builder.target_scanner != scanner, tgt.builder.target_scanner assert not tgt.builder.source_scanner, tgt.builder.source_scanner assert tgt.get_source_scanner(bar_y), tgt.get_source_scanner(bar_y) assert str(tgt.get_source_scanner(bar_y)) == 'EnvTestScanner', tgt.get_source_scanner(bar_y) assert not src.has_builder(), src.has_builder() s = src.get_source_scanner(bar_y) assert isinstance(s, SCons.Util.Null), repr(s) # Can't simply specify the scanner as a builder argument; it's # global to all invocations of this builder. tgt = builder(env3, target='foo3.x', source='bar.y', source_scanner = scanner)[0] src = tgt.sources[0] assert tgt.builder.target_scanner != scanner, tgt.builder.target_scanner assert not tgt.builder.source_scanner, tgt.builder.source_scanner assert tgt.get_source_scanner(bar_y), tgt.get_source_scanner(bar_y) assert str(tgt.get_source_scanner(bar_y)) == 'EnvTestScanner', tgt.get_source_scanner(bar_y) assert not src.has_builder(), src.has_builder() s = src.get_source_scanner(bar_y) assert isinstance(s, SCons.Util.Null), s # Now use a builder that actually has scanners and ensure that # the target is set accordingly (using the specified scanner # instead of the Environment's scanner) builder = SCons.Builder.Builder(action='action', source_scanner=scanner, target_scanner=scanner) tgt = builder(env3, target='foo4.x', source='bar.y')[0] src = tgt.sources[0] assert tgt.builder.target_scanner == scanner, tgt.builder.target_scanner assert tgt.builder.source_scanner, tgt.builder.source_scanner assert tgt.builder.source_scanner == scanner, tgt.builder.source_scanner assert str(tgt.builder.source_scanner) == 'TestScanner', str(tgt.builder.source_scanner) assert tgt.get_source_scanner(bar_y), tgt.get_source_scanner(bar_y) assert tgt.get_source_scanner(bar_y) == scanner, tgt.get_source_scanner(bar_y) assert str(tgt.get_source_scanner(bar_y)) == 'TestScanner', tgt.get_source_scanner(bar_y) assert not src.has_builder(), src.has_builder() s = src.get_source_scanner(bar_y) assert isinstance(s, SCons.Util.Null), s def test_Builder_API(self): """Test Builder interface. Some of this is tested elsewhere in this file, but this is a quick collection of common operations on builders with various forms of component specifications.""" builder = SCons.Builder.Builder() env = Environment(BUILDERS={'Bld':builder}) r = builder.get_name(env) assert r == 'Bld', r r = builder.get_prefix(env) assert r == '', r r = builder.get_suffix(env) assert r == '', r r = builder.get_src_suffix(env) assert r == '', r r = builder.src_suffixes(env) assert r == [], r # src_suffix can be a single string or a list of strings # src_suffixes() caches its return value, so we use a new # Builder each time we do any of these tests bld = SCons.Builder.Builder() env = Environment(BUILDERS={'Bld':bld}) bld.set_src_suffix('.foo') r = bld.get_src_suffix(env) assert r == '.foo', r r = bld.src_suffixes(env) assert r == ['.foo'], r bld = SCons.Builder.Builder() env = Environment(BUILDERS={'Bld':bld}) bld.set_src_suffix(['.foo', '.bar']) r = bld.get_src_suffix(env) assert r == '.foo', r r = bld.src_suffixes(env) assert r == ['.foo', '.bar'], r bld = SCons.Builder.Builder() env = Environment(BUILDERS={'Bld':bld}) bld.set_src_suffix(['.bar', '.foo']) r = bld.get_src_suffix(env) assert r == '.bar', r r = sorted(bld.src_suffixes(env)) assert r == ['.bar', '.foo'], r # adjust_suffix normalizes the suffix, adding a `.' if needed r = builder.adjust_suffix('.foo') assert r == '.foo', r r = builder.adjust_suffix('_foo') assert r == '_foo', r r = builder.adjust_suffix('$foo') assert r == '$foo', r r = builder.adjust_suffix('foo') assert r == '.foo', r r = builder.adjust_suffix('f._$oo') assert r == '.f._$oo', r # prefix and suffix can be one of: # 1. a string (adjusted and env variables substituted), # 2. a function (passed (env,sources), returns suffix string) # 3. a dict of src_suffix:suffix settings, key==None is # default suffix (special case of #2, so adjust_suffix # not applied) builder = SCons.Builder.Builder(prefix='lib', suffix='foo') env = Environment(BUILDERS={'Bld':builder}) r = builder.get_name(env) assert r == 'Bld', r r = builder.get_prefix(env) assert r == 'lib', r r = builder.get_suffix(env) assert r == '.foo', r mkpref = lambda env,sources: 'Lib' mksuff = lambda env,sources: '.Foo' builder = SCons.Builder.Builder(prefix=mkpref, suffix=mksuff) env = Environment(BUILDERS={'Bld':builder}) r = builder.get_name(env) assert r == 'Bld', r r = builder.get_prefix(env) assert r == 'Lib', r r = builder.get_suffix(env) assert r == '.Foo', r builder = SCons.Builder.Builder(prefix='$PREF', suffix='$SUFF') env = Environment(BUILDERS={'Bld':builder},PREF="LIB",SUFF=".FOO") r = builder.get_name(env) assert r == 'Bld', r r = builder.get_prefix(env) assert r == 'LIB', r r = builder.get_suffix(env) assert r == '.FOO', r builder = SCons.Builder.Builder(prefix={None:'A_', '.C':'E_'}, suffix={None:'.B', '.C':'.D'}) env = Environment(BUILDERS={'Bld':builder}) r = builder.get_name(env) assert r == 'Bld', r r = builder.get_prefix(env) assert r == 'A_', r r = builder.get_suffix(env) assert r == '.B', r r = builder.get_prefix(env, [MyNode('X.C')]) assert r == 'E_', r r = builder.get_suffix(env, [MyNode('X.C')]) assert r == '.D', r builder = SCons.Builder.Builder(prefix='A_', suffix={}, action={}) env = Environment(BUILDERS={'Bld':builder}) r = builder.get_name(env) assert r == 'Bld', r r = builder.get_prefix(env) assert r == 'A_', r r = builder.get_suffix(env) assert r is None, r r = builder.get_src_suffix(env) assert r == '', r r = builder.src_suffixes(env) assert r == [], r # Builder actions can be a string, a list, or a dictionary # whose keys are the source suffix. The add_action() # specifies a new source suffix/action binding. builder = SCons.Builder.Builder(prefix='A_', suffix={}, action={}) env = Environment(BUILDERS={'Bld':builder}) builder.add_action('.src_sfx1', 'FOO') r = builder.get_name(env) assert r == 'Bld', r r = builder.get_prefix(env) assert r == 'A_', r r = builder.get_suffix(env) assert r is None, r r = builder.get_suffix(env, [MyNode('X.src_sfx1')]) assert r is None, r r = builder.get_src_suffix(env) assert r == '.src_sfx1', r r = builder.src_suffixes(env) assert r == ['.src_sfx1'], r builder = SCons.Builder.Builder(prefix='A_', suffix={}, action={}) env = Environment(BUILDERS={'Bld':builder}) builder.add_action('.src_sfx1', 'FOO') builder.add_action('.src_sfx2', 'BAR') r = builder.get_name(env) assert r == 'Bld', r r = builder.get_prefix(env) assert r == 'A_', r r = builder.get_suffix(env) assert r is None, r r = builder.get_src_suffix(env) assert r == '.src_sfx1', r r = sorted(builder.src_suffixes(env)) assert r == ['.src_sfx1', '.src_sfx2'], r def test_Builder_Args(self): """Testing passing extra args to a builder.""" def buildFunc(target, source, env, s=self): s.foo=env['foo'] s.bar=env['bar'] assert env['CC'] == 'mycc' env=Environment(CC='cc') builder = SCons.Builder.Builder(action=buildFunc) tgt = builder(env, target='foo', source='bar', foo=1, bar=2, CC='mycc')[0] tgt.build() assert self.foo == 1, self.foo assert self.bar == 2, self.bar def test_emitter(self): """Test emitter functions.""" def emit(target, source, env): foo = env.get('foo', 0) bar = env.get('bar', 0) for t in target: assert isinstance(t, MyNode) assert t.has_builder() for s in source: assert isinstance(s, MyNode) if foo: target.append("bar%d"%foo) if bar: source.append("baz") return ( target, source ) env = Environment() builder = SCons.Builder.Builder(action='foo', emitter=emit, target_factory=MyNode, source_factory=MyNode) tgt = builder(env, target='foo2', source='bar')[0] assert str(tgt) == 'foo2', str(tgt) assert str(tgt.sources[0]) == 'bar', str(tgt.sources[0]) tgt = builder(env, target='foo3', source='bar', foo=1) assert len(tgt) == 2, len(tgt) assert 'foo3' in list(map(str, tgt)), list(map(str, tgt)) assert 'bar1' in list(map(str, tgt)), list(map(str, tgt)) tgt = builder(env, target='foo4', source='bar', bar=1)[0] assert str(tgt) == 'foo4', str(tgt) assert len(tgt.sources) == 2, len(tgt.sources) assert 'baz' in list(map(str, tgt.sources)), list(map(str, tgt.sources)) assert 'bar' in list(map(str, tgt.sources)), list(map(str, tgt.sources)) env2=Environment(FOO=emit) builder2=SCons.Builder.Builder(action='foo', emitter="$FOO", target_factory=MyNode, source_factory=MyNode) builder2a=SCons.Builder.Builder(action='foo', emitter="$FOO", target_factory=MyNode, source_factory=MyNode) assert builder2 == builder2a, repr(builder2.__dict__) + "\n" + repr(builder2a.__dict__) tgt = builder2(env2, target='foo5', source='bar')[0] assert str(tgt) == 'foo5', str(tgt) assert str(tgt.sources[0]) == 'bar', str(tgt.sources[0]) tgt = builder2(env2, target='foo6', source='bar', foo=2) assert len(tgt) == 2, len(tgt) assert 'foo6' in list(map(str, tgt)), list(map(str, tgt)) assert 'bar2' in list(map(str, tgt)), list(map(str, tgt)) tgt = builder2(env2, target='foo7', source='bar', bar=1)[0] assert str(tgt) == 'foo7', str(tgt) assert len(tgt.sources) == 2, len(tgt.sources) assert 'baz' in list(map(str, tgt.sources)), list(map(str, tgt.sources)) assert 'bar' in list(map(str, tgt.sources)), list(map(str, tgt.sources)) 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 emit(target, source, env, nb=new_builder, nn=new_node): for t in target: t.builder = nb return [nn], source 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 builder, tgt.builder assert node.builder is new_builder, node.builder def test_emitter_suffix_map(self): """Test mapping file suffixes to emitter functions""" env = Environment() def emit4a(target, source, env): source = list(map(str, source)) target = ['emit4a-' + x[:-3] for x in source] return (target, source) def emit4b(target, source, env): source = list(map(str, source)) target = ['emit4b-' + x[:-3] for x in source] return (target, source) 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 = builder(env, None, source='bbb.4b')[0] assert str(tgt) == 'emit4b-bbb', str(tgt) tgt = builder(env, None, source='ccc.4c')[0] assert str(tgt) == 'ccc', str(tgt) def emit4c(target, source, env): source = list(map(str, source)) target = ['emit4c-' + x[:-3] for x in source] return (target, source) builder.add_emitter('.4c', emit4c) tgt = builder(env, None, source='ccc.4c')[0] assert str(tgt) == 'emit4c-ccc', str(tgt) def test_emitter_function_list(self): """Test lists of emitter functions""" env = Environment() def emit1a(target, source, env): source = list(map(str, source)) target = target + ['emit1a-' + x[:-2] for x in source] return (target, source) def emit1b(target, source, env): source = list(map(str, source)) target = target + ['emit1b-' + x[:-2] for x in source] return (target, source) builder1 = SCons.Builder.Builder(action='foo', emitter=[emit1a, emit1b], node_factory=MyNode) tgts = builder1(env, target='target-1', source='aaa.1') tgts = list(map(str, tgts)) assert tgts == ['target-1', 'emit1a-aaa', 'emit1b-aaa'], tgts # Test a list of emitter functions through the environment. def emit2a(target, source, env): source = list(map(str, source)) target = target + ['emit2a-' + x[:-2] for x in source] return (target, source) def emit2b(target, source, env): source = list(map(str, source)) target = target + ['emit2b-' + x[:-2] for x in source] return (target, source) builder2 = SCons.Builder.Builder(action='foo', emitter='$EMITTERLIST', node_factory=MyNode) env = Environment(EMITTERLIST = [emit2a, emit2b]) tgts = builder2(env, target='target-2', source='aaa.2') tgts = list(map(str, 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 = list(map(str, targets)) sources = list(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.""" env = Environment() b = SCons.Builder.Builder(action='foo', suffix='.o') tgt = b(env, None, 'aaa')[0] assert str(tgt) == 'aaa.o', str(tgt) assert len(tgt.sources) == 1, list(map(str, tgt.sources)) assert str(tgt.sources[0]) == 'aaa', list(map(str, tgt.sources)) tgt = b(env, None, 'bbb.c')[0] assert str(tgt) == 'bbb.o', str(tgt) assert len(tgt.sources) == 1, list(map(str, tgt.sources)) assert str(tgt.sources[0]) == 'bbb.c', list(map(str, tgt.sources)) tgt = b(env, None, 'ccc.x.c')[0] assert str(tgt) == 'ccc.x.o', str(tgt) assert len(tgt.sources) == 1, list(map(str, tgt.sources)) assert str(tgt.sources[0]) == 'ccc.x.c', list(map(str, tgt.sources)) tgt = b(env, None, ['d0.c', 'd1.c'])[0] assert str(tgt) == 'd0.o', str(tgt) assert len(tgt.sources) == 2, list(map(str, tgt.sources)) assert str(tgt.sources[0]) == 'd0.c', list(map(str, tgt.sources)) assert str(tgt.sources[1]) == 'd1.c', list(map(str, tgt.sources)) tgt = b(env, target = None, source='eee')[0] assert str(tgt) == 'eee.o', str(tgt) assert len(tgt.sources) == 1, list(map(str, tgt.sources)) assert str(tgt.sources[0]) == 'eee', list(map(str, tgt.sources)) tgt = b(env, target = None, source='fff.c')[0] assert str(tgt) == 'fff.o', str(tgt) assert len(tgt.sources) == 1, list(map(str, tgt.sources)) assert str(tgt.sources[0]) == 'fff.c', list(map(str, tgt.sources)) tgt = b(env, target = None, source='ggg.x.c')[0] assert str(tgt) == 'ggg.x.o', str(tgt) assert len(tgt.sources) == 1, list(map(str, tgt.sources)) assert str(tgt.sources[0]) == 'ggg.x.c', list(map(str, tgt.sources)) tgt = b(env, target = None, source=['h0.c', 'h1.c'])[0] assert str(tgt) == 'h0.o', str(tgt) assert len(tgt.sources) == 2, list(map(str, tgt.sources)) assert str(tgt.sources[0]) == 'h0.c', list(map(str, tgt.sources)) assert str(tgt.sources[1]) == 'h1.c', list(map(str, tgt.sources)) w = b(env, target='i0.w', source=['i0.x'])[0] y = b(env, target='i1.y', source=['i1.z'])[0] tgt = b(env, None, source=[w, y])[0] assert str(tgt) == 'i0.o', str(tgt) assert len(tgt.sources) == 2, list(map(str, tgt.sources)) assert str(tgt.sources[0]) == 'i0.w', list(map(str, tgt.sources)) assert str(tgt.sources[1]) == 'i1.y', list(map(str, tgt.sources)) def test_get_name(self): """Test getting name of builder. Each type of builder should return its environment-specific name when queried appropriately. """ b1 = SCons.Builder.Builder(action='foo', suffix='.o') b2 = SCons.Builder.Builder(action='foo', suffix='.c') b3 = SCons.Builder.Builder(action='bar', src_suffix = '.foo', src_builder = b1) b4 = SCons.Builder.Builder(action={}) b5 = SCons.Builder.Builder(action='foo', name='builder5') b6 = SCons.Builder.Builder(action='foo') assert isinstance(b4, SCons.Builder.CompositeBuilder) assert isinstance(b4.action, SCons.Action.CommandGeneratorAction) env = Environment(BUILDERS={'bldr1': b1, 'bldr2': b2, 'bldr3': b3, 'bldr4': b4}) env2 = Environment(BUILDERS={'B1': b1, 'B2': b2, 'B3': b3, 'B4': b4}) # With no name, get_name will return the class. Allow # for caching... b6_names = [ 'SCons.Builder.BuilderBase', "", 'SCons.Memoize.BuilderBase', "", ] assert b1.get_name(env) == 'bldr1', b1.get_name(env) assert b2.get_name(env) == 'bldr2', b2.get_name(env) assert b3.get_name(env) == 'bldr3', b3.get_name(env) assert b4.get_name(env) == 'bldr4', b4.get_name(env) assert b5.get_name(env) == 'builder5', b5.get_name(env) assert b6.get_name(env) in b6_names, b6.get_name(env) assert b1.get_name(env2) == 'B1', b1.get_name(env2) assert b2.get_name(env2) == 'B2', b2.get_name(env2) assert b3.get_name(env2) == 'B3', b3.get_name(env2) assert b4.get_name(env2) == 'B4', b4.get_name(env2) assert b5.get_name(env2) == 'builder5', b5.get_name(env2) assert b6.get_name(env2) in b6_names, b6.get_name(env2) assert b5.get_name(None) == 'builder5', b5.get_name(None) assert b6.get_name(None) in b6_names, b6.get_name(None) # This test worked before adding batch builders, but we must now # be able to disambiguate a CompositeAction into a more specific # action based on file suffix at call time. Leave this commented # out (for now) in case this reflects a real-world use case that # we must accomodate and we want to resurrect this test. #tgt = b4(env, target = 'moo', source='cow') #assert tgt[0].builder.get_name(env) == 'bldr4' class CompositeBuilderTestCase(unittest.TestCase): def setUp(self): def func_action(target, source, env): return 0 builder = SCons.Builder.Builder(action={ '.foo' : func_action, '.bar' : func_action}) self.func_action = func_action self.builder = builder def test___init__(self): """Test CompositeBuilder creation""" env = Environment() builder = SCons.Builder.Builder(action={}) tgt = builder(env, source=[]) assert tgt == [], tgt assert isinstance(builder, SCons.Builder.CompositeBuilder) assert isinstance(builder.action, SCons.Action.CommandGeneratorAction) def test_target_action(self): """Test CompositeBuilder setting of target builder actions""" env = Environment() builder = self.builder tgt = builder(env, target='test1', source='test1.foo')[0] assert isinstance(tgt.builder, SCons.Builder.BuilderBase) assert tgt.builder.action is builder.action tgt = builder(env, target='test2', source='test1.bar')[0] assert isinstance(tgt.builder, SCons.Builder.BuilderBase) assert tgt.builder.action is builder.action def test_multiple_suffix_error(self): """Test the CompositeBuilder multiple-source-suffix error""" env = Environment() builder = self.builder flag = 0 try: builder(env, target='test3', source=['test2.bar', 'test1.foo'])[0] except SCons.Errors.UserError, e: flag = 1 assert flag, "UserError should be thrown when we call a builder with files of different suffixes." expect = "While building `['test3']' from `test1.foo': Cannot build multiple sources with different extensions: .bar, .foo" assert str(e) == expect, e def test_source_ext_match(self): """Test the CompositeBuilder source_ext_match argument""" env = Environment() func_action = self.func_action builder = SCons.Builder.Builder(action={ '.foo' : func_action, '.bar' : func_action}, source_ext_match = None) tgt = builder(env, target='test3', source=['test2.bar', 'test1.foo'])[0] tgt.build() def test_suffix_variable(self): """Test CompositeBuilder defining action suffixes through a variable""" env = Environment(BAR_SUFFIX = '.BAR2', FOO_SUFFIX = '.FOO2') func_action = self.func_action builder = SCons.Builder.Builder(action={ '.foo' : func_action, '.bar' : func_action, '$BAR_SUFFIX' : func_action, '$FOO_SUFFIX' : func_action }) tgt = builder(env, target='test4', source=['test4.BAR2'])[0] assert isinstance(tgt.builder, SCons.Builder.BuilderBase) try: tgt.build() flag = 1 except SCons.Errors.UserError, e: print e flag = 0 assert flag, "It should be possible to define actions in composite builders using variables." env['FOO_SUFFIX'] = '.BAR2' builder.add_action('$NEW_SUFFIX', func_action) flag = 0 try: builder(env, target='test5', source=['test5.BAR2'])[0] except SCons.Errors.UserError: flag = 1 assert flag, "UserError should be thrown when we call a builder with ambigous suffixes." def test_src_builder(self): """Test CompositeBuilder's use of a src_builder""" env = Environment() foo_bld = SCons.Builder.Builder(action = 'a-foo', src_suffix = '.ina', suffix = '.foo') assert isinstance(foo_bld, SCons.Builder.BuilderBase) builder = SCons.Builder.Builder(action = { '.foo' : 'foo', '.bar' : 'bar' }, src_builder = foo_bld) assert isinstance(builder, SCons.Builder.CompositeBuilder) assert isinstance(builder.action, SCons.Action.CommandGeneratorAction) tgt = builder(env, target='t1', source='t1a.ina t1b.ina')[0] assert isinstance(tgt.builder, SCons.Builder.BuilderBase) tgt = builder(env, target='t2', source='t2a.foo t2b.ina')[0] assert isinstance(tgt.builder, SCons.Builder.BuilderBase), tgt.builder.__dict__ bar_bld = SCons.Builder.Builder(action = 'a-bar', src_suffix = '.inb', suffix = '.bar') assert isinstance(bar_bld, SCons.Builder.BuilderBase) builder = SCons.Builder.Builder(action = { '.foo' : 'foo'}, src_builder = [foo_bld, bar_bld]) assert isinstance(builder, SCons.Builder.CompositeBuilder) assert isinstance(builder.action, SCons.Action.CommandGeneratorAction) builder.add_action('.bar', 'bar') tgt = builder(env, target='t3-foo', source='t3a.foo t3b.ina')[0] assert isinstance(tgt.builder, SCons.Builder.BuilderBase) tgt = builder(env, target='t3-bar', source='t3a.bar t3b.inb')[0] assert isinstance(tgt.builder, SCons.Builder.BuilderBase) flag = 0 try: builder(env, target='t5', source=['test5a.foo', 'test5b.inb'])[0] except SCons.Errors.UserError, e: flag = 1 assert flag, "UserError should be thrown when we call a builder with files of different suffixes." expect = "While building `['t5']' from `test5b.bar': Cannot build multiple sources with different extensions: .foo, .bar" assert str(e) == expect, e flag = 0 try: builder(env, target='t6', source=['test6a.bar', 'test6b.ina'])[0] except SCons.Errors.UserError, e: flag = 1 assert flag, "UserError should be thrown when we call a builder with files of different suffixes." expect = "While building `['t6']' from `test6b.foo': Cannot build multiple sources with different extensions: .bar, .foo" assert str(e) == expect, e flag = 0 try: builder(env, target='t4', source=['test4a.ina', 'test4b.inb'])[0] except SCons.Errors.UserError, e: flag = 1 assert flag, "UserError should be thrown when we call a builder with files of different suffixes." expect = "While building `['t4']' from `test4b.bar': Cannot build multiple sources with different extensions: .foo, .bar" assert str(e) == expect, e flag = 0 try: builder(env, target='t7', source=[env.fs.File('test7')])[0] except SCons.Errors.UserError, e: flag = 1 assert flag, "UserError should be thrown when we call a builder with files of different suffixes." expect = "While building `['t7']': Cannot deduce file extension from source files: ['test7']" assert str(e) == expect, e flag = 0 try: builder(env, target='t8', source=['test8.unknown'])[0] except SCons.Errors.UserError, e: flag = 1 assert flag, "UserError should be thrown when we call a builder target with an unknown suffix." expect = "While building `['t8']' from `['test8.unknown']': Don't know how to build from a source file with suffix `.unknown'. Expected a suffix in this list: ['.foo', '.bar']." assert str(e) == expect, e if __name__ == "__main__": suite = unittest.TestSuite() tclasses = [ BuilderTestCase, CompositeBuilderTestCase ] for tclass in tclasses: names = unittest.getTestCaseNames(tclass, 'test_') suite.addTests(list(map(tclass, names))) if not unittest.TextTestRunner().run(suite).wasSuccessful(): sys.exit(1) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/cpp.py0000644000175000017500000004637112114661560020444 0ustar dktrkranzdktrkranz# # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/cpp.py 2013/03/03 09:48:35 garyo" __doc__ = """ SCons C Pre-Processor module """ #TODO 2.3 and before has no sorted() import SCons.compat import os import re # # First "subsystem" of regular expressions that we set up: # # Stuff to turn the C preprocessor directives in a file's contents into # a list of tuples that we can process easily. # # A table of regular expressions that fetch the arguments from the rest of # a C preprocessor line. Different directives have different arguments # that we want to fetch, using the regular expressions to which the lists # of preprocessor directives map. cpp_lines_dict = { # Fetch the rest of a #if/#elif/#ifdef/#ifndef as one argument, # separated from the keyword by white space. ('if', 'elif', 'ifdef', 'ifndef',) : '\s+(.+)', # Fetch the rest of a #import/#include/#include_next line as one # argument, with white space optional. ('import', 'include', 'include_next',) : '\s*(.+)', # We don't care what comes after a #else or #endif line. ('else', 'endif',) : '', # Fetch three arguments from a #define line: # 1) The #defined keyword. # 2) The optional parentheses and arguments (if it's a function-like # macro, '' if it's not). # 3) The expansion value. ('define',) : '\s+([_A-Za-z][_A-Za-z0-9_]*)(\([^)]*\))?\s*(.*)', # Fetch the #undefed keyword from a #undef line. ('undef',) : '\s+([_A-Za-z][A-Za-z0-9_]*)', } # Create a table that maps each individual C preprocessor directive to # the corresponding compiled regular expression that fetches the arguments # we care about. Table = {} for op_list, expr in cpp_lines_dict.items(): e = re.compile(expr) for op in op_list: Table[op] = e del e del op del op_list # Create a list of the expressions we'll use to match all of the # preprocessor directives. These are the same as the directives # themselves *except* that we must use a negative lookahead assertion # when matching "if" so it doesn't match the "if" in "ifdef." override = { 'if' : 'if(?!def)', } l = [override.get(x, x) for x in Table.keys()] # Turn the list of expressions into one big honkin' regular expression # that will match all the preprocessor lines at once. This will return # a list of tuples, one for each preprocessor line. The preprocessor # directive will be the first element in each tuple, and the rest of # the line will be the second element. e = '^\s*#\s*(' + '|'.join(l) + ')(.*)$' # And last but not least, compile the expression. CPP_Expression = re.compile(e, re.M) # # Second "subsystem" of regular expressions that we set up: # # Stuff to translate a C preprocessor expression (as found on a #if or # #elif line) into an equivalent Python expression that we can eval(). # # A dictionary that maps the C representation of Boolean operators # to their Python equivalents. CPP_to_Python_Ops_Dict = { '!' : ' not ', '!=' : ' != ', '&&' : ' and ', '||' : ' or ', '?' : ' and ', ':' : ' or ', '\r' : '', } CPP_to_Python_Ops_Sub = lambda m: CPP_to_Python_Ops_Dict[m.group(0)] # We have to sort the keys by length so that longer expressions # come *before* shorter expressions--in particular, "!=" must # come before "!" in the alternation. Without this, the Python # re module, as late as version 2.2.2, empirically matches the # "!" in "!=" first, instead of finding the longest match. # What's up with that? l = sorted(CPP_to_Python_Ops_Dict.keys(), key=lambda a: len(a), reverse=True) # Turn the list of keys into one regular expression that will allow us # to substitute all of the operators at once. expr = '|'.join(map(re.escape, l)) # ...and compile the expression. CPP_to_Python_Ops_Expression = re.compile(expr) # A separate list of expressions to be evaluated and substituted # sequentially, not all at once. CPP_to_Python_Eval_List = [ ['defined\s+(\w+)', '"\\1" in __dict__'], ['defined\s*\((\w+)\)', '"\\1" in __dict__'], ['/\*.*\*/', ''], ['/\*.*', ''], ['//.*', ''], ['(0x[0-9A-Fa-f]*)[UL]+', '\\1'], ] # Replace the string representations of the regular expressions in the # list with compiled versions. for l in CPP_to_Python_Eval_List: l[0] = re.compile(l[0]) # Wrap up all of the above into a handy function. def CPP_to_Python(s): """ Converts a C pre-processor expression into an equivalent Python expression that can be evaluated. """ s = CPP_to_Python_Ops_Expression.sub(CPP_to_Python_Ops_Sub, s) for expr, repl in CPP_to_Python_Eval_List: s = expr.sub(repl, s) return s del expr del l del override class FunctionEvaluator(object): """ Handles delayed evaluation of a #define function call. """ def __init__(self, name, args, expansion): """ Squirrels away the arguments and expansion value of a #define macro function for later evaluation when we must actually expand a value that uses it. """ self.name = name self.args = function_arg_separator.split(args) try: expansion = expansion.split('##') except AttributeError: pass self.expansion = expansion def __call__(self, *values): """ Evaluates the expansion of a #define macro function called with the specified values. """ if len(self.args) != len(values): raise ValueError("Incorrect number of arguments to `%s'" % self.name) # Create a dictionary that maps the macro arguments to the # corresponding values in this "call." We'll use this when we # eval() the expansion so that arguments will get expanded to # the right values. locals = {} for k, v in zip(self.args, values): locals[k] = v parts = [] for s in self.expansion: if not s in self.args: s = repr(s) parts.append(s) statement = ' + '.join(parts) return eval(statement, globals(), locals) # Find line continuations. line_continuations = re.compile('\\\\\r?\n') # Search for a "function call" macro on an expansion. Returns the # two-tuple of the "function" name itself, and a string containing the # arguments within the call parentheses. function_name = re.compile('(\S+)\(([^)]*)\)') # Split a string containing comma-separated function call arguments into # the separate arguments. function_arg_separator = re.compile(',\s*') class PreProcessor(object): """ The main workhorse class for handling C pre-processing. """ def __init__(self, current=os.curdir, cpppath=(), dict={}, all=0): global Table cpppath = tuple(cpppath) self.searchpath = { '"' : (current,) + cpppath, '<' : cpppath + (current,), } # Initialize our C preprocessor namespace for tracking the # values of #defined keywords. We use this namespace to look # for keywords on #ifdef/#ifndef lines, and to eval() the # expressions on #if/#elif lines (after massaging them from C to # Python). self.cpp_namespace = dict.copy() self.cpp_namespace['__dict__'] = self.cpp_namespace if all: self.do_include = self.all_include # For efficiency, a dispatch table maps each C preprocessor # directive (#if, #define, etc.) to the method that should be # called when we see it. We accomodate state changes (#if, # #ifdef, #ifndef) by pushing the current dispatch table on a # stack and changing what method gets called for each relevant # directive we might see next at this level (#else, #elif). # #endif will simply pop the stack. d = { 'scons_current_file' : self.scons_current_file } for op in Table.keys(): d[op] = getattr(self, 'do_' + op) self.default_table = d # Controlling methods. def tupleize(self, contents): """ Turns the contents of a file into a list of easily-processed tuples describing the CPP lines in the file. The first element of each tuple is the line's preprocessor directive (#if, #include, #define, etc., minus the initial '#'). The remaining elements are specific to the type of directive, as pulled apart by the regular expression. """ global CPP_Expression, Table contents = line_continuations.sub('', contents) cpp_tuples = CPP_Expression.findall(contents) return [(m[0],) + Table[m[0]].match(m[1]).groups() for m in cpp_tuples] def __call__(self, file): """ Pre-processes a file. This is the main public entry point. """ self.current_file = file return self.process_contents(self.read_file(file), file) def process_contents(self, contents, fname=None): """ Pre-processes a file contents. This is the main internal entry point. """ self.stack = [] self.dispatch_table = self.default_table.copy() self.current_file = fname self.tuples = self.tupleize(contents) self.initialize_result(fname) while self.tuples: t = self.tuples.pop(0) # Uncomment to see the list of tuples being processed (e.g., # to validate the CPP lines are being translated correctly). #print t self.dispatch_table[t[0]](t) return self.finalize_result(fname) # Dispatch table stack manipulation methods. def save(self): """ Pushes the current dispatch table on the stack and re-initializes the current dispatch table to the default. """ self.stack.append(self.dispatch_table) self.dispatch_table = self.default_table.copy() def restore(self): """ Pops the previous dispatch table off the stack and makes it the current one. """ try: self.dispatch_table = self.stack.pop() except IndexError: pass # Utility methods. def do_nothing(self, t): """ Null method for when we explicitly want the action for a specific preprocessor directive to do nothing. """ pass def scons_current_file(self, t): self.current_file = t[1] def eval_expression(self, t): """ Evaluates a C preprocessor expression. This is done by converting it to a Python equivalent and eval()ing it in the C preprocessor namespace we use to track #define values. """ t = CPP_to_Python(' '.join(t[1:])) try: return eval(t, self.cpp_namespace) except (NameError, TypeError): return 0 def initialize_result(self, fname): self.result = [fname] def finalize_result(self, fname): return self.result[1:] def find_include_file(self, t): """ Finds the #include file for a given preprocessor tuple. """ fname = t[2] for d in self.searchpath[t[1]]: if d == os.curdir: f = fname else: f = os.path.join(d, fname) if os.path.isfile(f): return f return None def read_file(self, file): return open(file).read() # Start and stop processing include lines. def start_handling_includes(self, t=None): """ Causes the PreProcessor object to start processing #import, #include and #include_next lines. This method will be called when a #if, #ifdef, #ifndef or #elif evaluates True, or when we reach the #else in a #if, #ifdef, #ifndef or #elif block where a condition already evaluated False. """ d = self.dispatch_table d['import'] = self.do_import d['include'] = self.do_include d['include_next'] = self.do_include def stop_handling_includes(self, t=None): """ Causes the PreProcessor object to stop processing #import, #include and #include_next lines. This method will be called when a #if, #ifdef, #ifndef or #elif evaluates False, or when we reach the #else in a #if, #ifdef, #ifndef or #elif block where a condition already evaluated True. """ d = self.dispatch_table d['import'] = self.do_nothing d['include'] = self.do_nothing d['include_next'] = self.do_nothing # Default methods for handling all of the preprocessor directives. # (Note that what actually gets called for a given directive at any # point in time is really controlled by the dispatch_table.) def _do_if_else_condition(self, condition): """ Common logic for evaluating the conditions on #if, #ifdef and #ifndef lines. """ self.save() d = self.dispatch_table if condition: self.start_handling_includes() d['elif'] = self.stop_handling_includes d['else'] = self.stop_handling_includes else: self.stop_handling_includes() d['elif'] = self.do_elif d['else'] = self.start_handling_includes def do_ifdef(self, t): """ Default handling of a #ifdef line. """ self._do_if_else_condition(t[1] in self.cpp_namespace) def do_ifndef(self, t): """ Default handling of a #ifndef line. """ self._do_if_else_condition(t[1] not in self.cpp_namespace) def do_if(self, t): """ Default handling of a #if line. """ self._do_if_else_condition(self.eval_expression(t)) def do_elif(self, t): """ Default handling of a #elif line. """ d = self.dispatch_table if self.eval_expression(t): self.start_handling_includes() d['elif'] = self.stop_handling_includes d['else'] = self.stop_handling_includes def do_else(self, t): """ Default handling of a #else line. """ pass def do_endif(self, t): """ Default handling of a #endif line. """ self.restore() def do_define(self, t): """ Default handling of a #define line. """ _, name, args, expansion = t try: expansion = int(expansion) except (TypeError, ValueError): pass if args: evaluator = FunctionEvaluator(name, args[1:-1], expansion) self.cpp_namespace[name] = evaluator else: self.cpp_namespace[name] = expansion def do_undef(self, t): """ Default handling of a #undef line. """ try: del self.cpp_namespace[t[1]] except KeyError: pass def do_import(self, t): """ Default handling of a #import line. """ # XXX finish this -- maybe borrow/share logic from do_include()...? pass def do_include(self, t): """ Default handling of a #include line. """ t = self.resolve_include(t) include_file = self.find_include_file(t) if include_file: #print "include_file =", include_file self.result.append(include_file) contents = self.read_file(include_file) new_tuples = [('scons_current_file', include_file)] + \ self.tupleize(contents) + \ [('scons_current_file', self.current_file)] self.tuples[:] = new_tuples + self.tuples # Date: Tue, 22 Nov 2005 20:26:09 -0500 # From: Stefan Seefeld # # By the way, #include_next is not the same as #include. The difference # being that #include_next starts its search in the path following the # path that let to the including file. In other words, if your system # include paths are ['/foo', '/bar'], and you are looking at a header # '/foo/baz.h', it might issue an '#include_next ' which would # correctly resolve to '/bar/baz.h' (if that exists), but *not* see # '/foo/baz.h' again. See http://www.delorie.com/gnu/docs/gcc/cpp_11.html # for more reasoning. # # I have no idea in what context 'import' might be used. # XXX is #include_next really the same as #include ? do_include_next = do_include # Utility methods for handling resolution of include files. def resolve_include(self, t): """Resolve a tuple-ized #include line. This handles recursive expansion of values without "" or <> surrounding the name until an initial " or < is found, to handle #include FILE where FILE is a #define somewhere else. """ s = t[1] while not s[0] in '<"': #print "s =", s try: s = self.cpp_namespace[s] except KeyError: m = function_name.search(s) s = self.cpp_namespace[m.group(1)] if callable(s): args = function_arg_separator.split(m.group(2)) s = s(*args) if not s: return None return (t[0], s[0], s[1:-1]) def all_include(self, t): """ """ self.result.append(self.resolve_include(t)) class DumbPreProcessor(PreProcessor): """A preprocessor that ignores all #if/#elif/#else/#endif directives and just reports back *all* of the #include files (like the classic SCons scanner did). This is functionally equivalent to using a regular expression to find all of the #include lines, only slower. It exists mainly as an example of how the main PreProcessor class can be sub-classed to tailor its behavior. """ def __init__(self, *args, **kw): PreProcessor.__init__(self, *args, **kw) d = self.default_table for func in ['if', 'elif', 'else', 'endif', 'ifdef', 'ifndef']: d[func] = d[func] = self.do_nothing del __revision__ # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/ActionTests.py0000644000175000017500000023011412114661557022116 0ustar dktrkranzdktrkranz# # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/ActionTests.py 2013/03/03 09:48:35 garyo" import SCons.compat # Define a null function and a null class for use as builder actions. # Where these are defined in the file seems to affect their byte-code # contents, so try to minimize changes by defining them here, before we # even import anything. def GlobalFunc(): pass class GlobalActFunc(object): def __call__(self): pass import collections import io import os import re import sys import types import unittest import SCons.Action import SCons.Environment import SCons.Errors import TestCmd # Initial setup of the common environment for all tests, # a temporary working directory containing a # script for writing arguments to an output file. # # We don't do this as a setUp() method because it's # unnecessary to create a separate directory and script # for each test, they can just use the one. test = TestCmd.TestCmd(workdir = '') test.write('act.py', """\ import os, string, sys f = open(sys.argv[1], 'w') f.write("act.py: '" + string.join(sys.argv[2:], "' '") + "'\\n") try: if sys.argv[3]: f.write("act.py: '" + os.environ[sys.argv[3]] + "'\\n") except: pass f.close() if 'ACTPY_PIPE' in os.environ: if 'PIPE_STDOUT_FILE' in os.environ: stdout_msg = open(os.environ['PIPE_STDOUT_FILE'], 'r').read() else: stdout_msg = "act.py: stdout: executed act.py %s\\n" % ' '.join(sys.argv[1:]) sys.stdout.write( stdout_msg ) if 'PIPE_STDERR_FILE' in os.environ: stderr_msg = open(os.environ['PIPE_STDERR_FILE'], 'r').read() else: stderr_msg = "act.py: stderr: executed act.py %s\\n" % ' '.join(sys.argv[1:]) sys.stderr.write( stderr_msg ) sys.exit(0) """) test.write('exit.py', """\ import sys sys.exit(int(sys.argv[1])) """) act_py = test.workpath('act.py') exit_py = test.workpath('exit.py') outfile = test.workpath('outfile') outfile2 = test.workpath('outfile2') pipe_file = test.workpath('pipe.out') scons_env = SCons.Environment.Environment() # Capture all the stuff the Actions will print, # so it doesn't clutter the output. sys.stdout = io.StringIO() class CmdStringHolder(object): def __init__(self, cmd, literal=None): self.data = str(cmd) self.literal = literal def is_literal(self): return self.literal def escape(self, escape_func): """Escape the string with the supplied function. The function is expected to take an arbitrary string, then return it with all special characters escaped and ready for passing to the command interpreter. After calling this function, the next call to str() will return the escaped string. """ if self.is_literal(): return escape_func(self.data) elif ' ' in self.data or '\t' in self.data: return '"%s"' % self.data else: return self.data class Environment(object): def __init__(self, **kw): self.d = {} self.d['SHELL'] = scons_env['SHELL'] self.d['SPAWN'] = scons_env['SPAWN'] self.d['PSPAWN'] = scons_env['PSPAWN'] self.d['ESCAPE'] = scons_env['ESCAPE'] for k, v in kw.items(): self.d[k] = v # Just use the underlying scons_subst*() utility methods. def subst(self, strSubst, raw=0, target=[], source=[], conv=None): return SCons.Subst.scons_subst(strSubst, self, raw, target, source, self.d, conv=conv) subst_target_source = subst def subst_list(self, strSubst, raw=0, target=[], source=[], conv=None): return SCons.Subst.scons_subst_list(strSubst, self, raw, target, source, self.d, conv=conv) def __getitem__(self, item): return self.d[item] def __setitem__(self, item, value): self.d[item] = value def has_key(self, item): return item in self.d def get(self, key, value=None): return self.d.get(key, value) def items(self): return list(self.d.items()) def Dictionary(self): return self.d def Clone(self, **kw): res = Environment() res.d = SCons.Util.semi_deepcopy(self.d) for k, v in kw.items(): res.d[k] = v return res def sig_dict(self): d = {} for k,v in self.items(): d[k] = v d['TARGETS'] = ['__t1__', '__t2__', '__t3__', '__t4__', '__t5__', '__t6__'] d['TARGET'] = d['TARGETS'][0] d['SOURCES'] = ['__s1__', '__s2__', '__s3__', '__s4__', '__s5__', '__s6__'] d['SOURCE'] = d['SOURCES'][0] return d class DummyNode(object): def __init__(self, name): self.name = name def str_for_display(self): return '"' + self.name + '"' def __str__(self): return self.name def rfile(self): return self def get_subst_proxy(self): return self if os.name == 'java': python = os.path.join(sys.prefix, 'jython') else: python = sys.executable _python_ = '"' + python + '"' _null = SCons.Action._null def test_varlist(pos_call, str_call, cmd, cmdstrfunc, **kw): def call_action(a, pos_call=pos_call, str_call=str_call, kw=kw): a = SCons.Action.Action(*a, **kw) # returned object must provide these entry points assert hasattr(a, '__call__') assert hasattr(a, 'get_contents') assert hasattr(a, 'genstring') pos_call(a) str_call(a) return a a = call_action((cmd, cmdstrfunc)) assert a.varlist == (), a.varlist a = call_action((cmd, cmdstrfunc, 'foo')) assert a.varlist == ('foo',), a.varlist a = call_action((cmd, cmdstrfunc, 'a', 'b', 'c')) assert a.varlist == ('a', 'b', 'c'), a.varlist kw['varlist'] = 'foo' a = call_action((cmd, cmdstrfunc)) assert a.varlist == ('foo',), a.varlist kw['varlist'] = ['x', 'y', 'z'] a = call_action((cmd, cmdstrfunc)) assert a.varlist == ('x', 'y', 'z'), a.varlist a = call_action((cmd, cmdstrfunc, 'foo')) assert a.varlist == ('foo', 'x', 'y', 'z'), a.varlist a = call_action((cmd, cmdstrfunc, 'a', 'b', 'c')) assert a.varlist == ('a', 'b', 'c', 'x', 'y', 'z'), a.varlist def test_positional_args(pos_callback, cmd, **kw): """Test that Action() returns the expected type and that positional args work. """ #FUTURE act = SCons.Action.Action(cmd, **kw) act = SCons.Action.Action(cmd, **kw) pos_callback(act) assert act.varlist is (), act.varlist if not isinstance(act, SCons.Action._ActionAction): # only valid cmdstrfunc is None def none(a): pass #FUTURE test_varlist(pos_callback, none, cmd, None, **kw) test_varlist(pos_callback, none, cmd, None, **kw) else: # _ActionAction should have set these assert hasattr(act, 'strfunction') assert act.cmdstr is _null, act.cmdstr assert act.presub is _null, act.presub assert act.chdir is None, act.chdir assert act.exitstatfunc is SCons.Action.default_exitstatfunc, \ act.exitstatfunc def cmdstr(a): assert hasattr(a, 'strfunction') assert a.cmdstr == 'cmdstr', a.cmdstr #FUTURE test_varlist(pos_callback, cmdstr, cmd, 'cmdstr', **kw) test_varlist(pos_callback, cmdstr, cmd, 'cmdstr', **kw) def fun(): pass def strfun(a, fun=fun): assert a.strfunction is fun, a.strfunction assert a.cmdstr == _null, a.cmdstr #FUTURE test_varlist(pos_callback, strfun, cmd, fun, **kw) test_varlist(pos_callback, strfun, cmd, fun, **kw) def none(a): assert hasattr(a, 'strfunction') assert a.cmdstr is None, a.cmdstr #FUTURE test_varlist(pos_callback, none, cmd, None, **kw) test_varlist(pos_callback, none, cmd, None, **kw) """Test handling of bad cmdstrfunc arguments """ try: #FUTURE a = SCons.Action.Action(cmd, [], **kw) a = SCons.Action.Action(cmd, [], **kw) except SCons.Errors.UserError, e: s = str(e) m = 'Invalid command display variable' assert s.find(m) != -1, 'Unexpected string: %s' % s else: raise Exception("did not catch expected UserError") return act class ActionTestCase(unittest.TestCase): """Test the Action() factory function""" def test_FunctionAction(self): """Test the Action() factory's creation of FunctionAction objects """ def foo(): pass def func_action(a, foo=foo): assert isinstance(a, SCons.Action.FunctionAction), a assert a.execfunction == foo, a.execfunction test_positional_args(func_action, foo) # a singleton list returns the contained action test_positional_args(func_action, [foo]) def test_CommandAction(self): """Test the Action() factory's creation of CommandAction objects """ def cmd_action(a): assert isinstance(a, SCons.Action.CommandAction), a assert a.cmd_list == "string", a.cmd_list test_positional_args(cmd_action, "string") # a singleton list returns the contained action test_positional_args(cmd_action, ["string"]) try: unicode except NameError: pass else: a2 = eval("SCons.Action.Action(u'string')") assert isinstance(a2, SCons.Action.CommandAction), a2 def line_action(a): assert isinstance(a, SCons.Action.CommandAction), a assert a.cmd_list == [ "explicit", "command", "line" ], a.cmd_list test_positional_args(line_action, [[ "explicit", "command", "line" ]]) def test_ListAction(self): """Test the Action() factory's creation of ListAction objects """ a1 = SCons.Action.Action(["x", "y", "z", [ "a", "b", "c"]]) assert isinstance(a1, SCons.Action.ListAction), a1 assert a1.varlist is (), a1.varlist assert isinstance(a1.list[0], SCons.Action.CommandAction), a1.list[0] assert a1.list[0].cmd_list == "x", a1.list[0].cmd_list assert isinstance(a1.list[1], SCons.Action.CommandAction), a1.list[1] assert a1.list[1].cmd_list == "y", a1.list[1].cmd_list assert isinstance(a1.list[2], SCons.Action.CommandAction), a1.list[2] assert a1.list[2].cmd_list == "z", a1.list[2].cmd_list assert isinstance(a1.list[3], SCons.Action.CommandAction), a1.list[3] assert a1.list[3].cmd_list == [ "a", "b", "c" ], a1.list[3].cmd_list a2 = SCons.Action.Action("x\ny\nz") assert isinstance(a2, SCons.Action.ListAction), a2 assert a2.varlist is (), a2.varlist assert isinstance(a2.list[0], SCons.Action.CommandAction), a2.list[0] assert a2.list[0].cmd_list == "x", a2.list[0].cmd_list assert isinstance(a2.list[1], SCons.Action.CommandAction), a2.list[1] assert a2.list[1].cmd_list == "y", a2.list[1].cmd_list assert isinstance(a2.list[2], SCons.Action.CommandAction), a2.list[2] assert a2.list[2].cmd_list == "z", a2.list[2].cmd_list def foo(): pass a3 = SCons.Action.Action(["x", foo, "z"]) assert isinstance(a3, SCons.Action.ListAction), a3 assert a3.varlist is (), a3.varlist assert isinstance(a3.list[0], SCons.Action.CommandAction), a3.list[0] assert a3.list[0].cmd_list == "x", a3.list[0].cmd_list assert isinstance(a3.list[1], SCons.Action.FunctionAction), a3.list[1] assert a3.list[1].execfunction == foo, a3.list[1].execfunction assert isinstance(a3.list[2], SCons.Action.CommandAction), a3.list[2] assert a3.list[2].cmd_list == "z", a3.list[2].cmd_list a4 = SCons.Action.Action(["x", "y"], strfunction=foo) assert isinstance(a4, SCons.Action.ListAction), a4 assert a4.varlist is (), a4.varlist assert isinstance(a4.list[0], SCons.Action.CommandAction), a4.list[0] assert a4.list[0].cmd_list == "x", a4.list[0].cmd_list assert a4.list[0].strfunction == foo, a4.list[0].strfunction assert isinstance(a4.list[1], SCons.Action.CommandAction), a4.list[1] assert a4.list[1].cmd_list == "y", a4.list[1].cmd_list assert a4.list[1].strfunction == foo, a4.list[1].strfunction a5 = SCons.Action.Action("x\ny", strfunction=foo) assert isinstance(a5, SCons.Action.ListAction), a5 assert a5.varlist is (), a5.varlist assert isinstance(a5.list[0], SCons.Action.CommandAction), a5.list[0] assert a5.list[0].cmd_list == "x", a5.list[0].cmd_list assert a5.list[0].strfunction == foo, a5.list[0].strfunction assert isinstance(a5.list[1], SCons.Action.CommandAction), a5.list[1] assert a5.list[1].cmd_list == "y", a5.list[1].cmd_list assert a5.list[1].strfunction == foo, a5.list[1].strfunction def test_CommandGeneratorAction(self): """Test the Action() factory's creation of CommandGeneratorAction objects """ def foo(): pass def gen_action(a, foo=foo): assert isinstance(a, SCons.Action.CommandGeneratorAction), a assert a.generator is foo, a.generator test_positional_args(gen_action, foo, generator=1) def test_LazyCmdGeneratorAction(self): """Test the Action() factory's creation of lazy CommandGeneratorAction objects """ def lazy_action(a): assert isinstance(a, SCons.Action.LazyAction), a assert a.var == "FOO", a.var assert a.cmd_list == "${FOO}", a.cmd_list test_positional_args(lazy_action, "$FOO") test_positional_args(lazy_action, "${FOO}") def test_no_action(self): """Test when the Action() factory can't create an action object """ try: a5 = SCons.Action.Action(1) except TypeError: pass else: assert 0, "Should have thrown a TypeError creating Action from an int." def test_reentrance(self): """Test the Action() factory when the action is already an Action object """ a1 = SCons.Action.Action("foo") a2 = SCons.Action.Action(a1) assert a2 is a1, a2 class _ActionActionTestCase(unittest.TestCase): def test__init__(self): """Test creation of _ActionAction objects """ def func1(): pass def func2(): pass def func3(): pass a = SCons.Action._ActionAction() assert not hasattr(a, 'strfunction') assert a.cmdstr is _null, a.cmdstr assert a.varlist == (), a.varlist assert a.presub is _null, a.presub assert a.chdir is None, a.chdir assert a.exitstatfunc is SCons.Action.default_exitstatfunc, a.exitstatfunc assert SCons.Action._ActionAction(kwarg = 1) assert not hasattr(a, 'kwarg') assert not hasattr(a, 'strfunction') assert a.cmdstr is _null, a.cmdstr assert a.varlist == (), a.varlist assert a.presub is _null, a.presub assert a.chdir is None, a.chdir assert a.exitstatfunc is SCons.Action.default_exitstatfunc, a.exitstatfunc a = SCons.Action._ActionAction(strfunction=func1) assert a.strfunction is func1, a.strfunction a = SCons.Action._ActionAction(strfunction=None) assert not hasattr(a, 'strfunction') assert a.cmdstr is None, a.cmdstr a = SCons.Action._ActionAction(cmdstr='cmdstr') assert not hasattr(a, 'strfunction') assert a.cmdstr is 'cmdstr', a.cmdstr a = SCons.Action._ActionAction(cmdstr=None) assert not hasattr(a, 'strfunction') assert a.cmdstr is None, a.cmdstr t = ('a','b','c') a = SCons.Action._ActionAction(varlist=t) assert a.varlist == t, a.varlist a = SCons.Action._ActionAction(presub=func1) assert a.presub is func1, a.presub a = SCons.Action._ActionAction(chdir=1) assert a.chdir is 1, a.chdir a = SCons.Action._ActionAction(exitstatfunc=func1) assert a.exitstatfunc is func1, a.exitstatfunc a = SCons.Action._ActionAction( # alphabetical order ... chdir='x', cmdstr='cmdstr', exitstatfunc=func3, presub=func2, strfunction=func1, varlist=t, ) assert a.chdir is 'x', a.chdir assert a.cmdstr is 'cmdstr', a.cmdstr assert a.exitstatfunc is func3, a.exitstatfunc assert a.presub is func2, a.presub assert a.strfunction is func1, a.strfunction assert a.varlist is t, a.varlist def test_dup_keywords(self): """Test handling of both cmdstr and strfunction arguments """ def func(): pass try: a = SCons.Action.Action('foo', cmdstr='string', strfunction=func) except SCons.Errors.UserError, e: s = str(e) m = 'Cannot have both strfunction and cmdstr args to Action()' assert s.find(m) != -1, 'Unexpected string: %s' % s else: raise Exception("did not catch expected UserError") def test___cmp__(self): """Test Action comparison """ a1 = SCons.Action.Action("x") a2 = SCons.Action.Action("x") assert a1 == a2 a3 = SCons.Action.Action("y") assert a1 != a3 assert a2 != a3 def test_print_cmd_lines(self): """Test the print_cmd_lines() method """ save_stdout = sys.stdout try: def execfunc(target, source, env): pass a = SCons.Action.Action(execfunc) sio = io.StringIO() sys.stdout = sio a.print_cmd_line("foo bar", None, None, None) s = sio.getvalue() assert s == "foo bar\n", s finally: sys.stdout = save_stdout def test___call__(self): """Test calling an Action """ save_stdout = sys.stdout save_print_actions = SCons.Action.print_actions save_print_actions_presub = SCons.Action.print_actions_presub save_execute_actions = SCons.Action.execute_actions #SCons.Action.print_actions = 0 test = TestCmd.TestCmd(workdir = '') test.subdir('sub', 'xyz') os.chdir(test.workpath()) try: env = Environment() def execfunc(target, source, env): assert isinstance(target, list), type(target) assert isinstance(source, list), type(source) return 7 a = SCons.Action.Action(execfunc) def firstfunc(target, source, env): assert isinstance(target, list), type(target) assert isinstance(source, list), type(source) return 0 def lastfunc(target, source, env): assert isinstance(target, list), type(target) assert isinstance(source, list), type(source) return 9 b = SCons.Action.Action([firstfunc, execfunc, lastfunc]) sio = io.StringIO() sys.stdout = sio result = a("out", "in", env) assert result.status == 7, result s = sio.getvalue() assert s == "execfunc(['out'], ['in'])\n", s a.chdir = 'xyz' expect = "os.chdir(%s)\nexecfunc(['out'], ['in'])\nos.chdir(%s)\n" sio = io.StringIO() sys.stdout = sio result = a("out", "in", env) assert result.status == 7, result.status s = sio.getvalue() assert s == expect % (repr('xyz'), repr(test.workpath())), s sio = io.StringIO() sys.stdout = sio result = a("out", "in", env, chdir='sub') assert result.status == 7, result.status s = sio.getvalue() assert s == expect % (repr('sub'), repr(test.workpath())), s a.chdir = None sio = io.StringIO() sys.stdout = sio result = b("out", "in", env) assert result.status == 7, result.status s = sio.getvalue() assert s == "firstfunc(['out'], ['in'])\nexecfunc(['out'], ['in'])\n", s SCons.Action.execute_actions = 0 sio = io.StringIO() sys.stdout = sio result = a("out", "in", env) assert result == 0, result s = sio.getvalue() assert s == "execfunc(['out'], ['in'])\n", s sio = io.StringIO() sys.stdout = sio result = b("out", "in", env) assert result == 0, result s = sio.getvalue() assert s == "firstfunc(['out'], ['in'])\nexecfunc(['out'], ['in'])\nlastfunc(['out'], ['in'])\n", s SCons.Action.print_actions_presub = 1 SCons.Action.execute_actions = 1 sio = io.StringIO() sys.stdout = sio result = a("out", "in", env) assert result.status == 7, result.status s = sio.getvalue() assert s == "Building out with action:\n execfunc(target, source, env)\nexecfunc(['out'], ['in'])\n", s sio = io.StringIO() sys.stdout = sio result = a("out", "in", env, presub=0) assert result.status == 7, result.status s = sio.getvalue() assert s == "execfunc(['out'], ['in'])\n", s sio = io.StringIO() sys.stdout = sio result = a("out", "in", env, presub=1) assert result.status == 7, result.status s = sio.getvalue() assert s == "Building out with action:\n execfunc(target, source, env)\nexecfunc(['out'], ['in'])\n", s sio = io.StringIO() sys.stdout = sio result = b(["out"], "in", env, presub=1) assert result.status == 7, result.status s = sio.getvalue() assert s == "Building out with action:\n firstfunc(target, source, env)\nfirstfunc(['out'], ['in'])\nBuilding out with action:\n execfunc(target, source, env)\nexecfunc(['out'], ['in'])\n", s sio = io.StringIO() sys.stdout = sio result = b(["out", "list"], "in", env, presub=1) assert result.status == 7, result.status s = sio.getvalue() assert s == "Building out and list with action:\n firstfunc(target, source, env)\nfirstfunc(['out', 'list'], ['in'])\nBuilding out and list with action:\n execfunc(target, source, env)\nexecfunc(['out', 'list'], ['in'])\n", s a2 = SCons.Action.Action(execfunc) sio = io.StringIO() sys.stdout = sio result = a2("out", "in", env) assert result.status == 7, result.status s = sio.getvalue() assert s == "Building out with action:\n execfunc(target, source, env)\nexecfunc(['out'], ['in'])\n", s sio = io.StringIO() sys.stdout = sio result = a2("out", "in", env, presub=0) assert result.status == 7, result.status s = sio.getvalue() assert s == "execfunc(['out'], ['in'])\n", s SCons.Action.execute_actions = 0 sio = io.StringIO() sys.stdout = sio result = a2("out", "in", env, presub=0) assert result == 0, result s = sio.getvalue() assert s == "execfunc(['out'], ['in'])\n", s sio = io.StringIO() sys.stdout = sio result = a("out", "in", env, presub=0, execute=1, show=0) assert result.status == 7, result.status s = sio.getvalue() assert s == '', s sys.stdout = save_stdout exitstatfunc_result = [] def exitstatfunc(stat, result=exitstatfunc_result): result.append(stat) return stat result = a("out", "in", env, exitstatfunc=exitstatfunc) assert result == 0, result assert exitstatfunc_result == [], exitstatfunc_result result = a("out", "in", env, execute=1, exitstatfunc=exitstatfunc) assert result.status == 7, result.status assert exitstatfunc_result == [7], exitstatfunc_result SCons.Action.execute_actions = 1 result = [] def my_print_cmd_line(s, target, source, env, result=result): result.append(s) env['PRINT_CMD_LINE_FUNC'] = my_print_cmd_line a("output", "input", env) assert result == ["execfunc(['output'], ['input'])"], result finally: sys.stdout = save_stdout SCons.Action.print_actions = save_print_actions SCons.Action.print_actions_presub = save_print_actions_presub SCons.Action.execute_actions = save_execute_actions def test_presub_lines(self): """Test the presub_lines() method """ env = Environment() a = SCons.Action.Action("x") s = a.presub_lines(env) assert s == ['x'], s a = SCons.Action.Action(["y", "z"]) s = a.presub_lines(env) assert s == ['y', 'z'], s def func(): pass a = SCons.Action.Action(func) s = a.presub_lines(env) assert s == ["func(target, source, env)"], s def gen(target, source, env, for_signature): return 'generat' + env.get('GEN', 'or') a = SCons.Action.Action(gen, generator=1) s = a.presub_lines(env) assert s == ["generator"], s s = a.presub_lines(Environment(GEN = 'ed')) assert s == ["generated"], s a = SCons.Action.Action("$ACT") s = a.presub_lines(env) assert s == [''], s s = a.presub_lines(Environment(ACT = 'expanded action')) assert s == ['expanded action'], s def test_add(self): """Test adding Actions to stuff.""" # Adding actions to other Actions or to stuff that can # be converted into an Action should produce a ListAction # containing all the Actions. def bar(): return None baz = SCons.Action.Action(bar, generator=1) act1 = SCons.Action.Action('foo bar') act2 = SCons.Action.Action([ 'foo', bar ]) sum = act1 + act2 assert isinstance(sum, SCons.Action.ListAction), str(sum) assert len(sum.list) == 3, len(sum.list) assert [isinstance(x, SCons.Action.ActionBase) for x in sum.list] == [ 1, 1, 1 ] sum = act1 + act1 assert isinstance(sum, SCons.Action.ListAction), str(sum) assert len(sum.list) == 2, len(sum.list) sum = act2 + act2 assert isinstance(sum, SCons.Action.ListAction), str(sum) assert len(sum.list) == 4, len(sum.list) # Should also be able to add command generators to each other # or to actions sum = baz + baz assert isinstance(sum, SCons.Action.ListAction), str(sum) assert len(sum.list) == 2, len(sum.list) sum = baz + act1 assert isinstance(sum, SCons.Action.ListAction), str(sum) assert len(sum.list) == 2, len(sum.list) sum = act2 + baz assert isinstance(sum, SCons.Action.ListAction), str(sum) assert len(sum.list) == 3, len(sum.list) # Also should be able to add Actions to anything that can # be converted into an action. sum = act1 + bar assert isinstance(sum, SCons.Action.ListAction), str(sum) assert len(sum.list) == 2, len(sum.list) assert isinstance(sum.list[1], SCons.Action.FunctionAction) sum = 'foo bar' + act2 assert isinstance(sum, SCons.Action.ListAction), str(sum) assert len(sum.list) == 3, len(sum.list) assert isinstance(sum.list[0], SCons.Action.CommandAction) sum = [ 'foo', 'bar' ] + act1 assert isinstance(sum, SCons.Action.ListAction), str(sum) assert len(sum.list) == 3, sum.list assert isinstance(sum.list[0], SCons.Action.CommandAction) assert isinstance(sum.list[1], SCons.Action.CommandAction) sum = act2 + [ baz, bar ] assert isinstance(sum, SCons.Action.ListAction), str(sum) assert len(sum.list) == 4, len(sum.list) assert isinstance(sum.list[2], SCons.Action.CommandGeneratorAction) assert isinstance(sum.list[3], SCons.Action.FunctionAction) # OK to add None on either side (should be ignored) sum = act1 + None assert sum == act1 sum = None + act1 assert sum == act1 try: sum = act2 + 1 except TypeError: pass else: assert 0, "Should have thrown a TypeError adding to an int." try: sum = 1 + act2 except TypeError: pass else: assert 0, "Should have thrown a TypeError adding to an int." class CommandActionTestCase(unittest.TestCase): def test___init__(self): """Test creation of a command Action """ a = SCons.Action.CommandAction(["xyzzy"]) assert a.cmd_list == [ "xyzzy" ], a.cmd_list assert a.cmdstr is _null, a.cmdstr a = SCons.Action.CommandAction(["abra"], cmdstr="cadabra") assert a.cmd_list == [ "abra" ], a.cmd_list assert a.cmdstr == "cadabra", a.cmdstr def test___str__(self): """Test fetching the pre-substitution string for command Actions """ env = Environment() act = SCons.Action.CommandAction('xyzzy $TARGET $SOURCE') s = str(act) assert s == 'xyzzy $TARGET $SOURCE', s act = SCons.Action.CommandAction(['xyzzy', '$TARGET', '$SOURCE', '$TARGETS', '$SOURCES']) s = str(act) assert s == "xyzzy $TARGET $SOURCE $TARGETS $SOURCES", s def test_genstring(self): """Test the genstring() method for command Actions """ env = Environment() t1 = DummyNode('t1') t2 = DummyNode('t2') s1 = DummyNode('s1') s2 = DummyNode('s2') act = SCons.Action.CommandAction('xyzzy $TARGET $SOURCE') expect = 'xyzzy $TARGET $SOURCE' s = act.genstring([], [], env) assert s == expect, s s = act.genstring([t1], [s1], env) assert s == expect, s s = act.genstring([t1, t2], [s1, s2], env) assert s == expect, s act = SCons.Action.CommandAction('xyzzy $TARGETS $SOURCES') expect = 'xyzzy $TARGETS $SOURCES' s = act.genstring([], [], env) assert s == expect, s s = act.genstring([t1], [s1], env) assert s == expect, s s = act.genstring([t1, t2], [s1, s2], env) assert s == expect, s act = SCons.Action.CommandAction(['xyzzy', '$TARGET', '$SOURCE', '$TARGETS', '$SOURCES']) expect = "xyzzy $TARGET $SOURCE $TARGETS $SOURCES" s = act.genstring([], [], env) assert s == expect, s s = act.genstring([t1], [s1], env) assert s == expect, s s = act.genstring([t1, t2], [s1, s2], env) assert s == expect, s def test_strfunction(self): """Test fetching the string representation of command Actions """ env = Environment() t1 = DummyNode('t1') t2 = DummyNode('t2') s1 = DummyNode('s1') s2 = DummyNode('s2') act = SCons.Action.CommandAction('xyzzy $TARGET $SOURCE') s = act.strfunction([], [], env) assert s == 'xyzzy', s s = act.strfunction([t1], [s1], env) assert s == 'xyzzy t1 s1', s s = act.strfunction([t1, t2], [s1, s2], env) assert s == 'xyzzy t1 s1', s act = SCons.Action.CommandAction('xyzzy $TARGET $SOURCE', cmdstr='cmdstr - $SOURCE - $TARGET -') s = act.strfunction([], [], env) assert s == 'cmdstr - - -', s s = act.strfunction([t1], [s1], env) assert s == 'cmdstr - s1 - t1 -', s s = act.strfunction([t1, t2], [s1, s2], env) assert s == 'cmdstr - s1 - t1 -', s act = SCons.Action.CommandAction('xyzzy $TARGETS $SOURCES') s = act.strfunction([], [], env) assert s == 'xyzzy', s s = act.strfunction([t1], [s1], env) assert s == 'xyzzy t1 s1', s s = act.strfunction([t1, t2], [s1, s2], env) assert s == 'xyzzy t1 t2 s1 s2', s act = SCons.Action.CommandAction('xyzzy $TARGETS $SOURCES', cmdstr='cmdstr = $SOURCES = $TARGETS =') s = act.strfunction([], [], env) assert s == 'cmdstr = = =', s s = act.strfunction([t1], [s1], env) assert s == 'cmdstr = s1 = t1 =', s s = act.strfunction([t1, t2], [s1, s2], env) assert s == 'cmdstr = s1 s2 = t1 t2 =', s act = SCons.Action.CommandAction(['xyzzy', '$TARGET', '$SOURCE', '$TARGETS', '$SOURCES']) s = act.strfunction([], [], env) assert s == 'xyzzy', s s = act.strfunction([t1], [s1], env) assert s == 'xyzzy t1 s1 t1 s1', s s = act.strfunction([t1, t2], [s1, s2], env) assert s == 'xyzzy t1 s1 t1 t2 s1 s2', s act = SCons.Action.CommandAction('xyzzy $TARGETS $SOURCES', cmdstr='cmdstr\t$TARGETS\n$SOURCES ') s = act.strfunction([], [], env) assert s == 'cmdstr\t\n ', s s = act.strfunction([t1], [s1], env) assert s == 'cmdstr\tt1\ns1 ', s s = act.strfunction([t1, t2], [s1, s2], env) assert s == 'cmdstr\tt1 t2\ns1 s2 ', s def sf(target, source, env): return "sf was called" act = SCons.Action.CommandAction('foo', strfunction=sf) s = act.strfunction([], [], env) assert s == "sf was called", s class actclass1(object): def __init__(self, targets, sources, env): pass def __call__(self): return 1 class actclass2(object): def __init__(self, targets, sources, env): self.strfunction = 5 def __call__(self): return 2 class actclass3(object): def __init__(self, targets, sources, env): pass def __call__(self): return 3 def strfunction(self, targets, sources, env): return 'actclass3 on %s to get %s'%(str(sources[0]), str(targets[0])) class actclass4(object): def __init__(self, targets, sources, env): pass def __call__(self): return 4 strfunction = None act1 = SCons.Action.Action(actclass1([t1], [s1], env)) s = act1.strfunction([t1], [s1], env) assert s == 'actclass1(["t1"], ["s1"])', s act2 = SCons.Action.Action(actclass2([t1], [s1], env)) s = act2.strfunction([t1], [s1], env) assert s == 'actclass2(["t1"], ["s1"])', s act3 = SCons.Action.Action(actclass3([t1], [s1], env)) s = act3.strfunction([t1], [s1], env) assert s == 'actclass3 on s1 to get t1', s act4 = SCons.Action.Action(actclass4([t1], [s1], env)) s = act4.strfunction([t1], [s1], env) assert s is None, s act = SCons.Action.CommandAction("@foo bar") s = act.strfunction([], [], env) assert s == "", s act = SCons.Action.CommandAction("@-foo bar") s = act.strfunction([], [], env) assert s == "", s act = SCons.Action.CommandAction("-@foo bar") s = act.strfunction([], [], env) assert s == "", s act = SCons.Action.CommandAction("-foo bar") s = act.strfunction([], [], env) assert s == "foo bar", s act = SCons.Action.CommandAction("@ foo bar") s = act.strfunction([], [], env) assert s == "", s act = SCons.Action.CommandAction("@- foo bar") s = act.strfunction([], [], env) assert s == "", s act = SCons.Action.CommandAction("-@ foo bar") s = act.strfunction([], [], env) assert s == "", s act = SCons.Action.CommandAction("- foo bar") s = act.strfunction([], [], env) assert s == "foo bar", s def test_execute(self): """Test execution of command Actions """ try: env = self.env except AttributeError: env = Environment() cmd1 = r'%s %s %s xyzzy' % (_python_, act_py, outfile) act = SCons.Action.CommandAction(cmd1) r = act([], [], env.Clone()) assert r == 0 c = test.read(outfile, 'r') assert c == "act.py: 'xyzzy'\n", c cmd2 = r'%s %s %s $TARGET' % (_python_, act_py, outfile) act = SCons.Action.CommandAction(cmd2) r = act(DummyNode('foo'), [], env.Clone()) assert r == 0 c = test.read(outfile, 'r') assert c == "act.py: 'foo'\n", c cmd3 = r'%s %s %s ${TARGETS}' % (_python_, act_py, outfile) act = SCons.Action.CommandAction(cmd3) r = act(list(map(DummyNode, ['aaa', 'bbb'])), [], env.Clone()) assert r == 0 c = test.read(outfile, 'r') assert c == "act.py: 'aaa' 'bbb'\n", c cmd4 = r'%s %s %s $SOURCES' % (_python_, act_py, outfile) act = SCons.Action.CommandAction(cmd4) r = act([], [DummyNode('one'), DummyNode('two')], env.Clone()) assert r == 0 c = test.read(outfile, 'r') assert c == "act.py: 'one' 'two'\n", c cmd4 = r'%s %s %s ${SOURCES[:2]}' % (_python_, act_py, outfile) act = SCons.Action.CommandAction(cmd4) sources = [DummyNode('three'), DummyNode('four'), DummyNode('five')] env2 = env.Clone() r = act([], source = sources, env = env2) assert r == 0 c = test.read(outfile, 'r') assert c == "act.py: 'three' 'four'\n", c cmd5 = r'%s %s %s $TARGET XYZZY' % (_python_, act_py, outfile) act = SCons.Action.CommandAction(cmd5) env5 = Environment() if 'ENV' in scons_env: env5['ENV'] = scons_env['ENV'] PATH = scons_env['ENV'].get('PATH', '') else: env5['ENV'] = {} PATH = '' env5['ENV']['XYZZY'] = 'xyzzy' r = act(target = DummyNode('out5'), source = [], env = env5) act = SCons.Action.CommandAction(cmd5) r = act(target = DummyNode('out5'), source = [], env = env.Clone(ENV = {'XYZZY' : 'xyzzy5', 'PATH' : PATH})) assert r == 0 c = test.read(outfile, 'r') assert c == "act.py: 'out5' 'XYZZY'\nact.py: 'xyzzy5'\n", c class Obj(object): def __init__(self, str): self._str = str def __str__(self): return self._str def rfile(self): return self def get_subst_proxy(self): return self cmd6 = r'%s %s %s ${TARGETS[1]} $TARGET ${SOURCES[:2]}' % (_python_, act_py, outfile) act = SCons.Action.CommandAction(cmd6) r = act(target = [Obj('111'), Obj('222')], source = [Obj('333'), Obj('444'), Obj('555')], env = env.Clone()) assert r == 0 c = test.read(outfile, 'r') assert c == "act.py: '222' '111' '333' '444'\n", c if os.name == 'nt': # NT treats execs of directories and non-executable files # as "file not found" errors expect_nonexistent = 1 expect_nonexecutable_file = 1 expect_nonexecutable_dir = 1 elif sys.platform == 'cygwin': expect_nonexistent = 127 # Newer cygwin seems to return 126 for following expect_nonexecutable_file = 126 expect_nonexecutable_dir = 127 elif sys.platform.find('sunos') != -1: expect_nonexistent = 1 expect_nonexecutable_file = 1 expect_nonexecutable_dir = 1 else: expect_nonexistent = 127 expect_nonexecutable_file = 126 expect_nonexecutable_dir = 126 # Test that a nonexistent command returns 127 act = SCons.Action.CommandAction(python + "_no_such_command_") r = act([], [], env.Clone(out = outfile)) assert r.status == expect_nonexistent, r.status # Test that trying to execute a directory returns 126 dir, tail = os.path.split(python) act = SCons.Action.CommandAction(dir) r = act([], [], env.Clone(out = outfile)) assert r.status == expect_nonexecutable_file, r.status # Test that trying to execute a non-executable file returns 126 act = SCons.Action.CommandAction(outfile) r = act([], [], env.Clone(out = outfile)) assert r.status == expect_nonexecutable_dir, r.status act = SCons.Action.CommandAction('%s %s 1' % (_python_, exit_py)) r = act([], [], env) assert r.status == 1, r.status act = SCons.Action.CommandAction('@%s %s 1' % (_python_, exit_py)) r = act([], [], env) assert r.status == 1, r.status act = SCons.Action.CommandAction('@-%s %s 1' % (_python_, exit_py)) r = act([], [], env) assert r == 0, r act = SCons.Action.CommandAction('-%s %s 1' % (_python_, exit_py)) r = act([], [], env) assert r == 0, r act = SCons.Action.CommandAction('@ %s %s 1' % (_python_, exit_py)) r = act([], [], env) assert r.status == 1, r.status act = SCons.Action.CommandAction('@- %s %s 1' % (_python_, exit_py)) r = act([], [], env) assert r == 0, r act = SCons.Action.CommandAction('- %s %s 1' % (_python_, exit_py)) r = act([], [], env) assert r == 0, r def _DO_NOT_EXECUTE_test_pipe_execute(self): """Test capturing piped output from an action We used to have PIPE_BUILD support built right into Action.execute() for the benefit of the SConf subsystem, but we've moved that logic back into SConf itself. We'll leave this code here, just in case we ever want to resurrect this functionality in the future, but change the name of the test so it doesn't get executed as part of the normal test suite. """ pipe = open( pipe_file, "w" ) self.env = Environment(ENV = {'ACTPY_PIPE' : '1'}, PIPE_BUILD = 1, PSTDOUT = pipe, PSTDERR = pipe) # everything should also work when piping output self.test_execute() self.env['PSTDOUT'].close() pipe_out = test.read( pipe_file ) act_out = "act.py: stdout: executed act.py" act_err = "act.py: stderr: executed act.py" # Since we are now using select(), stdout and stderr can be # intermixed, so count the lines separately. outlines = re.findall(act_out, pipe_out) errlines = re.findall(act_err, pipe_out) assert len(outlines) == 6, pipe_out + repr(outlines) assert len(errlines) == 6, pipe_out + repr(errlines) # test redirection operators def test_redirect(self, redir, stdout_msg, stderr_msg): cmd = r'%s %s %s xyzzy %s' % (_python_, act_py, outfile, redir) # Write the output and error messages to files because # Windows can't handle strings that are too big in its # external environment (os.spawnve() returns EINVAL, # "Invalid argument"). stdout_file = test.workpath('stdout_msg') stderr_file = test.workpath('stderr_msg') open(stdout_file, 'w').write(stdout_msg) open(stderr_file, 'w').write(stderr_msg) pipe = open( pipe_file, "w" ) act = SCons.Action.CommandAction(cmd) env = Environment( ENV = {'ACTPY_PIPE' : '1', 'PIPE_STDOUT_FILE' : stdout_file, 'PIPE_STDERR_FILE' : stderr_file}, PIPE_BUILD = 1, PSTDOUT = pipe, PSTDERR = pipe ) r = act([], [], env) pipe.close() assert r == 0 return (test.read(outfile2, 'r'), test.read(pipe_file, 'r')) (redirected, pipe_out) = test_redirect(self,'> %s' % outfile2, act_out, act_err) assert redirected == act_out assert pipe_out == act_err (redirected, pipe_out) = test_redirect(self,'2> %s' % outfile2, act_out, act_err) assert redirected == act_err assert pipe_out == act_out (redirected, pipe_out) = test_redirect(self,'> %s 2>&1' % outfile2, act_out, act_err) assert (redirected == act_out + act_err or redirected == act_err + act_out) assert pipe_out == "" act_err = "Long Command Output\n"*3000 # the size of the string should exceed the system's default block size act_out = "" (redirected, pipe_out) = test_redirect(self,'> %s' % outfile2, act_out, act_err) assert (redirected == act_out) assert (pipe_out == act_err) def test_set_handler(self): """Test setting the command handler... """ class Test(object): def __init__(self): self.executed = 0 t=Test() def func(sh, escape, cmd, args, env, test=t): test.executed = args test.shell = sh return 0 def escape_func(cmd): return '**' + cmd + '**' class LiteralStr(object): def __init__(self, x): self.data = x def __str__(self): return self.data def escape(self, escape_func): return escape_func(self.data) def is_literal(self): return 1 a = SCons.Action.CommandAction(["xyzzy"]) e = Environment(SPAWN = func) a([], [], e) assert t.executed == [ 'xyzzy' ], t.executed a = SCons.Action.CommandAction(["xyzzy"]) e = Environment(SPAWN = '$FUNC', FUNC = func) a([], [], e) assert t.executed == [ 'xyzzy' ], t.executed a = SCons.Action.CommandAction(["xyzzy"]) e = Environment(SPAWN = func, SHELL = 'fake shell') a([], [], e) assert t.executed == [ 'xyzzy' ], t.executed assert t.shell == 'fake shell', t.shell a = SCons.Action.CommandAction([ LiteralStr("xyzzy") ]) e = Environment(SPAWN = func, ESCAPE = escape_func) a([], [], e) assert t.executed == [ '**xyzzy**' ], t.executed def test_get_contents(self): """Test fetching the contents of a command Action """ def CmdGen(target, source, env, for_signature): assert for_signature return "%s %s" % \ (env["foo"], env["bar"]) # The number 1 is there to make sure all args get converted to strings. a = SCons.Action.CommandAction(["|", "$(", "$foo", "|", "$bar", "$)", "|", "$baz", 1]) c = a.get_contents(target=[], source=[], env=Environment(foo = 'FFF', bar = 'BBB', baz = CmdGen)) assert c == "| | FFF BBB 1", c # Make sure that CommandActions use an Environment's # subst_target_source() method for substitution. class SpecialEnvironment(Environment): def subst_target_source(self, strSubst, raw=0, target=[], source=[]): return 'subst_target_source: ' + strSubst c = a.get_contents(target=DummyNode('ttt'), source = DummyNode('sss'), env=SpecialEnvironment(foo = 'GGG', bar = 'CCC', baz = 'ZZZ')) assert c == 'subst_target_source: | $( $foo | $bar $) | $baz 1', c # We've discussed using the real target and source names in a # CommandAction's signature contents. This would have have the # advantage of recompiling when a file's name changes (keeping # debug info current), but it would currently break repository # logic that will change the file name based on whether the # files come from a repository or locally. If we ever move to # that scheme, then all of the '__t1__' and '__s6__' file names # in the asserts below would change to 't1' and 's6' and the # like. t = list(map(DummyNode, ['t1', 't2', 't3', 't4', 't5', 't6'])) s = list(map(DummyNode, ['s1', 's2', 's3', 's4', 's5', 's6'])) env = Environment() a = SCons.Action.CommandAction(["$TARGET"]) c = a.get_contents(target=t, source=s, env=env) assert c == "t1", c a = SCons.Action.CommandAction(["$TARGETS"]) c = a.get_contents(target=t, source=s, env=env) assert c == "t1 t2 t3 t4 t5 t6", c a = SCons.Action.CommandAction(["${TARGETS[2]}"]) c = a.get_contents(target=t, source=s, env=env) assert c == "t3", c a = SCons.Action.CommandAction(["${TARGETS[3:5]}"]) c = a.get_contents(target=t, source=s, env=env) assert c == "t4 t5", c a = SCons.Action.CommandAction(["$SOURCE"]) c = a.get_contents(target=t, source=s, env=env) assert c == "s1", c a = SCons.Action.CommandAction(["$SOURCES"]) c = a.get_contents(target=t, source=s, env=env) assert c == "s1 s2 s3 s4 s5 s6", c a = SCons.Action.CommandAction(["${SOURCES[2]}"]) c = a.get_contents(target=t, source=s, env=env) assert c == "s3", c a = SCons.Action.CommandAction(["${SOURCES[3:5]}"]) c = a.get_contents(target=t, source=s, env=env) assert c == "s4 s5", c class CommandGeneratorActionTestCase(unittest.TestCase): def factory(self, act, **kw): """Pass any keywords as a dict""" return SCons.Action.CommandGeneratorAction(act, kw) def test___init__(self): """Test creation of a command generator Action """ def f(target, source, env): pass a = self.factory(f) assert a.generator == f def test___str__(self): """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 = self.factory(f) s = str(a) assert s == 'FOO', s def test_genstring(self): """Test the command generator Action genstring() method """ def f(target, source, env, for_signature, self=self): dummy = env['dummy'] self.dummy = dummy return "$FOO $TARGET $SOURCE $TARGETS $SOURCES" a = self.factory(f) self.dummy = 0 s = a.genstring([], [], env=Environment(FOO='xyzzy', dummy=1)) assert self.dummy == 1, self.dummy assert s == "$FOO $TARGET $SOURCE $TARGETS $SOURCES", s def test_execute(self): """Test executing a command generator Action """ def f(target, source, env, for_signature, self=self): dummy = env['dummy'] self.dummy = dummy s = env.subst("$FOO") assert s == 'foo baz\nbar ack', s return "$FOO" def func_action(target, source, env, self=self): dummy=env['dummy'] s = env.subst('$foo') assert s == 'bar', s self.dummy=dummy def f2(target, source, env, for_signature, f=func_action): return f def ch(sh, escape, cmd, args, env, self=self): self.cmd.append(cmd) self.args.append(args) a = self.factory(f) self.dummy = 0 self.cmd = [] self.args = [] a([], [], env=Environment(FOO = 'foo baz\nbar ack', dummy = 1, SPAWN = ch)) assert self.dummy == 1, self.dummy assert self.cmd == ['foo', 'bar'], self.cmd assert self.args == [[ 'foo', 'baz' ], [ 'bar', 'ack' ]], self.args b = self.factory(f2) self.dummy = 0 b(target=[], source=[], env=Environment(foo = 'bar', dummy = 2 )) assert self.dummy==2, self.dummy del self.dummy class DummyFile(object): def __init__(self, t): self.t = t def rfile(self): self.t.rfile_called = 1 return self def get_subst_proxy(self): return self def f3(target, source, env, for_signature): return '' c = self.factory(f3) c(target=[], source=DummyFile(self), env=Environment()) assert self.rfile_called def test_get_contents(self): """Test fetching the contents of a command generator Action """ def f(target, source, env, for_signature): foo = env['foo'] bar = env['bar'] assert for_signature, for_signature return [["guux", foo, "$(", "$ignore", "$)", bar, '${test("$( foo $bar $)")}' ]] def test(mystr): assert mystr == "$( foo $bar $)", mystr return "test" env = Environment(foo = 'FFF', bar = 'BBB', ignore = 'foo', test=test) a = self.factory(f) c = a.get_contents(target=[], source=[], env=env) assert c == "guux FFF BBB test", c def test_get_contents_of_function_action(self): """Test contents of a CommandGeneratorAction-generated FunctionAction """ def LocalFunc(): pass func_matches = [ "0,0,0,0,(),(),(d\000\000S),(),()", "0,0,0,0,(),(),(d\x00\x00S),(),()", ] meth_matches = [ "1,1,0,0,(),(),(d\000\000S),(),()", "1,1,0,0,(),(),(d\x00\x00S),(),()", ] def f_global(target, source, env, for_signature): return SCons.Action.Action(GlobalFunc) def f_local(target, source, env, for_signature): return SCons.Action.Action(LocalFunc) env = Environment(XYZ = 'foo') a = self.factory(f_global) c = a.get_contents(target=[], source=[], env=env) assert c in func_matches, repr(c) a = self.factory(f_local) c = a.get_contents(target=[], source=[], env=env) assert c in func_matches, repr(c) def f_global(target, source, env, for_signature): return SCons.Action.Action(GlobalFunc, varlist=['XYZ']) def f_local(target, source, env, for_signature): return SCons.Action.Action(LocalFunc, varlist=['XYZ']) matches_foo = [x + "foo" for x in func_matches] a = self.factory(f_global) c = a.get_contents(target=[], source=[], env=env) assert c in matches_foo, repr(c) a = self.factory(f_local) c = a.get_contents(target=[], source=[], env=env) assert c in matches_foo, repr(c) class FunctionActionTestCase(unittest.TestCase): def test___init__(self): """Test creation of a function Action """ def func1(): pass def func2(): pass def func3(): pass def func4(): pass a = SCons.Action.FunctionAction(func1, {}) assert a.execfunction == func1, a.execfunction assert isinstance(a.strfunction, types.MethodType), type(a.strfunction) a = SCons.Action.FunctionAction(func2, { 'strfunction' : func3 }) assert a.execfunction == func2, a.execfunction assert a.strfunction == func3, a.strfunction def test___str__(self): """Test the __str__() method for function Actions """ def func1(): pass a = SCons.Action.FunctionAction(func1, {}) s = str(a) assert s == "func1(target, source, env)", s class class1(object): def __call__(self): pass a = SCons.Action.FunctionAction(class1(), {}) s = str(a) assert s == "class1(target, source, env)", s def test_execute(self): """Test executing a function Action """ self.inc = 0 def f(target, source, env): s = env['s'] s.inc = s.inc + 1 s.target = target s.source=source assert env.subst("$BAR") == 'foo bar', env.subst("$BAR") return 0 a = SCons.Action.FunctionAction(f, {}) a(target=1, source=2, env=Environment(BAR = 'foo bar', s = self)) assert self.inc == 1, self.inc assert self.source == [2], self.source assert self.target == [1], self.target global count count = 0 def function1(target, source, env): global count count = count + 1 for t in target: open(t, 'w').write("function1\n") return 1 act = SCons.Action.FunctionAction(function1, {}) r = act(target = [outfile, outfile2], source=[], env=Environment()) assert r.status == 1, r.status assert count == 1, count c = test.read(outfile, 'r') assert c == "function1\n", c c = test.read(outfile2, 'r') assert c == "function1\n", c class class1a(object): def __init__(self, target, source, env): open(env['out'], 'w').write("class1a\n") act = SCons.Action.FunctionAction(class1a, {}) r = act([], [], Environment(out = outfile)) assert isinstance(r.status, class1a), r.status c = test.read(outfile, 'r') assert c == "class1a\n", c class class1b(object): def __call__(self, target, source, env): open(env['out'], 'w').write("class1b\n") return 2 act = SCons.Action.FunctionAction(class1b(), {}) r = act([], [], Environment(out = outfile)) assert r.status == 2, r.status c = test.read(outfile, 'r') assert c == "class1b\n", c def build_it(target, source, env, executor=None, self=self): self.build_it = 1 return 0 def string_it(target, source, env, executor=None, self=self): self.string_it = 1 return None act = SCons.Action.FunctionAction(build_it, { 'strfunction' : string_it }) r = act([], [], Environment()) assert r == 0, r assert self.build_it assert self.string_it def test_get_contents(self): """Test fetching the contents of a function Action """ def LocalFunc(): pass func_matches = [ "0,0,0,0,(),(),(d\000\000S),(),()", "0,0,0,0,(),(),(d\x00\x00S),(),()", ] meth_matches = [ "1,1,0,0,(),(),(d\000\000S),(),()", "1,1,0,0,(),(),(d\x00\x00S),(),()", ] def factory(act, **kw): return SCons.Action.FunctionAction(act, kw) a = factory(GlobalFunc) c = a.get_contents(target=[], source=[], env=Environment()) assert c in func_matches, repr(c) a = factory(LocalFunc) c = a.get_contents(target=[], source=[], env=Environment()) assert c in func_matches, repr(c) matches_foo = [x + "foo" for x in func_matches] a = factory(GlobalFunc, varlist=['XYZ']) c = a.get_contents(target=[], source=[], env=Environment()) assert c in func_matches, repr(c) c = a.get_contents(target=[], source=[], env=Environment(XYZ='foo')) assert c in matches_foo, repr(c) ##TODO: is this set of tests still needed? # Make sure a bare string varlist works a = factory(GlobalFunc, varlist='XYZ') c = a.get_contents(target=[], source=[], env=Environment()) assert c in func_matches, repr(c) c = a.get_contents(target=[], source=[], env=Environment(XYZ='foo')) assert c in matches_foo, repr(c) class Foo(object): def get_contents(self, target, source, env): return 'xyzzy' a = factory(Foo()) c = a.get_contents(target=[], source=[], env=Environment()) assert c == 'xyzzy', repr(c) class LocalClass(object): def LocalMethod(self): pass lc = LocalClass() a = factory(lc.LocalMethod) c = a.get_contents(target=[], source=[], env=Environment()) assert c in meth_matches, repr(c) def test_strfunction(self): """Test the FunctionAction.strfunction() method """ def func(): pass def factory(act, **kw): return SCons.Action.FunctionAction(act, kw) a = factory(func) s = a.strfunction(target=[], source=[], env=Environment()) assert s == 'func([], [])', s a = factory(func, strfunction=None) s = a.strfunction(target=[], source=[], env=Environment()) assert s is None, s a = factory(func, cmdstr='function') s = a.strfunction(target=[], source=[], env=Environment()) assert s == 'function', s class ListActionTestCase(unittest.TestCase): def test___init__(self): """Test creation of a list of subsidiary Actions """ def func(): pass a = SCons.Action.ListAction(["x", func, ["y", "z"]]) assert isinstance(a.list[0], SCons.Action.CommandAction) assert isinstance(a.list[1], SCons.Action.FunctionAction) assert isinstance(a.list[2], SCons.Action.ListAction) assert a.list[2].list[0].cmd_list == 'y' def test___str__(self): """Test the __str__() method for a list of subsidiary Actions """ def f(target,source,env): pass def g(target,source,env): pass a = SCons.Action.ListAction([f, g, "XXX", f]) s = str(a) assert s == "f(target, source, env)\ng(target, source, env)\nXXX\nf(target, source, env)", s def test_genstring(self): """Test the genstring() method for a list of subsidiary Actions """ def f(target,source,env): pass def g(target,source,env,for_signature): return 'generated %s %s' % (target[0], source[0]) g = SCons.Action.Action(g, generator=1) a = SCons.Action.ListAction([f, g, "XXX", f]) s = a.genstring(['foo.x'], ['bar.y'], Environment()) assert s == "f(target, source, env)\ngenerated foo.x bar.y\nXXX\nf(target, source, env)", s def test_execute(self): """Test executing a list of subsidiary Actions """ self.inc = 0 def f(target,source,env): s = env['s'] s.inc = s.inc + 1 a = SCons.Action.ListAction([f, f, f]) a([], [], Environment(s = self)) assert self.inc == 3, self.inc cmd2 = r'%s %s %s syzygy' % (_python_, act_py, outfile) def function2(target, source, env): open(env['out'], 'a').write("function2\n") return 0 class class2a(object): def __call__(self, target, source, env): open(env['out'], 'a').write("class2a\n") return 0 class class2b(object): def __init__(self, target, source, env): open(env['out'], 'a').write("class2b\n") act = SCons.Action.ListAction([cmd2, function2, class2a(), class2b]) r = act([], [], Environment(out = outfile)) assert isinstance(r.status, class2b), r.status c = test.read(outfile, 'r') assert c == "act.py: 'syzygy'\nfunction2\nclass2a\nclass2b\n", c def test_get_contents(self): """Test fetching the contents of a list of subsidiary Actions """ self.foo=0 def gen(target, source, env, for_signature): s = env['s'] s.foo=1 return "y" a = SCons.Action.ListAction(["x", SCons.Action.Action(gen, generator=1), "z"]) c = a.get_contents(target=[], source=[], env=Environment(s = self)) assert self.foo==1, self.foo assert c == "xyz", c class LazyActionTestCase(unittest.TestCase): def test___init__(self): """Test creation of a lazy-evaluation Action """ # Environment variable references should create a special type # of LazyAction that lazily evaluates the variable for whether # it's a string or something else before doing anything. a9 = SCons.Action.Action('$FOO') assert isinstance(a9, SCons.Action.LazyAction), a9 assert a9.var == 'FOO', a9.var a10 = SCons.Action.Action('${FOO}') assert isinstance(a10, SCons.Action.LazyAction), a10 assert a10.var == 'FOO', a10.var def test_genstring(self): """Test the lazy-evaluation Action genstring() method """ def f(target, source, env): pass a = SCons.Action.Action('$BAR') env1 = Environment(BAR=f, s=self) env2 = Environment(BAR='xxx', s=self) s = a.genstring([], [], env=env1) assert s == "f(target, source, env)", s s = a.genstring([], [], env=env2) assert s == 'xxx', s def test_execute(self): """Test executing a lazy-evaluation Action """ def f(target, source, env): s = env['s'] s.test=1 return 0 a = SCons.Action.Action('$BAR') a([], [], env=Environment(BAR = f, s = self)) assert self.test == 1, self.test cmd = r'%s %s %s lazy' % (_python_, act_py, outfile) a([], [], env=Environment(BAR = cmd, s = self)) c = test.read(outfile, 'r') assert c == "act.py: 'lazy'\n", c def test_get_contents(self): """Test fetching the contents of a lazy-evaluation Action """ a = SCons.Action.Action("${FOO}") env = Environment(FOO = [["This", "is", "a", "test"]]) c = a.get_contents(target=[], source=[], env=env) assert c == "This is a test", c def test_get_contents_of_function_action(self): """Test fetching the contents of a lazy-evaluation FunctionAction """ def LocalFunc(): pass func_matches = [ "0,0,0,0,(),(),(d\000\000S),(),()", "0,0,0,0,(),(),(d\x00\x00S),(),()", ] meth_matches = [ "1,1,0,0,(),(),(d\000\000S),(),()", "1,1,0,0,(),(),(d\x00\x00S),(),()", ] def factory(act, **kw): return SCons.Action.FunctionAction(act, kw) a = SCons.Action.Action("${FOO}") env = Environment(FOO = factory(GlobalFunc)) c = a.get_contents(target=[], source=[], env=env) assert c in func_matches, repr(c) env = Environment(FOO = factory(LocalFunc)) c = a.get_contents(target=[], source=[], env=env) assert c in func_matches, repr(c) matches_foo = [x + "foo" for x in func_matches] env = Environment(FOO = factory(GlobalFunc, varlist=['XYZ'])) c = a.get_contents(target=[], source=[], env=env) assert c in func_matches, repr(c) env['XYZ'] = 'foo' c = a.get_contents(target=[], source=[], env=env) assert c in matches_foo, repr(c) class ActionCallerTestCase(unittest.TestCase): def test___init__(self): """Test creation of an ActionCaller""" ac = SCons.Action.ActionCaller(1, [2, 3], {'FOO' : 4, 'BAR' : 5}) assert ac.parent == 1, ac.parent assert ac.args == [2, 3], ac.args assert ac.kw == {'FOO' : 4, 'BAR' : 5}, ac.kw def test_get_contents(self): """Test fetching the contents of an ActionCaller""" def strfunc(): pass def LocalFunc(): pass matches = [ "d\000\000S", "d\x00\x00S" ] af = SCons.Action.ActionFactory(GlobalFunc, strfunc) ac = SCons.Action.ActionCaller(af, [], {}) c = ac.get_contents([], [], Environment()) assert c in matches, repr(c) af = SCons.Action.ActionFactory(LocalFunc, strfunc) ac = SCons.Action.ActionCaller(af, [], {}) c = ac.get_contents([], [], Environment()) assert c in matches, repr(c) matches = [ 'd\000\000S', "d\x00\x00S" ] class LocalActFunc(object): def __call__(self): pass af = SCons.Action.ActionFactory(GlobalActFunc(), strfunc) ac = SCons.Action.ActionCaller(af, [], {}) c = ac.get_contents([], [], Environment()) assert c in matches, repr(c) af = SCons.Action.ActionFactory(LocalActFunc(), strfunc) ac = SCons.Action.ActionCaller(af, [], {}) c = ac.get_contents([], [], Environment()) assert c in matches, repr(c) matches = [ "", "", ] af = SCons.Action.ActionFactory(str, strfunc) ac = SCons.Action.ActionCaller(af, [], {}) c = ac.get_contents([], [], Environment()) assert c == "" or \ c == "", repr(c) def test___call__(self): """Test calling an ActionCaller""" actfunc_args = [] def actfunc(a1, a2, a3, args=actfunc_args): args.extend([a1, a2, a3]) def strfunc(a1, a2, a3): pass e = Environment(FOO = 2, BAR = 5) af = SCons.Action.ActionFactory(actfunc, strfunc) ac = SCons.Action.ActionCaller(af, ['$__env__', '$FOO', 3], {}) ac([], [], e) assert actfunc_args[0] is e, actfunc_args assert actfunc_args[1] == '2', actfunc_args assert actfunc_args[2] == 3, actfunc_args del actfunc_args[:] ac = SCons.Action.ActionCaller(af, [], {'a3' : '$__env__', 'a2' : '$BAR', 'a1' : 4}) ac([], [], e) assert actfunc_args[0] == 4, actfunc_args assert actfunc_args[1] == '5', actfunc_args assert actfunc_args[2] is e, actfunc_args del actfunc_args[:] def test_strfunction(self): """Test calling the ActionCaller strfunction() method""" strfunc_args = [] def actfunc(a1, a2, a3, a4): pass def strfunc(a1, a2, a3, a4, args=strfunc_args): args.extend([a1, a2, a3, a4]) af = SCons.Action.ActionFactory(actfunc, strfunc) ac = SCons.Action.ActionCaller(af, [1, '$FOO', 3, '$WS'], {}) ac.strfunction([], [], Environment(FOO = 2, WS='white space')) assert strfunc_args == [1, '2', 3, 'white space'], strfunc_args del strfunc_args[:] d = {'a3' : 6, 'a2' : '$BAR', 'a1' : 4, 'a4' : '$WS'} ac = SCons.Action.ActionCaller(af, [], d) ac.strfunction([], [], Environment(BAR = 5, WS='w s')) assert strfunc_args == [4, '5', 6, 'w s'], strfunc_args class ActionFactoryTestCase(unittest.TestCase): def test___init__(self): """Test creation of an ActionFactory""" def actfunc(): pass def strfunc(): pass ac = SCons.Action.ActionFactory(actfunc, strfunc) assert ac.actfunc is actfunc, ac.actfunc assert ac.strfunc is strfunc, ac.strfunc def test___call__(self): """Test calling whatever's returned from an ActionFactory""" actfunc_args = [] strfunc_args = [] def actfunc(a1, a2, a3, args=actfunc_args): args.extend([a1, a2, a3]) def strfunc(a1, a2, a3, args=strfunc_args): args.extend([a1, a2, a3]) af = SCons.Action.ActionFactory(actfunc, strfunc) af(3, 6, 9)([], [], Environment()) assert actfunc_args == [3, 6, 9], actfunc_args assert strfunc_args == [3, 6, 9], strfunc_args class ActionCompareTestCase(unittest.TestCase): def test_1_solo_name(self): """Test Lazy Cmd Generator Action get_name alone. Basically ensures we can locate the builder, comparing it to itself along the way.""" bar = SCons.Builder.Builder(action = {}) env = Environment( BUILDERS = {'BAR' : bar} ) name = bar.get_name(env) assert name == 'BAR', name def test_2_multi_name(self): """Test LazyCmdGenerator Action get_name multi builders. Ensure that we can compare builders (and thereby actions) to each other safely.""" foo = SCons.Builder.Builder(action = '$FOO', suffix = '.foo') bar = SCons.Builder.Builder(action = {}) assert foo != bar assert foo.action != bar.action env = Environment( BUILDERS = {'FOO' : foo, 'BAR' : bar} ) name = foo.get_name(env) assert name == 'FOO', name name = bar.get_name(env) assert name == 'BAR', name def test_3_dict_names(self): """Test Action/Suffix dicts with get_name. Verifies that Action/Suffix dictionaries work correctly, especially two builders that can generate the same suffix, where one of the builders has a suffix dictionary with a None key.""" foo = SCons.Builder.Builder(action = '$FOO', suffix = '.foo') bar = SCons.Builder.Builder(action = {}, suffix={None:'.bar'}) bar.add_action('.cow', "$MOO") dog = SCons.Builder.Builder(suffix = '.bar') env = Environment( BUILDERS = {'FOO' : foo, 'BAR' : bar, 'DOG' : dog} ) assert foo.get_name(env) == 'FOO', foo.get_name(env) assert bar.get_name(env) == 'BAR', bar.get_name(env) assert dog.get_name(env) == 'DOG', dog.get_name(env) if __name__ == "__main__": suite = unittest.TestSuite() tclasses = [ _ActionActionTestCase, ActionTestCase, CommandActionTestCase, CommandGeneratorActionTestCase, FunctionActionTestCase, ListActionTestCase, LazyActionTestCase, ActionCallerTestCase, ActionFactoryTestCase, ActionCompareTestCase ] for tclass in tclasses: names = unittest.getTestCaseNames(tclass, 'test_') suite.addTests(list(map(tclass, names))) if not unittest.TextTestRunner().run(suite).wasSuccessful(): sys.exit(1) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/compat/0000755000175000017500000000000012114661560020560 5ustar dktrkranzdktrkranzscons-doc-2.3.0/src/engine/SCons/compat/_scons_hashlib.py0000644000175000017500000000474212114661560024117 0ustar dktrkranzdktrkranz# # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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. # __doc__ = """ hashlib backwards-compatibility module for older (pre-2.5) Python versions This does not not NOT (repeat, *NOT*) provide complete hashlib functionality. It only wraps the portions of MD5 functionality used by SCons, in an interface that looks like hashlib (or enough for our purposes, anyway). In fact, this module will raise an ImportError if the underlying md5 module isn't available. """ __revision__ = "src/engine/SCons/compat/_scons_hashlib.py 2013/03/03 09:48:35 garyo" import md5 from string import hexdigits class md5obj(object): md5_module = md5 def __init__(self, name, string=''): if not name in ('MD5', 'md5'): raise ValueError("unsupported hash type") self.name = 'md5' self.m = self.md5_module.md5() def __repr__(self): return '<%s HASH object @ %#x>' % (self.name, id(self)) def copy(self): import copy result = copy.copy(self) result.m = self.m.copy() return result def digest(self): return self.m.digest() def update(self, arg): return self.m.update(arg) def hexdigest(self): return self.m.hexdigest() new = md5obj def md5(string=''): return md5obj('md5', string) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/compat/_scons_collections.py0000644000175000017500000000353312114661560025020 0ustar dktrkranzdktrkranz# # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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. # __doc__ = """ collections compatibility module for older (pre-2.4) Python versions This does not not NOT (repeat, *NOT*) provide complete collections functionality. It only wraps the portions of collections functionality used by SCons, in an interface that looks enough like collections for our purposes. """ __revision__ = "src/engine/SCons/compat/_scons_collections.py 2013/03/03 09:48:35 garyo" # Use exec to hide old names from fixers. exec("""if True: from UserDict import UserDict from UserList import UserList from UserString import UserString""") # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/compat/_scons_subprocess.py0000644000175000017500000012672412114661560024702 0ustar dktrkranzdktrkranz# subprocess - Subprocesses with accessible I/O streams # # For more information about this module, see PEP 324. # # This module should remain compatible with Python 2.2, see PEP 291. # # Copyright (c) 2003-2005 by Peter Astrand # # Licensed to PSF under a Contributor Agreement. # See http://www.python.org/2.4/license for licensing details. r"""subprocess - Subprocesses with accessible I/O streams This module allows you to spawn processes, connect to their input/output/error pipes, and obtain their return codes. This module intends to replace several other, older modules and functions, like: os.system os.spawn* os.popen* popen2.* commands.* Information about how the subprocess module can be used to replace these modules and functions can be found below. Using the subprocess module =========================== This module defines one class called Popen: class Popen(args, bufsize=0, executable=None, stdin=None, stdout=None, stderr=None, preexec_fn=None, close_fds=False, shell=False, cwd=None, env=None, universal_newlines=False, startupinfo=None, creationflags=0): Arguments are: args should be a string, or a sequence of program arguments. The program to execute is normally the first item in the args sequence or string, but can be explicitly set by using the executable argument. On UNIX, with shell=False (default): In this case, the Popen class uses os.execvp() to execute the child program. args should normally be a sequence. A string will be treated as a sequence with the string as the only item (the program to execute). On UNIX, with shell=True: If args is a string, it specifies the command string to execute through the shell. If args is a sequence, the first item specifies the command string, and any additional items will be treated as additional shell arguments. On Windows: the Popen class uses CreateProcess() to execute the child program, which operates on strings. If args is a sequence, it will be converted to a string using the list2cmdline method. Please note that not all MS Windows applications interpret the command line the same way: The list2cmdline is designed for applications using the same rules as the MS C runtime. bufsize, if given, has the same meaning as the corresponding argument to the built-in open() function: 0 means unbuffered, 1 means line buffered, any other positive value means use a buffer of (approximately) that size. A negative bufsize means to use the system default, which usually means fully buffered. The default value for bufsize is 0 (unbuffered). stdin, stdout and stderr specify the executed programs' standard input, standard output and standard error file handles, respectively. Valid values are PIPE, an existing file descriptor (a positive integer), an existing file object, and None. PIPE indicates that a new pipe to the child should be created. With None, no redirection will occur; the child's file handles will be inherited from the parent. Additionally, stderr can be STDOUT, which indicates that the stderr data from the applications should be captured into the same file handle as for stdout. If preexec_fn is set to a callable object, this object will be called in the child process just before the child is executed. If close_fds is true, all file descriptors except 0, 1 and 2 will be closed before the child process is executed. if shell is true, the specified command will be executed through the shell. If cwd is not None, the current directory will be changed to cwd before the child is executed. If env is not None, it defines the environment variables for the new process. If universal_newlines is true, the file objects stdout and stderr are opened as a text files, but lines may be terminated by any of '\n', the Unix end-of-line convention, '\r', the Macintosh convention or '\r\n', the Windows convention. All of these external representations are seen as '\n' by the Python program. Note: This feature is only available if Python is built with universal newline support (the default). Also, the newlines attribute of the file objects stdout, stdin and stderr are not updated by the communicate() method. The startupinfo and creationflags, if given, will be passed to the underlying CreateProcess() function. They can specify things such as appearance of the main window and priority for the new process. (Windows only) This module also defines two shortcut functions: call(*popenargs, **kwargs): Run command with arguments. Wait for command to complete, then return the returncode attribute. The arguments are the same as for the Popen constructor. Example: retcode = call(["ls", "-l"]) check_call(*popenargs, **kwargs): Run command with arguments. Wait for command to complete. If the exit code was zero then return, otherwise raise CalledProcessError. The CalledProcessError object will have the return code in the returncode attribute. The arguments are the same as for the Popen constructor. Example: check_call(["ls", "-l"]) Exceptions ---------- Exceptions raised in the child process, before the new program has started to execute, will be re-raised in the parent. Additionally, the exception object will have one extra attribute called 'child_traceback', which is a string containing traceback information from the childs point of view. The most common exception raised is OSError. This occurs, for example, when trying to execute a non-existent file. Applications should prepare for OSErrors. A ValueError will be raised if Popen is called with invalid arguments. check_call() will raise CalledProcessError, if the called process returns a non-zero return code. Security -------- Unlike some other popen functions, this implementation will never call /bin/sh implicitly. This means that all characters, including shell metacharacters, can safely be passed to child processes. Popen objects ============= Instances of the Popen class have the following methods: poll() Check if child process has terminated. Returns returncode attribute. wait() Wait for child process to terminate. Returns returncode attribute. communicate(input=None) Interact with process: Send data to stdin. Read data from stdout and stderr, until end-of-file is reached. Wait for process to terminate. The optional stdin argument should be a string to be sent to the child process, or None, if no data should be sent to the child. communicate() returns a tuple (stdout, stderr). Note: The data read is buffered in memory, so do not use this method if the data size is large or unlimited. The following attributes are also available: stdin If the stdin argument is PIPE, this attribute is a file object that provides input to the child process. Otherwise, it is None. stdout If the stdout argument is PIPE, this attribute is a file object that provides output from the child process. Otherwise, it is None. stderr If the stderr argument is PIPE, this attribute is file object that provides error output from the child process. Otherwise, it is None. pid The process ID of the child process. returncode The child return code. A None value indicates that the process hasn't terminated yet. A negative value -N indicates that the child was terminated by signal N (UNIX only). Replacing older functions with the subprocess module ==================================================== In this section, "a ==> b" means that b can be used as a replacement for a. Note: All functions in this section fail (more or less) silently if the executed program cannot be found; this module raises an OSError exception. In the following examples, we assume that the subprocess module is imported with "from subprocess import *". Replacing /bin/sh shell backquote --------------------------------- output=`mycmd myarg` ==> output = Popen(["mycmd", "myarg"], stdout=PIPE).communicate()[0] Replacing shell pipe line ------------------------- output=`dmesg | grep hda` ==> p1 = Popen(["dmesg"], stdout=PIPE) p2 = Popen(["grep", "hda"], stdin=p1.stdout, stdout=PIPE) output = p2.communicate()[0] Replacing os.system() --------------------- sts = os.system("mycmd" + " myarg") ==> p = Popen("mycmd" + " myarg", shell=True) pid, sts = os.waitpid(p.pid, 0) Note: * Calling the program through the shell is usually not required. * It's easier to look at the returncode attribute than the exitstatus. A more real-world example would look like this: try: retcode = call("mycmd" + " myarg", shell=True) if retcode < 0: print >>sys.stderr, "Child was terminated by signal", -retcode else: print >>sys.stderr, "Child returned", retcode except OSError, e: print >>sys.stderr, "Execution failed:", e Replacing os.spawn* ------------------- P_NOWAIT example: pid = os.spawnlp(os.P_NOWAIT, "/bin/mycmd", "mycmd", "myarg") ==> pid = Popen(["/bin/mycmd", "myarg"]).pid P_WAIT example: retcode = os.spawnlp(os.P_WAIT, "/bin/mycmd", "mycmd", "myarg") ==> retcode = call(["/bin/mycmd", "myarg"]) Vector example: os.spawnvp(os.P_NOWAIT, path, args) ==> Popen([path] + args[1:]) Environment example: os.spawnlpe(os.P_NOWAIT, "/bin/mycmd", "mycmd", "myarg", env) ==> Popen(["/bin/mycmd", "myarg"], env={"PATH": "/usr/bin"}) Replacing os.popen* ------------------- pipe = os.popen(cmd, mode='r', bufsize) ==> pipe = Popen(cmd, shell=True, bufsize=bufsize, stdout=PIPE).stdout pipe = os.popen(cmd, mode='w', bufsize) ==> pipe = Popen(cmd, shell=True, bufsize=bufsize, stdin=PIPE).stdin (child_stdin, child_stdout) = os.popen2(cmd, mode, bufsize) ==> p = Popen(cmd, shell=True, bufsize=bufsize, stdin=PIPE, stdout=PIPE, close_fds=True) (child_stdin, child_stdout) = (p.stdin, p.stdout) (child_stdin, child_stdout, child_stderr) = os.popen3(cmd, mode, bufsize) ==> p = Popen(cmd, shell=True, bufsize=bufsize, stdin=PIPE, stdout=PIPE, stderr=PIPE, close_fds=True) (child_stdin, child_stdout, child_stderr) = (p.stdin, p.stdout, p.stderr) (child_stdin, child_stdout_and_stderr) = os.popen4(cmd, mode, bufsize) ==> p = Popen(cmd, shell=True, bufsize=bufsize, stdin=PIPE, stdout=PIPE, stderr=STDOUT, close_fds=True) (child_stdin, child_stdout_and_stderr) = (p.stdin, p.stdout) Replacing popen2.* ------------------ Note: If the cmd argument to popen2 functions is a string, the command is executed through /bin/sh. If it is a list, the command is directly executed. (child_stdout, child_stdin) = popen2.popen2("somestring", bufsize, mode) ==> p = Popen(["somestring"], shell=True, bufsize=bufsize stdin=PIPE, stdout=PIPE, close_fds=True) (child_stdout, child_stdin) = (p.stdout, p.stdin) (child_stdout, child_stdin) = popen2.popen2(["mycmd", "myarg"], bufsize, mode) ==> p = Popen(["mycmd", "myarg"], bufsize=bufsize, stdin=PIPE, stdout=PIPE, close_fds=True) (child_stdout, child_stdin) = (p.stdout, p.stdin) The popen2.Popen3 and popen3.Popen4 basically works as subprocess.Popen, except that: * subprocess.Popen raises an exception if the execution fails * the capturestderr argument is replaced with the stderr argument. * stdin=PIPE and stdout=PIPE must be specified. * popen2 closes all filedescriptors by default, but you have to specify close_fds=True with subprocess.Popen. """ import sys mswindows = (sys.platform == "win32") import os import types import traceback # Exception classes used by this module. class CalledProcessError(Exception): """This exception is raised when a process run by check_call() returns a non-zero exit status. The exit status will be stored in the returncode attribute.""" def __init__(self, returncode, cmd): self.returncode = returncode self.cmd = cmd def __str__(self): return "Command '%s' returned non-zero exit status %d" % (self.cmd, self.returncode) if mswindows: try: import threading except ImportError: # SCons: the threading module is only used by the communicate() # method, which we don't actually use, so don't worry if we # can't import it. pass import msvcrt try: # Try to get _subprocess from _subprocess import * class STARTUPINFO(object): dwFlags = 0 hStdInput = None hStdOutput = None hStdError = None wShowWindow = 0 class pywintypes(object): error = IOError except ImportError: # If not there, then drop back to requiring pywin32 # TODO: Should this be wrapped in try as well? To notify user to install # pywin32 ? With URL to it? import pywintypes from win32api import GetStdHandle, STD_INPUT_HANDLE, \ STD_OUTPUT_HANDLE, STD_ERROR_HANDLE from win32api import GetCurrentProcess, DuplicateHandle, \ GetModuleFileName, GetVersion from win32con import DUPLICATE_SAME_ACCESS, SW_HIDE from win32pipe import CreatePipe from win32process import CreateProcess, STARTUPINFO, \ GetExitCodeProcess, STARTF_USESTDHANDLES, \ STARTF_USESHOWWINDOW, CREATE_NEW_CONSOLE from win32event import WaitForSingleObject, INFINITE, WAIT_OBJECT_0 else: import select import errno import fcntl import pickle try: fcntl.F_GETFD except AttributeError: fcntl.F_GETFD = 1 try: fcntl.F_SETFD except AttributeError: fcntl.F_SETFD = 2 __all__ = ["Popen", "PIPE", "STDOUT", "call", "check_call", "CalledProcessError"] try: MAXFD = os.sysconf("SC_OPEN_MAX") except KeyboardInterrupt: raise # SCons: don't swallow keyboard interrupts except: MAXFD = 256 try: isinstance(1, int) except TypeError: def is_int(obj): return isinstance(obj, type(1)) def is_int_or_long(obj): return type(obj) in (type(1), type(1L)) else: def is_int(obj): return isinstance(obj, int) def is_int_or_long(obj): return isinstance(obj, (int, long)) try: types.StringTypes except AttributeError: try: types.StringTypes = (str, unicode) except NameError: types.StringTypes = (str,) def is_string(obj): return isinstance(obj, types.StringTypes) _active = [] def _cleanup(): for inst in _active[:]: if inst.poll(_deadstate=sys.maxsize) >= 0: try: _active.remove(inst) except ValueError: # This can happen if two threads create a new Popen instance. # It's harmless that it was already removed, so ignore. pass PIPE = -1 STDOUT = -2 def call(*popenargs, **kwargs): """Run command with arguments. Wait for command to complete, then return the returncode attribute. The arguments are the same as for the Popen constructor. Example: retcode = call(["ls", "-l"]) """ return apply(Popen, popenargs, kwargs).wait() def check_call(*popenargs, **kwargs): """Run command with arguments. Wait for command to complete. If the exit code was zero then return, otherwise raise CalledProcessError. The CalledProcessError object will have the return code in the returncode attribute. The arguments are the same as for the Popen constructor. Example: check_call(["ls", "-l"]) """ retcode = call(*popenargs, **kwargs) cmd = kwargs.get("args") if cmd is None: cmd = popenargs[0] if retcode: raise CalledProcessError(retcode, cmd) return retcode def list2cmdline(seq): """ Translate a sequence of arguments into a command line string, using the same rules as the MS C runtime: 1) Arguments are delimited by white space, which is either a space or a tab. 2) A string surrounded by double quotation marks is interpreted as a single argument, regardless of white space contained within. A quoted string can be embedded in an argument. 3) A double quotation mark preceded by a backslash is interpreted as a literal double quotation mark. 4) Backslashes are interpreted literally, unless they immediately precede a double quotation mark. 5) If backslashes immediately precede a double quotation mark, every pair of backslashes is interpreted as a literal backslash. If the number of backslashes is odd, the last backslash escapes the next double quotation mark as described in rule 3. """ # See # http://msdn.microsoft.com/library/en-us/vccelng/htm/progs_12.asp result = [] needquote = False for arg in seq: bs_buf = [] # Add a space to separate this argument from the others if result: result.append(' ') needquote = (" " in arg) or ("\t" in arg) if needquote: result.append('"') for c in arg: if c == '\\': # Don't know if we need to double yet. bs_buf.append(c) elif c == '"': # Double backspaces. result.append('\\' * len(bs_buf)*2) bs_buf = [] result.append('\\"') else: # Normal char if bs_buf: result.extend(bs_buf) bs_buf = [] result.append(c) # Add remaining backspaces, if any. if bs_buf: result.extend(bs_buf) if needquote: result.extend(bs_buf) result.append('"') return ''.join(result) class Popen(object): def __init__(self, args, bufsize=0, executable=None, stdin=None, stdout=None, stderr=None, preexec_fn=None, close_fds=False, shell=False, cwd=None, env=None, universal_newlines=False, startupinfo=None, creationflags=0): """Create new Popen instance.""" _cleanup() self._child_created = False if not is_int_or_long(bufsize): raise TypeError("bufsize must be an integer") if mswindows: if preexec_fn is not None: raise ValueError("preexec_fn is not supported on Windows " "platforms") if close_fds: raise ValueError("close_fds is not supported on Windows " "platforms") else: # POSIX if startupinfo is not None: raise ValueError("startupinfo is only supported on Windows " "platforms") if creationflags != 0: raise ValueError("creationflags is only supported on Windows " "platforms") self.stdin = None self.stdout = None self.stderr = None self.pid = None self.returncode = None self.universal_newlines = universal_newlines # Input and output objects. The general principle is like # this: # # Parent Child # ------ ----- # p2cwrite ---stdin---> p2cread # c2pread <--stdout--- c2pwrite # errread <--stderr--- errwrite # # On POSIX, the child objects are file descriptors. On # Windows, these are Windows file handles. The parent objects # are file descriptors on both platforms. The parent objects # are None when not using PIPEs. The child objects are None # when not redirecting. (p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite) = self._get_handles(stdin, stdout, stderr) self._execute_child(args, executable, preexec_fn, close_fds, cwd, env, universal_newlines, startupinfo, creationflags, shell, p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite) if p2cwrite: self.stdin = os.fdopen(p2cwrite, 'wb', bufsize) if c2pread: if universal_newlines: self.stdout = os.fdopen(c2pread, 'rU', bufsize) else: self.stdout = os.fdopen(c2pread, 'rb', bufsize) if errread: if universal_newlines: self.stderr = os.fdopen(errread, 'rU', bufsize) else: self.stderr = os.fdopen(errread, 'rb', bufsize) def _translate_newlines(self, data): data = data.replace("\r\n", "\n") data = data.replace("\r", "\n") return data def __del__(self): if not self._child_created: # We didn't get to successfully create a child process. return # In case the child hasn't been waited on, check if it's done. self.poll(_deadstate=sys.maxsize) if self.returncode is None and _active is not None: # Child is still running, keep us alive until we can wait on it. _active.append(self) def communicate(self, input=None): """Interact with process: Send data to stdin. Read data from stdout and stderr, until end-of-file is reached. Wait for process to terminate. The optional input argument should be a string to be sent to the child process, or None, if no data should be sent to the child. communicate() returns a tuple (stdout, stderr).""" # Optimization: If we are only using one pipe, or no pipe at # all, using select() or threads is unnecessary. if [self.stdin, self.stdout, self.stderr].count(None) >= 2: stdout = None stderr = None if self.stdin: if input: self.stdin.write(input) self.stdin.close() elif self.stdout: stdout = self.stdout.read() elif self.stderr: stderr = self.stderr.read() self.wait() return (stdout, stderr) return self._communicate(input) if mswindows: # # Windows methods # def _get_handles(self, stdin, stdout, stderr): """Construct and return tupel with IO objects: p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite """ if stdin is None and stdout is None and stderr is None: return (None, None, None, None, None, None) p2cread, p2cwrite = None, None c2pread, c2pwrite = None, None errread, errwrite = None, None if stdin is None: p2cread = GetStdHandle(STD_INPUT_HANDLE) elif stdin == PIPE: p2cread, p2cwrite = CreatePipe(None, 0) # Detach and turn into fd p2cwrite = p2cwrite.Detach() p2cwrite = msvcrt.open_osfhandle(p2cwrite, 0) elif is_int(stdin): p2cread = msvcrt.get_osfhandle(stdin) else: # Assuming file-like object p2cread = msvcrt.get_osfhandle(stdin.fileno()) p2cread = self._make_inheritable(p2cread) if stdout is None: c2pwrite = GetStdHandle(STD_OUTPUT_HANDLE) elif stdout == PIPE: c2pread, c2pwrite = CreatePipe(None, 0) # Detach and turn into fd c2pread = c2pread.Detach() c2pread = msvcrt.open_osfhandle(c2pread, 0) elif is_int(stdout): c2pwrite = msvcrt.get_osfhandle(stdout) else: # Assuming file-like object c2pwrite = msvcrt.get_osfhandle(stdout.fileno()) c2pwrite = self._make_inheritable(c2pwrite) if stderr is None: errwrite = GetStdHandle(STD_ERROR_HANDLE) elif stderr == PIPE: errread, errwrite = CreatePipe(None, 0) # Detach and turn into fd errread = errread.Detach() errread = msvcrt.open_osfhandle(errread, 0) elif stderr == STDOUT: errwrite = c2pwrite elif is_int(stderr): errwrite = msvcrt.get_osfhandle(stderr) else: # Assuming file-like object errwrite = msvcrt.get_osfhandle(stderr.fileno()) errwrite = self._make_inheritable(errwrite) return (p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite) def _make_inheritable(self, handle): """Return a duplicate of handle, which is inheritable""" return DuplicateHandle(GetCurrentProcess(), handle, GetCurrentProcess(), 0, 1, DUPLICATE_SAME_ACCESS) def _find_w9xpopen(self): """Find and return absolut path to w9xpopen.exe""" w9xpopen = os.path.join(os.path.dirname(GetModuleFileName(0)), "w9xpopen.exe") if not os.path.exists(w9xpopen): # Eeek - file-not-found - possibly an embedding # situation - see if we can locate it in sys.exec_prefix w9xpopen = os.path.join(os.path.dirname(sys.exec_prefix), "w9xpopen.exe") if not os.path.exists(w9xpopen): raise RuntimeError("Cannot locate w9xpopen.exe, which is " "needed for Popen to work with your " "shell or platform.") return w9xpopen def _execute_child(self, args, executable, preexec_fn, close_fds, cwd, env, universal_newlines, startupinfo, creationflags, shell, p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite): """Execute program (MS Windows version)""" if not isinstance(args, types.StringTypes): args = list2cmdline(args) # Process startup details if startupinfo is None: startupinfo = STARTUPINFO() if None not in (p2cread, c2pwrite, errwrite): startupinfo.dwFlags = startupinfo.dwFlags | STARTF_USESTDHANDLES startupinfo.hStdInput = p2cread startupinfo.hStdOutput = c2pwrite startupinfo.hStdError = errwrite if shell: startupinfo.dwFlags = startupinfo.dwFlags | STARTF_USESHOWWINDOW startupinfo.wShowWindow = SW_HIDE comspec = os.environ.get("COMSPEC", "cmd.exe") args = comspec + " /c " + args if (GetVersion() >= 0x80000000L or os.path.basename(comspec).lower() == "command.com"): # Win9x, or using command.com on NT. We need to # use the w9xpopen intermediate program. For more # information, see KB Q150956 # (http://web.archive.org/web/20011105084002/http://support.microsoft.com/support/kb/articles/Q150/9/56.asp) w9xpopen = self._find_w9xpopen() args = '"%s" %s' % (w9xpopen, args) # Not passing CREATE_NEW_CONSOLE has been known to # cause random failures on win9x. Specifically a # dialog: "Your program accessed mem currently in # use at xxx" and a hopeful warning about the # stability of your system. Cost is Ctrl+C wont # kill children. creationflags = creationflags | CREATE_NEW_CONSOLE # Start the process try: hp, ht, pid, tid = CreateProcess(executable, args, # no special security None, None, # must inherit handles to pass std # handles 1, creationflags, env, cwd, startupinfo) except pywintypes.error, e: # Translate pywintypes.error to WindowsError, which is # a subclass of OSError. FIXME: We should really # translate errno using _sys_errlist (or simliar), but # how can this be done from Python? raise WindowsError(*e.args) # Retain the process handle, but close the thread handle self._child_created = True self._handle = hp self.pid = pid ht.Close() # Child is launched. Close the parent's copy of those pipe # handles that only the child should have open. You need # to make sure that no handles to the write end of the # output pipe are maintained in this process or else the # pipe will not close when the child process exits and the # ReadFile will hang. if p2cread is not None: p2cread.Close() if c2pwrite is not None: c2pwrite.Close() if errwrite is not None: errwrite.Close() def poll(self, _deadstate=None): """Check if child process has terminated. Returns returncode attribute.""" if self.returncode is None: if WaitForSingleObject(self._handle, 0) == WAIT_OBJECT_0: self.returncode = GetExitCodeProcess(self._handle) return self.returncode def wait(self): """Wait for child process to terminate. Returns returncode attribute.""" if self.returncode is None: obj = WaitForSingleObject(self._handle, INFINITE) self.returncode = GetExitCodeProcess(self._handle) return self.returncode def _readerthread(self, fh, buffer): buffer.append(fh.read()) def _communicate(self, input): stdout = None # Return stderr = None # Return if self.stdout: stdout = [] stdout_thread = threading.Thread(target=self._readerthread, args=(self.stdout, stdout)) stdout_thread.setDaemon(True) stdout_thread.start() if self.stderr: stderr = [] stderr_thread = threading.Thread(target=self._readerthread, args=(self.stderr, stderr)) stderr_thread.setDaemon(True) stderr_thread.start() if self.stdin: if input is not None: self.stdin.write(input) self.stdin.close() if self.stdout: stdout_thread.join() if self.stderr: stderr_thread.join() # All data exchanged. Translate lists into strings. if stdout is not None: stdout = stdout[0] if stderr is not None: stderr = stderr[0] # Translate newlines, if requested. We cannot let the file # object do the translation: It is based on stdio, which is # impossible to combine with select (unless forcing no # buffering). if self.universal_newlines and hasattr(file, 'newlines'): if stdout: stdout = self._translate_newlines(stdout) if stderr: stderr = self._translate_newlines(stderr) self.wait() return (stdout, stderr) else: # # POSIX methods # def _get_handles(self, stdin, stdout, stderr): """Construct and return tupel with IO objects: p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite """ p2cread, p2cwrite = None, None c2pread, c2pwrite = None, None errread, errwrite = None, None if stdin is None: pass elif stdin == PIPE: p2cread, p2cwrite = os.pipe() elif is_int(stdin): p2cread = stdin else: # Assuming file-like object p2cread = stdin.fileno() if stdout is None: pass elif stdout == PIPE: c2pread, c2pwrite = os.pipe() elif is_int(stdout): c2pwrite = stdout else: # Assuming file-like object c2pwrite = stdout.fileno() if stderr is None: pass elif stderr == PIPE: errread, errwrite = os.pipe() elif stderr == STDOUT: errwrite = c2pwrite elif is_int(stderr): errwrite = stderr else: # Assuming file-like object errwrite = stderr.fileno() return (p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite) def _set_cloexec_flag(self, fd): try: cloexec_flag = fcntl.FD_CLOEXEC except AttributeError: cloexec_flag = 1 old = fcntl.fcntl(fd, fcntl.F_GETFD) fcntl.fcntl(fd, fcntl.F_SETFD, old | cloexec_flag) def _close_fds(self, but): for i in range(3, MAXFD): if i == but: continue try: os.close(i) except KeyboardInterrupt: raise # SCons: don't swallow keyboard interrupts except: pass def _execute_child(self, args, executable, preexec_fn, close_fds, cwd, env, universal_newlines, startupinfo, creationflags, shell, p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite): """Execute program (POSIX version)""" if is_string(args): args = [args] if shell: args = ["/bin/sh", "-c"] + args if executable is None: executable = args[0] # For transferring possible exec failure from child to parent # The first char specifies the exception type: 0 means # OSError, 1 means some other error. errpipe_read, errpipe_write = os.pipe() self._set_cloexec_flag(errpipe_write) self.pid = os.fork() self._child_created = True if self.pid == 0: # Child try: # Close parent's pipe ends if p2cwrite: os.close(p2cwrite) if c2pread: os.close(c2pread) if errread: os.close(errread) os.close(errpipe_read) # Dup fds for child if p2cread: os.dup2(p2cread, 0) if c2pwrite: os.dup2(c2pwrite, 1) if errwrite: os.dup2(errwrite, 2) # Close pipe fds. Make sure we don't close the same # fd more than once, or standard fds. try: set except NameError: # Fall-back for earlier Python versions, so epydoc # can use this module directly to execute things. if p2cread: os.close(p2cread) if c2pwrite and c2pwrite not in (p2cread,): os.close(c2pwrite) if errwrite and errwrite not in (p2cread, c2pwrite): os.close(errwrite) else: for fd in set((p2cread, c2pwrite, errwrite))-set((0,1,2)): if fd: os.close(fd) # Close all other fds, if asked for if close_fds: self._close_fds(but=errpipe_write) if cwd is not None: os.chdir(cwd) if preexec_fn: apply(preexec_fn) if env is None: os.execvp(executable, args) else: os.execvpe(executable, args, env) except KeyboardInterrupt: raise # SCons: don't swallow keyboard interrupts except: exc_type, exc_value, tb = sys.exc_info() # Save the traceback and attach it to the exception object exc_lines = traceback.format_exception(exc_type, exc_value, tb) exc_value.child_traceback = ''.join(exc_lines) os.write(errpipe_write, pickle.dumps(exc_value)) # This exitcode won't be reported to applications, so it # really doesn't matter what we return. os._exit(255) # Parent os.close(errpipe_write) if p2cread and p2cwrite: os.close(p2cread) if c2pwrite and c2pread: os.close(c2pwrite) if errwrite and errread: os.close(errwrite) # Wait for exec to fail or succeed; possibly raising exception data = os.read(errpipe_read, 1048576) # Exceptions limited to 1 MB os.close(errpipe_read) if data != "": os.waitpid(self.pid, 0) child_exception = pickle.loads(data) raise child_exception def _handle_exitstatus(self, sts): if os.WIFSIGNALED(sts): self.returncode = -os.WTERMSIG(sts) elif os.WIFEXITED(sts): self.returncode = os.WEXITSTATUS(sts) else: # Should never happen raise RuntimeError("Unknown child exit status!") def poll(self, _deadstate=None): """Check if child process has terminated. Returns returncode attribute.""" if self.returncode is None: try: pid, sts = os.waitpid(self.pid, os.WNOHANG) if pid == self.pid: self._handle_exitstatus(sts) except os.error: if _deadstate is not None: self.returncode = _deadstate return self.returncode def wait(self): """Wait for child process to terminate. Returns returncode attribute.""" if self.returncode is None: pid, sts = os.waitpid(self.pid, 0) self._handle_exitstatus(sts) return self.returncode def _communicate(self, input): read_set = [] write_set = [] stdout = None # Return stderr = None # Return if self.stdin: # Flush stdio buffer. This might block, if the user has # been writing to .stdin in an uncontrolled fashion. self.stdin.flush() if input: write_set.append(self.stdin) else: self.stdin.close() if self.stdout: read_set.append(self.stdout) stdout = [] if self.stderr: read_set.append(self.stderr) stderr = [] input_offset = 0 while read_set or write_set: rlist, wlist, xlist = select.select(read_set, write_set, []) if self.stdin in wlist: # When select has indicated that the file is writable, # we can write up to PIPE_BUF bytes without risk # blocking. POSIX defines PIPE_BUF >= 512 m = memoryview(input)[input_offset:input_offset+512] bytes_written = os.write(self.stdin.fileno(), m) input_offset = input_offset + bytes_written if input_offset >= len(input): self.stdin.close() write_set.remove(self.stdin) if self.stdout in rlist: data = os.read(self.stdout.fileno(), 1024) if data == "": self.stdout.close() read_set.remove(self.stdout) stdout.append(data) if self.stderr in rlist: data = os.read(self.stderr.fileno(), 1024) if data == "": self.stderr.close() read_set.remove(self.stderr) stderr.append(data) # All data exchanged. Translate lists into strings. if stdout is not None: stdout = ''.join(stdout) if stderr is not None: stderr = ''.join(stderr) # Translate newlines, if requested. We cannot let the file # object do the translation: It is based on stdio, which is # impossible to combine with select (unless forcing no # buffering). if self.universal_newlines and hasattr(file, 'newlines'): if stdout: stdout = self._translate_newlines(stdout) if stderr: stderr = self._translate_newlines(stderr) self.wait() return (stdout, stderr) def _demo_posix(): # # Example 1: Simple redirection: Get process list # plist = Popen(["ps"], stdout=PIPE).communicate()[0] print "Process list:" print plist # # Example 2: Change uid before executing child # if os.getuid() == 0: p = Popen(["id"], preexec_fn=lambda: os.setuid(100)) p.wait() # # Example 3: Connecting several subprocesses # print "Looking for 'hda'..." p1 = Popen(["dmesg"], stdout=PIPE) p2 = Popen(["grep", "hda"], stdin=p1.stdout, stdout=PIPE) print repr(p2.communicate()[0]) # # Example 4: Catch execution error # print print "Trying a weird file..." try: print Popen(["/this/path/does/not/exist"]).communicate() except OSError, e: if e.errno == errno.ENOENT: print "The file didn't exist. I thought so..." print "Child traceback:" print e.child_traceback else: print "Error", e.errno else: sys.stderr.write( "Gosh. No error.\n" ) def _demo_windows(): # # Example 1: Connecting several subprocesses # print "Looking for 'PROMPT' in set output..." p1 = Popen("set", stdout=PIPE, shell=True) p2 = Popen('find "PROMPT"', stdin=p1.stdout, stdout=PIPE) print repr(p2.communicate()[0]) # # Example 2: Simple execution of program # print "Executing calc..." p = Popen("calc") p.wait() if __name__ == "__main__": if mswindows: _demo_windows() else: _demo_posix() # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/compat/_scons_sets.py0000644000175000017500000004546512114661560023472 0ustar dktrkranzdktrkranz"""Classes to represent arbitrary sets (including sets of sets). This module implements sets using dictionaries whose values are ignored. The usual operations (union, intersection, deletion, etc.) are provided as both methods and operators. Important: sets are not sequences! While they support 'x in s', 'len(s)', and 'for x in s', none of those operations are unique for sequences; for example, mappings support all three as well. The characteristic operation for sequences is subscripting with small integers: s[i], for i in range(len(s)). Sets don't support subscripting at all. Also, sequences allow multiple occurrences and their elements have a definite order; sets on the other hand don't record multiple occurrences and don't remember the order of element insertion (which is why they don't support s[i]). The following classes are provided: BaseSet -- All the operations common to both mutable and immutable sets. This is an abstract class, not meant to be directly instantiated. Set -- Mutable sets, subclass of BaseSet; not hashable. ImmutableSet -- Immutable sets, subclass of BaseSet; hashable. An iterable argument is mandatory to create an ImmutableSet. _TemporarilyImmutableSet -- A wrapper around a Set, hashable, giving the same hash value as the immutable set equivalent would have. Do not use this class directly. Only hashable objects can be added to a Set. In particular, you cannot really add a Set as an element to another Set; if you try, what is actually added is an ImmutableSet built from it (it compares equal to the one you tried adding). When you ask if `x in y' where x is a Set and y is a Set or ImmutableSet, x is wrapped into a _TemporarilyImmutableSet z, and what's tested is actually `z in y'. """ # Code history: # # - Greg V. Wilson wrote the first version, using a different approach # to the mutable/immutable problem, and inheriting from dict. # # - Alex Martelli modified Greg's version to implement the current # Set/ImmutableSet approach, and make the data an attribute. # # - Guido van Rossum rewrote much of the code, made some API changes, # and cleaned up the docstrings. # # - Raymond Hettinger added a number of speedups and other # improvements. # protect this import from the fixers... exec('from itertools import ifilterfalse as filterfalse') __all__ = ['BaseSet', 'Set', 'ImmutableSet'] class BaseSet(object): """Common base class for mutable and immutable sets.""" __slots__ = ['_data'] # Constructor def __init__(self): """This is an abstract class.""" # Don't call this from a concrete subclass! if self.__class__ is BaseSet: raise TypeError("BaseSet is an abstract class. " "Use Set or ImmutableSet.") # Standard protocols: __len__, __repr__, __str__, __iter__ def __len__(self): """Return the number of elements of a set.""" return len(self._data) def __repr__(self): """Return string representation of a set. This looks like 'Set([])'. """ return self._repr() # __str__ is the same as __repr__ __str__ = __repr__ def _repr(self, sort_them=False): elements = list(self._data.keys()) if sort_them: elements.sort() return '%s(%r)' % (self.__class__.__name__, elements) def __iter__(self): """Return an iterator over the elements or a set. This is the keys iterator for the underlying dict. """ # Wrapping name in () prevents fixer from "fixing" this return (self._data.iterkeys)() # Three-way comparison is not supported. However, because __eq__ is # tried before __cmp__, if Set x == Set y, x.__eq__(y) returns True and # then cmp(x, y) returns 0 (Python doesn't actually call __cmp__ in this # case). def __cmp__(self, other): raise TypeError("can't compare sets using cmp()") # Equality comparisons using the underlying dicts. Mixed-type comparisons # are allowed here, where Set == z for non-Set z always returns False, # and Set != z always True. This allows expressions like "x in y" to # give the expected result when y is a sequence of mixed types, not # raising a pointless TypeError just because y contains a Set, or x is # a Set and y contain's a non-set ("in" invokes only __eq__). # Subtle: it would be nicer if __eq__ and __ne__ could return # NotImplemented instead of True or False. Then the other comparand # would get a chance to determine the result, and if the other comparand # also returned NotImplemented then it would fall back to object address # comparison (which would always return False for __eq__ and always # True for __ne__). However, that doesn't work, because this type # *also* implements __cmp__: if, e.g., __eq__ returns NotImplemented, # Python tries __cmp__ next, and the __cmp__ here then raises TypeError. def __eq__(self, other): if isinstance(other, BaseSet): return self._data == other._data else: return False def __ne__(self, other): if isinstance(other, BaseSet): return self._data != other._data else: return True # Copying operations def copy(self): """Return a shallow copy of a set.""" result = self.__class__() result._data.update(self._data) return result __copy__ = copy # For the copy module def __deepcopy__(self, memo): """Return a deep copy of a set; used by copy module.""" # This pre-creates the result and inserts it in the memo # early, in case the deep copy recurses into another reference # to this same set. A set can't be an element of itself, but # it can certainly contain an object that has a reference to # itself. from copy import deepcopy result = self.__class__() memo[id(self)] = result data = result._data value = True for elt in self: data[deepcopy(elt, memo)] = value return result # Standard set operations: union, intersection, both differences. # Each has an operator version (e.g. __or__, invoked with |) and a # method version (e.g. union). # Subtle: Each pair requires distinct code so that the outcome is # correct when the type of other isn't suitable. For example, if # we did "union = __or__" instead, then Set().union(3) would return # NotImplemented instead of raising TypeError (albeit that *why* it # raises TypeError as-is is also a bit subtle). def __or__(self, other): """Return the union of two sets as a new set. (I.e. all elements that are in either set.) """ if not isinstance(other, BaseSet): return NotImplemented return self.union(other) def union(self, other): """Return the union of two sets as a new set. (I.e. all elements that are in either set.) """ result = self.__class__(self) result._update(other) return result def __and__(self, other): """Return the intersection of two sets as a new set. (I.e. all elements that are in both sets.) """ if not isinstance(other, BaseSet): return NotImplemented return self.intersection(other) def intersection(self, other): """Return the intersection of two sets as a new set. (I.e. all elements that are in both sets.) """ if not isinstance(other, BaseSet): other = Set(other) if len(self) <= len(other): little, big = self, other else: little, big = other, self common = iter(filter(big._data.has_key, little)) return self.__class__(common) def __xor__(self, other): """Return the symmetric difference of two sets as a new set. (I.e. all elements that are in exactly one of the sets.) """ if not isinstance(other, BaseSet): return NotImplemented return self.symmetric_difference(other) def symmetric_difference(self, other): """Return the symmetric difference of two sets as a new set. (I.e. all elements that are in exactly one of the sets.) """ result = self.__class__() data = result._data value = True selfdata = self._data try: otherdata = other._data except AttributeError: otherdata = Set(other)._data for elt in filterfalse(otherdata.has_key, selfdata): data[elt] = value for elt in filterfalse(selfdata.has_key, otherdata): data[elt] = value return result def __sub__(self, other): """Return the difference of two sets as a new Set. (I.e. all elements that are in this set and not in the other.) """ if not isinstance(other, BaseSet): return NotImplemented return self.difference(other) def difference(self, other): """Return the difference of two sets as a new Set. (I.e. all elements that are in this set and not in the other.) """ result = self.__class__() data = result._data try: otherdata = other._data except AttributeError: otherdata = Set(other)._data value = True for elt in filterfalse(otherdata.has_key, self): data[elt] = value return result # Membership test def __contains__(self, element): """Report whether an element is a member of a set. (Called in response to the expression `element in self'.) """ try: return element in self._data except TypeError: transform = getattr(element, "__as_temporarily_immutable__", None) if transform is None: raise # re-raise the TypeError exception we caught return transform() in self._data # Subset and superset test def issubset(self, other): """Report whether another set contains this set.""" self._binary_sanity_check(other) if len(self) > len(other): # Fast check for obvious cases return False for elt in filterfalse(other._data.has_key, self): return False return True def issuperset(self, other): """Report whether this set contains another set.""" self._binary_sanity_check(other) if len(self) < len(other): # Fast check for obvious cases return False for elt in filterfalse(self._data.has_key, other): return False return True # Inequality comparisons using the is-subset relation. __le__ = issubset __ge__ = issuperset def __lt__(self, other): self._binary_sanity_check(other) return len(self) < len(other) and self.issubset(other) def __gt__(self, other): self._binary_sanity_check(other) return len(self) > len(other) and self.issuperset(other) # Assorted helpers def _binary_sanity_check(self, other): # Check that the other argument to a binary operation is also # a set, raising a TypeError otherwise. if not isinstance(other, BaseSet): raise TypeError("Binary operation only permitted between sets") def _compute_hash(self): # Calculate hash code for a set by xor'ing the hash codes of # the elements. This ensures that the hash code does not depend # on the order in which elements are added to the set. This is # not called __hash__ because a BaseSet should not be hashable; # only an ImmutableSet is hashable. result = 0 for elt in self: result ^= hash(elt) return result def _update(self, iterable): # The main loop for update() and the subclass __init__() methods. data = self._data # Use the fast update() method when a dictionary is available. if isinstance(iterable, BaseSet): data.update(iterable._data) return value = True if type(iterable) in (list, tuple, xrange): # Optimized: we know that __iter__() and next() can't # raise TypeError, so we can move 'try:' out of the loop. it = iter(iterable) while True: try: for element in it: data[element] = value return except TypeError: transform = getattr(element, "__as_immutable__", None) if transform is None: raise # re-raise the TypeError exception we caught data[transform()] = value else: # Safe: only catch TypeError where intended for element in iterable: try: data[element] = value except TypeError: transform = getattr(element, "__as_immutable__", None) if transform is None: raise # re-raise the TypeError exception we caught data[transform()] = value class ImmutableSet(BaseSet): """Immutable set class.""" __slots__ = ['_hashcode'] # BaseSet + hashing def __init__(self, iterable=None): """Construct an immutable set from an optional iterable.""" self._hashcode = None self._data = {} if iterable is not None: self._update(iterable) def __hash__(self): if self._hashcode is None: self._hashcode = self._compute_hash() return self._hashcode def __getstate__(self): return self._data, self._hashcode def __setstate__(self, state): self._data, self._hashcode = state class Set(BaseSet): """ Mutable set class.""" __slots__ = [] # BaseSet + operations requiring mutability; no hashing def __init__(self, iterable=None): """Construct a set from an optional iterable.""" self._data = {} if iterable is not None: self._update(iterable) def __getstate__(self): # getstate's results are ignored if it is not return self._data, def __setstate__(self, data): self._data, = data def __hash__(self): """A Set cannot be hashed.""" # We inherit object.__hash__, so we must deny this explicitly raise TypeError("Can't hash a Set, only an ImmutableSet.") # In-place union, intersection, differences. # Subtle: The xyz_update() functions deliberately return None, # as do all mutating operations on built-in container types. # The __xyz__ spellings have to return self, though. def __ior__(self, other): """Update a set with the union of itself and another.""" self._binary_sanity_check(other) self._data.update(other._data) return self def union_update(self, other): """Update a set with the union of itself and another.""" self._update(other) def __iand__(self, other): """Update a set with the intersection of itself and another.""" self._binary_sanity_check(other) self._data = (self & other)._data return self def intersection_update(self, other): """Update a set with the intersection of itself and another.""" if isinstance(other, BaseSet): self &= other else: self._data = (self.intersection(other))._data def __ixor__(self, other): """Update a set with the symmetric difference of itself and another.""" self._binary_sanity_check(other) self.symmetric_difference_update(other) return self def symmetric_difference_update(self, other): """Update a set with the symmetric difference of itself and another.""" data = self._data value = True if not isinstance(other, BaseSet): other = Set(other) if self is other: self.clear() for elt in other: if elt in data: del data[elt] else: data[elt] = value def __isub__(self, other): """Remove all elements of another set from this set.""" self._binary_sanity_check(other) self.difference_update(other) return self def difference_update(self, other): """Remove all elements of another set from this set.""" data = self._data if not isinstance(other, BaseSet): other = Set(other) if self is other: self.clear() for elt in filter(data.has_key, other): del data[elt] # Python dict-like mass mutations: update, clear def update(self, iterable): """Add all values from an iterable (such as a list or file).""" self._update(iterable) def clear(self): """Remove all elements from this set.""" self._data.clear() # Single-element mutations: add, remove, discard def add(self, element): """Add an element to a set. This has no effect if the element is already present. """ try: self._data[element] = True except TypeError: transform = getattr(element, "__as_immutable__", None) if transform is None: raise # re-raise the TypeError exception we caught self._data[transform()] = True def remove(self, element): """Remove an element from a set; it must be a member. If the element is not a member, raise a KeyError. """ try: del self._data[element] except TypeError: transform = getattr(element, "__as_temporarily_immutable__", None) if transform is None: raise # re-raise the TypeError exception we caught del self._data[transform()] def discard(self, element): """Remove an element from a set if it is a member. If the element is not a member, do nothing. """ try: self.remove(element) except KeyError: pass def pop(self): """Remove and return an arbitrary set element.""" return self._data.popitem()[0] def __as_immutable__(self): # Return a copy of self as an immutable set return ImmutableSet(self) def __as_temporarily_immutable__(self): # Return self wrapped in a temporarily immutable set return _TemporarilyImmutableSet(self) class _TemporarilyImmutableSet(BaseSet): # Wrap a mutable set as if it was temporarily immutable. # This only supplies hashing and equality comparisons. def __init__(self, set): self._set = set self._data = set._data # Needed by ImmutableSet.__eq__() def __hash__(self): return self._set._compute_hash() # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/compat/_scons_builtins.py0000644000175000017500000000711512114661560024333 0ustar dktrkranzdktrkranz# # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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. # # Portions of the following are derived from the compat.py file in # Twisted, under the following copyright: # # Copyright (c) 2001-2004 Twisted Matrix Laboratories __doc__ = """ Compatibility idioms for builtins names This module adds names to the builtins module for things that we want to use in SCons but which don't show up until later Python versions than the earliest ones we support. This module checks for the following builtins names: all() any() memoryview() Implementations of functions are *NOT* guaranteed to be fully compliant with these functions in later versions of Python. We are only concerned with adding functionality that we actually use in SCons, so be wary if you lift this code for other uses. (That said, making these more nearly the same as later, official versions is still a desirable goal, we just don't need to be obsessive about it.) If you're looking at this with pydoc and various names don't show up in the FUNCTIONS or DATA output, that means those names are already built in to this version of Python and we don't need to add them from this module. """ __revision__ = "src/engine/SCons/compat/_scons_builtins.py 2013/03/03 09:48:35 garyo" import builtins try: all except NameError: # Pre-2.5 Python has no all() function. def all(iterable): """ Returns True if all elements of the iterable are true. """ for element in iterable: if not element: return False return True builtins.all = all all = all try: any except NameError: # Pre-2.5 Python has no any() function. def any(iterable): """ Returns True if any element of the iterable is true. """ for element in iterable: if element: return True return False builtins.any = any any = any try: memoryview except NameError: # Pre-2.7 doesn't have the memoryview() built-in. class memoryview(object): def __init__(self, obj): # wrapping buffer in () keeps the fixer from changing it self.obj = (buffer)(obj) def __getitem__(self, indx): if isinstance(indx, slice): return self.obj[indx.start:indx.stop] else: return self.obj[indx] builtins.memoryview = memoryview # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/compat/_scons_dbm.py0000644000175000017500000000341712114661560023245 0ustar dktrkranzdktrkranz# # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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. # __doc__ = """ dbm compatibility module for Python versions that don't have dbm. This does not not NOT (repeat, *NOT*) provide complete dbm functionality. It's just a stub on which to hang just enough pieces of dbm functionality that the whichdb.whichdb() implementstation in the various 2.X versions of Python won't blow up even if dbm wasn't compiled in. """ __revision__ = "src/engine/SCons/compat/_scons_dbm.py 2013/03/03 09:48:35 garyo" class error(Exception): pass def open(*args, **kw): raise error() # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/compat/_scons_io.py0000644000175000017500000000346112114661560023111 0ustar dktrkranzdktrkranz# # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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. # __doc__ = """ io compatibility module for older (pre-2.6) Python versions This does not not NOT (repeat, *NOT*) provide complete io functionality. It only wraps the portions of io functionality used by SCons, in an interface that looks enough like io for our purposes. """ __revision__ = "src/engine/SCons/compat/_scons_io.py 2013/03/03 09:48:35 garyo" # Use the "imp" module to protect the imports below from fixers. import imp _cStringIO = imp.load_module('cStringIO', *imp.find_module('cStringIO')) StringIO = _cStringIO.StringIO del _cStringIO # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/compat/__init__.py0000644000175000017500000001777112114661560022706 0ustar dktrkranzdktrkranz# # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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. # __doc__ = """ SCons compatibility package for old Python versions This subpackage holds modules that provide backwards-compatible implementations of various things that we'd like to use in SCons but which only show up in later versions of Python than the early, old version(s) we still support. Other code will not generally reference things in this package through the SCons.compat namespace. The modules included here add things to the builtins namespace or the global module list so that the rest of our code can use the objects and names imported here regardless of Python version. Simply enough, things that go in the builtins name space come from our _scons_builtins module. The rest of the things here will be in individual compatibility modules that are either: 1) suitably modified copies of the future modules that we want to use; or 2) backwards compatible re-implementations of the specific portions of a future module's API that we want to use. GENERAL WARNINGS: Implementations of functions in the SCons.compat modules are *NOT* guaranteed to be fully compliant with these functions in later versions of Python. We are only concerned with adding functionality that we actually use in SCons, so be wary if you lift this code for other uses. (That said, making these more nearly the same as later, official versions is still a desirable goal, we just don't need to be obsessive about it.) We name the compatibility modules with an initial '_scons_' (for example, _scons_subprocess.py is our compatibility module for subprocess) so that we can still try to import the real module name and fall back to our compatibility module if we get an ImportError. The import_as() function defined below loads the module as the "real" name (without the '_scons'), after which all of the "import {module}" statements in the rest of our code will find our pre-loaded compatibility module. """ __revision__ = "src/engine/SCons/compat/__init__.py 2013/03/03 09:48:35 garyo" import os import sys import imp # Use the "imp" module to protect imports from fixers. def import_as(module, name): """ Imports the specified module (from our local directory) as the specified name, returning the loaded module object. """ dir = os.path.split(__file__)[0] return imp.load_module(name, *imp.find_module(module, [dir])) def rename_module(new, old): """ Attempts to import the old module and load it under the new name. Used for purely cosmetic name changes in Python 3.x. """ try: sys.modules[new] = imp.load_module(old, *imp.find_module(old)) return True except ImportError: return False rename_module('builtins', '__builtin__') import _scons_builtins try: import hashlib except ImportError: # Pre-2.5 Python has no hashlib module. try: import_as('_scons_hashlib', 'hashlib') except ImportError: # If we failed importing our compatibility module, it probably # means this version of Python has no md5 module. Don't do # anything and let the higher layer discover this fact, so it # can fall back to using timestamp. pass try: set except NameError: # Pre-2.4 Python has no native set type import_as('_scons_sets', 'sets') import builtins, sets builtins.set = sets.Set try: import collections except ImportError: # Pre-2.4 Python has no collections module. import_as('_scons_collections', 'collections') else: try: collections.UserDict except AttributeError: exec('from UserDict import UserDict as _UserDict') collections.UserDict = _UserDict del _UserDict try: collections.UserList except AttributeError: exec('from UserList import UserList as _UserList') collections.UserList = _UserList del _UserList try: collections.UserString except AttributeError: exec('from UserString import UserString as _UserString') collections.UserString = _UserString del _UserString try: import io except ImportError: # Pre-2.6 Python has no io module. import_as('_scons_io', 'io') try: os.devnull except AttributeError: # Pre-2.4 Python has no os.devnull attribute _names = sys.builtin_module_names if 'posix' in _names: os.devnull = '/dev/null' elif 'nt' in _names: os.devnull = 'nul' os.path.devnull = os.devnull try: os.path.lexists except AttributeError: # Pre-2.4 Python has no os.path.lexists function def lexists(path): return os.path.exists(path) or os.path.islink(path) os.path.lexists = lexists # When we're using the '-3' option during regression tests, importing # cPickle gives a warning no matter how it's done, so always use the # real profile module, whether it's fast or not. if os.environ.get('SCONS_HORRIBLE_REGRESSION_TEST_HACK') is None: # Not a regression test with '-3', so try to use faster version. # In 3.x, 'pickle' automatically loads the fast version if available. rename_module('pickle', 'cPickle') # In 3.x, 'profile' automatically loads the fast version if available. rename_module('profile', 'cProfile') # Before Python 3.0, the 'queue' module was named 'Queue'. rename_module('queue', 'Queue') # Before Python 3.0, the 'winreg' module was named '_winreg' rename_module('winreg', '_winreg') try: import subprocess except ImportError: # Pre-2.4 Python has no subprocess module. import_as('_scons_subprocess', 'subprocess') try: sys.intern except AttributeError: # Pre-2.6 Python has no sys.intern() function. import builtins try: sys.intern = builtins.intern except AttributeError: # Pre-2.x Python has no builtin intern() function. def intern(x): return x sys.intern = intern del intern try: sys.maxsize except AttributeError: # Pre-2.6 Python has no sys.maxsize attribute # Wrapping sys in () is silly, but protects it from 2to3 renames fixer sys.maxsize = (sys).maxint if os.environ.get('SCONS_HORRIBLE_REGRESSION_TEST_HACK') is not None: # We can't apply the 'callable' fixer until the floor is 2.6, but the # '-3' option to Python 2.6 and 2.7 generates almost ten thousand # warnings. This hack allows us to run regression tests with the '-3' # option by replacing the callable() built-in function with a hack # that performs the same function but doesn't generate the warning. # Note that this hack is ONLY intended to be used for regression # testing, and should NEVER be used for real runs. from types import ClassType def callable(obj): if hasattr(obj, '__call__'): return True if isinstance(obj, (ClassType, type)): return True return False import builtins builtins.callable = callable del callable # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/SConsign.py0000644000175000017500000003121512114661557021402 0ustar dktrkranzdktrkranz"""SCons.SConsign Writing and reading information to the .sconsign file or files. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/SConsign.py 2013/03/03 09:48:35 garyo" import SCons.compat import os # compat layer imports "cPickle" for us if it's available. import pickle import SCons.dblite import SCons.Warnings def corrupt_dblite_warning(filename): SCons.Warnings.warn(SCons.Warnings.CorruptSConsignWarning, "Ignoring corrupt .sconsign file: %s"%filename) SCons.dblite.ignore_corrupt_dbfiles = 1 SCons.dblite.corruption_warning = corrupt_dblite_warning #XXX Get rid of the global array so this becomes re-entrant. sig_files = [] # Info for the database SConsign implementation (now the default): # "DataBase" is a dictionary that maps top-level SConstruct directories # to open database handles. # "DB_Module" is the Python database module to create the handles. # "DB_Name" is the base name of the database file (minus any # extension the underlying DB module will add). DataBase = {} DB_Module = SCons.dblite DB_Name = ".sconsign" DB_sync_list = [] def Get_DataBase(dir): global DataBase, DB_Module, DB_Name top = dir.fs.Top if not os.path.isabs(DB_Name) and top.repositories: mode = "c" for d in [top] + top.repositories: if dir.is_under(d): try: return DataBase[d], mode except KeyError: path = d.entry_abspath(DB_Name) try: db = DataBase[d] = DB_Module.open(path, mode) except (IOError, OSError): pass else: if mode != "r": DB_sync_list.append(db) return db, mode mode = "r" try: return DataBase[top], "c" except KeyError: db = DataBase[top] = DB_Module.open(DB_Name, "c") DB_sync_list.append(db) return db, "c" except TypeError: print "DataBase =", DataBase raise def Reset(): """Reset global state. Used by unit tests that end up using SConsign multiple times to get a clean slate for each test.""" global sig_files, DB_sync_list sig_files = [] DB_sync_list = [] normcase = os.path.normcase def write(): global sig_files for sig_file in sig_files: sig_file.write(sync=0) for db in DB_sync_list: try: syncmethod = db.sync except AttributeError: pass # Not all dbm modules have sync() methods. else: syncmethod() try: closemethod = db.close except AttributeError: pass # Not all dbm modules have close() methods. else: closemethod() class SConsignEntry(object): """ Wrapper class for the generic entry in a .sconsign file. The Node subclass populates it with attributes as it pleases. XXX As coded below, we do expect a '.binfo' attribute to be added, but we'll probably generalize this in the next refactorings. """ current_version_id = 1 def __init__(self): # Create an object attribute from the class attribute so it ends up # in the pickled data in the .sconsign file. _version_id = self.current_version_id def convert_to_sconsign(self): self.binfo.convert_to_sconsign() def convert_from_sconsign(self, dir, name): self.binfo.convert_from_sconsign(dir, name) class Base(object): """ This is the controlling class for the signatures for the collection of entries associated with a specific directory. The actual directory association will be maintained by a subclass that is specific to the underlying storage method. This class provides a common set of methods for fetching and storing the individual bits of information that make up signature entry. """ def __init__(self): self.entries = {} self.dirty = False self.to_be_merged = {} def get_entry(self, filename): """ Fetch the specified entry attribute. """ return self.entries[filename] def set_entry(self, filename, obj): """ Set the entry. """ self.entries[filename] = obj self.dirty = True def do_not_set_entry(self, filename, obj): pass def store_info(self, filename, node): entry = node.get_stored_info() entry.binfo.merge(node.get_binfo()) self.to_be_merged[filename] = node self.dirty = True def do_not_store_info(self, filename, node): pass def merge(self): for key, node in self.to_be_merged.items(): entry = node.get_stored_info() try: ninfo = entry.ninfo except AttributeError: # This happens with SConf Nodes, because the configuration # subsystem takes direct control over how the build decision # is made and its information stored. pass else: ninfo.merge(node.get_ninfo()) self.entries[key] = entry self.to_be_merged = {} class DB(Base): """ A Base subclass that reads and writes signature information from a global .sconsign.db* file--the actual file suffix is determined by the database module. """ def __init__(self, dir): Base.__init__(self) self.dir = dir db, mode = Get_DataBase(dir) # Read using the path relative to the top of the Repository # (self.dir.tpath) from which we're fetching the signature # information. path = normcase(dir.tpath) try: rawentries = db[path] except KeyError: pass else: try: self.entries = pickle.loads(rawentries) if not isinstance(self.entries, dict): self.entries = {} raise TypeError except KeyboardInterrupt: raise except Exception, e: SCons.Warnings.warn(SCons.Warnings.CorruptSConsignWarning, "Ignoring corrupt sconsign entry : %s (%s)\n"%(self.dir.tpath, e)) for key, entry in self.entries.items(): entry.convert_from_sconsign(dir, key) if mode == "r": # This directory is actually under a repository, which means # likely they're reaching in directly for a dependency on # a file there. Don't actually set any entry info, so we # won't try to write to that .sconsign.dblite file. self.set_entry = self.do_not_set_entry self.store_info = self.do_not_store_info global sig_files sig_files.append(self) def write(self, sync=1): if not self.dirty: return self.merge() db, mode = Get_DataBase(self.dir) # Write using the path relative to the top of the SConstruct # directory (self.dir.path), not relative to the top of # the Repository; we only write to our own .sconsign file, # not to .sconsign files in Repositories. path = normcase(self.dir.path) for key, entry in self.entries.items(): entry.convert_to_sconsign() db[path] = pickle.dumps(self.entries, 1) if sync: try: syncmethod = db.sync except AttributeError: # Not all anydbm modules have sync() methods. pass else: syncmethod() class Dir(Base): def __init__(self, fp=None, dir=None): """ fp - file pointer to read entries from """ Base.__init__(self) if not fp: return self.entries = pickle.load(fp) if not isinstance(self.entries, dict): self.entries = {} raise TypeError if dir: for key, entry in self.entries.items(): entry.convert_from_sconsign(dir, key) class DirFile(Dir): """ Encapsulates reading and writing a per-directory .sconsign file. """ def __init__(self, dir): """ dir - the directory for the file """ self.dir = dir self.sconsign = os.path.join(dir.path, '.sconsign') try: fp = open(self.sconsign, 'rb') except IOError: fp = None try: Dir.__init__(self, fp, dir) except KeyboardInterrupt: raise except: SCons.Warnings.warn(SCons.Warnings.CorruptSConsignWarning, "Ignoring corrupt .sconsign file: %s"%self.sconsign) global sig_files sig_files.append(self) def write(self, sync=1): """ Write the .sconsign file to disk. Try to write to a temporary file first, and rename it if we succeed. If we can't write to the temporary file, it's probably because the directory isn't writable (and if so, how did we build anything in this directory, anyway?), so try to write directly to the .sconsign file as a backup. If we can't rename, try to copy the temporary contents back to the .sconsign file. Either way, always try to remove the temporary file at the end. """ if not self.dirty: return self.merge() temp = os.path.join(self.dir.path, '.scons%d' % os.getpid()) try: file = open(temp, 'wb') fname = temp except IOError: try: file = open(self.sconsign, 'wb') fname = self.sconsign except IOError: return for key, entry in self.entries.items(): entry.convert_to_sconsign() pickle.dump(self.entries, file, 1) file.close() if fname != self.sconsign: try: mode = os.stat(self.sconsign)[0] os.chmod(self.sconsign, 0666) os.unlink(self.sconsign) except (IOError, OSError): # Try to carry on in the face of either OSError # (things like permission issues) or IOError (disk # or network issues). If there's a really dangerous # issue, it should get re-raised by the calls below. pass try: os.rename(fname, self.sconsign) except OSError: # An OSError failure to rename may indicate something # like the directory has no write permission, but # the .sconsign file itself might still be writable, # so try writing on top of it directly. An IOError # here, or in any of the following calls, would get # raised, indicating something like a potentially # serious disk or network issue. open(self.sconsign, 'wb').write(open(fname, 'rb').read()) os.chmod(self.sconsign, mode) try: os.unlink(temp) except (IOError, OSError): pass ForDirectory = DB def File(name, dbm_module=None): """ Arrange for all signatures to be stored in a global .sconsign.db* file. """ global ForDirectory, DB_Name, DB_Module if name is None: ForDirectory = DirFile DB_Module = None else: ForDirectory = DB DB_Name = name if not dbm_module is None: DB_Module = dbm_module # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/SubstTests.py0000644000175000017500000012513612114661557022010 0ustar dktrkranzdktrkranz# # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/SubstTests.py 2013/03/03 09:48:35 garyo" import SCons.compat import os import sys import unittest from collections import UserDict import SCons.Errors from SCons.Subst import * class DummyNode(object): """Simple node work-alike.""" def __init__(self, name): self.name = os.path.normpath(name) def __str__(self): return self.name def is_literal(self): return 1 def rfile(self): return self def get_subst_proxy(self): return self class DummyEnv(object): def __init__(self, dict={}): self.dict = dict def Dictionary(self, key = None): if not key: return self.dict return self.dict[key] def __getitem__(self, key): return self.dict[key] def get(self, key, default): return self.dict.get(key, default) def sig_dict(self): dict = self.dict.copy() dict["TARGETS"] = 'tsig' dict["SOURCES"] = 'ssig' return dict def cs(target=None, source=None, env=None, for_signature=None): return 'cs' def cl(target=None, source=None, env=None, for_signature=None): return ['cl'] def CmdGen1(target, source, env, for_signature): # Nifty trick...since Environment references are interpolated, # instantiate an instance of a callable class with this one, # which will then get evaluated. assert str(target) == 't', target assert str(source) == 's', source return "${CMDGEN2('foo', %d)}" % for_signature class CmdGen2(object): def __init__(self, mystr, forsig): self.mystr = mystr self.expect_for_signature = forsig def __call__(self, target, source, env, for_signature): assert str(target) == 't', target assert str(source) == 's', source assert for_signature == self.expect_for_signature, for_signature return [ self.mystr, env.Dictionary('BAR') ] if os.sep == '/': def cvt(str): return str else: def cvt(str): return str.replace('/', os.sep) class SubstTestCase(unittest.TestCase): class MyNode(DummyNode): """Simple node work-alike with some extra stuff for testing.""" def __init__(self, name): DummyNode.__init__(self, name) class Attribute(object): pass self.attribute = Attribute() self.attribute.attr1 = 'attr$1-' + os.path.basename(name) self.attribute.attr2 = 'attr$2-' + os.path.basename(name) def get_stuff(self, extra): return self.name + extra foo = 1 class TestLiteral(object): def __init__(self, literal): self.literal = literal def __str__(self): return self.literal def is_literal(self): return 1 class TestCallable(object): def __init__(self, value): self.value = value def __call__(self): pass def __str__(self): return self.value def function_foo(arg): pass target = [ MyNode("./foo/bar.exe"), MyNode("/bar/baz with spaces.obj"), MyNode("../foo/baz.obj") ] source = [ MyNode("./foo/blah with spaces.cpp"), MyNode("/bar/ack.cpp"), MyNode("../foo/ack.c") ] callable_object_1 = TestCallable('callable-1') callable_object_2 = TestCallable('callable-2') def _defines(defs): l = [] for d in defs: if SCons.Util.is_List(d) or isinstance(d, tuple): l.append(str(d[0]) + '=' + str(d[1])) else: l.append(str(d)) return l loc = { 'xxx' : None, 'NEWLINE' : 'before\nafter', 'null' : '', 'zero' : 0, 'one' : 1, 'BAZ' : 'baz', 'ONE' : '$TWO', 'TWO' : '$THREE', 'THREE' : 'four', 'AAA' : 'a', 'BBB' : 'b', 'CCC' : 'c', 'DO' : DummyNode('do something'), 'FOO' : DummyNode('foo.in'), 'BAR' : DummyNode('bar with spaces.out'), 'CRAZY' : DummyNode('crazy\nfile.in'), # $XXX$HHH should expand to GGGIII, not BADNEWS. 'XXX' : '$FFF', 'FFF' : 'GGG', 'HHH' : 'III', 'FFFIII' : 'BADNEWS', 'LITERAL' : TestLiteral("$XXX"), # Test that we can expand to and return a function. #'FUNCTION' : function_foo, 'CMDGEN1' : CmdGen1, 'CMDGEN2' : CmdGen2, 'LITERALS' : [ Literal('foo\nwith\nnewlines'), Literal('bar\nwith\nnewlines') ], 'NOTHING' : "", 'NONE' : None, # Test various combinations of strings, lists and functions. 'N' : None, 'X' : 'x', 'Y' : '$X', 'R' : '$R', 'S' : 'x y', 'LS' : ['x y'], 'L' : ['x', 'y'], 'TS' : ('x y'), 'T' : ('x', 'y'), 'CS' : cs, 'CL' : cl, 'US' : collections.UserString('us'), # Test function calls within ${}. 'FUNCCALL' : '${FUNC1("$AAA $FUNC2 $BBB")}', 'FUNC1' : lambda x: x, 'FUNC2' : lambda target, source, env, for_signature: ['x$CCC'], # Various tests refactored from ActionTests.py. 'LIST' : [["This", "is", "$(", "$a", "$)", "test"]], # Test recursion. 'RECURSE' : 'foo $RECURSE bar', 'RRR' : 'foo $SSS bar', 'SSS' : '$RRR', # Test callables that don't match the calling arguments. 'CALLABLE1' : callable_object_1, 'CALLABLE2' : callable_object_2, '_defines' : _defines, 'DEFS' : [ ('Q1', '"q1"'), ('Q2', '"$AAA"') ], } def basic_comparisons(self, function, convert): env = DummyEnv(self.loc) cases = self.basic_cases[:] kwargs = {'target' : self.target, 'source' : self.source, 'gvars' : env.Dictionary()} failed = 0 while cases: input, expect = cases[:2] expect = convert(expect) try: result = function(input, env, **kwargs) except Exception, e: fmt = " input %s generated %s (%s)" print fmt % (repr(input), e.__class__.__name__, repr(e)) failed = failed + 1 else: if result != expect: if failed == 0: print print " input %s => %s did not match %s" % (repr(input), repr(result), repr(expect)) failed = failed + 1 del cases[:2] fmt = "%d %s() cases failed" assert failed == 0, fmt % (failed, function.__name__) class scons_subst_TestCase(SubstTestCase): # Basic tests of substitution functionality. basic_cases = [ # Basics: strings without expansions are left alone, and # the simplest possible expansion to a null-string value. "test", "test", "$null", "", # Test expansion of integer values. "test $zero", "test 0", "test $one", "test 1", # Test multiple re-expansion of values. "test $ONE", "test four", # Test a whole bunch of $TARGET[S] and $SOURCE[S] expansions. "test $TARGETS $SOURCES", "test foo/bar.exe /bar/baz with spaces.obj ../foo/baz.obj foo/blah with spaces.cpp /bar/ack.cpp ../foo/ack.c", "test ${TARGETS[:]} ${SOURCES[0]}", "test foo/bar.exe /bar/baz with spaces.obj ../foo/baz.obj foo/blah with spaces.cpp", "test ${TARGETS[1:]}v", "test /bar/baz with spaces.obj ../foo/baz.objv", "test $TARGET", "test foo/bar.exe", "test $TARGET$NO_SUCH_VAR[0]", "test foo/bar.exe[0]", "test $TARGETS.foo", "test 1 1 1", "test ${SOURCES[0:2].foo}", "test 1 1", "test $SOURCE.foo", "test 1", "test ${TARGET.get_stuff('blah')}", "test foo/bar.exeblah", "test ${SOURCES.get_stuff('blah')}", "test foo/blah with spaces.cppblah /bar/ack.cppblah ../foo/ack.cblah", "test ${SOURCES[0:2].get_stuff('blah')}", "test foo/blah with spaces.cppblah /bar/ack.cppblah", "test ${SOURCES[0:2].get_stuff('blah')}", "test foo/blah with spaces.cppblah /bar/ack.cppblah", "test ${SOURCES.attribute.attr1}", "test attr$1-blah with spaces.cpp attr$1-ack.cpp attr$1-ack.c", "test ${SOURCES.attribute.attr2}", "test attr$2-blah with spaces.cpp attr$2-ack.cpp attr$2-ack.c", # Test adjacent expansions. "foo$BAZ", "foobaz", "foo${BAZ}", "foobaz", # Test that adjacent expansions don't get re-interpreted # together. The correct disambiguated expansion should be: # $XXX$HHH => ${FFF}III => GGGIII # not: # $XXX$HHH => ${FFFIII} => BADNEWS "$XXX$HHH", "GGGIII", # Test double-dollar-sign behavior. "$$FFF$HHH", "$FFFIII", # Test that a Literal will stop dollar-sign substitution. "$XXX $LITERAL $FFF", "GGG $XXX GGG", # Test that we don't blow up even if they subscript # something in ways they "can't." "${FFF[0]}", "G", "${FFF[7]}", "", "${NOTHING[1]}", "", # Test various combinations of strings and lists. #None, '', '', '', 'x', 'x', 'x y', 'x y', '$N', '', '$X', 'x', '$Y', 'x', '$R', '', '$S', 'x y', '$LS', 'x y', '$L', 'x y', '$TS', 'x y', '$T', 'x y', '$S z', 'x y z', '$LS z', 'x y z', '$L z', 'x y z', '$TS z', 'x y z', '$T z', 'x y z', #cs, 'cs', #cl, 'cl', '$CS', 'cs', '$CL', 'cl', # Various uses of UserString. collections.UserString('x'), 'x', collections.UserString('$X'), 'x', collections.UserString('$US'), 'us', '$US', 'us', # Test function calls within ${}. '$FUNCCALL', 'a xc b', # Bug reported by Christoph Wiedemann. cvt('$xxx/bin'), '/bin', # Tests callables that don't match our calling arguments. '$CALLABLE1', 'callable-1', # Test handling of quotes. 'aaa "bbb ccc" ddd', 'aaa "bbb ccc" ddd', ] def test_scons_subst(self): """Test scons_subst(): basic substitution""" return self.basic_comparisons(scons_subst, cvt) subst_cases = [ "test $xxx", "test ", "test", "test", "test $($xxx$)", "test $($)", "test", "test", "test $( $xxx $)", "test $( $)", "test", "test", "$AAA ${AAA}A $BBBB $BBB", "a aA b", "a aA b", "a aA b", "$RECURSE", "foo bar", "foo bar", "foo bar", "$RRR", "foo bar", "foo bar", "foo bar", # Verify what happens with no target or source nodes. "$TARGET $SOURCES", " ", "", "", "$TARGETS $SOURCE", " ", "", "", # Various tests refactored from ActionTests.py. "${LIST}", "This is $( $) test", "This is test", "This is test", ["|", "$(", "$AAA", "|", "$BBB", "$)", "|", "$CCC", 1], ["|", "$(", "a", "|", "b", "$)", "|", "c", "1"], ["|", "a", "|", "b", "|", "c", "1"], ["|", "|", "c", "1"], ] def test_subst_env(self): """Test scons_subst(): expansion dictionary""" # The expansion dictionary no longer comes from the construction # environment automatically. env = DummyEnv(self.loc) s = scons_subst('$AAA', env) assert s == '', s def test_subst_SUBST_modes(self): """Test scons_subst(): SUBST_* modes""" env = DummyEnv(self.loc) subst_cases = self.subst_cases[:] gvars = env.Dictionary() failed = 0 while subst_cases: input, eraw, ecmd, esig = subst_cases[:4] result = scons_subst(input, env, mode=SUBST_RAW, gvars=gvars) if result != eraw: if failed == 0: print print " input %s => RAW %s did not match %s" % (repr(input), repr(result), repr(eraw)) failed = failed + 1 result = scons_subst(input, env, mode=SUBST_CMD, gvars=gvars) if result != ecmd: if failed == 0: print print " input %s => CMD %s did not match %s" % (repr(input), repr(result), repr(ecmd)) failed = failed + 1 result = scons_subst(input, env, mode=SUBST_SIG, gvars=gvars) if result != esig: if failed == 0: print print " input %s => SIG %s did not match %s" % (repr(input), repr(result), repr(esig)) failed = failed + 1 del subst_cases[:4] assert failed == 0, "%d subst() mode cases failed" % failed def test_subst_target_source(self): """Test scons_subst(): target= and source= arguments""" env = DummyEnv(self.loc) t1 = self.MyNode('t1') t2 = self.MyNode('t2') s1 = self.MyNode('s1') s2 = self.MyNode('s2') result = scons_subst("$TARGET $SOURCES", env, target=[t1, t2], source=[s1, s2]) assert result == "t1 s1 s2", result result = scons_subst("$TARGET $SOURCES", env, target=[t1, t2], source=[s1, s2], gvars={}) assert result == "t1 s1 s2", result result = scons_subst("$TARGET $SOURCES", env, target=[], source=[]) assert result == " ", result result = scons_subst("$TARGETS $SOURCE", env, target=[], source=[]) assert result == " ", result def test_subst_callable_expansion(self): """Test scons_subst(): expanding a callable""" env = DummyEnv(self.loc) gvars = env.Dictionary() newcom = scons_subst("test $CMDGEN1 $SOURCES $TARGETS", env, target=self.MyNode('t'), source=self.MyNode('s'), gvars=gvars) assert newcom == "test foo bar with spaces.out s t", newcom def test_subst_attribute_errors(self): """Test scons_subst(): handling attribute errors""" env = DummyEnv(self.loc) try: class Foo(object): pass scons_subst('${foo.bar}', env, gvars={'foo':Foo()}) except SCons.Errors.UserError, e: expect = [ "AttributeError `bar' trying to evaluate `${foo.bar}'", "AttributeError `Foo instance has no attribute 'bar'' trying to evaluate `${foo.bar}'", "AttributeError `'Foo' instance has no attribute 'bar'' trying to evaluate `${foo.bar}'", "AttributeError `'Foo' object has no attribute 'bar'' trying to evaluate `${foo.bar}'", ] assert str(e) in expect, e else: raise AssertionError("did not catch expected UserError") def test_subst_syntax_errors(self): """Test scons_subst(): handling syntax errors""" env = DummyEnv(self.loc) try: scons_subst('$foo.bar.3.0', env) except SCons.Errors.UserError, e: expect = [ # Python 2.3, 2.4 "SyntaxError `invalid syntax (line 1)' trying to evaluate `$foo.bar.3.0'", # Python 2.5 "SyntaxError `invalid syntax (, line 1)' trying to evaluate `$foo.bar.3.0'", ] assert str(e) in expect, e else: raise AssertionError("did not catch expected UserError") def test_subst_type_errors(self): """Test scons_subst(): handling type errors""" env = DummyEnv(self.loc) try: scons_subst("${NONE[2]}", env, gvars={'NONE':None}) except SCons.Errors.UserError, e: expect = [ # Python 2.3, 2.4 "TypeError `unsubscriptable object' trying to evaluate `${NONE[2]}'", # Python 2.5, 2.6 "TypeError `'NoneType' object is unsubscriptable' trying to evaluate `${NONE[2]}'", # Python 2.7 and later "TypeError `'NoneType' object is not subscriptable' trying to evaluate `${NONE[2]}'", # Python 2.7 and later under Fedora "TypeError `'NoneType' object has no attribute '__getitem__'' trying to evaluate `${NONE[2]}'", ] assert str(e) in expect, e else: raise AssertionError("did not catch expected UserError") try: def func(a, b, c): pass scons_subst("${func(1)}", env, gvars={'func':func}) except SCons.Errors.UserError, e: expect = [ # Python 2.3, 2.4, 2.5 "TypeError `func() takes exactly 3 arguments (1 given)' trying to evaluate `${func(1)}'" ] assert str(e) in expect, repr(str(e)) else: raise AssertionError("did not catch expected UserError") def test_subst_raw_function(self): """Test scons_subst(): fetch function with SUBST_RAW plus conv""" # Test that the combination of SUBST_RAW plus a pass-through # conversion routine allows us to fetch a function through the # dictionary. CommandAction uses this to allow delayed evaluation # of $SPAWN variables. env = DummyEnv(self.loc) gvars = env.Dictionary() x = lambda x: x r = scons_subst("$CALLABLE1", env, mode=SUBST_RAW, conv=x, gvars=gvars) assert r is self.callable_object_1, repr(r) r = scons_subst("$CALLABLE1", env, mode=SUBST_RAW, gvars=gvars) assert r == 'callable-1', repr(r) # Test how we handle overriding the internal conversion routines. def s(obj): return obj n1 = self.MyNode('n1') env = DummyEnv({'NODE' : n1}) gvars = env.Dictionary() node = scons_subst("$NODE", env, mode=SUBST_RAW, conv=s, gvars=gvars) assert node is n1, node node = scons_subst("$NODE", env, mode=SUBST_CMD, conv=s, gvars=gvars) assert node is n1, node node = scons_subst("$NODE", env, mode=SUBST_SIG, conv=s, gvars=gvars) assert node is n1, node #def test_subst_function_return(self): # """Test scons_subst(): returning a function""" # env = DummyEnv({'FUNCTION' : foo}) # gvars = env.Dictionary() # func = scons_subst("$FUNCTION", env, mode=SUBST_RAW, call=None, gvars=gvars) # assert func is function_foo, func # func = scons_subst("$FUNCTION", env, mode=SUBST_CMD, call=None, gvars=gvars) # assert func is function_foo, func # func = scons_subst("$FUNCTION", env, mode=SUBST_SIG, call=None, gvars=gvars) # assert func is function_foo, func def test_subst_overriding_gvars(self): """Test scons_subst(): supplying an overriding gvars dictionary""" env = DummyEnv({'XXX' : 'xxx'}) result = scons_subst('$XXX', env, gvars=env.Dictionary()) assert result == 'xxx', result result = scons_subst('$XXX', env, gvars={'XXX' : 'yyy'}) assert result == 'yyy', result class CLVar_TestCase(unittest.TestCase): def test_CLVar(self): """Test scons_subst() and scons_subst_list() with CLVar objects""" loc = {} loc['FOO'] = 'foo' loc['BAR'] = SCons.Util.CLVar('bar') loc['CALL'] = lambda target, source, env, for_signature: 'call' env = DummyEnv(loc) cmd = SCons.Util.CLVar("test $FOO $BAR $CALL test") newcmd = scons_subst(cmd, env, gvars=env.Dictionary()) assert newcmd == ['test', 'foo', 'bar', 'call', 'test'], newcmd cmd_list = scons_subst_list(cmd, env, gvars=env.Dictionary()) assert len(cmd_list) == 1, cmd_list assert cmd_list[0][0] == "test", cmd_list[0][0] assert cmd_list[0][1] == "foo", cmd_list[0][1] assert cmd_list[0][2] == "bar", cmd_list[0][2] assert cmd_list[0][3] == "call", cmd_list[0][3] assert cmd_list[0][4] == "test", cmd_list[0][4] class scons_subst_list_TestCase(SubstTestCase): basic_cases = [ "$TARGETS", [ ["foo/bar.exe", "/bar/baz with spaces.obj", "../foo/baz.obj"], ], "$SOURCES $NEWLINE $TARGETS", [ ["foo/blah with spaces.cpp", "/bar/ack.cpp", "../foo/ack.c", "before"], ["after", "foo/bar.exe", "/bar/baz with spaces.obj", "../foo/baz.obj"], ], "$SOURCES$NEWLINE", [ ["foo/blah with spaces.cpp", "/bar/ack.cpp", "../foo/ack.cbefore"], ["after"], ], "foo$FFF", [ ["fooGGG"], ], "foo${FFF}", [ ["fooGGG"], ], "test ${SOURCES.attribute.attr1}", [ ["test", "attr$1-blah with spaces.cpp", "attr$1-ack.cpp", "attr$1-ack.c"], ], "test ${SOURCES.attribute.attr2}", [ ["test", "attr$2-blah with spaces.cpp", "attr$2-ack.cpp", "attr$2-ack.c"], ], "$DO --in=$FOO --out=$BAR", [ ["do something", "--in=foo.in", "--out=bar with spaces.out"], ], # This test is now fixed, and works like it should. "$DO --in=$CRAZY --out=$BAR", [ ["do something", "--in=crazy\nfile.in", "--out=bar with spaces.out"], ], # Try passing a list to scons_subst_list(). [ "$SOURCES$NEWLINE", "$TARGETS", "This is a test"], [ ["foo/blah with spaces.cpp", "/bar/ack.cpp", "../foo/ack.cbefore"], ["after", "foo/bar.exe", "/bar/baz with spaces.obj", "../foo/baz.obj", "This is a test"], ], # Test against a former bug in scons_subst_list(). "$XXX$HHH", [ ["GGGIII"], ], # Test double-dollar-sign behavior. "$$FFF$HHH", [ ["$FFFIII"], ], # Test various combinations of strings, lists and functions. None, [[]], [None], [[]], '', [[]], [''], [[]], 'x', [['x']], ['x'], [['x']], 'x y', [['x', 'y']], ['x y'], [['x y']], ['x', 'y'], [['x', 'y']], '$N', [[]], ['$N'], [[]], '$X', [['x']], ['$X'], [['x']], '$Y', [['x']], ['$Y'], [['x']], #'$R', [[]], #['$R'], [[]], '$S', [['x', 'y']], '$S z', [['x', 'y', 'z']], ['$S'], [['x', 'y']], ['$S z'], [['x', 'y z']], # XXX - IS THIS BEST? ['$S', 'z'], [['x', 'y', 'z']], '$LS', [['x y']], '$LS z', [['x y', 'z']], ['$LS'], [['x y']], ['$LS z'], [['x y z']], ['$LS', 'z'], [['x y', 'z']], '$L', [['x', 'y']], '$L z', [['x', 'y', 'z']], ['$L'], [['x', 'y']], ['$L z'], [['x', 'y z']], # XXX - IS THIS BEST? ['$L', 'z'], [['x', 'y', 'z']], cs, [['cs']], [cs], [['cs']], cl, [['cl']], [cl], [['cl']], '$CS', [['cs']], ['$CS'], [['cs']], '$CL', [['cl']], ['$CL'], [['cl']], # Various uses of UserString. collections.UserString('x'), [['x']], [collections.UserString('x')], [['x']], collections.UserString('$X'), [['x']], [collections.UserString('$X')], [['x']], collections.UserString('$US'), [['us']], [collections.UserString('$US')], [['us']], '$US', [['us']], ['$US'], [['us']], # Test function calls within ${}. '$FUNCCALL', [['a', 'xc', 'b']], # Test handling of newlines in white space. 'foo\nbar', [['foo'], ['bar']], 'foo\n\nbar', [['foo'], ['bar']], 'foo \n \n bar', [['foo'], ['bar']], 'foo \nmiddle\n bar', [['foo'], ['middle'], ['bar']], # Bug reported by Christoph Wiedemann. cvt('$xxx/bin'), [['/bin']], # Test variables smooshed together with different prefixes. 'foo$AAA', [['fooa']], '<$AAA', [['<', 'a']], '>$AAA', [['>', 'a']], '|$AAA', [['|', 'a']], # Test callables that don't match our calling arguments. '$CALLABLE2', [['callable-2']], # Test handling of quotes. # XXX Find a way to handle this in the future. #'aaa "bbb ccc" ddd', [['aaa', 'bbb ccc', 'ddd']], '${_defines(DEFS)}', [['Q1="q1"', 'Q2="a"']], ] def test_scons_subst_list(self): """Test scons_subst_list(): basic substitution""" def convert_lists(expect): return [list(map(cvt, l)) for l in expect] return self.basic_comparisons(scons_subst_list, convert_lists) subst_list_cases = [ "test $xxx", [["test"]], [["test"]], [["test"]], "test $($xxx$)", [["test", "$($)"]], [["test"]], [["test"]], "test $( $xxx $)", [["test", "$(", "$)"]], [["test"]], [["test"]], "$AAA ${AAA}A $BBBB $BBB", [["a", "aA", "b"]], [["a", "aA", "b"]], [["a", "aA", "b"]], "$RECURSE", [["foo", "bar"]], [["foo", "bar"]], [["foo", "bar"]], "$RRR", [["foo", "bar"]], [["foo", "bar"]], [["foo", "bar"]], # Verify what happens with no target or source nodes. "$TARGET $SOURCES", [[]], [[]], [[]], "$TARGETS $SOURCE", [[]], [[]], [[]], # Various test refactored from ActionTests.py "${LIST}", [['This', 'is', '$(', '$)', 'test']], [['This', 'is', 'test']], [['This', 'is', 'test']], ["|", "$(", "$AAA", "|", "$BBB", "$)", "|", "$CCC", 1], [["|", "$(", "a", "|", "b", "$)", "|", "c", "1"]], [["|", "a", "|", "b", "|", "c", "1"]], [["|", "|", "c", "1"]], ] def test_subst_env(self): """Test scons_subst_list(): expansion dictionary""" # The expansion dictionary no longer comes from the construction # environment automatically. env = DummyEnv() s = scons_subst_list('$AAA', env) assert s == [[]], s def test_subst_target_source(self): """Test scons_subst_list(): target= and source= arguments""" env = DummyEnv(self.loc) gvars = env.Dictionary() t1 = self.MyNode('t1') t2 = self.MyNode('t2') s1 = self.MyNode('s1') s2 = self.MyNode('s2') result = scons_subst_list("$TARGET $SOURCES", env, target=[t1, t2], source=[s1, s2], gvars=gvars) assert result == [['t1', 's1', 's2']], result result = scons_subst_list("$TARGET $SOURCES", env, target=[t1, t2], source=[s1, s2], gvars={}) assert result == [['t1', 's1', 's2']], result # Test interpolating a callable. _t = DummyNode('t') _s = DummyNode('s') cmd_list = scons_subst_list("testing $CMDGEN1 $TARGETS $SOURCES", env, target=_t, source=_s, gvars=gvars) assert cmd_list == [['testing', 'foo', 'bar with spaces.out', 't', 's']], cmd_list def test_subst_escape(self): """Test scons_subst_list(): escape functionality""" env = DummyEnv(self.loc) gvars = env.Dictionary() def escape_func(foo): return '**' + foo + '**' cmd_list = scons_subst_list("abc $LITERALS xyz", env, gvars=gvars) assert cmd_list == [['abc', 'foo\nwith\nnewlines', 'bar\nwith\nnewlines', 'xyz']], cmd_list c = cmd_list[0][0].escape(escape_func) assert c == 'abc', c c = cmd_list[0][1].escape(escape_func) assert c == '**foo\nwith\nnewlines**', c c = cmd_list[0][2].escape(escape_func) assert c == '**bar\nwith\nnewlines**', c c = cmd_list[0][3].escape(escape_func) assert c == 'xyz', c # We used to treat literals smooshed together like the whole # thing was literal and escape it as a unit. The commented-out # asserts below are in case we ever have to find a way to # resurrect that functionality in some way. cmd_list = scons_subst_list("abc${LITERALS}xyz", env, gvars=gvars) c = cmd_list[0][0].escape(escape_func) #assert c == '**abcfoo\nwith\nnewlines**', c assert c == 'abcfoo\nwith\nnewlines', c c = cmd_list[0][1].escape(escape_func) #assert c == '**bar\nwith\nnewlinesxyz**', c assert c == 'bar\nwith\nnewlinesxyz', c _t = DummyNode('t') cmd_list = scons_subst_list('echo "target: $TARGET"', env, target=_t, gvars=gvars) c = cmd_list[0][0].escape(escape_func) assert c == 'echo', c c = cmd_list[0][1].escape(escape_func) assert c == '"target:', c c = cmd_list[0][2].escape(escape_func) assert c == 't"', c def test_subst_SUBST_modes(self): """Test scons_subst_list(): SUBST_* modes""" env = DummyEnv(self.loc) subst_list_cases = self.subst_list_cases[:] gvars = env.Dictionary() r = scons_subst_list("$TARGET $SOURCES", env, mode=SUBST_RAW, gvars=gvars) assert r == [[]], r failed = 0 while subst_list_cases: input, eraw, ecmd, esig = subst_list_cases[:4] result = scons_subst_list(input, env, mode=SUBST_RAW, gvars=gvars) if result != eraw: if failed == 0: print print " input %s => RAW %s did not match %s" % (repr(input), repr(result), repr(eraw)) failed = failed + 1 result = scons_subst_list(input, env, mode=SUBST_CMD, gvars=gvars) if result != ecmd: if failed == 0: print print " input %s => CMD %s did not match %s" % (repr(input), repr(result), repr(ecmd)) failed = failed + 1 result = scons_subst_list(input, env, mode=SUBST_SIG, gvars=gvars) if result != esig: if failed == 0: print print " input %s => SIG %s did not match %s" % (repr(input), repr(result), repr(esig)) failed = failed + 1 del subst_list_cases[:4] assert failed == 0, "%d subst() mode cases failed" % failed def test_subst_attribute_errors(self): """Test scons_subst_list(): handling attribute errors""" env = DummyEnv() try: class Foo(object): pass scons_subst_list('${foo.bar}', env, gvars={'foo':Foo()}) except SCons.Errors.UserError, e: expect = [ "AttributeError `bar' trying to evaluate `${foo.bar}'", "AttributeError `Foo instance has no attribute 'bar'' trying to evaluate `${foo.bar}'", "AttributeError `'Foo' instance has no attribute 'bar'' trying to evaluate `${foo.bar}'", "AttributeError `'Foo' object has no attribute 'bar'' trying to evaluate `${foo.bar}'", ] assert str(e) in expect, e else: raise AssertionError("did not catch expected UserError") def test_subst_syntax_errors(self): """Test scons_subst_list(): handling syntax errors""" env = DummyEnv() try: scons_subst_list('$foo.bar.3.0', env) except SCons.Errors.UserError, e: expect = [ "SyntaxError `invalid syntax' trying to evaluate `$foo.bar.3.0'", "SyntaxError `invalid syntax (line 1)' trying to evaluate `$foo.bar.3.0'", "SyntaxError `invalid syntax (, line 1)' trying to evaluate `$foo.bar.3.0'", ] assert str(e) in expect, e else: raise AssertionError("did not catch expected SyntaxError") def test_subst_raw_function(self): """Test scons_subst_list(): fetch function with SUBST_RAW plus conv""" # Test that the combination of SUBST_RAW plus a pass-through # conversion routine allows us to fetch a function through the # dictionary. env = DummyEnv(self.loc) gvars = env.Dictionary() x = lambda x: x r = scons_subst_list("$CALLABLE2", env, mode=SUBST_RAW, conv=x, gvars=gvars) assert r == [[self.callable_object_2]], repr(r) r = scons_subst_list("$CALLABLE2", env, mode=SUBST_RAW, gvars=gvars) assert r == [['callable-2']], repr(r) def test_subst_list_overriding_gvars(self): """Test scons_subst_list(): overriding conv()""" env = DummyEnv() def s(obj): return obj n1 = self.MyNode('n1') env = DummyEnv({'NODE' : n1}) gvars=env.Dictionary() node = scons_subst_list("$NODE", env, mode=SUBST_RAW, conv=s, gvars=gvars) assert node == [[n1]], node node = scons_subst_list("$NODE", env, mode=SUBST_CMD, conv=s, gvars=gvars) assert node == [[n1]], node node = scons_subst_list("$NODE", env, mode=SUBST_SIG, conv=s, gvars=gvars) assert node == [[n1]], node def test_subst_list_overriding_gvars(self): """Test scons_subst_list(): supplying an overriding gvars dictionary""" env = DummyEnv({'XXX' : 'xxx'}) result = scons_subst_list('$XXX', env, gvars=env.Dictionary()) assert result == [['xxx']], result result = scons_subst_list('$XXX', env, gvars={'XXX' : 'yyy'}) assert result == [['yyy']], result class scons_subst_once_TestCase(unittest.TestCase): loc = { 'CCFLAGS' : '-DFOO', 'ONE' : 1, 'RECURSE' : 'r $RECURSE r', 'LIST' : ['a', 'b', 'c'], } basic_cases = [ '$CCFLAGS -DBAR', 'OTHER_KEY', '$CCFLAGS -DBAR', '$CCFLAGS -DBAR', 'CCFLAGS', '-DFOO -DBAR', 'x $ONE y', 'ONE', 'x 1 y', 'x $RECURSE y', 'RECURSE', 'x r $RECURSE r y', '$LIST', 'LIST', 'a b c', ['$LIST'], 'LIST', ['a', 'b', 'c'], ['x', '$LIST', 'y'], 'LIST', ['x', 'a', 'b', 'c', 'y'], ['x', 'x $LIST y', 'y'], 'LIST', ['x', 'x a b c y', 'y'], ['x', 'x $CCFLAGS y', 'y'], 'LIST', ['x', 'x $CCFLAGS y', 'y'], ['x', 'x $RECURSE y', 'y'], 'LIST', ['x', 'x $RECURSE y', 'y'], ] def test_subst_once(self): """Test the scons_subst_once() function""" env = DummyEnv(self.loc) cases = self.basic_cases[:] failed = 0 while cases: input, key, expect = cases[:3] result = scons_subst_once(input, env, key) if result != expect: if failed == 0: print print " input %s (%s) => %s did not match %s" % (repr(input), repr(key), repr(result), repr(expect)) failed = failed + 1 del cases[:3] assert failed == 0, "%d subst() cases failed" % failed class quote_spaces_TestCase(unittest.TestCase): def test_quote_spaces(self): """Test the quote_spaces() method...""" q = quote_spaces('x') assert q == 'x', q q = quote_spaces('x x') assert q == '"x x"', q q = quote_spaces('x\tx') assert q == '"x\tx"', q class Node(object): def __init__(self, name, children=[]): self.children = children self.name = name def __str__(self): return self.name def exists(self): return 1 def rexists(self): return 1 def has_builder(self): return 1 def has_explicit_builder(self): return 1 def side_effect(self): return 1 def precious(self): return 1 def always_build(self): return 1 def current(self): return 1 class LiteralTestCase(unittest.TestCase): def test_Literal(self): """Test the Literal() function.""" input_list = [ '$FOO', Literal('$BAR') ] gvars = { 'FOO' : 'BAZ', 'BAR' : 'BLAT' } def escape_func(cmd): return '**' + cmd + '**' cmd_list = scons_subst_list(input_list, None, gvars=gvars) cmd_list = escape_list(cmd_list[0], escape_func) assert cmd_list == ['BAZ', '**$BAR**'], cmd_list class SpecialAttrWrapperTestCase(unittest.TestCase): def test_SpecialAttrWrapper(self): """Test the SpecialAttrWrapper() function.""" input_list = [ '$FOO', SpecialAttrWrapper('$BAR', 'BLEH') ] gvars = { 'FOO' : 'BAZ', 'BAR' : 'BLAT' } def escape_func(cmd): return '**' + cmd + '**' cmd_list = scons_subst_list(input_list, None, gvars=gvars) cmd_list = escape_list(cmd_list[0], escape_func) assert cmd_list == ['BAZ', '**$BAR**'], cmd_list cmd_list = scons_subst_list(input_list, None, mode=SUBST_SIG, gvars=gvars) cmd_list = escape_list(cmd_list[0], escape_func) assert cmd_list == ['BAZ', '**BLEH**'], cmd_list class subst_dict_TestCase(unittest.TestCase): def test_subst_dict(self): """Test substituting dictionary values in an Action """ t = DummyNode('t') s = DummyNode('s') d = subst_dict(target=t, source=s) assert str(d['TARGETS'][0]) == 't', d['TARGETS'] assert str(d['TARGET']) == 't', d['TARGET'] assert str(d['SOURCES'][0]) == 's', d['SOURCES'] assert str(d['SOURCE']) == 's', d['SOURCE'] t1 = DummyNode('t1') t2 = DummyNode('t2') s1 = DummyNode('s1') s2 = DummyNode('s2') d = subst_dict(target=[t1, t2], source=[s1, s2]) TARGETS = sorted([str(x) for x in d['TARGETS']]) assert TARGETS == ['t1', 't2'], d['TARGETS'] assert str(d['TARGET']) == 't1', d['TARGET'] SOURCES = sorted([str(x) for x in d['SOURCES']]) assert SOURCES == ['s1', 's2'], d['SOURCES'] assert str(d['SOURCE']) == 's1', d['SOURCE'] class V(object): # Fake Value node with no rfile() method. def __init__(self, name): self.name = name def __str__(self): return 'v-'+self.name def get_subst_proxy(self): return self class N(V): def rfile(self): return self.__class__('rstr-' + self.name) t3 = N('t3') t4 = DummyNode('t4') t5 = V('t5') s3 = DummyNode('s3') s4 = N('s4') s5 = V('s5') d = subst_dict(target=[t3, t4, t5], source=[s3, s4, s5]) TARGETS = sorted([str(x) for x in d['TARGETS']]) assert TARGETS == ['t4', 'v-t3', 'v-t5'], TARGETS SOURCES = sorted([str(x) for x in d['SOURCES']]) assert SOURCES == ['s3', 'v-rstr-s4', 'v-s5'], SOURCES if __name__ == "__main__": suite = unittest.TestSuite() tclasses = [ CLVar_TestCase, LiteralTestCase, SpecialAttrWrapperTestCase, quote_spaces_TestCase, scons_subst_TestCase, scons_subst_list_TestCase, scons_subst_once_TestCase, subst_dict_TestCase, ] for tclass in tclasses: names = unittest.getTestCaseNames(tclass, 'test_') suite.addTests(list(map(tclass, names))) if not unittest.TextTestRunner().run(suite).wasSuccessful(): sys.exit(1) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/UtilTests.py0000644000175000017500000006522712114661560021623 0ustar dktrkranzdktrkranz# # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/UtilTests.py 2013/03/03 09:48:35 garyo" import SCons.compat import io import os import sys import unittest from collections import UserDict, UserList, UserString import TestCmd import SCons.Errors from SCons.Util import * try: eval('unicode') except NameError: HasUnicode = False else: HasUnicode = True class OutBuffer(object): def __init__(self): self.buffer = "" def write(self, str): self.buffer = self.buffer + str class dictifyTestCase(unittest.TestCase): def test_dictify(self): """Test the dictify() function""" r = SCons.Util.dictify(['a', 'b', 'c'], [1, 2, 3]) assert r == {'a':1, 'b':2, 'c':3}, r r = {} SCons.Util.dictify(['a'], [1], r) SCons.Util.dictify(['b'], [2], r) SCons.Util.dictify(['c'], [3], r) assert r == {'a':1, 'b':2, 'c':3}, r class UtilTestCase(unittest.TestCase): def test_splitext(self): assert splitext('foo') == ('foo','') assert splitext('foo.bar') == ('foo','.bar') assert splitext(os.path.join('foo.bar', 'blat')) == (os.path.join('foo.bar', 'blat'),'') class Node(object): def __init__(self, name, children=[]): self.children = children self.name = name self.nocache = None def __str__(self): return self.name def exists(self): return 1 def rexists(self): return 1 def has_builder(self): return 1 def has_explicit_builder(self): return 1 def side_effect(self): return 1 def precious(self): return 1 def always_build(self): return 1 def is_up_to_date(self): return 1 def noclean(self): return 1 def tree_case_1(self): """Fixture for the render_tree() and print_tree() tests.""" windows_h = self.Node("windows.h") stdlib_h = self.Node("stdlib.h") stdio_h = self.Node("stdio.h") bar_c = self.Node("bar.c", [stdlib_h, windows_h]) bar_o = self.Node("bar.o", [bar_c]) foo_c = self.Node("foo.c", [stdio_h]) foo_o = self.Node("foo.o", [foo_c]) foo = self.Node("foo", [foo_o, bar_o]) expect = """\ +-foo +-foo.o | +-foo.c | +-stdio.h +-bar.o +-bar.c +-stdlib.h +-windows.h """ lines = expect.split('\n')[:-1] lines = ['[E BSPACN ]'+l for l in lines] withtags = '\n'.join(lines) + '\n' return foo, expect, withtags def tree_case_2(self, prune=1): """Fixture for the render_tree() and print_tree() tests.""" stdlib_h = self.Node("stdlib.h") bar_h = self.Node('bar.h', [stdlib_h]) blat_h = self.Node('blat.h', [stdlib_h]) blat_c = self.Node('blat.c', [blat_h, bar_h]) blat_o = self.Node('blat.o', [blat_c]) expect = """\ +-blat.o +-blat.c +-blat.h | +-stdlib.h +-bar.h +-[stdlib.h] """ if not prune: expect = expect.replace('[', '') expect = expect.replace(']', '') lines = expect.split('\n')[:-1] lines = ['[E BSPACN ]'+l for l in lines] withtags = '\n'.join(lines) + '\n' return blat_o, expect, withtags def test_render_tree(self): """Test the render_tree() function""" def get_children(node): return node.children node, expect, withtags = self.tree_case_1() actual = render_tree(node, get_children) assert expect == actual, (expect, actual) node, expect, withtags = self.tree_case_2() actual = render_tree(node, get_children, 1) assert expect == actual, (expect, actual) def test_print_tree(self): """Test the print_tree() function""" def get_children(node): return node.children save_stdout = sys.stdout try: node, expect, withtags = self.tree_case_1() sys.stdout = io.StringIO() print_tree(node, get_children) actual = sys.stdout.getvalue() assert expect == actual, (expect, actual) sys.stdout = io.StringIO() print_tree(node, get_children, showtags=1) actual = sys.stdout.getvalue() assert withtags == actual, (withtags, actual) node, expect, withtags = self.tree_case_2(prune=0) sys.stdout = io.StringIO() print_tree(node, get_children, 1) actual = sys.stdout.getvalue() assert expect == actual, (expect, actual) sys.stdout = io.StringIO() # The following call should work here: # print_tree(node, get_children, 1, showtags=1) # For some reason I don't understand, though, *this* # time that we call print_tree, the visited dictionary # is still populated with the values from the last call! # I can't see why this would be, short of a bug in Python, # and rather than continue banging my head against the # brick wall for a *test*, we're going to going with # the cheap, easy workaround: print_tree(node, get_children, 1, showtags=1, visited={}) actual = sys.stdout.getvalue() assert withtags == actual, (withtags, actual) finally: sys.stdout = save_stdout def test_is_Dict(self): assert is_Dict({}) assert is_Dict(UserDict()) assert is_Dict(os.environ) try: class mydict(dict): pass except TypeError: pass else: assert is_Dict(mydict({})) assert not is_Dict([]) assert not is_Dict(()) assert not is_Dict("") if HasUnicode: exec "assert not is_Dict(u'')" def test_is_List(self): assert is_List([]) assert is_List(UserList()) try: class mylist(list): pass except TypeError: pass else: assert is_List(mylist([])) assert not is_List(()) assert not is_List({}) assert not is_List("") if HasUnicode: exec "assert not is_List(u'')" def test_is_String(self): assert is_String("") if HasUnicode: exec "assert is_String(u'')" assert is_String(UserString('')) try: class mystr(str): pass except TypeError: pass else: assert is_String(mystr('')) assert not is_String({}) assert not is_String([]) assert not is_String(()) def test_is_Tuple(self): assert is_Tuple(()) try: class mytuple(tuple): pass except TypeError: pass else: assert is_Tuple(mytuple(())) assert not is_Tuple([]) assert not is_Tuple({}) assert not is_Tuple("") if HasUnicode: exec "assert not is_Tuple(u'')" def test_to_String(self): """Test the to_String() method.""" assert to_String(1) == "1", to_String(1) assert to_String([ 1, 2, 3]) == str([1, 2, 3]), to_String([1,2,3]) assert to_String("foo") == "foo", to_String("foo") s1=UserString('blah') assert to_String(s1) == s1, s1 assert to_String(s1) == 'blah', s1 class Derived(UserString): pass s2 = Derived('foo') assert to_String(s2) == s2, s2 assert to_String(s2) == 'foo', s2 if HasUnicode: s3=UserString(unicode('bar')) assert to_String(s3) == s3, s3 assert to_String(s3) == unicode('bar'), s3 assert isinstance(to_String(s3), unicode), \ type(to_String(s3)) if HasUnicode: s4 = unicode('baz') assert to_String(s4) == unicode('baz'), to_String(s4) assert isinstance(to_String(s4), unicode), \ type(to_String(s4)) def test_WhereIs(self): test = TestCmd.TestCmd(workdir = '') sub1_xxx_exe = test.workpath('sub1', 'xxx.exe') sub2_xxx_exe = test.workpath('sub2', 'xxx.exe') sub3_xxx_exe = test.workpath('sub3', 'xxx.exe') sub4_xxx_exe = test.workpath('sub4', 'xxx.exe') test.subdir('subdir', 'sub1', 'sub2', 'sub3', 'sub4') if sys.platform != 'win32': test.write(sub1_xxx_exe, "\n") os.mkdir(sub2_xxx_exe) test.write(sub3_xxx_exe, "\n") os.chmod(sub3_xxx_exe, 0777) test.write(sub4_xxx_exe, "\n") os.chmod(sub4_xxx_exe, 0777) env_path = os.environ['PATH'] try: pathdirs_1234 = [ test.workpath('sub1'), test.workpath('sub2'), test.workpath('sub3'), test.workpath('sub4'), ] + env_path.split(os.pathsep) pathdirs_1243 = [ test.workpath('sub1'), test.workpath('sub2'), test.workpath('sub4'), test.workpath('sub3'), ] + env_path.split(os.pathsep) os.environ['PATH'] = os.pathsep.join(pathdirs_1234) wi = WhereIs('xxx.exe') assert wi == test.workpath(sub3_xxx_exe), wi wi = WhereIs('xxx.exe', pathdirs_1243) assert wi == test.workpath(sub4_xxx_exe), wi wi = WhereIs('xxx.exe', os.pathsep.join(pathdirs_1243)) assert wi == test.workpath(sub4_xxx_exe), wi wi = WhereIs('xxx.exe',reject = sub3_xxx_exe) assert wi == test.workpath(sub4_xxx_exe), wi wi = WhereIs('xxx.exe', pathdirs_1243, reject = sub3_xxx_exe) assert wi == test.workpath(sub4_xxx_exe), wi os.environ['PATH'] = os.pathsep.join(pathdirs_1243) wi = WhereIs('xxx.exe') assert wi == test.workpath(sub4_xxx_exe), wi wi = WhereIs('xxx.exe', pathdirs_1234) assert wi == test.workpath(sub3_xxx_exe), wi wi = WhereIs('xxx.exe', os.pathsep.join(pathdirs_1234)) assert wi == test.workpath(sub3_xxx_exe), wi if sys.platform == 'win32': wi = WhereIs('xxx', pathext = '') assert wi is None, wi wi = WhereIs('xxx', pathext = '.exe') assert wi == test.workpath(sub4_xxx_exe), wi wi = WhereIs('xxx', path = pathdirs_1234, pathext = '.BAT;.EXE') assert wi.lower() == test.workpath(sub3_xxx_exe).lower(), wi # Test that we return a normalized path even when # the path contains forward slashes. forward_slash = test.workpath('') + '/sub3' wi = WhereIs('xxx', path = forward_slash, pathext = '.EXE') assert wi.lower() == test.workpath(sub3_xxx_exe).lower(), wi del os.environ['PATH'] wi = WhereIs('xxx.exe') assert wi is None, wi finally: os.environ['PATH'] = env_path def test_get_env_var(self): """Testing get_environment_var().""" assert get_environment_var("$FOO") == "FOO", get_environment_var("$FOO") assert get_environment_var("${BAR}") == "BAR", get_environment_var("${BAR}") assert get_environment_var("$FOO_BAR1234") == "FOO_BAR1234", get_environment_var("$FOO_BAR1234") assert get_environment_var("${BAR_FOO1234}") == "BAR_FOO1234", get_environment_var("${BAR_FOO1234}") assert get_environment_var("${BAR}FOO") == None, get_environment_var("${BAR}FOO") assert get_environment_var("$BAR ") == None, get_environment_var("$BAR ") assert get_environment_var("FOO$BAR") == None, get_environment_var("FOO$BAR") assert get_environment_var("$FOO[0]") == None, get_environment_var("$FOO[0]") assert get_environment_var("${some('complex expression')}") == None, get_environment_var("${some('complex expression')}") def test_Proxy(self): """Test generic Proxy class.""" class Subject(object): def foo(self): return 1 def bar(self): return 2 s=Subject() s.baz = 3 class ProxyTest(Proxy): def bar(self): return 4 p=ProxyTest(s) assert p.foo() == 1, p.foo() assert p.bar() == 4, p.bar() assert p.baz == 3, p.baz p.baz = 5 s.baz = 6 assert p.baz == 5, p.baz assert p.get() == s, p.get() def test_display(self): old_stdout = sys.stdout sys.stdout = OutBuffer() display("line1") display.set_mode(0) display("line2") display.set_mode(1) display("line3") display("line4\n", append_newline=0) display.set_mode(0) display("dont print1") display("dont print2\n", append_newline=0) display.set_mode(1) assert sys.stdout.buffer == "line1\nline3\nline4\n" sys.stdout = old_stdout def test_get_native_path(self): """Test the get_native_path() function.""" import tempfile filename = tempfile.mktemp() str = '1234567890 ' + filename try: open(filename, 'w').write(str) assert open(get_native_path(filename)).read() == str finally: try: os.unlink(filename) except OSError: pass def test_PrependPath(self): """Test prepending to a path""" p1 = r'C:\dir\num\one;C:\dir\num\two' p2 = r'C:\mydir\num\one;C:\mydir\num\two' # have to include the pathsep here so that the test will work on UNIX too. p1 = PrependPath(p1,r'C:\dir\num\two',sep = ';') p1 = PrependPath(p1,r'C:\dir\num\three',sep = ';') p2 = PrependPath(p2,r'C:\mydir\num\three',sep = ';') p2 = PrependPath(p2,r'C:\mydir\num\one',sep = ';') assert(p1 == r'C:\dir\num\three;C:\dir\num\two;C:\dir\num\one') assert(p2 == r'C:\mydir\num\one;C:\mydir\num\three;C:\mydir\num\two') def test_AppendPath(self): """Test appending to a path.""" p1 = r'C:\dir\num\one;C:\dir\num\two' p2 = r'C:\mydir\num\one;C:\mydir\num\two' # have to include the pathsep here so that the test will work on UNIX too. p1 = AppendPath(p1,r'C:\dir\num\two',sep = ';') p1 = AppendPath(p1,r'C:\dir\num\three',sep = ';') p2 = AppendPath(p2,r'C:\mydir\num\three',sep = ';') p2 = AppendPath(p2,r'C:\mydir\num\one',sep = ';') assert(p1 == r'C:\dir\num\one;C:\dir\num\two;C:\dir\num\three') assert(p2 == r'C:\mydir\num\two;C:\mydir\num\three;C:\mydir\num\one') def test_PrependPathPreserveOld(self): """Test prepending to a path while preserving old paths""" p1 = r'C:\dir\num\one;C:\dir\num\two' # have to include the pathsep here so that the test will work on UNIX too. p1 = PrependPath(p1,r'C:\dir\num\two',sep = ';', delete_existing=0) p1 = PrependPath(p1,r'C:\dir\num\three',sep = ';') assert(p1 == r'C:\dir\num\three;C:\dir\num\one;C:\dir\num\two') def test_AppendPathPreserveOld(self): """Test appending to a path while preserving old paths""" p1 = r'C:\dir\num\one;C:\dir\num\two' # have to include the pathsep here so that the test will work on UNIX too. p1 = AppendPath(p1,r'C:\dir\num\one',sep = ';', delete_existing=0) p1 = AppendPath(p1,r'C:\dir\num\three',sep = ';') assert(p1 == r'C:\dir\num\one;C:\dir\num\two;C:\dir\num\three') def test_CLVar(self): """Test the command-line construction variable class""" f = SCons.Util.CLVar('a b') r = f + 'c d' assert isinstance(r, SCons.Util.CLVar), type(r) assert r.data == ['a', 'b', 'c', 'd'], r.data assert str(r) == 'a b c d', str(r) r = f + ' c d' assert isinstance(r, SCons.Util.CLVar), type(r) assert r.data == ['a', 'b', 'c', 'd'], r.data assert str(r) == 'a b c d', str(r) r = f + ['c d'] assert isinstance(r, SCons.Util.CLVar), type(r) assert r.data == ['a', 'b', 'c d'], r.data assert str(r) == 'a b c d', str(r) r = f + [' c d'] assert isinstance(r, SCons.Util.CLVar), type(r) assert r.data == ['a', 'b', ' c d'], r.data assert str(r) == 'a b c d', str(r) r = f + ['c', 'd'] assert isinstance(r, SCons.Util.CLVar), type(r) assert r.data == ['a', 'b', 'c', 'd'], r.data assert str(r) == 'a b c d', str(r) r = f + [' c', 'd'] assert isinstance(r, SCons.Util.CLVar), type(r) assert r.data == ['a', 'b', ' c', 'd'], r.data assert str(r) == 'a b c d', str(r) f = SCons.Util.CLVar(['a b']) r = f + 'c d' assert isinstance(r, SCons.Util.CLVar), type(r) assert r.data == ['a b', 'c', 'd'], r.data assert str(r) == 'a b c d', str(r) r = f + ' c d' assert isinstance(r, SCons.Util.CLVar), type(r) assert r.data == ['a b', 'c', 'd'], r.data assert str(r) == 'a b c d', str(r) r = f + ['c d'] assert isinstance(r, SCons.Util.CLVar), type(r) assert r.data == ['a b', 'c d'], r.data assert str(r) == 'a b c d', str(r) r = f + [' c d'] assert isinstance(r, SCons.Util.CLVar), type(r) assert r.data == ['a b', ' c d'], r.data assert str(r) == 'a b c d', str(r) r = f + ['c', 'd'] assert isinstance(r, SCons.Util.CLVar), type(r) assert r.data == ['a b', 'c', 'd'], r.data assert str(r) == 'a b c d', str(r) r = f + [' c', 'd'] assert isinstance(r, SCons.Util.CLVar), type(r) assert r.data == ['a b', ' c', 'd'], r.data assert str(r) == 'a b c d', str(r) f = SCons.Util.CLVar(['a', 'b']) r = f + 'c d' assert isinstance(r, SCons.Util.CLVar), type(r) assert r.data == ['a', 'b', 'c', 'd'], r.data assert str(r) == 'a b c d', str(r) r = f + ' c d' assert isinstance(r, SCons.Util.CLVar), type(r) assert r.data == ['a', 'b', 'c', 'd'], r.data assert str(r) == 'a b c d', str(r) r = f + ['c d'] assert isinstance(r, SCons.Util.CLVar), type(r) assert r.data == ['a', 'b', 'c d'], r.data assert str(r) == 'a b c d', str(r) r = f + [' c d'] assert isinstance(r, SCons.Util.CLVar), type(r) assert r.data == ['a', 'b', ' c d'], r.data assert str(r) == 'a b c d', str(r) r = f + ['c', 'd'] assert isinstance(r, SCons.Util.CLVar), type(r) assert r.data == ['a', 'b', 'c', 'd'], r.data assert str(r) == 'a b c d', str(r) r = f + [' c', 'd'] assert isinstance(r, SCons.Util.CLVar), type(r) assert r.data == ['a', 'b', ' c', 'd'], r.data assert str(r) == 'a b c d', str(r) def test_Selector(self): """Test the Selector class""" class MyNode(object): def __init__(self, name): self.name = name self.suffix = os.path.splitext(name)[1] def __str__(self): return self.name s = Selector({'a' : 'AAA', 'b' : 'BBB'}) assert s['a'] == 'AAA', s['a'] assert s['b'] == 'BBB', s['b'] exc_caught = None try: x = s['c'] except KeyError: exc_caught = 1 assert exc_caught, "should have caught a KeyError" s['c'] = 'CCC' assert s['c'] == 'CCC', s['c'] class DummyEnv(UserDict): def subst(self, key): if key[0] == '$': return self[key[1:]] return key env = DummyEnv() s = Selector({'.d' : 'DDD', '.e' : 'EEE'}) ret = s(env, []) assert ret is None, ret ret = s(env, [MyNode('foo.d')]) assert ret == 'DDD', ret ret = s(env, [MyNode('bar.e')]) assert ret == 'EEE', ret ret = s(env, [MyNode('bar.x')]) assert ret is None, ret s[None] = 'XXX' ret = s(env, [MyNode('bar.x')]) assert ret == 'XXX', ret env = DummyEnv({'FSUFF' : '.f', 'GSUFF' : '.g'}) s = Selector({'$FSUFF' : 'FFF', '$GSUFF' : 'GGG'}) ret = s(env, [MyNode('foo.f')]) assert ret == 'FFF', ret ret = s(env, [MyNode('bar.g')]) assert ret == 'GGG', ret def test_adjustixes(self): """Test the adjustixes() function""" r = adjustixes('file', 'pre-', '-suf') assert r == 'pre-file-suf', r r = adjustixes('pre-file', 'pre-', '-suf') assert r == 'pre-file-suf', r r = adjustixes('file-suf', 'pre-', '-suf') assert r == 'pre-file-suf', r r = adjustixes('pre-file-suf', 'pre-', '-suf') assert r == 'pre-file-suf', r r = adjustixes('pre-file.xxx', 'pre-', '-suf') assert r == 'pre-file.xxx', r r = adjustixes('dir/file', 'pre-', '-suf') assert r == os.path.join('dir', 'pre-file-suf'), r def test_containsAny(self): """Test the containsAny() function""" assert containsAny('*.py', '*?[]') assert not containsAny('file.txt', '*?[]') def test_containsAll(self): """Test the containsAll() function""" assert containsAll('43221', '123') assert not containsAll('134', '123') def test_containsOnly(self): """Test the containsOnly() function""" assert containsOnly('.83', '0123456789.') assert not containsOnly('43221', '123') def test_LogicalLines(self): """Test the LogicalLines class""" content = r""" foo \ bar \ baz foo bling \ bling \ bling bling """ try: fobj = io.StringIO(content) except TypeError: # Python 2.7 and beyond require unicode strings. fobj = io.StringIO(unicode(content)) lines = LogicalLines(fobj).readlines() assert lines == [ '\n', 'foo bar baz\n', 'foo\n', 'bling bling \\ bling\n', 'bling\n', ], lines def test_intern(self): s1 = silent_intern("spam") # Python 3.x does not have a unicode() global function if sys.version[0] == '2': s2 = silent_intern(unicode("unicode spam")) s3 = silent_intern(42) s4 = silent_intern("spam") assert id(s1) == id(s4) class MD5TestCase(unittest.TestCase): def test_collect(self): """Test collecting a list of signatures into a new signature value """ s = list(map(MD5signature, ('111', '222', '333'))) assert '698d51a19d8a121ce581499d7b701668' == MD5collect(s[0:1]) assert '8980c988edc2c78cc43ccb718c06efd5' == MD5collect(s[0:2]) assert '53fd88c84ff8a285eb6e0a687e55b8c7' == MD5collect(s) def test_MD5signature(self): """Test generating a signature""" s = MD5signature('111') assert '698d51a19d8a121ce581499d7b701668' == s, s s = MD5signature('222') assert 'bcbe3365e6ac95ea2c0343a2395834dd' == s, s class NodeListTestCase(unittest.TestCase): def test_simple_attributes(self): """Test simple attributes of a NodeList class""" class TestClass(object): def __init__(self, name, child=None): self.child = child self.bar = name t1 = TestClass('t1', TestClass('t1child')) t2 = TestClass('t2', TestClass('t2child')) t3 = TestClass('t3') nl = NodeList([t1, t2, t3]) assert nl.bar == [ 't1', 't2', 't3' ], nl.bar assert nl[0:2].child.bar == [ 't1child', 't2child' ], \ nl[0:2].child.bar def test_callable_attributes(self): """Test callable attributes of a NodeList class""" class TestClass(object): def __init__(self, name, child=None): self.child = child self.bar = name def foo(self): return self.bar + "foo" def getself(self): return self t1 = TestClass('t1', TestClass('t1child')) t2 = TestClass('t2', TestClass('t2child')) t3 = TestClass('t3') nl = NodeList([t1, t2, t3]) assert nl.foo() == [ 't1foo', 't2foo', 't3foo' ], nl.foo() assert nl.bar == [ 't1', 't2', 't3' ], nl.bar assert nl.getself().bar == [ 't1', 't2', 't3' ], nl.getself().bar assert nl[0:2].child.foo() == [ 't1childfoo', 't2childfoo' ], \ nl[0:2].child.foo() assert nl[0:2].child.bar == [ 't1child', 't2child' ], \ nl[0:2].child.bar def test_null(self): """Test a null NodeList""" nl = NodeList([]) r = str(nl) assert r == '', r for node in nl: raise Exception("should not enter this loop") class flattenTestCase(unittest.TestCase): def test_scalar(self): """Test flattening a scalar""" result = flatten('xyz') assert result == ['xyz'], result if __name__ == "__main__": suite = unittest.TestSuite() tclasses = [ dictifyTestCase, flattenTestCase, MD5TestCase, NodeListTestCase, UtilTestCase, ] for tclass in tclasses: names = unittest.getTestCaseNames(tclass, 'test_') suite.addTests(list(map(tclass, names))) if not unittest.TextTestRunner().run(suite).wasSuccessful(): sys.exit(1) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/DefaultsTests.py0000644000175000017500000000603312114661557022451 0ustar dktrkranzdktrkranz# # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/DefaultsTests.py 2013/03/03 09:48:35 garyo" import SCons.compat import os import sys import unittest from collections import UserDict import TestCmd import SCons.Errors from SCons.Defaults import * class DefaultsTestCase(unittest.TestCase): def test_mkdir_func0(self): test = TestCmd.TestCmd(workdir = '') test.subdir('sub') subdir2 = test.workpath('sub', 'dir1', 'dir2') # Simple smoke test mkdir_func(subdir2) mkdir_func(subdir2) # 2nd time should be OK too def test_mkdir_func1(self): test = TestCmd.TestCmd(workdir = '') test.subdir('sub') subdir1 = test.workpath('sub', 'dir1') subdir2 = test.workpath('sub', 'dir1', 'dir2') # No error if asked to create existing dir os.makedirs(subdir2) mkdir_func(subdir2) mkdir_func(subdir1) def test_mkdir_func2(self): test = TestCmd.TestCmd(workdir = '') test.subdir('sub') subdir1 = test.workpath('sub', 'dir1') subdir2 = test.workpath('sub', 'dir1', 'dir2') file = test.workpath('sub', 'dir1', 'dir2', 'file') # make sure it does error if asked to create a dir # where there's already a file os.makedirs(subdir2) test.write(file, "test\n") try: mkdir_func(file) except os.error, e: pass else: fail("expected os.error") if __name__ == "__main__": suite = unittest.TestSuite() tclasses = [ DefaultsTestCase, ] for tclass in tclasses: names = unittest.getTestCaseNames(tclass, 'test_') suite.addTests(list(map(tclass, names))) if not unittest.TextTestRunner().run(suite).wasSuccessful(): sys.exit(1) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Taskmaster.py0000644000175000017500000011575212114661557022006 0ustar dktrkranzdktrkranz# # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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. __doc__ = """ Generic Taskmaster module for the SCons build engine. This module contains the primary interface(s) between a wrapping user interface and the SCons build engine. There are two key classes here: Taskmaster This is the main engine for walking the dependency graph and calling things to decide what does or doesn't need to be built. Task This is the base class for allowing a wrapping interface to decide what does or doesn't actually need to be done. The intention is for a wrapping interface to subclass this as appropriate for different types of behavior it may need. The canonical example is the SCons native Python interface, which has Task subclasses that handle its specific behavior, like printing "`foo' is up to date" when a top-level target doesn't need to be built, and handling the -c option by removing targets as its "build" action. There is also a separate subclass for suppressing this output when the -q option is used. The Taskmaster instantiates a Task object for each (set of) target(s) that it decides need to be evaluated and/or built. """ __revision__ = "src/engine/SCons/Taskmaster.py 2013/03/03 09:48:35 garyo" from itertools import chain import operator import sys import traceback import SCons.Errors import SCons.Node import SCons.Warnings StateString = SCons.Node.StateString NODE_NO_STATE = SCons.Node.no_state NODE_PENDING = SCons.Node.pending NODE_EXECUTING = SCons.Node.executing NODE_UP_TO_DATE = SCons.Node.up_to_date NODE_EXECUTED = SCons.Node.executed NODE_FAILED = SCons.Node.failed print_prepare = 0 # set by option --debug=prepare # A subsystem for recording stats about how different Nodes are handled by # the main Taskmaster loop. There's no external control here (no need for # a --debug= option); enable it by changing the value of CollectStats. CollectStats = None class Stats(object): """ A simple class for holding statistics about the disposition of a Node by the Taskmaster. If we're collecting statistics, each Node processed by the Taskmaster gets one of these attached, in which case the Taskmaster records its decision each time it processes the Node. (Ideally, that's just once per Node.) """ def __init__(self): """ Instantiates a Taskmaster.Stats object, initializing all appropriate counters to zero. """ self.considered = 0 self.already_handled = 0 self.problem = 0 self.child_failed = 0 self.not_built = 0 self.side_effects = 0 self.build = 0 StatsNodes = [] fmt = "%(considered)3d "\ "%(already_handled)3d " \ "%(problem)3d " \ "%(child_failed)3d " \ "%(not_built)3d " \ "%(side_effects)3d " \ "%(build)3d " def dump_stats(): for n in sorted(StatsNodes, key=lambda a: str(a)): print (fmt % n.stats.__dict__) + str(n) class Task(object): """ Default SCons build engine task. This controls the interaction of the actual building of node and the rest of the engine. This is expected to handle all of the normally-customizable aspects of controlling a build, so any given application *should* be able to do what it wants by sub-classing this class and overriding methods as appropriate. If an application needs to customze something by sub-classing Taskmaster (or some other build engine class), we should first try to migrate that functionality into this class. Note that it's generally a good idea for sub-classes to call these methods explicitly to update state, etc., rather than roll their own interaction with Taskmaster from scratch. """ def __init__(self, tm, targets, top, node): self.tm = tm self.targets = targets self.top = top self.node = node self.exc_clear() def trace_message(self, method, node, description='node'): fmt = '%-20s %s %s\n' return fmt % (method + ':', description, self.tm.trace_node(node)) def display(self, message): """ Hook to allow the calling interface to display a message. This hook gets called as part of preparing a task for execution (that is, a Node to be built). As part of figuring out what Node should be built next, the actually target list may be altered, along with a message describing the alteration. The calling interface can subclass Task and provide a concrete implementation of this method to see those messages. """ pass def prepare(self): """ Called just before the task is executed. This is mainly intended to give the target Nodes a chance to unlink underlying files and make all necessary directories before the Action is actually called to build the targets. """ global print_prepare T = self.tm.trace if T: T.write(self.trace_message(u'Task.prepare()', self.node)) # Now that it's the appropriate time, give the TaskMaster a # chance to raise any exceptions it encountered while preparing # this task. self.exception_raise() if self.tm.message: self.display(self.tm.message) self.tm.message = None # Let the targets take care of any necessary preparations. # This includes verifying that all of the necessary sources # and dependencies exist, removing the target file(s), etc. # # As of April 2008, the get_executor().prepare() method makes # sure that all of the aggregate sources necessary to build this # Task's target(s) exist in one up-front check. The individual # target t.prepare() methods check that each target's explicit # or implicit dependencies exists, and also initialize the # .sconsign info. executor = self.targets[0].get_executor() executor.prepare() for t in executor.get_action_targets(): if print_prepare: print "Preparing target %s..."%t for s in t.side_effects: print "...with side-effect %s..."%s t.prepare() for s in t.side_effects: if print_prepare: print "...Preparing side-effect %s..."%s s.prepare() def get_target(self): """Fetch the target being built or updated by this task. """ return self.node def needs_execute(self): # TODO(deprecate): "return True" is the old default behavior; # change it to NotImplementedError (after running through the # Deprecation Cycle) so the desired behavior is explicitly # determined by which concrete subclass is used. #raise NotImplementedError msg = ('Taskmaster.Task is an abstract base class; instead of\n' '\tusing it directly, ' 'derive from it and override the abstract methods.') SCons.Warnings.warn(SCons.Warnings.TaskmasterNeedsExecuteWarning, msg) return True def execute(self): """ Called to execute the task. This method is called from multiple threads in a parallel build, so only do thread safe stuff here. Do thread unsafe stuff in prepare(), executed() or failed(). """ T = self.tm.trace if T: T.write(self.trace_message(u'Task.execute()', self.node)) try: cached_targets = [] for t in self.targets: if not t.retrieve_from_cache(): break cached_targets.append(t) if len(cached_targets) < len(self.targets): # Remove targets before building. It's possible that we # partially retrieved targets from the cache, leaving # them in read-only mode. That might cause the command # to fail. # for t in cached_targets: try: t.fs.unlink(t.path) except (IOError, OSError): pass self.targets[0].build() else: for t in cached_targets: t.cached = 1 except SystemExit: exc_value = sys.exc_info()[1] raise SCons.Errors.ExplicitExit(self.targets[0], exc_value.code) except SCons.Errors.UserError: raise except SCons.Errors.BuildError: raise except Exception, e: buildError = SCons.Errors.convert_to_BuildError(e) buildError.node = self.targets[0] buildError.exc_info = sys.exc_info() raise buildError def executed_without_callbacks(self): """ Called when the task has been successfully executed and the Taskmaster instance doesn't want to call the Node's callback methods. """ T = self.tm.trace if T: T.write(self.trace_message('Task.executed_without_callbacks()', self.node)) for t in self.targets: if t.get_state() == NODE_EXECUTING: for side_effect in t.side_effects: side_effect.set_state(NODE_NO_STATE) t.set_state(NODE_EXECUTED) def executed_with_callbacks(self): """ Called when the task has been successfully executed and the Taskmaster instance wants to call the Node's callback methods. This may have been a do-nothing operation (to preserve build order), so we must check the node's state before deciding whether it was "built", in which case we call the appropriate Node method. In any event, we always call "visited()", which will handle any post-visit actions that must take place regardless of whether or not the target was an actual built target or a source Node. """ T = self.tm.trace if T: T.write(self.trace_message('Task.executed_with_callbacks()', self.node)) for t in self.targets: if t.get_state() == NODE_EXECUTING: for side_effect in t.side_effects: side_effect.set_state(NODE_NO_STATE) t.set_state(NODE_EXECUTED) if not t.cached: t.push_to_cache() t.built() t.visited() executed = executed_with_callbacks def failed(self): """ Default action when a task fails: stop the build. Note: Although this function is normally invoked on nodes in the executing state, it might also be invoked on up-to-date nodes when using Configure(). """ self.fail_stop() def fail_stop(self): """ Explicit stop-the-build failure. This sets failure status on the target nodes and all of their dependent parent nodes. Note: Although this function is normally invoked on nodes in the executing state, it might also be invoked on up-to-date nodes when using Configure(). """ T = self.tm.trace if T: T.write(self.trace_message('Task.failed_stop()', self.node)) # Invoke will_not_build() to clean-up the pending children # list. self.tm.will_not_build(self.targets, lambda n: n.set_state(NODE_FAILED)) # Tell the taskmaster to not start any new tasks self.tm.stop() # We're stopping because of a build failure, but give the # calling Task class a chance to postprocess() the top-level # target under which the build failure occurred. self.targets = [self.tm.current_top] self.top = 1 def fail_continue(self): """ Explicit continue-the-build failure. This sets failure status on the target nodes and all of their dependent parent nodes. Note: Although this function is normally invoked on nodes in the executing state, it might also be invoked on up-to-date nodes when using Configure(). """ T = self.tm.trace if T: T.write(self.trace_message('Task.failed_continue()', self.node)) self.tm.will_not_build(self.targets, lambda n: n.set_state(NODE_FAILED)) def make_ready_all(self): """ Marks all targets in a task ready for execution. This is used when the interface needs every target Node to be visited--the canonical example being the "scons -c" option. """ T = self.tm.trace if T: T.write(self.trace_message('Task.make_ready_all()', self.node)) self.out_of_date = self.targets[:] for t in self.targets: t.disambiguate().set_state(NODE_EXECUTING) for s in t.side_effects: # add disambiguate here to mirror the call on targets above s.disambiguate().set_state(NODE_EXECUTING) def make_ready_current(self): """ Marks all targets in a task ready for execution if any target is not current. This is the default behavior for building only what's necessary. """ T = self.tm.trace if T: T.write(self.trace_message(u'Task.make_ready_current()', self.node)) self.out_of_date = [] needs_executing = False for t in self.targets: try: t.disambiguate().make_ready() is_up_to_date = not t.has_builder() or \ (not t.always_build and t.is_up_to_date()) except EnvironmentError, e: raise SCons.Errors.BuildError(node=t, errstr=e.strerror, filename=e.filename) if not is_up_to_date: self.out_of_date.append(t) needs_executing = True if needs_executing: for t in self.targets: t.set_state(NODE_EXECUTING) for s in t.side_effects: # add disambiguate here to mirror the call on targets in first loop above s.disambiguate().set_state(NODE_EXECUTING) else: for t in self.targets: # We must invoke visited() to ensure that the node # information has been computed before allowing the # parent nodes to execute. (That could occur in a # parallel build...) t.visited() t.set_state(NODE_UP_TO_DATE) make_ready = make_ready_current def postprocess(self): """ Post-processes a task after it's been executed. This examines all the targets just built (or not, we don't care if the build was successful, or even if there was no build because everything was up-to-date) to see if they have any waiting parent Nodes, or Nodes waiting on a common side effect, that can be put back on the candidates list. """ T = self.tm.trace if T: T.write(self.trace_message(u'Task.postprocess()', self.node)) # We may have built multiple targets, some of which may have # common parents waiting for this build. Count up how many # targets each parent was waiting for so we can subtract the # values later, and so we *don't* put waiting side-effect Nodes # back on the candidates list if the Node is also a waiting # parent. targets = set(self.targets) pending_children = self.tm.pending_children parents = {} for t in targets: # A node can only be in the pending_children set if it has # some waiting_parents. if t.waiting_parents: if T: T.write(self.trace_message(u'Task.postprocess()', t, 'removing')) pending_children.discard(t) for p in t.waiting_parents: parents[p] = parents.get(p, 0) + 1 for t in targets: for s in t.side_effects: if s.get_state() == NODE_EXECUTING: s.set_state(NODE_NO_STATE) for p in s.waiting_parents: parents[p] = parents.get(p, 0) + 1 for p in s.waiting_s_e: if p.ref_count == 0: self.tm.candidates.append(p) for p, subtract in parents.items(): p.ref_count = p.ref_count - subtract if T: T.write(self.trace_message(u'Task.postprocess()', p, 'adjusted parent ref count')) if p.ref_count == 0: self.tm.candidates.append(p) for t in targets: t.postprocess() # Exception handling subsystem. # # Exceptions that occur while walking the DAG or examining Nodes # must be raised, but must be raised at an appropriate time and in # a controlled manner so we can, if necessary, recover gracefully, # possibly write out signature information for Nodes we've updated, # etc. This is done by having the Taskmaster tell us about the # exception, and letting def exc_info(self): """ Returns info about a recorded exception. """ return self.exception def exc_clear(self): """ Clears any recorded exception. This also changes the "exception_raise" attribute to point to the appropriate do-nothing method. """ self.exception = (None, None, None) self.exception_raise = self._no_exception_to_raise def exception_set(self, exception=None): """ Records an exception to be raised at the appropriate time. This also changes the "exception_raise" attribute to point to the method that will, in fact """ if not exception: exception = sys.exc_info() self.exception = exception self.exception_raise = self._exception_raise def _no_exception_to_raise(self): pass def _exception_raise(self): """ Raises a pending exception that was recorded while getting a Task ready for execution. """ exc = self.exc_info()[:] try: exc_type, exc_value, exc_traceback = exc except ValueError: exc_type, exc_value = exc exc_traceback = None raise exc_type, exc_value, exc_traceback class AlwaysTask(Task): def needs_execute(self): """ Always returns True (indicating this Task should always be executed). Subclasses that need this behavior (as opposed to the default of only executing Nodes that are out of date w.r.t. their dependencies) can use this as follows: class MyTaskSubclass(SCons.Taskmaster.Task): needs_execute = SCons.Taskmaster.Task.execute_always """ return True class OutOfDateTask(Task): def needs_execute(self): """ Returns True (indicating this Task should be executed) if this Task's target state indicates it needs executing, which has already been determined by an earlier up-to-date check. """ return self.targets[0].get_state() == SCons.Node.executing def find_cycle(stack, visited): if stack[-1] in visited: return None visited.add(stack[-1]) for n in stack[-1].waiting_parents: stack.append(n) if stack[0] == stack[-1]: return stack if find_cycle(stack, visited): return stack stack.pop() return None class Taskmaster(object): """ The Taskmaster for walking the dependency DAG. """ def __init__(self, targets=[], tasker=None, order=None, trace=None): self.original_top = targets self.top_targets_left = targets[:] self.top_targets_left.reverse() self.candidates = [] if tasker is None: tasker = OutOfDateTask self.tasker = tasker if not order: order = lambda l: l self.order = order self.message = None self.trace = trace self.next_candidate = self.find_next_candidate self.pending_children = set() def find_next_candidate(self): """ Returns the next candidate Node for (potential) evaluation. The candidate list (really a stack) initially consists of all of the top-level (command line) targets provided when the Taskmaster was initialized. While we walk the DAG, visiting Nodes, all the children that haven't finished processing get pushed on to the candidate list. Each child can then be popped and examined in turn for whether *their* children are all up-to-date, in which case a Task will be created for their actual evaluation and potential building. Here is where we also allow candidate Nodes to alter the list of Nodes that should be examined. This is used, for example, when invoking SCons in a source directory. A source directory Node can return its corresponding build directory Node, essentially saying, "Hey, you really need to build this thing over here instead." """ try: return self.candidates.pop() except IndexError: pass try: node = self.top_targets_left.pop() except IndexError: return None self.current_top = node alt, message = node.alter_targets() if alt: self.message = message self.candidates.append(node) self.candidates.extend(self.order(alt)) node = self.candidates.pop() return node def no_next_candidate(self): """ Stops Taskmaster processing by not returning a next candidate. Note that we have to clean-up the Taskmaster candidate list because the cycle detection depends on the fact all nodes have been processed somehow. """ while self.candidates: candidates = self.candidates self.candidates = [] self.will_not_build(candidates) return None def _validate_pending_children(self): """ Validate the content of the pending_children set. Assert if an internal error is found. This function is used strictly for debugging the taskmaster by checking that no invariants are violated. It is not used in normal operation. The pending_children set is used to detect cycles in the dependency graph. We call a "pending child" a child that is found in the "pending" state when checking the dependencies of its parent node. A pending child can occur when the Taskmaster completes a loop through a cycle. For example, lets imagine a graph made of three node (A, B and C) making a cycle. The evaluation starts at node A. The taskmaster first consider whether node A's child B is up-to-date. Then, recursively, node B needs to check whether node C is up-to-date. This leaves us with a dependency graph looking like: Next candidate \ \ Node A (Pending) --> Node B(Pending) --> Node C (NoState) ^ | | | +-------------------------------------+ Now, when the Taskmaster examines the Node C's child Node A, it finds that Node A is in the "pending" state. Therefore, Node A is a pending child of node C. Pending children indicate that the Taskmaster has potentially loop back through a cycle. We say potentially because it could also occur when a DAG is evaluated in parallel. For example, consider the following graph: Node A (Pending) --> Node B(Pending) --> Node C (Pending) --> ... | ^ | | +----------> Node D (NoState) --------+ / Next candidate / The Taskmaster first evaluates the nodes A, B, and C and starts building some children of node C. Assuming, that the maximum parallel level has not been reached, the Taskmaster will examine Node D. It will find that Node C is a pending child of Node D. In summary, evaluating a graph with a cycle will always involve a pending child at one point. A pending child might indicate either a cycle or a diamond-shaped DAG. Only a fraction of the nodes ends-up being a "pending child" of another node. This keeps the pending_children set small in practice. We can differentiate between the two cases if we wait until the end of the build. At this point, all the pending children nodes due to a diamond-shaped DAG will have been properly built (or will have failed to build). But, the pending children involved in a cycle will still be in the pending state. The taskmaster removes nodes from the pending_children set as soon as a pending_children node moves out of the pending state. This also helps to keep the pending_children set small. """ for n in self.pending_children: assert n.state in (NODE_PENDING, NODE_EXECUTING), \ (str(n), StateString[n.state]) assert len(n.waiting_parents) != 0, (str(n), len(n.waiting_parents)) for p in n.waiting_parents: assert p.ref_count > 0, (str(n), str(p), p.ref_count) def trace_message(self, message): return 'Taskmaster: %s\n' % message def trace_node(self, node): return '<%-10s %-3s %s>' % (StateString[node.get_state()], node.ref_count, repr(str(node))) def _find_next_ready_node(self): """ Finds the next node that is ready to be built. This is *the* main guts of the DAG walk. We loop through the list of candidates, looking for something that has no un-built children (i.e., that is a leaf Node or has dependencies that are all leaf Nodes or up-to-date). Candidate Nodes are re-scanned (both the target Node itself and its sources, which are always scanned in the context of a given target) to discover implicit dependencies. A Node that must wait for some children to be built will be put back on the candidates list after the children have finished building. A Node that has been put back on the candidates list in this way may have itself (or its sources) re-scanned, in order to handle generated header files (e.g.) and the implicit dependencies therein. Note that this method does not do any signature calculation or up-to-date check itself. All of that is handled by the Task class. This is purely concerned with the dependency graph walk. """ self.ready_exc = None T = self.trace if T: T.write(u'\n' + self.trace_message('Looking for a node to evaluate')) while True: node = self.next_candidate() if node is None: if T: T.write(self.trace_message('No candidate anymore.') + u'\n') return None node = node.disambiguate() state = node.get_state() # For debugging only: # # try: # self._validate_pending_children() # except: # self.ready_exc = sys.exc_info() # return node if CollectStats: if not hasattr(node, 'stats'): node.stats = Stats() StatsNodes.append(node) S = node.stats S.considered = S.considered + 1 else: S = None if T: T.write(self.trace_message(u' Considering node %s and its children:' % self.trace_node(node))) if state == NODE_NO_STATE: # Mark this node as being on the execution stack: node.set_state(NODE_PENDING) elif state > NODE_PENDING: # Skip this node if it has already been evaluated: if S: S.already_handled = S.already_handled + 1 if T: T.write(self.trace_message(u' already handled (executed)')) continue executor = node.get_executor() try: children = executor.get_all_children() except SystemExit: exc_value = sys.exc_info()[1] e = SCons.Errors.ExplicitExit(node, exc_value.code) self.ready_exc = (SCons.Errors.ExplicitExit, e) if T: T.write(self.trace_message(' SystemExit')) return node except Exception, e: # We had a problem just trying to figure out the # children (like a child couldn't be linked in to a # VariantDir, or a Scanner threw something). Arrange to # raise the exception when the Task is "executed." self.ready_exc = sys.exc_info() if S: S.problem = S.problem + 1 if T: T.write(self.trace_message(' exception %s while scanning children.\n' % e)) return node children_not_visited = [] children_pending = set() children_not_ready = [] children_failed = False for child in chain(executor.get_all_prerequisites(), children): childstate = child.get_state() if T: T.write(self.trace_message(u' ' + self.trace_node(child))) if childstate == NODE_NO_STATE: children_not_visited.append(child) elif childstate == NODE_PENDING: children_pending.add(child) elif childstate == NODE_FAILED: children_failed = True if childstate <= NODE_EXECUTING: children_not_ready.append(child) # These nodes have not even been visited yet. Add # them to the list so that on some next pass we can # take a stab at evaluating them (or their children). children_not_visited.reverse() self.candidates.extend(self.order(children_not_visited)) #if T and children_not_visited: # T.write(self.trace_message(' adding to candidates: %s' % map(str, children_not_visited))) # T.write(self.trace_message(' candidates now: %s\n' % map(str, self.candidates))) # Skip this node if any of its children have failed. # # This catches the case where we're descending a top-level # target and one of our children failed while trying to be # built by a *previous* descent of an earlier top-level # target. # # It can also occur if a node is reused in multiple # targets. One first descends though the one of the # target, the next time occurs through the other target. # # Note that we can only have failed_children if the # --keep-going flag was used, because without it the build # will stop before diving in the other branch. # # Note that even if one of the children fails, we still # added the other children to the list of candidate nodes # to keep on building (--keep-going). if children_failed: for n in executor.get_action_targets(): n.set_state(NODE_FAILED) if S: S.child_failed = S.child_failed + 1 if T: T.write(self.trace_message('****** %s\n' % self.trace_node(node))) continue if children_not_ready: for child in children_not_ready: # We're waiting on one or more derived targets # that have not yet finished building. if S: S.not_built = S.not_built + 1 # Add this node to the waiting parents lists of # anything we're waiting on, with a reference # count so we can be put back on the list for # re-evaluation when they've all finished. node.ref_count = node.ref_count + child.add_to_waiting_parents(node) if T: T.write(self.trace_message(u' adjusted ref count: %s, child %s' % (self.trace_node(node), repr(str(child))))) if T: for pc in children_pending: T.write(self.trace_message(' adding %s to the pending children set\n' % self.trace_node(pc))) self.pending_children = self.pending_children | children_pending continue # Skip this node if it has side-effects that are # currently being built: wait_side_effects = False for se in executor.get_action_side_effects(): if se.get_state() == NODE_EXECUTING: se.add_to_waiting_s_e(node) wait_side_effects = True if wait_side_effects: if S: S.side_effects = S.side_effects + 1 continue # The default when we've gotten through all of the checks above: # this node is ready to be built. if S: S.build = S.build + 1 if T: T.write(self.trace_message(u'Evaluating %s\n' % self.trace_node(node))) # For debugging only: # # try: # self._validate_pending_children() # except: # self.ready_exc = sys.exc_info() # return node return node return None def next_task(self): """ Returns the next task to be executed. This simply asks for the next Node to be evaluated, and then wraps it in the specific Task subclass with which we were initialized. """ node = self._find_next_ready_node() if node is None: return None tlist = node.get_executor().get_all_targets() task = self.tasker(self, tlist, node in self.original_top, node) try: task.make_ready() except: # We had a problem just trying to get this task ready (like # a child couldn't be linked in to a VariantDir when deciding # whether this node is current). Arrange to raise the # exception when the Task is "executed." self.ready_exc = sys.exc_info() if self.ready_exc: task.exception_set(self.ready_exc) self.ready_exc = None return task def will_not_build(self, nodes, node_func=lambda n: None): """ Perform clean-up about nodes that will never be built. Invokes a user defined function on all of these nodes (including all of their parents). """ T = self.trace pending_children = self.pending_children to_visit = set(nodes) pending_children = pending_children - to_visit if T: for n in nodes: T.write(self.trace_message(' removing node %s from the pending children set\n' % self.trace_node(n))) try: while len(to_visit): node = to_visit.pop() node_func(node) # Prune recursion by flushing the waiting children # list immediately. parents = node.waiting_parents node.waiting_parents = set() to_visit = to_visit | parents pending_children = pending_children - parents for p in parents: p.ref_count = p.ref_count - 1 if T: T.write(self.trace_message(' removing parent %s from the pending children set\n' % self.trace_node(p))) except KeyError: # The container to_visit has been emptied. pass # We have the stick back the pending_children list into the # taskmaster because the python 1.5.2 compatibility does not # allow us to use in-place updates self.pending_children = pending_children def stop(self): """ Stops the current build completely. """ self.next_candidate = self.no_next_candidate def cleanup(self): """ Check for dependency cycles. """ if not self.pending_children: return nclist = [(n, find_cycle([n], set())) for n in self.pending_children] genuine_cycles = [ node for node,cycle in nclist if cycle or node.get_state() != NODE_EXECUTED ] if not genuine_cycles: # All of the "cycles" found were single nodes in EXECUTED state, # which is to say, they really weren't cycles. Just return. return desc = 'Found dependency cycle(s):\n' for node, cycle in nclist: if cycle: desc = desc + " " + " -> ".join(map(str, cycle)) + "\n" else: desc = desc + \ " Internal Error: no cycle found for node %s (%s) in state %s\n" % \ (node, repr(node), StateString[node.get_state()]) raise SCons.Errors.UserError(desc) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Builder.py0000644000175000017500000010230412114661557021243 0ustar dktrkranzdktrkranz"""SCons.Builder Builder object subsystem. A Builder object is a callable that encapsulates information about how to execute actions to create a target Node (file) from source Nodes (files), and how to create those dependencies for tracking. The main entry point here is the Builder() factory method. This provides a procedural interface that creates the right underlying Builder object based on the keyword arguments supplied and the types of the arguments. The goal is for this external interface to be simple enough that the vast majority of users can create new Builders as necessary to support building new types of files in their configurations, without having to dive any deeper into this subsystem. The base class here is BuilderBase. This is a concrete base class which does, in fact, represent the Builder objects that we (or users) create. There is also a proxy that looks like a Builder: CompositeBuilder This proxies for a Builder with an action that is actually a dictionary that knows how to map file suffixes to a specific action. This is so that we can invoke different actions (compilers, compile options) for different flavors of source files. Builders and their proxies have the following public interface methods used by other modules: __call__() THE public interface. Calling a Builder object (with the use of internal helper methods) sets up the target and source dependencies, appropriate mapping to a specific action, and the environment manipulation necessary for overridden construction variable. This also takes care of warning about possible mistakes in keyword arguments. add_emitter() Adds an emitter for a specific file suffix, used by some Tool modules to specify that (for example) a yacc invocation on a .y can create a .h *and* a .c file. add_action() Adds an action for a specific file suffix, heavily used by Tool modules to add their specific action(s) for turning a source file into an object file to the global static and shared object file Builders. There are the following methods for internal use within this module: _execute() The internal method that handles the heavily lifting when a Builder is called. This is used so that the __call__() methods can set up warning about possible mistakes in keyword-argument overrides, and *then* execute all of the steps necessary so that the warnings only occur once. get_name() Returns the Builder's name within a specific Environment, primarily used to try to return helpful information in error messages. adjust_suffix() get_prefix() get_suffix() get_src_suffix() set_src_suffix() Miscellaneous stuff for handling the prefix and suffix manipulation we use in turning source file names into target file names. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Builder.py 2013/03/03 09:48:35 garyo" import collections import SCons.Action from SCons.Debug import logInstanceCreation from SCons.Errors import InternalError, UserError import SCons.Executor import SCons.Memoize import SCons.Node import SCons.Node.FS import SCons.Util import SCons.Warnings class _Null(object): pass _null = _Null def match_splitext(path, suffixes = []): if suffixes: matchsuf = [S for S in suffixes if path[-len(S):] == S] if matchsuf: suf = max([(len(_f),_f) for _f in matchsuf])[1] return [path[:-len(suf)], path[-len(suf):]] return SCons.Util.splitext(path) class DictCmdGenerator(SCons.Util.Selector): """This is a callable class that can be used as a command generator function. It holds on to a dictionary mapping file suffixes to Actions. It uses that dictionary to return the proper action based on the file suffix of the source file.""" def __init__(self, dict=None, source_ext_match=1): SCons.Util.Selector.__init__(self, dict) self.source_ext_match = source_ext_match def src_suffixes(self): return list(self.keys()) def add_action(self, suffix, action): """Add a suffix-action pair to the mapping. """ self[suffix] = action def __call__(self, target, source, env, for_signature): if not source: return [] if self.source_ext_match: suffixes = self.src_suffixes() ext = None for src in map(str, source): my_ext = match_splitext(src, suffixes)[1] if ext and my_ext != ext: raise UserError("While building `%s' from `%s': Cannot build multiple sources with different extensions: %s, %s" % (repr(list(map(str, target))), src, ext, my_ext)) ext = my_ext else: ext = match_splitext(str(source[0]), self.src_suffixes())[1] if not ext: #return ext raise UserError("While building `%s': " "Cannot deduce file extension from source files: %s" % (repr(list(map(str, target))), repr(list(map(str, source))))) try: ret = SCons.Util.Selector.__call__(self, env, source, ext) except KeyError, e: raise UserError("Ambiguous suffixes after environment substitution: %s == %s == %s" % (e.args[0], e.args[1], e.args[2])) if ret is None: raise UserError("While building `%s' from `%s': Don't know how to build from a source file with suffix `%s'. Expected a suffix in this list: %s." % \ (repr(list(map(str, target))), repr(list(map(str, source))), ext, repr(list(self.keys())))) return ret class CallableSelector(SCons.Util.Selector): """A callable dictionary that will, in turn, call the value it finds if it can.""" def __call__(self, env, source): value = SCons.Util.Selector.__call__(self, env, source) if callable(value): value = value(env, source) return value class DictEmitter(SCons.Util.Selector): """A callable dictionary that maps file suffixes to emitters. When called, it finds the right emitter in its dictionary for the suffix of the first source file, and calls that emitter to get the right lists of targets and sources to return. If there's no emitter for the suffix in its dictionary, the original target and source are returned. """ def __call__(self, target, source, env): emitter = SCons.Util.Selector.__call__(self, env, source) if emitter: target, source = emitter(target, source, env) return (target, source) class ListEmitter(collections.UserList): """A callable list of emitters that calls each in sequence, returning the result. """ def __call__(self, target, source, env): for e in self.data: target, source = e(target, source, env) return (target, source) # These are a common errors when calling a Builder; # they are similar to the 'target' and 'source' keyword args to builders, # so we issue warnings when we see them. The warnings can, of course, # be disabled. misleading_keywords = { 'targets' : 'target', 'sources' : 'source', } class OverrideWarner(collections.UserDict): """A class for warning about keyword arguments that we use as overrides in a Builder call. This class exists to handle the fact that a single Builder call can actually invoke multiple builders. This class only emits the warnings once, no matter how many Builders are invoked. """ def __init__(self, dict): collections.UserDict.__init__(self, dict) if __debug__: logInstanceCreation(self, 'Builder.OverrideWarner') self.already_warned = None def warn(self): if self.already_warned: return for k in self.keys(): if k in misleading_keywords: alt = misleading_keywords[k] msg = "Did you mean to use `%s' instead of `%s'?" % (alt, k) SCons.Warnings.warn(SCons.Warnings.MisleadingKeywordsWarning, msg) self.already_warned = 1 def Builder(**kw): """A factory for builder objects.""" composite = None if 'generator' in kw: if 'action' in kw: raise UserError("You must not specify both an action and a generator.") kw['action'] = SCons.Action.CommandGeneratorAction(kw['generator'], {}) del kw['generator'] elif 'action' in kw: source_ext_match = kw.get('source_ext_match', 1) if 'source_ext_match' in kw: del kw['source_ext_match'] if SCons.Util.is_Dict(kw['action']): composite = DictCmdGenerator(kw['action'], source_ext_match) kw['action'] = SCons.Action.CommandGeneratorAction(composite, {}) kw['src_suffix'] = composite.src_suffixes() else: kw['action'] = SCons.Action.Action(kw['action']) if 'emitter' in kw: emitter = kw['emitter'] if SCons.Util.is_String(emitter): # This allows users to pass in an Environment # variable reference (like "$FOO") as an emitter. # We will look in that Environment variable for # a callable to use as the actual emitter. var = SCons.Util.get_environment_var(emitter) if not var: raise UserError("Supplied emitter '%s' does not appear to refer to an Environment variable" % emitter) kw['emitter'] = EmitterProxy(var) elif SCons.Util.is_Dict(emitter): kw['emitter'] = DictEmitter(emitter) elif SCons.Util.is_List(emitter): kw['emitter'] = ListEmitter(emitter) result = BuilderBase(**kw) if not composite is None: result = CompositeBuilder(result, composite) return result def _node_errors(builder, env, tlist, slist): """Validate that the lists of target and source nodes are legal for this builder and environment. Raise errors or issue warnings as appropriate. """ # First, figure out if there are any errors in the way the targets # were specified. for t in tlist: if t.side_effect: raise UserError("Multiple ways to build the same target were specified for: %s" % t) if t.has_explicit_builder(): if not t.env is None and not t.env is env: action = t.builder.action t_contents = action.get_contents(tlist, slist, t.env) contents = action.get_contents(tlist, slist, env) if t_contents == contents: msg = "Two different environments were specified for target %s,\n\tbut they appear to have the same action: %s" % (t, action.genstring(tlist, slist, t.env)) SCons.Warnings.warn(SCons.Warnings.DuplicateEnvironmentWarning, msg) else: msg = "Two environments with different actions were specified for the same target: %s" % t raise UserError(msg) if builder.multi: if t.builder != builder: msg = "Two different builders (%s and %s) were specified for the same target: %s" % (t.builder.get_name(env), builder.get_name(env), t) raise UserError(msg) # TODO(batch): list constructed each time! if t.get_executor().get_all_targets() != tlist: msg = "Two different target lists have a target in common: %s (from %s and from %s)" % (t, list(map(str, t.get_executor().get_all_targets())), list(map(str, tlist))) raise UserError(msg) elif t.sources != slist: msg = "Multiple ways to build the same target were specified for: %s (from %s and from %s)" % (t, list(map(str, t.sources)), list(map(str, slist))) raise UserError(msg) if builder.single_source: if len(slist) > 1: raise UserError("More than one source given for single-source builder: targets=%s sources=%s" % (list(map(str,tlist)), list(map(str,slist)))) class EmitterProxy(object): """This is a callable class that can act as a Builder emitter. It holds on to a string that is a key into an Environment dictionary, and will look there at actual build time to see if it holds a callable. If so, we will call that as the actual emitter.""" def __init__(self, var): self.var = SCons.Util.to_String(var) def __call__(self, target, source, env): emitter = self.var # Recursively substitute the variable. # We can't use env.subst() because it deals only # in strings. Maybe we should change that? while SCons.Util.is_String(emitter) and emitter in env: emitter = env[emitter] if callable(emitter): target, source = emitter(target, source, env) elif SCons.Util.is_List(emitter): for e in emitter: target, source = e(target, source, env) return (target, source) def __cmp__(self, other): return cmp(self.var, other.var) class BuilderBase(object): """Base class for Builders, objects that create output nodes (files) from input nodes (files). """ if SCons.Memoize.use_memoizer: __metaclass__ = SCons.Memoize.Memoized_Metaclass memoizer_counters = [] def __init__(self, action = None, prefix = '', suffix = '', src_suffix = '', target_factory = None, source_factory = None, target_scanner = None, source_scanner = None, emitter = None, multi = 0, env = None, single_source = 0, name = None, chdir = _null, is_explicit = 1, src_builder = None, ensure_suffix = False, **overrides): if __debug__: logInstanceCreation(self, 'Builder.BuilderBase') self._memo = {} self.action = action self.multi = multi if SCons.Util.is_Dict(prefix): prefix = CallableSelector(prefix) self.prefix = prefix if SCons.Util.is_Dict(suffix): suffix = CallableSelector(suffix) self.env = env self.single_source = single_source if 'overrides' in overrides: SCons.Warnings.warn(SCons.Warnings.DeprecatedBuilderKeywordsWarning, "The \"overrides\" keyword to Builder() creation has been deprecated;\n" +\ "\tspecify the items as keyword arguments to the Builder() call instead.") overrides.update(overrides['overrides']) del overrides['overrides'] if 'scanner' in overrides: SCons.Warnings.warn(SCons.Warnings.DeprecatedBuilderKeywordsWarning, "The \"scanner\" keyword to Builder() creation has been deprecated;\n" "\tuse: source_scanner or target_scanner as appropriate.") del overrides['scanner'] self.overrides = overrides self.set_suffix(suffix) self.set_src_suffix(src_suffix) self.ensure_suffix = ensure_suffix self.target_factory = target_factory self.source_factory = source_factory self.target_scanner = target_scanner self.source_scanner = source_scanner self.emitter = emitter # Optional Builder name should only be used for Builders # that don't get attached to construction environments. if name: self.name = name self.executor_kw = {} if not chdir is _null: self.executor_kw['chdir'] = chdir self.is_explicit = is_explicit if src_builder is None: src_builder = [] elif not SCons.Util.is_List(src_builder): src_builder = [ src_builder ] self.src_builder = src_builder def __nonzero__(self): raise InternalError("Do not test for the Node.builder attribute directly; use Node.has_builder() instead") def get_name(self, env): """Attempts to get the name of the Builder. Look at the BUILDERS variable of env, expecting it to be a dictionary containing this Builder, and return the key of the dictionary. If there's no key, then return a directly-configured name (if there is one) or the name of the class (by default).""" try: index = list(env['BUILDERS'].values()).index(self) return list(env['BUILDERS'].keys())[index] except (AttributeError, KeyError, TypeError, ValueError): try: return self.name except AttributeError: return str(self.__class__) def __cmp__(self, other): return cmp(self.__dict__, other.__dict__) def splitext(self, path, env=None): if not env: env = self.env if env: suffixes = self.src_suffixes(env) else: suffixes = [] return match_splitext(path, suffixes) def _adjustixes(self, files, pre, suf, ensure_suffix=False): if not files: return [] result = [] if not SCons.Util.is_List(files): files = [files] for f in files: if SCons.Util.is_String(f): f = SCons.Util.adjustixes(f, pre, suf, ensure_suffix) result.append(f) return result def _create_nodes(self, env, target = None, source = None): """Create and return lists of target and source nodes. """ src_suf = self.get_src_suffix(env) target_factory = env.get_factory(self.target_factory) source_factory = env.get_factory(self.source_factory) source = self._adjustixes(source, None, src_suf) slist = env.arg2nodes(source, source_factory) pre = self.get_prefix(env, slist) suf = self.get_suffix(env, slist) if target is None: try: t_from_s = slist[0].target_from_source except AttributeError: raise UserError("Do not know how to create a target from source `%s'" % slist[0]) except IndexError: tlist = [] else: splitext = lambda S: self.splitext(S,env) tlist = [ t_from_s(pre, suf, splitext) ] else: target = self._adjustixes(target, pre, suf, self.ensure_suffix) tlist = env.arg2nodes(target, target_factory, target=target, source=source) if self.emitter: # The emitter is going to do str(node), but because we're # being called *from* a builder invocation, the new targets # don't yet have a builder set on them and will look like # source files. Fool the emitter's str() calls by setting # up a temporary builder on the new targets. new_targets = [] for t in tlist: if not t.is_derived(): 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 # new targets, so that _node_errors() doesn't do weird stuff # to them because it thinks they already have builders. for t in new_targets: if t.builder is self: # Only delete the temporary builder if the emitter # didn't change it on us. t.builder_set(None) # 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, target=orig_tlist, source=orig_slist) slist = env.arg2nodes(source, source_factory, target=orig_tlist, source=orig_slist) return tlist, slist def _execute(self, env, target, source, overwarn={}, executor_kw={}): # We now assume that target and source are lists or None. if self.src_builder: source = self.src_builder_sources(env, source, overwarn) if self.single_source and len(source) > 1 and target is None: result = [] if target is None: target = [None]*len(source) for tgt, src in zip(target, source): if not tgt is None: tgt = [tgt] if not src is None: src = [src] result.extend(self._execute(env, tgt, src, overwarn)) return SCons.Node.NodeList(result) overwarn.warn() tlist, slist = self._create_nodes(env, target, source) # Check for errors with the specified target/source lists. _node_errors(self, env, tlist, slist) # The targets are fine, so find or make the appropriate Executor to # build this particular list of targets from this particular list of # sources. executor = None key = None if self.multi: try: executor = tlist[0].get_executor(create = 0) except (AttributeError, IndexError): pass else: executor.add_sources(slist) if executor is None: if not self.action: fmt = "Builder %s must have an action to build %s." raise UserError(fmt % (self.get_name(env or self.env), list(map(str,tlist)))) key = self.action.batch_key(env or self.env, tlist, slist) if key: try: executor = SCons.Executor.GetBatchExecutor(key) except KeyError: pass else: executor.add_batch(tlist, slist) if executor is None: executor = SCons.Executor.Executor(self.action, env, [], tlist, slist, executor_kw) if key: SCons.Executor.AddBatchExecutor(key, executor) # Now set up the relevant information in the target Nodes themselves. for t in tlist: t.cwd = env.fs.getcwd() t.builder_set(self) t.env_set(env) t.add_source(slist) t.set_executor(executor) t.set_explicit(self.is_explicit) return SCons.Node.NodeList(tlist) def __call__(self, env, target=None, source=None, chdir=_null, **kw): # We now assume that target and source are lists or None. # The caller (typically Environment.BuilderWrapper) is # responsible for converting any scalar values to lists. if chdir is _null: ekw = self.executor_kw else: ekw = self.executor_kw.copy() ekw['chdir'] = chdir if kw: if 'srcdir' in kw: def prependDirIfRelative(f, srcdir=kw['srcdir']): import os.path if SCons.Util.is_String(f) and not os.path.isabs(f): f = os.path.join(srcdir, f) return f if not SCons.Util.is_List(source): source = [source] source = list(map(prependDirIfRelative, source)) del kw['srcdir'] if self.overrides: env_kw = self.overrides.copy() env_kw.update(kw) else: env_kw = kw else: env_kw = self.overrides env = env.Override(env_kw) return self._execute(env, target, source, OverrideWarner(kw), ekw) def adjust_suffix(self, suff): if suff and not suff[0] in [ '.', '_', '$' ]: return '.' + suff return suff def get_prefix(self, env, sources=[]): prefix = self.prefix if callable(prefix): prefix = prefix(env, sources) return env.subst(prefix) def set_suffix(self, suffix): if not callable(suffix): suffix = self.adjust_suffix(suffix) self.suffix = suffix def get_suffix(self, env, sources=[]): suffix = self.suffix if callable(suffix): suffix = suffix(env, sources) return env.subst(suffix) def set_src_suffix(self, src_suffix): if not src_suffix: src_suffix = [] elif not SCons.Util.is_List(src_suffix): src_suffix = [ src_suffix ] self.src_suffix = [callable(suf) and suf or self.adjust_suffix(suf) for suf in src_suffix] def get_src_suffix(self, env): """Get the first src_suffix in the list of src_suffixes.""" ret = self.src_suffixes(env) if not ret: return '' return ret[0] def add_emitter(self, suffix, emitter): """Add a suffix-emitter mapping to this Builder. This assumes that emitter has been initialized with an appropriate dictionary type, and will throw a TypeError if not, so the caller is responsible for knowing that this is an appropriate method to call for the Builder in question. """ self.emitter[suffix] = emitter def add_src_builder(self, builder): """ Add a new Builder to the list of src_builders. This requires wiping out cached values so that the computed lists of source suffixes get re-calculated. """ self._memo = {} self.src_builder.append(builder) def _get_sdict(self, env): """ Returns a dictionary mapping all of the source suffixes of all src_builders of this Builder to the underlying Builder that should be called first. This dictionary is used for each target specified, so we save a lot of extra computation by memoizing it for each construction environment. Note that this is re-computed each time, not cached, because there might be changes to one of our source Builders (or one of their source Builders, and so on, and so on...) that we can't "see." The underlying methods we call cache their computed values, though, so we hope repeatedly aggregating them into a dictionary like this won't be too big a hit. We may need to look for a better way to do this if performance data show this has turned into a significant bottleneck. """ sdict = {} for bld in self.get_src_builders(env): for suf in bld.src_suffixes(env): sdict[suf] = bld return sdict def src_builder_sources(self, env, source, overwarn={}): sdict = self._get_sdict(env) src_suffixes = self.src_suffixes(env) lengths = list(set(map(len, src_suffixes))) def match_src_suffix(name, src_suffixes=src_suffixes, lengths=lengths): node_suffixes = [name[-l:] for l in lengths] for suf in src_suffixes: if suf in node_suffixes: return suf return None result = [] for s in SCons.Util.flatten(source): if SCons.Util.is_String(s): match_suffix = match_src_suffix(env.subst(s)) if not match_suffix and not '.' in s: src_suf = self.get_src_suffix(env) s = self._adjustixes(s, None, src_suf)[0] else: match_suffix = match_src_suffix(s.name) if match_suffix: try: bld = sdict[match_suffix] except KeyError: result.append(s) else: tlist = bld._execute(env, None, [s], overwarn) # If the subsidiary Builder returned more than one # target, then filter out any sources that this # Builder isn't capable of building. if len(tlist) > 1: tlist = [t for t in tlist if match_src_suffix(t.name)] result.extend(tlist) else: result.append(s) source_factory = env.get_factory(self.source_factory) return env.arg2nodes(result, source_factory) def _get_src_builders_key(self, env): return id(env) memoizer_counters.append(SCons.Memoize.CountDict('get_src_builders', _get_src_builders_key)) def get_src_builders(self, env): """ Returns the list of source Builders for this Builder. This exists mainly to look up Builders referenced as strings in the 'BUILDER' variable of the construction environment and cache the result. """ memo_key = id(env) try: memo_dict = self._memo['get_src_builders'] except KeyError: memo_dict = {} self._memo['get_src_builders'] = memo_dict else: try: return memo_dict[memo_key] except KeyError: pass builders = [] for bld in self.src_builder: if SCons.Util.is_String(bld): try: bld = env['BUILDERS'][bld] except KeyError: continue builders.append(bld) memo_dict[memo_key] = builders return builders def _subst_src_suffixes_key(self, env): return id(env) memoizer_counters.append(SCons.Memoize.CountDict('subst_src_suffixes', _subst_src_suffixes_key)) def subst_src_suffixes(self, env): """ The suffix list may contain construction variable expansions, so we have to evaluate the individual strings. To avoid doing this over and over, we memoize the results for each construction environment. """ memo_key = id(env) try: memo_dict = self._memo['subst_src_suffixes'] except KeyError: memo_dict = {} self._memo['subst_src_suffixes'] = memo_dict else: try: return memo_dict[memo_key] except KeyError: pass suffixes = [env.subst(x) for x in self.src_suffix] memo_dict[memo_key] = suffixes return suffixes def src_suffixes(self, env): """ Returns the list of source suffixes for all src_builders of this Builder. This is essentially a recursive descent of the src_builder "tree." (This value isn't cached because there may be changes in a src_builder many levels deep that we can't see.) """ sdict = {} suffixes = self.subst_src_suffixes(env) for s in suffixes: sdict[s] = 1 for builder in self.get_src_builders(env): for s in builder.src_suffixes(env): if s not in sdict: sdict[s] = 1 suffixes.append(s) return suffixes class CompositeBuilder(SCons.Util.Proxy): """A Builder Proxy whose main purpose is to always have a DictCmdGenerator as its action, and to provide access to the DictCmdGenerator's add_action() method. """ def __init__(self, builder, cmdgen): if __debug__: logInstanceCreation(self, 'Builder.CompositeBuilder') SCons.Util.Proxy.__init__(self, builder) # cmdgen should always be an instance of DictCmdGenerator. self.cmdgen = cmdgen self.builder = builder __call__ = SCons.Util.Delegate('__call__') def add_action(self, suffix, action): self.cmdgen.add_action(suffix, action) self.set_src_suffix(self.cmdgen.src_suffixes()) def is_a_Builder(obj): """"Returns True iff the specified obj is one of our Builder classes. The test is complicated a bit by the fact that CompositeBuilder is a proxy, not a subclass of BuilderBase. """ return (isinstance(obj, BuilderBase) or isinstance(obj, CompositeBuilder) or callable(obj)) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Defaults.py0000644000175000017500000004236712114661557021440 0ustar dktrkranzdktrkranz"""SCons.Defaults Builders and other things for the local site. Here's where we'll duplicate the functionality of autoconf until we move it into the installation procedure or use something like qmconf. The code that reads the registry to find MSVC components was borrowed from distutils.msvccompiler. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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. # from __future__ import division __revision__ = "src/engine/SCons/Defaults.py 2013/03/03 09:48:35 garyo" import os import errno import shutil import stat import time import sys import SCons.Action import SCons.Builder import SCons.CacheDir import SCons.Environment import SCons.PathList import SCons.Subst import SCons.Tool # A placeholder for a default Environment (for fetching source files # from source code management systems and the like). This must be # initialized later, after the top-level directory is set by the calling # interface. _default_env = None # Lazily instantiate the default environment so the overhead of creating # it doesn't apply when it's not needed. def _fetch_DefaultEnvironment(*args, **kw): """ Returns the already-created default construction environment. """ global _default_env return _default_env def DefaultEnvironment(*args, **kw): """ Initial public entry point for creating the default construction Environment. After creating the environment, we overwrite our name (DefaultEnvironment) with the _fetch_DefaultEnvironment() function, which more efficiently returns the initialized default construction environment without checking for its existence. (This function still exists with its _default_check because someone else (*cough* Script/__init__.py *cough*) may keep a reference to this function. So we can't use the fully functional idiom of having the name originally be a something that *only* creates the construction environment and then overwrites the name.) """ global _default_env if not _default_env: import SCons.Util _default_env = SCons.Environment.Environment(*args, **kw) if SCons.Util.md5: _default_env.Decider('MD5') else: _default_env.Decider('timestamp-match') global DefaultEnvironment DefaultEnvironment = _fetch_DefaultEnvironment _default_env._CacheDir_path = None return _default_env # Emitters for setting the shared attribute on object files, # and an action for checking that all of the source files # going into a shared library are, in fact, shared. def StaticObjectEmitter(target, source, env): for tgt in target: tgt.attributes.shared = None return (target, source) def SharedObjectEmitter(target, source, env): for tgt in target: tgt.attributes.shared = 1 return (target, source) def SharedFlagChecker(source, target, env): same = env.subst('$STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME') if same == '0' or same == '' or same == 'False': for src in source: try: shared = src.attributes.shared except AttributeError: shared = None if not shared: raise SCons.Errors.UserError("Source file: %s is static and is not compatible with shared target: %s" % (src, target[0])) SharedCheck = SCons.Action.Action(SharedFlagChecker, None) # Some people were using these variable name before we made # SourceFileScanner part of the public interface. Don't break their # SConscript files until we've given them some fair warning and a # transition period. CScan = SCons.Tool.CScanner DScan = SCons.Tool.DScanner LaTeXScan = SCons.Tool.LaTeXScanner ObjSourceScan = SCons.Tool.SourceFileScanner ProgScan = SCons.Tool.ProgramScanner # These aren't really tool scanners, so they don't quite belong with # the rest of those in Tool/__init__.py, but I'm not sure where else # they should go. Leave them here for now. import SCons.Scanner.Dir DirScanner = SCons.Scanner.Dir.DirScanner() DirEntryScanner = SCons.Scanner.Dir.DirEntryScanner() # Actions for common languages. CAction = SCons.Action.Action("$CCCOM", "$CCCOMSTR") ShCAction = SCons.Action.Action("$SHCCCOM", "$SHCCCOMSTR") CXXAction = SCons.Action.Action("$CXXCOM", "$CXXCOMSTR") ShCXXAction = SCons.Action.Action("$SHCXXCOM", "$SHCXXCOMSTR") ASAction = SCons.Action.Action("$ASCOM", "$ASCOMSTR") ASPPAction = SCons.Action.Action("$ASPPCOM", "$ASPPCOMSTR") LinkAction = SCons.Action.Action("$LINKCOM", "$LINKCOMSTR") ShLinkAction = SCons.Action.Action("$SHLINKCOM", "$SHLINKCOMSTR") LdModuleLinkAction = SCons.Action.Action("$LDMODULECOM", "$LDMODULECOMSTR") # Common tasks that we allow users to perform in platform-independent # ways by creating ActionFactory instances. ActionFactory = SCons.Action.ActionFactory def get_paths_str(dest): # If dest is a list, we need to manually call str() on each element if SCons.Util.is_List(dest): elem_strs = [] for element in dest: elem_strs.append('"' + str(element) + '"') return '[' + ', '.join(elem_strs) + ']' else: return '"' + str(dest) + '"' def chmod_func(dest, mode): SCons.Node.FS.invalidate_node_memos(dest) if not SCons.Util.is_List(dest): dest = [dest] for element in dest: os.chmod(str(element), mode) def chmod_strfunc(dest, mode): return 'Chmod(%s, 0%o)' % (get_paths_str(dest), mode) Chmod = ActionFactory(chmod_func, chmod_strfunc) def copy_func(dest, src): SCons.Node.FS.invalidate_node_memos(dest) if SCons.Util.is_List(src) and os.path.isdir(dest): for file in src: shutil.copy2(file, dest) return 0 elif os.path.isfile(src): return shutil.copy2(src, dest) else: return shutil.copytree(src, dest, 1) Copy = ActionFactory(copy_func, lambda dest, src: 'Copy("%s", "%s")' % (dest, src), convert=str) def delete_func(dest, must_exist=0): SCons.Node.FS.invalidate_node_memos(dest) if not SCons.Util.is_List(dest): dest = [dest] for entry in dest: entry = str(entry) # os.path.exists returns False with broken links that exist entry_exists = os.path.exists(entry) or os.path.islink(entry) if not entry_exists and not must_exist: continue # os.path.isdir returns True when entry is a link to a dir if os.path.isdir(entry) and not os.path.islink(entry): shutil.rmtree(entry, 1) continue os.unlink(entry) def delete_strfunc(dest, must_exist=0): return 'Delete(%s)' % get_paths_str(dest) Delete = ActionFactory(delete_func, delete_strfunc) def mkdir_func(dest): SCons.Node.FS.invalidate_node_memos(dest) if not SCons.Util.is_List(dest): dest = [dest] for entry in dest: try: os.makedirs(str(entry)) except os.error, e: p = str(entry) if (e.args[0] == errno.EEXIST or (sys.platform=='win32' and e.args[0]==183)) \ and os.path.isdir(str(entry)): pass # not an error if already exists else: raise Mkdir = ActionFactory(mkdir_func, lambda dir: 'Mkdir(%s)' % get_paths_str(dir)) def move_func(dest, src): SCons.Node.FS.invalidate_node_memos(dest) SCons.Node.FS.invalidate_node_memos(src) shutil.move(src, dest) Move = ActionFactory(move_func, lambda dest, src: 'Move("%s", "%s")' % (dest, src), convert=str) def touch_func(dest): SCons.Node.FS.invalidate_node_memos(dest) if not SCons.Util.is_List(dest): dest = [dest] for file in dest: file = str(file) mtime = int(time.time()) if os.path.exists(file): atime = os.path.getatime(file) else: open(file, 'w') atime = mtime os.utime(file, (atime, mtime)) Touch = ActionFactory(touch_func, lambda file: 'Touch(%s)' % get_paths_str(file)) # Internal utility functions def _concat(prefix, list, suffix, env, f=lambda x: x, target=None, source=None): """ Creates a new list from 'list' by first interpolating each element in the list using the 'env' dictionary and then calling f on the list, and finally calling _concat_ixes to concatenate 'prefix' and 'suffix' onto each element of the list. """ if not list: return list l = f(SCons.PathList.PathList(list).subst_path(env, target, source)) if l is not None: list = l return _concat_ixes(prefix, list, suffix, env) def _concat_ixes(prefix, list, suffix, env): """ Creates a new list from 'list' by concatenating the 'prefix' and 'suffix' arguments onto each element of the list. A trailing space on 'prefix' or leading space on 'suffix' will cause them to be put into separate list elements rather than being concatenated. """ result = [] # ensure that prefix and suffix are strings prefix = str(env.subst(prefix, SCons.Subst.SUBST_RAW)) suffix = str(env.subst(suffix, SCons.Subst.SUBST_RAW)) for x in list: if isinstance(x, SCons.Node.FS.File): result.append(x) continue x = str(x) if x: if prefix: if prefix[-1] == ' ': result.append(prefix[:-1]) elif x[:len(prefix)] != prefix: x = prefix + x result.append(x) if suffix: if suffix[0] == ' ': result.append(suffix[1:]) elif x[-len(suffix):] != suffix: result[-1] = result[-1]+suffix return result def _stripixes(prefix, itms, suffix, stripprefixes, stripsuffixes, env, c=None): """ This is a wrapper around _concat()/_concat_ixes() that checks for the existence of prefixes or suffixes on list items and strips them where it finds them. This is used by tools (like the GNU linker) that need to turn something like 'libfoo.a' into '-lfoo'. """ if not itms: return itms if not callable(c): env_c = env['_concat'] if env_c != _concat and callable(env_c): # There's a custom _concat() method in the construction # environment, and we've allowed people to set that in # the past (see test/custom-concat.py), so preserve the # backwards compatibility. c = env_c else: c = _concat_ixes stripprefixes = list(map(env.subst, SCons.Util.flatten(stripprefixes))) stripsuffixes = list(map(env.subst, SCons.Util.flatten(stripsuffixes))) stripped = [] for l in SCons.PathList.PathList(itms).subst_path(env, None, None): if isinstance(l, SCons.Node.FS.File): stripped.append(l) continue if not SCons.Util.is_String(l): l = str(l) for stripprefix in stripprefixes: lsp = len(stripprefix) if l[:lsp] == stripprefix: l = l[lsp:] # Do not strip more than one prefix break for stripsuffix in stripsuffixes: lss = len(stripsuffix) if l[-lss:] == stripsuffix: l = l[:-lss] # Do not strip more than one suffix break stripped.append(l) return c(prefix, stripped, suffix, env) def processDefines(defs): """process defines, resolving strings, lists, dictionaries, into a list of strings """ if SCons.Util.is_List(defs): l = [] for d in defs: if d is None: continue elif SCons.Util.is_List(d) or isinstance(d, tuple): if len(d) >= 2: l.append(str(d[0]) + '=' + str(d[1])) else: l.append(str(d[0])) elif SCons.Util.is_Dict(d): for macro,value in d.iteritems(): if value is not None: l.append(str(macro) + '=' + str(value)) else: l.append(str(macro)) elif SCons.Util.is_String(d): l.append(str(d)) else: raise SCons.Errors.UserError("DEFINE %s is not a list, dict, string or None."%repr(d)) elif SCons.Util.is_Dict(defs): # The items in a dictionary are stored in random order, but # if the order of the command-line options changes from # invocation to invocation, then the signature of the command # line will change and we'll get random unnecessary rebuilds. # Consequently, we have to sort the keys to ensure a # consistent order... l = [] for k,v in sorted(defs.items()): if v is None: l.append(str(k)) else: l.append(str(k) + '=' + str(v)) else: l = [str(defs)] return l def _defines(prefix, defs, suffix, env, c=_concat_ixes): """A wrapper around _concat_ixes that turns a list or string into a list of C preprocessor command-line definitions. """ return c(prefix, env.subst_path(processDefines(defs)), suffix, env) class NullCmdGenerator(object): """This is a callable class that can be used in place of other command generators if you don't want them to do anything. The __call__ method for this class simply returns the thing you instantiated it with. Example usage: env["DO_NOTHING"] = NullCmdGenerator env["LINKCOM"] = "${DO_NOTHING('$LINK $SOURCES $TARGET')}" """ def __init__(self, cmd): self.cmd = cmd def __call__(self, target, source, env, for_signature=None): return self.cmd class Variable_Method_Caller(object): """A class for finding a construction variable on the stack and calling one of its methods. We use this to support "construction variables" in our string eval()s that actually stand in for methods--specifically, use of "RDirs" in call to _concat that should actually execute the "TARGET.RDirs" method. (We used to support this by creating a little "build dictionary" that mapped RDirs to the method, but this got in the way of Memoizing construction environments, because we had to create new environment objects to hold the variables.) """ def __init__(self, variable, method): self.variable = variable self.method = method def __call__(self, *args, **kw): try: 1//0 except ZeroDivisionError: # Don't start iterating with the current stack-frame to # prevent creating reference cycles (f_back is safe). frame = sys.exc_info()[2].tb_frame.f_back variable = self.variable while frame: if variable in frame.f_locals: v = frame.f_locals[variable] if v: method = getattr(v, self.method) return method(*args, **kw) frame = frame.f_back return None ConstructionEnvironment = { 'BUILDERS' : {}, 'SCANNERS' : [], 'CONFIGUREDIR' : '#/.sconf_temp', 'CONFIGURELOG' : '#/config.log', 'CPPSUFFIXES' : SCons.Tool.CSuffixes, 'DSUFFIXES' : SCons.Tool.DSuffixes, 'ENV' : {}, 'IDLSUFFIXES' : SCons.Tool.IDLSuffixes, # 'LATEXSUFFIXES' : SCons.Tool.LaTeXSuffixes, # moved to the TeX tools generate functions '_concat' : _concat, '_defines' : _defines, '_stripixes' : _stripixes, '_LIBFLAGS' : '${_concat(LIBLINKPREFIX, LIBS, LIBLINKSUFFIX, __env__)}', '_LIBDIRFLAGS' : '$( ${_concat(LIBDIRPREFIX, LIBPATH, LIBDIRSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)', '_CPPINCFLAGS' : '$( ${_concat(INCPREFIX, CPPPATH, INCSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)', '_CPPDEFFLAGS' : '${_defines(CPPDEFPREFIX, CPPDEFINES, CPPDEFSUFFIX, __env__)}', 'TEMPFILE' : NullCmdGenerator, 'Dir' : Variable_Method_Caller('TARGET', 'Dir'), 'Dirs' : Variable_Method_Caller('TARGET', 'Dirs'), 'File' : Variable_Method_Caller('TARGET', 'File'), 'RDirs' : Variable_Method_Caller('TARGET', 'RDirs'), } # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Errors.py0000644000175000017500000001643612114661557021143 0ustar dktrkranzdktrkranz# # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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. # """SCons.Errors This file contains the exception classes used to handle internal and user errors in SCons. """ __revision__ = "src/engine/SCons/Errors.py 2013/03/03 09:48:35 garyo" import SCons.Util import exceptions class BuildError(Exception): """ Errors occuring while building. BuildError have the following attributes: Information about the cause of the build error: ----------------------------------------------- errstr : a description of the error message status : the return code of the action that caused the build error. Must be set to a non-zero value even if the build error is not due to an action returning a non-zero returned code. exitstatus : SCons exit status due to this build error. Must be nonzero unless due to an explicit Exit() call. Not always the same as status, since actions return a status code that should be respected, but SCons typically exits with 2 irrespective of the return value of the failed action. filename : The name of the file or directory that caused the build error. Set to None if no files are associated with this error. This might be different from the target being built. For example, failure to create the directory in which the target file will appear. It can be None if the error is not due to a particular filename. exc_info : Info about exception that caused the build error. Set to (None, None, None) if this build error is not due to an exception. Information about the cause of the location of the error: --------------------------------------------------------- node : the error occured while building this target node(s) executor : the executor that caused the build to fail (might be None if the build failures is not due to the executor failing) action : the action that caused the build to fail (might be None if the build failures is not due to the an action failure) command : the command line for the action that caused the build to fail (might be None if the build failures is not due to the an action failure) """ def __init__(self, node=None, errstr="Unknown error", status=2, exitstatus=2, filename=None, executor=None, action=None, command=None, exc_info=(None, None, None)): self.errstr = errstr self.status = status self.exitstatus = exitstatus self.filename = filename self.exc_info = exc_info self.node = node self.executor = executor self.action = action self.command = command Exception.__init__(self, node, errstr, status, exitstatus, filename, executor, action, command, exc_info) def __str__(self): if self.filename: return self.filename + ': ' + self.errstr else: return self.errstr class InternalError(Exception): pass class UserError(Exception): pass class StopError(Exception): pass class EnvironmentError(Exception): pass class MSVCError(IOError): pass class ExplicitExit(Exception): def __init__(self, node=None, status=None, *args): self.node = node self.status = status self.exitstatus = status Exception.__init__(self, *args) def convert_to_BuildError(status, exc_info=None): """ Convert any return code a BuildError Exception. `status' can either be a return code or an Exception. The buildError.status we set here will normally be used as the exit status of the "scons" process. """ if not exc_info and isinstance(status, Exception): exc_info = (status.__class__, status, None) if isinstance(status, BuildError): buildError = status buildError.exitstatus = 2 # always exit with 2 on build errors elif isinstance(status, ExplicitExit): status = status.status errstr = 'Explicit exit, status %s' % status buildError = BuildError( errstr=errstr, status=status, # might be 0, OK here exitstatus=status, # might be 0, OK here exc_info=exc_info) elif isinstance(status, (StopError, UserError)): buildError = BuildError( errstr=str(status), status=2, exitstatus=2, exc_info=exc_info) elif isinstance(status, exceptions.EnvironmentError): # If an IOError/OSError happens, raise a BuildError. # Report the name of the file or directory that caused the # error, which might be different from the target being built # (for example, failure to create the directory in which the # target file will appear). try: filename = status.filename except AttributeError: filename = None buildError = BuildError( errstr=status.strerror, status=status.errno, exitstatus=2, filename=filename, exc_info=exc_info) elif isinstance(status, Exception): buildError = BuildError( errstr='%s : %s' % (status.__class__.__name__, status), status=2, exitstatus=2, exc_info=exc_info) elif SCons.Util.is_String(status): buildError = BuildError( errstr=status, status=2, exitstatus=2) else: buildError = BuildError( errstr="Error %s" % status, status=status, exitstatus=2) #import sys #sys.stderr.write("convert_to_BuildError: status %s => (errstr %s, status %s)"%(status,buildError.errstr, buildError.status)) return buildError # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Variables/0000755000175000017500000000000012114661560021205 5ustar dktrkranzdktrkranzscons-doc-2.3.0/src/engine/SCons/Variables/PackageVariableTests.py0000644000175000017500000001055112114661560025605 0ustar dktrkranzdktrkranz# # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Variables/PackageVariableTests.py 2013/03/03 09:48:35 garyo" import sys import unittest import SCons.Errors import SCons.Variables import TestCmd class PackageVariableTestCase(unittest.TestCase): def test_PackageVariable(self): """Test PackageVariable creation""" opts = SCons.Variables.Variables() opts.Add(SCons.Variables.PackageVariable('test', 'test option help', '/default/path')) o = opts.options[0] assert o.key == 'test', o.key assert o.help == 'test option help\n ( yes | no | /path/to/test )', repr(o.help) assert o.default == '/default/path', o.default assert o.validator is not None, o.validator assert o.converter is not None, o.converter def test_converter(self): """Test the PackageVariable converter""" opts = SCons.Variables.Variables() opts.Add(SCons.Variables.PackageVariable('test', 'test option help', '/default/path')) o = opts.options[0] true_values = [ 'yes', 'YES', 'true', 'TRUE', 'on', 'ON', 'enable', 'ENABLE', 'search', 'SEARCH', ] false_values = [ 'no', 'NO', 'false', 'FALSE', 'off', 'OFF', 'disable', 'DISABLE', ] for t in true_values: x = o.converter(t) assert x, "converter returned false for '%s'" % t for f in false_values: x = o.converter(f) assert not x, "converter returned true for '%s'" % f x = o.converter('/explicit/path') assert x == '/explicit/path', x # Make sure the converter returns True if we give it str(True) and # False when we give it str(False). This assures consistent operation # through a cycle of Variables.Save() -> Variables(). x = o.converter(str(True)) assert x == True, "converter returned a string when given str(True)" x = o.converter(str(False)) assert x == False, "converter returned a string when given str(False)" def test_validator(self): """Test the PackageVariable validator""" opts = SCons.Variables.Variables() opts.Add(SCons.Variables.PackageVariable('test', 'test option help', '/default/path')) test = TestCmd.TestCmd(workdir='') test.write('exists', 'exists\n') o = opts.options[0] env = {'F':False, 'T':True, 'X':'x'} exists = test.workpath('exists') does_not_exist = test.workpath('does_not_exist') o.validator('F', '/path', env) o.validator('T', '/path', env) o.validator('X', exists, env) caught = None try: o.validator('X', does_not_exist, env) except SCons.Errors.UserError: caught = 1 assert caught, "did not catch expected UserError" if __name__ == "__main__": suite = unittest.makeSuite(PackageVariableTestCase, 'test_') if not unittest.TextTestRunner().run(suite).wasSuccessful(): sys.exit(1) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Variables/PathVariable.py0000644000175000017500000001306112114661560024122 0ustar dktrkranzdktrkranz"""SCons.Variables.PathVariable This file defines an option type for SCons implementing path settings. To be used whenever a a user-specified path override should be allowed. Arguments to PathVariable are: option-name = name of this option on the command line (e.g. "prefix") option-help = help string for option option-dflt = default value for this option validator = [optional] validator for option value. Predefined validators are: PathAccept -- accepts any path setting; no validation PathIsDir -- path must be an existing directory PathIsDirCreate -- path must be a dir; will create PathIsFile -- path must be a file PathExists -- path must exist (any type) [default] The validator is a function that is called and which should return True or False to indicate if the path is valid. The arguments to the validator function are: (key, val, env). The key is the name of the option, the val is the path specified for the option, and the env is the env to which the Otions have been added. Usage example: Examples: prefix=/usr/local opts = Variables() opts = Variables() opts.Add(PathVariable('qtdir', 'where the root of Qt is installed', qtdir, PathIsDir)) opts.Add(PathVariable('qt_includes', 'where the Qt includes are installed', '$qtdir/includes', PathIsDirCreate)) opts.Add(PathVariable('qt_libraries', 'where the Qt library is installed', '$qtdir/lib')) """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Variables/PathVariable.py 2013/03/03 09:48:35 garyo" __all__ = ['PathVariable',] import os import os.path import SCons.Errors class _PathVariableClass(object): def PathAccept(self, key, val, env): """Accepts any path, no checking done.""" pass def PathIsDir(self, key, val, env): """Validator to check if Path is a directory.""" if not os.path.isdir(val): if os.path.isfile(val): m = 'Directory path for option %s is a file: %s' else: m = 'Directory path for option %s does not exist: %s' raise SCons.Errors.UserError(m % (key, val)) def PathIsDirCreate(self, key, val, env): """Validator to check if Path is a directory, creating it if it does not exist.""" if os.path.isfile(val): m = 'Path for option %s is a file, not a directory: %s' raise SCons.Errors.UserError(m % (key, val)) if not os.path.isdir(val): os.makedirs(val) def PathIsFile(self, key, val, env): """validator to check if Path is a file""" if not os.path.isfile(val): if os.path.isdir(val): m = 'File path for option %s is a directory: %s' else: m = 'File path for option %s does not exist: %s' raise SCons.Errors.UserError(m % (key, val)) def PathExists(self, key, val, env): """validator to check if Path exists""" if not os.path.exists(val): m = 'Path for option %s does not exist: %s' raise SCons.Errors.UserError(m % (key, val)) def __call__(self, key, help, default, validator=None): # NB: searchfunc is currenty undocumented and unsupported """ The input parameters describe a 'path list' option, thus they are returned with the correct converter and validator appended. The result is usable for input to opts.Add() . The 'default' option specifies the default path to use if the user does not specify an override with this option. validator is a validator, see this file for examples """ if validator is None: validator = self.PathExists if SCons.Util.is_List(key) or SCons.Util.is_Tuple(key): return (key, '%s ( /path/to/%s )' % (help, key[0]), default, validator, None) else: return (key, '%s ( /path/to/%s )' % (help, key), default, validator, None) PathVariable = _PathVariableClass() # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Variables/PackageVariable.py0000644000175000017500000000705012114661560024562 0ustar dktrkranzdktrkranz"""engine.SCons.Variables.PackageVariable This file defines the option type for SCons implementing 'package activation'. To be used whenever a 'package' may be enabled/disabled and the package path may be specified. Usage example: Examples: x11=no (disables X11 support) x11=yes (will search for the package installation dir) x11=/usr/local/X11 (will check this path for existance) To replace autoconf's --with-xxx=yyy opts = Variables() opts.Add(PackageVariable('x11', 'use X11 installed here (yes = search some places', 'yes')) ... if env['x11'] == True: dir = ... search X11 in some standard places ... env['x11'] = dir if env['x11']: ... build with x11 ... """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Variables/PackageVariable.py 2013/03/03 09:48:35 garyo" __all__ = ['PackageVariable',] import SCons.Errors __enable_strings = ('1', 'yes', 'true', 'on', 'enable', 'search') __disable_strings = ('0', 'no', 'false', 'off', 'disable') def _converter(val): """ """ lval = val.lower() if lval in __enable_strings: return True if lval in __disable_strings: return False #raise ValueError("Invalid value for boolean option: %s" % val) return val def _validator(key, val, env, searchfunc): # NB: searchfunc is currenty undocumented and unsupported """ """ # todo: write validator, check for path import os if env[key] is True: if searchfunc: env[key] = searchfunc(key, val) elif env[key] and not os.path.exists(val): raise SCons.Errors.UserError( 'Path does not exist for option %s: %s' % (key, val)) def PackageVariable(key, help, default, searchfunc=None): # NB: searchfunc is currenty undocumented and unsupported """ The input parameters describe a 'package list' option, thus they are returned with the correct converter and validator appended. The result is usable for input to opts.Add() . A 'package list' option may either be 'all', 'none' or a list of package names (seperated by space). """ help = '\n '.join( (help, '( yes | no | /path/to/%s )' % key)) return (key, help, default, lambda k, v, e: _validator(k,v,e,searchfunc), _converter) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Variables/BoolVariableTests.py0000644000175000017500000000775012114661560025154 0ustar dktrkranzdktrkranz# # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Variables/BoolVariableTests.py 2013/03/03 09:48:35 garyo" import sys import unittest import SCons.Errors import SCons.Variables class BoolVariableTestCase(unittest.TestCase): def test_BoolVariable(self): """Test BoolVariable creation""" opts = SCons.Variables.Variables() opts.Add(SCons.Variables.BoolVariable('test', 'test option help', 0)) o = opts.options[0] assert o.key == 'test', o.key assert o.help == 'test option help (yes|no)', o.help assert o.default == 0, o.default assert o.validator is not None, o.validator assert o.converter is not None, o.converter def test_converter(self): """Test the BoolVariable converter""" opts = SCons.Variables.Variables() opts.Add(SCons.Variables.BoolVariable('test', 'test option help', 0)) o = opts.options[0] true_values = [ 'y', 'Y', 'yes', 'YES', 't', 'T', 'true', 'TRUE', 'on', 'ON', 'all', 'ALL', '1', ] false_values = [ 'n', 'N', 'no', 'NO', 'f', 'F', 'false', 'FALSE', 'off', 'OFF', 'none', 'NONE', '0', ] for t in true_values: x = o.converter(t) assert x, "converter returned false for '%s'" % t for f in false_values: x = o.converter(f) assert not x, "converter returned true for '%s'" % f caught = None try: o.converter('x') except ValueError: caught = 1 assert caught, "did not catch expected ValueError" def test_validator(self): """Test the BoolVariable validator""" opts = SCons.Variables.Variables() opts.Add(SCons.Variables.BoolVariable('test', 'test option help', 0)) o = opts.options[0] env = { 'T' : True, 'F' : False, 'N' : 'xyzzy', } o.validator('T', 0, env) o.validator('F', 0, env) caught = None try: o.validator('N', 0, env) except SCons.Errors.UserError: caught = 1 assert caught, "did not catch expected UserError for N" caught = None try: o.validator('NOSUCHKEY', 0, env) except KeyError: caught = 1 assert caught, "did not catch expected KeyError for NOSUCHKEY" if __name__ == "__main__": suite = unittest.makeSuite(BoolVariableTestCase, 'test_') if not unittest.TextTestRunner().run(suite).wasSuccessful(): sys.exit(1) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Variables/EnumVariableTests.py0000644000175000017500000002031712114661560025157 0ustar dktrkranzdktrkranz# # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Variables/EnumVariableTests.py 2013/03/03 09:48:35 garyo" import sys import unittest import SCons.Errors import SCons.Variables class EnumVariableTestCase(unittest.TestCase): def test_EnumVariable(self): """Test EnumVariable creation""" opts = SCons.Variables.Variables() opts.Add(SCons.Variables.EnumVariable('test', 'test option help', 0, ['one', 'two', 'three'], {})) o = opts.options[0] assert o.key == 'test', o.key assert o.help == 'test option help (one|two|three)', o.help assert o.default == 0, o.default assert o.validator is not None, o.validator assert o.converter is not None, o.converter def test_converter(self): """Test the EnumVariable converter""" opts = SCons.Variables.Variables() opts.Add(SCons.Variables.EnumVariable('test', 'test option help', 0, ['one', 'two', 'three'])) o = opts.options[0] for a in ['one', 'two', 'three', 'no_match']: x = o.converter(a) assert x == a, x opts = SCons.Variables.Variables() opts.Add(SCons.Variables.EnumVariable('test', 'test option help', 0, ['one', 'two', 'three'], {'1' : 'one', '2' : 'two', '3' : 'three'})) o = opts.options[0] x = o.converter('one') assert x == 'one', x x = o.converter('1') assert x == 'one', x x = o.converter('two') assert x == 'two', x x = o.converter('2') assert x == 'two', x x = o.converter('three') assert x == 'three', x x = o.converter('3') assert x == 'three', x opts = SCons.Variables.Variables() opts.Add(SCons.Variables.EnumVariable('test0', 'test option help', 0, ['one', 'two', 'three'], {'a' : 'one', 'b' : 'two', 'c' : 'three'}, ignorecase=0)) opts.Add(SCons.Variables.EnumVariable('test1', 'test option help', 0, ['one', 'two', 'three'], {'a' : 'one', 'b' : 'two', 'c' : 'three'}, ignorecase=1)) opts.Add(SCons.Variables.EnumVariable('test2', 'test option help', 0, ['one', 'two', 'three'], {'a' : 'one', 'b' : 'two', 'c' : 'three'}, ignorecase=2)) o0 = opts.options[0] o1 = opts.options[1] o2 = opts.options[2] table = { 'one' : ['one', 'one', 'one'], 'One' : ['One', 'One', 'one'], 'ONE' : ['ONE', 'ONE', 'one'], 'two' : ['two', 'two', 'two'], 'twO' : ['twO', 'twO', 'two'], 'TWO' : ['TWO', 'TWO', 'two'], 'three' : ['three', 'three', 'three'], 'thRee' : ['thRee', 'thRee', 'three'], 'THREE' : ['THREE', 'THREE', 'three'], 'a' : ['one', 'one', 'one'], 'A' : ['A', 'one', 'one'], 'b' : ['two', 'two', 'two'], 'B' : ['B', 'two', 'two'], 'c' : ['three', 'three', 'three'], 'C' : ['C', 'three', 'three'], } for k, l in table.items(): x = o0.converter(k) assert x == l[0], "o0 got %s, expected %s" % (x, l[0]) x = o1.converter(k) assert x == l[1], "o1 got %s, expected %s" % (x, l[1]) x = o2.converter(k) assert x == l[2], "o2 got %s, expected %s" % (x, l[2]) def test_validator(self): """Test the EnumVariable validator""" opts = SCons.Variables.Variables() opts.Add(SCons.Variables.EnumVariable('test0', 'test option help', 0, ['one', 'two', 'three'], {'a' : 'one', 'b' : 'two', 'c' : 'three'}, ignorecase=0)) opts.Add(SCons.Variables.EnumVariable('test1', 'test option help', 0, ['one', 'two', 'three'], {'a' : 'one', 'b' : 'two', 'c' : 'three'}, ignorecase=1)) opts.Add(SCons.Variables.EnumVariable('test2', 'test option help', 0, ['one', 'two', 'three'], {'a' : 'one', 'b' : 'two', 'c' : 'three'}, ignorecase=2)) o0 = opts.options[0] o1 = opts.options[1] o2 = opts.options[2] def valid(o, v): o.validator('X', v, {}) def invalid(o, v): caught = None try: o.validator('X', v, {}) except SCons.Errors.UserError: caught = 1 assert caught, "did not catch expected UserError for o = %s, v = %s" % (o.key, v) table = { 'one' : [ valid, valid, valid], 'One' : [invalid, valid, valid], 'ONE' : [invalid, valid, valid], 'two' : [ valid, valid, valid], 'twO' : [invalid, valid, valid], 'TWO' : [invalid, valid, valid], 'three' : [ valid, valid, valid], 'thRee' : [invalid, valid, valid], 'THREE' : [invalid, valid, valid], 'a' : [invalid, invalid, invalid], 'A' : [invalid, invalid, invalid], 'b' : [invalid, invalid, invalid], 'B' : [invalid, invalid, invalid], 'c' : [invalid, invalid, invalid], 'C' : [invalid, invalid, invalid], 'no_v' : [invalid, invalid, invalid], } for v, l in table.items(): l[0](o0, v) l[1](o1, v) l[2](o2, v) if __name__ == "__main__": suite = unittest.makeSuite(EnumVariableTestCase, 'test_') if not unittest.TextTestRunner().run(suite).wasSuccessful(): sys.exit(1) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Variables/PathVariableTests.py0000644000175000017500000002053712114661560025153 0ustar dktrkranzdktrkranz# # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Variables/PathVariableTests.py 2013/03/03 09:48:35 garyo" import os.path import sys import unittest import SCons.Errors import SCons.Variables import TestCmd class PathVariableTestCase(unittest.TestCase): def test_PathVariable(self): """Test PathVariable creation""" opts = SCons.Variables.Variables() opts.Add(SCons.Variables.PathVariable('test', 'test option help', '/default/path')) o = opts.options[0] assert o.key == 'test', o.key assert o.help == 'test option help ( /path/to/test )', repr(o.help) assert o.default == '/default/path', o.default assert o.validator is not None, o.validator assert o.converter is None, o.converter def test_PathExists(self): """Test the PathExists validator""" opts = SCons.Variables.Variables() opts.Add(SCons.Variables.PathVariable('test', 'test option help', '/default/path', SCons.Variables.PathVariable.PathExists)) test = TestCmd.TestCmd(workdir='') test.write('exists', 'exists\n') o = opts.options[0] o.validator('X', test.workpath('exists'), {}) dne = test.workpath('does_not_exist') try: o.validator('X', dne, {}) except SCons.Errors.UserError, e: assert str(e) == 'Path for option X does not exist: %s' % dne, e except: raise Exception("did not catch expected UserError") def test_PathIsDir(self): """Test the PathIsDir validator""" opts = SCons.Variables.Variables() opts.Add(SCons.Variables.PathVariable('test', 'test option help', '/default/path', SCons.Variables.PathVariable.PathIsDir)) test = TestCmd.TestCmd(workdir='') test.subdir('dir') test.write('file', "file\n") o = opts.options[0] o.validator('X', test.workpath('dir'), {}) f = test.workpath('file') try: o.validator('X', f, {}) except SCons.Errors.UserError, e: assert str(e) == 'Directory path for option X is a file: %s' % f, e except: raise Exception("did not catch expected UserError") dne = test.workpath('does_not_exist') try: o.validator('X', dne, {}) except SCons.Errors.UserError, e: assert str(e) == 'Directory path for option X does not exist: %s' % dne, e except: raise Exception("did not catch expected UserError") def test_PathIsDirCreate(self): """Test the PathIsDirCreate validator""" opts = SCons.Variables.Variables() opts.Add(SCons.Variables.PathVariable('test', 'test option help', '/default/path', SCons.Variables.PathVariable.PathIsDirCreate)) test = TestCmd.TestCmd(workdir='') test.write('file', "file\n") o = opts.options[0] d = test.workpath('dir') o.validator('X', d, {}) assert os.path.isdir(d) f = test.workpath('file') try: o.validator('X', f, {}) except SCons.Errors.UserError, e: assert str(e) == 'Path for option X is a file, not a directory: %s' % f, e except: raise Exception("did not catch expected UserError") def test_PathIsFile(self): """Test the PathIsFile validator""" opts = SCons.Variables.Variables() opts.Add(SCons.Variables.PathVariable('test', 'test option help', '/default/path', SCons.Variables.PathVariable.PathIsFile)) test = TestCmd.TestCmd(workdir='') test.subdir('dir') test.write('file', "file\n") o = opts.options[0] o.validator('X', test.workpath('file'), {}) d = test.workpath('d') try: o.validator('X', d, {}) except SCons.Errors.UserError, e: assert str(e) == 'File path for option X does not exist: %s' % d, e except: raise Exception("did not catch expected UserError") dne = test.workpath('does_not_exist') try: o.validator('X', dne, {}) except SCons.Errors.UserError, e: assert str(e) == 'File path for option X does not exist: %s' % dne, e except: raise Exception("did not catch expected UserError") def test_PathAccept(self): """Test the PathAccept validator""" opts = SCons.Variables.Variables() opts.Add(SCons.Variables.PathVariable('test', 'test option help', '/default/path', SCons.Variables.PathVariable.PathAccept)) test = TestCmd.TestCmd(workdir='') test.subdir('dir') test.write('file', "file\n") o = opts.options[0] o.validator('X', test.workpath('file'), {}) d = test.workpath('d') o.validator('X', d, {}) dne = test.workpath('does_not_exist') o.validator('X', dne, {}) def test_validator(self): """Test the PathVariable validator argument""" opts = SCons.Variables.Variables() opts.Add(SCons.Variables.PathVariable('test', 'test option help', '/default/path')) test = TestCmd.TestCmd(workdir='') test.write('exists', 'exists\n') o = opts.options[0] o.validator('X', test.workpath('exists'), {}) dne = test.workpath('does_not_exist') try: o.validator('X', dne, {}) except SCons.Errors.UserError, e: expect = 'Path for option X does not exist: %s' % dne assert str(e) == expect, e else: raise Exception("did not catch expected UserError") def my_validator(key, val, env): raise Exception("my_validator() got called for %s, %s!" % (key, val)) opts = SCons.Variables.Variables() opts.Add(SCons.Variables.PathVariable('test2', 'more help', '/default/path/again', my_validator)) o = opts.options[0] try: o.validator('Y', 'value', {}) except Exception, e: assert str(e) == 'my_validator() got called for Y, value!', e else: raise Exception("did not catch expected exception from my_validator()") if __name__ == "__main__": suite = unittest.makeSuite(PathVariableTestCase, 'test_') if not unittest.TextTestRunner().run(suite).wasSuccessful(): sys.exit(1) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Variables/VariablesTests.py0000644000175000017500000004730612114661560024524 0ustar dktrkranzdktrkranz# # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Variables/VariablesTests.py 2013/03/03 09:48:35 garyo" import sys import unittest import TestSCons import SCons.Variables import SCons.Subst import SCons.Warnings class Environment(object): def __init__(self): self.dict = {} def subst(self, x): return SCons.Subst.scons_subst(x, self, gvars=self.dict) def __setitem__(self, key, value): self.dict[key] = value def __getitem__(self, key): return self.dict[key] def __contains__(self, key): return self.dict.__contains__(key) def has_key(self, key): return key in self.dict def check(key, value, env): assert int(value) == 6 * 9, "key %s = %s" % (key, repr(value)) # Check saved option file by executing and comparing against # the expected dictionary def checkSave(file, expected): gdict = {} ldict = {} exec open(file, 'rU').read() in gdict, ldict assert expected == ldict, "%s\n...not equal to...\n%s" % (expected, ldict) class VariablesTestCase(unittest.TestCase): def test_keys(self): """Test the Variables.keys() method""" opts = SCons.Variables.Variables() opts.Add('VAR1') opts.Add('VAR2', 'THE answer to THE question', "42", check, lambda x: int(x) + 12) keys = list(opts.keys()) assert keys == ['VAR1', 'VAR2'], keys def test_Add(self): """Test adding to a Variables object""" opts = SCons.Variables.Variables() opts.Add('VAR') opts.Add('ANSWER', 'THE answer to THE question', "42", check, lambda x: int(x) + 12) o = opts.options[0] assert o.key == 'VAR' assert o.help == '' assert o.default is None assert o.validator is None assert o.converter is None o = opts.options[1] assert o.key == 'ANSWER' assert o.help == 'THE answer to THE question' assert o.default == "42" o.validator(o.key, o.converter(o.default), {}) def test_it(var, opts=opts): exc_caught = None try: opts.Add(var) except SCons.Errors.UserError: exc_caught = 1 assert exc_caught, "did not catch UserError for '%s'" % var test_it('foo/bar') test_it('foo-bar') test_it('foo.bar') def test_AddVariables(self): """Test adding a list of options to a Variables object""" opts = SCons.Variables.Variables() opts.AddVariables(('VAR2',), ('ANSWER2', 'THE answer to THE question', "42", check, lambda x: int(x) + 12)) o = opts.options[0] assert o.key == 'VAR2', o.key assert o.help == '', o.help assert o.default is None, o.default assert o.validator is None, o.validator assert o.converter is None, o.converter o = opts.options[1] assert o.key == 'ANSWER2', o.key assert o.help == 'THE answer to THE question', o.help assert o.default == "42", o.default o.validator(o.key, o.converter(o.default), {}) def test_Update(self): """Test updating an Environment""" # Test that a default value is validated correctly. test = TestSCons.TestSCons() file = test.workpath('custom.py') opts = SCons.Variables.Variables(file) opts.Add('ANSWER', 'THE answer to THE question', "42", check, lambda x: int(x) + 12) env = Environment() opts.Update(env) assert env['ANSWER'] == 54 env = Environment() opts.Update(env, {}) assert env['ANSWER'] == 54 # Test that a bad value from the file is used and # validation fails correctly. test = TestSCons.TestSCons() file = test.workpath('custom.py') test.write('custom.py', 'ANSWER=54') opts = SCons.Variables.Variables(file) opts.Add('ANSWER', 'THE answer to THE question', "42", check, lambda x: int(x) + 12) env = Environment() exc_caught = None try: opts.Update(env) except AssertionError: exc_caught = 1 assert exc_caught, "did not catch expected assertion" env = Environment() exc_caught = None try: opts.Update(env, {}) except AssertionError: exc_caught = 1 assert exc_caught, "did not catch expected assertion" # Test that a good value from the file is used and validated. test = TestSCons.TestSCons() file = test.workpath('custom.py') test.write('custom.py', 'ANSWER=42') opts = SCons.Variables.Variables(file) opts.Add('ANSWER', 'THE answer to THE question', "10", check, lambda x: int(x) + 12) env = Environment() opts.Update(env) assert env['ANSWER'] == 54 env = Environment() opts.Update(env, {}) assert env['ANSWER'] == 54 # Test that a bad value from an args dictionary passed to # Update() is used and validation fails correctly. test = TestSCons.TestSCons() file = test.workpath('custom.py') test.write('custom.py', 'ANSWER=10') opts = SCons.Variables.Variables(file) opts.Add('ANSWER', 'THE answer to THE question', "12", check, lambda x: int(x) + 12) env = Environment() exc_caught = None try: opts.Update(env, {'ANSWER':'54'}) except AssertionError: exc_caught = 1 assert exc_caught, "did not catch expected assertion" # Test that a good value from an args dictionary # passed to Update() is used and validated. test = TestSCons.TestSCons() file = test.workpath('custom.py') test.write('custom.py', 'ANSWER=10') opts = SCons.Variables.Variables(file) opts.Add('ANSWER', 'THE answer to THE question', "12", check, lambda x: int(x) + 12) env = Environment() opts.Update(env, {'ANSWER':'42'}) assert env['ANSWER'] == 54 # Test against a former bug. If we supply a converter, # but no default, the value should *not* appear in the # Environment if no value is specified in the options file # or args. test = TestSCons.TestSCons() file = test.workpath('custom.py') opts = SCons.Variables.Variables(file) opts.Add('ANSWER', help='THE answer to THE question', converter=str) env = Environment() opts.Update(env, {}) assert 'ANSWER' not in env # Test that a default value of None is all right. test = TestSCons.TestSCons() file = test.workpath('custom.py') opts = SCons.Variables.Variables(file) opts.Add('ANSWER', "This is the answer", None, check) env = Environment() opts.Update(env, {}) assert 'ANSWER' not in env def test_args(self): """Test updating an Environment with arguments overridden""" # Test that a bad (command-line) argument is used # and the validation fails correctly. test = TestSCons.TestSCons() file = test.workpath('custom.py') test.write('custom.py', 'ANSWER=42') opts = SCons.Variables.Variables(file, {'ANSWER':54}) opts.Add('ANSWER', 'THE answer to THE question', "42", check, lambda x: int(x) + 12) env = Environment() exc_caught = None try: opts.Update(env) except AssertionError: exc_caught = 1 assert exc_caught, "did not catch expected assertion" # Test that a good (command-line) argument is used and validated. test = TestSCons.TestSCons() file = test.workpath('custom.py') test.write('custom.py', 'ANSWER=54') opts = SCons.Variables.Variables(file, {'ANSWER':42}) opts.Add('ANSWER', 'THE answer to THE question', "54", check, lambda x: int(x) + 12) env = Environment() opts.Update(env) assert env['ANSWER'] == 54 # Test that a (command-line) argument is overridden by a dictionary # supplied to Update() and the dictionary value is validated correctly. test = TestSCons.TestSCons() file = test.workpath('custom.py') test.write('custom.py', 'ANSWER=54') opts = SCons.Variables.Variables(file, {'ANSWER':54}) opts.Add('ANSWER', 'THE answer to THE question', "54", check, lambda x: int(x) + 12) env = Environment() opts.Update(env, {'ANSWER':42}) assert env['ANSWER'] == 54 def test_Save(self): """Testing saving Variables""" test = TestSCons.TestSCons() cache_file = test.workpath('cached.options') opts = SCons.Variables.Variables() def bool_converter(val): if val in [1, 'y']: val = 1 if val in [0, 'n']: val = 0 return val # test saving out empty file opts.Add('OPT_VAL', 'An option to test', 21, None, None) opts.Add('OPT_VAL_2', default='foo') opts.Add('OPT_VAL_3', default=1) opts.Add('OPT_BOOL_0', default='n', converter=bool_converter) opts.Add('OPT_BOOL_1', default='y', converter=bool_converter) opts.Add('OPT_BOOL_2', default=0, converter=bool_converter) env = Environment() opts.Update(env, {'OPT_VAL_3' : 2}) assert env['OPT_VAL'] == 21, env['OPT_VAL'] assert env['OPT_VAL_2'] == 'foo', env['OPT_VAL_2'] assert env['OPT_VAL_3'] == 2, env['OPT_VAL_3'] assert env['OPT_BOOL_0'] == 0, env['OPT_BOOL_0'] assert env['OPT_BOOL_1'] == 1, env['OPT_BOOL_1'] assert env['OPT_BOOL_2'] == '0', env['OPT_BOOL_2'] env['OPT_VAL_2'] = 'bar' env['OPT_BOOL_0'] = 0 env['OPT_BOOL_1'] = 1 env['OPT_BOOL_2'] = 2 opts.Save(cache_file, env) checkSave(cache_file, { 'OPT_VAL_2' : 'bar', 'OPT_VAL_3' : 2, 'OPT_BOOL_2' : 2}) # Test against some old bugs class Foo(object): def __init__(self, x): self.x = x def __str__(self): return self.x test = TestSCons.TestSCons() cache_file = test.workpath('cached.options') opts = SCons.Variables.Variables() opts.Add('THIS_USED_TO_BREAK', 'An option to test', "Default") opts.Add('THIS_ALSO_BROKE', 'An option to test', "Default2") opts.Add('THIS_SHOULD_WORK', 'An option to test', Foo('bar')) env = Environment() opts.Update(env, { 'THIS_USED_TO_BREAK' : "Single'Quotes'In'String", 'THIS_ALSO_BROKE' : "\\Escape\nSequences\t", 'THIS_SHOULD_WORK' : Foo('baz') }) opts.Save(cache_file, env) checkSave(cache_file, { 'THIS_USED_TO_BREAK' : "Single'Quotes'In'String", 'THIS_ALSO_BROKE' : "\\Escape\nSequences\t", 'THIS_SHOULD_WORK' : 'baz' }) def test_GenerateHelpText(self): """Test generating the default format help text""" opts = SCons.Variables.Variables() opts.Add('ANSWER', 'THE answer to THE question', "42", check, lambda x: int(x) + 12) opts.Add('B', 'b - alpha test', "42", check, lambda x: int(x) + 12) opts.Add('A', 'a - alpha test', "42", check, lambda x: int(x) + 12) env = Environment() opts.Update(env, {}) expect = """ ANSWER: THE answer to THE question default: 42 actual: 54 B: b - alpha test default: 42 actual: 54 A: a - alpha test default: 42 actual: 54 """ text = opts.GenerateHelpText(env) assert text == expect, text expectAlpha = """ A: a - alpha test default: 42 actual: 54 ANSWER: THE answer to THE question default: 42 actual: 54 B: b - alpha test default: 42 actual: 54 """ text = opts.GenerateHelpText(env, sort=cmp) assert text == expectAlpha, text def test_FormatVariableHelpText(self): """Test generating custom format help text""" opts = SCons.Variables.Variables() def my_format(env, opt, help, default, actual, aliases): return '%s %s %s %s %s\n' % (opt, default, actual, help, aliases) opts.FormatVariableHelpText = my_format opts.Add('ANSWER', 'THE answer to THE question', "42", check, lambda x: int(x) + 12) opts.Add('B', 'b - alpha test', "42", check, lambda x: int(x) + 12) opts.Add('A', 'a - alpha test', "42", check, lambda x: int(x) + 12) env = Environment() opts.Update(env, {}) expect = """\ ANSWER 42 54 THE answer to THE question ['ANSWER'] B 42 54 b - alpha test ['B'] A 42 54 a - alpha test ['A'] """ text = opts.GenerateHelpText(env) assert text == expect, text expectAlpha = """\ A 42 54 a - alpha test ['A'] ANSWER 42 54 THE answer to THE question ['ANSWER'] B 42 54 b - alpha test ['B'] """ text = opts.GenerateHelpText(env, sort=cmp) assert text == expectAlpha, text def test_Aliases(self): """Test option aliases""" # test alias as a tuple opts = SCons.Variables.Variables() opts.AddVariables( (('ANSWER', 'ANSWERALIAS'), 'THE answer to THE question', "42"), ) env = Environment() opts.Update(env, {'ANSWER' : 'answer'}) assert 'ANSWER' in env env = Environment() opts.Update(env, {'ANSWERALIAS' : 'answer'}) assert 'ANSWER' in env and 'ANSWERALIAS' not in env # test alias as a list opts = SCons.Variables.Variables() opts.AddVariables( (['ANSWER', 'ANSWERALIAS'], 'THE answer to THE question', "42"), ) env = Environment() opts.Update(env, {'ANSWER' : 'answer'}) assert 'ANSWER' in env env = Environment() opts.Update(env, {'ANSWERALIAS' : 'answer'}) assert 'ANSWER' in env and 'ANSWERALIAS' not in env class UnknownVariablesTestCase(unittest.TestCase): def test_unknown(self): """Test the UnknownVariables() method""" opts = SCons.Variables.Variables() opts.Add('ANSWER', 'THE answer to THE question', "42") args = { 'ANSWER' : 'answer', 'UNKNOWN' : 'unknown', } env = Environment() opts.Update(env, args) r = opts.UnknownVariables() assert r == {'UNKNOWN' : 'unknown'}, r assert env['ANSWER'] == 'answer', env['ANSWER'] def test_AddOptionUpdatesUnknown(self): """Test updating of the 'unknown' dict""" opts = SCons.Variables.Variables() opts.Add('A', 'A test variable', "1") args = { 'A' : 'a', 'ADDEDLATER' : 'notaddedyet', } env = Environment() opts.Update(env,args) r = opts.UnknownVariables() assert r == {'ADDEDLATER' : 'notaddedyet'}, r assert env['A'] == 'a', env['A'] opts.Add('ADDEDLATER', 'An option not present initially', "1") args = { 'A' : 'a', 'ADDEDLATER' : 'added', } opts.Update(env, args) r = opts.UnknownVariables() assert len(r) == 0, r assert env['ADDEDLATER'] == 'added', env['ADDEDLATER'] def test_AddOptionWithAliasUpdatesUnknown(self): """Test updating of the 'unknown' dict (with aliases)""" opts = SCons.Variables.Variables() opts.Add('A', 'A test variable', "1") args = { 'A' : 'a', 'ADDEDLATERALIAS' : 'notaddedyet', } env = Environment() opts.Update(env,args) r = opts.UnknownVariables() assert r == {'ADDEDLATERALIAS' : 'notaddedyet'}, r assert env['A'] == 'a', env['A'] opts.AddVariables( (('ADDEDLATER', 'ADDEDLATERALIAS'), 'An option not present initially', "1"), ) args['ADDEDLATERALIAS'] = 'added' opts.Update(env, args) r = opts.UnknownVariables() assert len(r) == 0, r assert env['ADDEDLATER'] == 'added', env['ADDEDLATER'] if __name__ == "__main__": suite = unittest.TestSuite() tclasses = [ VariablesTestCase, UnknownVariablesTestCase ] for tclass in tclasses: names = unittest.getTestCaseNames(tclass, 'test_') suite.addTests(list(map(tclass, names))) if not unittest.TextTestRunner().run(suite).wasSuccessful(): sys.exit(1) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Variables/ListVariable.py0000644000175000017500000001056512114661560024147 0ustar dktrkranzdktrkranz"""engine.SCons.Variables.ListVariable This file defines the option type for SCons implementing 'lists'. A 'list' option may either be 'all', 'none' or a list of names separated by comma. After the option has been processed, the option value holds either the named list elements, all list elemens or no list elements at all. Usage example: list_of_libs = Split('x11 gl qt ical') opts = Variables() opts.Add(ListVariable('shared', 'libraries to build as shared libraries', 'all', elems = list_of_libs)) ... for lib in list_of_libs: if lib in env['shared']: env.SharedObject(...) else: env.Object(...) """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Variables/ListVariable.py 2013/03/03 09:48:35 garyo" # Know Bug: This should behave like a Set-Type, but does not really, # since elements can occur twice. __all__ = ['ListVariable',] import collections import SCons.Util class _ListVariable(collections.UserList): def __init__(self, initlist=[], allowedElems=[]): collections.UserList.__init__(self, [_f for _f in initlist if _f]) self.allowedElems = sorted(allowedElems) def __cmp__(self, other): raise NotImplementedError def __eq__(self, other): raise NotImplementedError def __ge__(self, other): raise NotImplementedError def __gt__(self, other): raise NotImplementedError def __le__(self, other): raise NotImplementedError def __lt__(self, other): raise NotImplementedError def __str__(self): if len(self) == 0: return 'none' self.data.sort() if self.data == self.allowedElems: return 'all' else: return ','.join(self) def prepare_to_store(self): return self.__str__() def _converter(val, allowedElems, mapdict): """ """ if val == 'none': val = [] elif val == 'all': val = allowedElems else: val = [_f for _f in val.split(',') if _f] val = [mapdict.get(v, v) for v in val] notAllowed = [v for v in val if not v in allowedElems] if notAllowed: raise ValueError("Invalid value(s) for option: %s" % ','.join(notAllowed)) return _ListVariable(val, allowedElems) ## def _validator(key, val, env): ## """ ## """ ## # todo: write validater for pgk list ## return 1 def ListVariable(key, help, default, names, map={}): """ The input parameters describe a 'package list' option, thus they are returned with the correct converter and validater appended. The result is usable for input to opts.Add() . A 'package list' option may either be 'all', 'none' or a list of package names (separated by space). """ names_str = 'allowed names: %s' % ' '.join(names) if SCons.Util.is_List(default): default = ','.join(default) help = '\n '.join( (help, '(all|none|comma-separated list of names)', names_str)) return (key, help, default, None, #_validator, lambda val: _converter(val, names, map)) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Variables/EnumVariable.py0000644000175000017500000000741212114661560024135 0ustar dktrkranzdktrkranz"""engine.SCons.Variables.EnumVariable This file defines the option type for SCons allowing only specified input-values. Usage example: opts = Variables() opts.Add(EnumVariable('debug', 'debug output and symbols', 'no', allowed_values=('yes', 'no', 'full'), map={}, ignorecase=2)) ... if env['debug'] == 'full': ... """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Variables/EnumVariable.py 2013/03/03 09:48:35 garyo" __all__ = ['EnumVariable',] import SCons.Errors def _validator(key, val, env, vals): if not val in vals: raise SCons.Errors.UserError( 'Invalid value for option %s: %s. Valid values are: %s' % (key, val, vals)) def EnumVariable(key, help, default, allowed_values, map={}, ignorecase=0): """ The input parameters describe a option with only certain values allowed. They are returned with an appropriate converter and validator appended. The result is usable for input to Variables.Add(). 'key' and 'default' are the values to be passed on to Variables.Add(). 'help' will be appended by the allowed values automatically 'allowed_values' is a list of strings, which are allowed as values for this option. The 'map'-dictionary may be used for converting the input value into canonical values (eg. for aliases). 'ignorecase' defines the behaviour of the validator: If ignorecase == 0, the validator/converter are case-sensitive. If ignorecase == 1, the validator/converter are case-insensitive. If ignorecase == 2, the validator/converter is case-insensitive and the converted value will always be lower-case. The 'validator' tests whether the value is in the list of allowed values. The 'converter' converts input values according to the given 'map'-dictionary (unmapped input values are returned unchanged). """ help = '%s (%s)' % (help, '|'.join(allowed_values)) # define validator if ignorecase >= 1: validator = lambda key, val, env: \ _validator(key, val.lower(), env, allowed_values) else: validator = lambda key, val, env: \ _validator(key, val, env, allowed_values) # define converter if ignorecase == 2: converter = lambda val: map.get(val.lower(), val).lower() elif ignorecase == 1: converter = lambda val: map.get(val.lower(), val) else: converter = lambda val: map.get(val, val) return (key, help, default, validator, converter) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Variables/ListVariableTests.py0000644000175000017500000001113512114661560025164 0ustar dktrkranzdktrkranz# # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Variables/ListVariableTests.py 2013/03/03 09:48:35 garyo" import copy import sys import unittest import SCons.Errors import SCons.Variables class ListVariableTestCase(unittest.TestCase): def test_ListVariable(self): """Test ListVariable creation""" opts = SCons.Variables.Variables() opts.Add(SCons.Variables.ListVariable('test', 'test option help', 'all', ['one', 'two', 'three'])) o = opts.options[0] assert o.key == 'test', o.key assert o.help == 'test option help\n (all|none|comma-separated list of names)\n allowed names: one two three', repr(o.help) assert o.default == 'all', o.default assert o.validator is None, o.validator assert not o.converter is None, o.converter opts = SCons.Variables.Variables() opts.Add(SCons.Variables.ListVariable('test2', 'test2 help', ['one', 'three'], ['one', 'two', 'three'])) o = opts.options[0] assert o.default == 'one,three' def test_converter(self): """Test the ListVariable converter""" opts = SCons.Variables.Variables() opts.Add(SCons.Variables.ListVariable('test', 'test option help', 'all', ['one', 'two', 'three'], {'ONE':'one', 'TWO':'two'})) o = opts.options[0] x = o.converter('all') assert str(x) == 'all', x x = o.converter('none') assert str(x) == 'none', x x = o.converter('one') assert str(x) == 'one', x x = o.converter('ONE') assert str(x) == 'one', x x = o.converter('two') assert str(x) == 'two', x x = o.converter('TWO') assert str(x) == 'two', x x = o.converter('three') assert str(x) == 'three', x x = o.converter('one,two') assert str(x) == 'one,two', x x = o.converter('two,one') assert str(x) == 'one,two', x x = o.converter('one,three') assert str(x) == 'one,three', x x = o.converter('three,one') assert str(x) == 'one,three', x x = o.converter('two,three') assert str(x) == 'three,two', x x = o.converter('three,two') assert str(x) == 'three,two', x x = o.converter('one,two,three') assert str(x) == 'all', x x = o.converter('three,two,one') assert str(x) == 'all', x x = o.converter('three,ONE,TWO') assert str(x) == 'all', x caught = None try: x = o.converter('no_match') except ValueError: caught = 1 assert caught, "did not catch expected ValueError" def test_copy(self): """Test copying a ListVariable like an Environment would""" opts = SCons.Variables.Variables() opts.Add(SCons.Variables.ListVariable('test', 'test option help', 'all', ['one', 'two', 'three'])) o = opts.options[0] l = o.converter('all') n = l.__class__(copy.copy(l)) if __name__ == "__main__": suite = unittest.makeSuite(ListVariableTestCase, 'test_') if not unittest.TextTestRunner().run(suite).wasSuccessful(): sys.exit(1) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Variables/__init__.py0000644000175000017500000002554312114661560023327 0ustar dktrkranzdktrkranz"""engine.SCons.Variables This file defines the Variables class that is used to add user-friendly customizable variables to an SCons build. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Variables/__init__.py 2013/03/03 09:48:35 garyo" import os.path import sys import SCons.Environment import SCons.Errors import SCons.Util import SCons.Warnings from BoolVariable import BoolVariable # okay from EnumVariable import EnumVariable # okay from ListVariable import ListVariable # naja from PackageVariable import PackageVariable # naja from PathVariable import PathVariable # okay class Variables(object): instance=None """ Holds all the options, updates the environment with the variables, and renders the help text. """ def __init__(self, files=[], args={}, is_global=1): """ files - [optional] List of option configuration files to load (backward compatibility) If a single string is passed it is automatically placed in a file list """ self.options = [] self.args = args if not SCons.Util.is_List(files): if files: files = [ files ] else: files = [] self.files = files self.unknown = {} # create the singleton instance if is_global: self=Variables.instance if not Variables.instance: Variables.instance=self def _do_add(self, key, help="", default=None, validator=None, converter=None): class Variable(object): pass option = Variable() # if we get a list or a tuple, we take the first element as the # option key and store the remaining in aliases. if SCons.Util.is_List(key) or SCons.Util.is_Tuple(key): option.key = key[0] option.aliases = key[1:] else: option.key = key option.aliases = [ key ] option.help = help option.default = default option.validator = validator option.converter = converter self.options.append(option) # options might be added after the 'unknown' dict has been set up, # so we remove the key and all its aliases from that dict for alias in list(option.aliases) + [ option.key ]: if alias in self.unknown: del self.unknown[alias] def keys(self): """ Returns the keywords for the options """ return [o.key for o in self.options] def Add(self, key, help="", default=None, validator=None, converter=None, **kw): """ Add an option. key - the name of the variable, or a list or tuple of arguments help - optional help text for the options default - optional default value validator - optional function that is called to validate the option's value Called with (key, value, environment) converter - optional function that is called to convert the option's value before putting it in the environment. """ if SCons.Util.is_List(key) or isinstance(key, tuple): self._do_add(*key) return if not SCons.Util.is_String(key) or \ not SCons.Environment.is_valid_construction_var(key): raise SCons.Errors.UserError("Illegal Variables.Add() key `%s'" % str(key)) self._do_add(key, help, default, validator, converter) def AddVariables(self, *optlist): """ Add a list of options. Each list element is a tuple/list of arguments to be passed on to the underlying method for adding options. Example: opt.AddVariables( ('debug', '', 0), ('CC', 'The C compiler'), ('VALIDATE', 'An option for testing validation', 'notset', validator, None), ) """ for o in optlist: self._do_add(*o) def Update(self, env, args=None): """ Update an environment with the option variables. env - the environment to update. """ values = {} # first set the defaults: for option in self.options: if not option.default is None: values[option.key] = option.default # next set the value specified in the options file for filename in self.files: if os.path.exists(filename): dir = os.path.split(os.path.abspath(filename))[0] if dir: sys.path.insert(0, dir) try: values['__name__'] = filename exec open(filename, 'rU').read() in {}, values finally: if dir: del sys.path[0] del values['__name__'] # set the values specified on the command line if args is None: args = self.args for arg, value in args.items(): added = False for option in self.options: if arg in list(option.aliases) + [ option.key ]: values[option.key] = value added = True if not added: self.unknown[arg] = value # put the variables in the environment: # (don't copy over variables that are not declared as options) for option in self.options: try: env[option.key] = values[option.key] except KeyError: pass # Call the convert functions: for option in self.options: if option.converter and option.key in values: value = env.subst('${%s}'%option.key) try: try: env[option.key] = option.converter(value) except TypeError: env[option.key] = option.converter(value, env) except ValueError, x: raise SCons.Errors.UserError('Error converting option: %s\n%s'%(option.key, x)) # Finally validate the values: for option in self.options: if option.validator and option.key in values: option.validator(option.key, env.subst('${%s}'%option.key), env) def UnknownVariables(self): """ Returns any options in the specified arguments lists that were not known, declared options in this object. """ return self.unknown def Save(self, filename, env): """ Saves all the options in the given file. This file can then be used to load the options next run. This can be used to create an option cache file. filename - Name of the file to save into env - the environment get the option values from """ # Create the file and write out the header try: fh = open(filename, 'w') try: # Make an assignment in the file for each option # within the environment that was assigned a value # other than the default. for option in self.options: try: value = env[option.key] try: prepare = value.prepare_to_store except AttributeError: try: eval(repr(value)) except KeyboardInterrupt: raise except: # Convert stuff that has a repr() that # cannot be evaluated into a string value = SCons.Util.to_String(value) else: value = prepare() defaultVal = env.subst(SCons.Util.to_String(option.default)) if option.converter: defaultVal = option.converter(defaultVal) if str(env.subst('${%s}' % option.key)) != str(defaultVal): fh.write('%s = %s\n' % (option.key, repr(value))) except KeyError: pass finally: fh.close() except IOError, x: raise SCons.Errors.UserError('Error writing options to file: %s\n%s' % (filename, x)) def GenerateHelpText(self, env, sort=None): """ Generate the help text for the options. env - an environment that is used to get the current values of the options. """ if sort: options = sorted(self.options, key=lambda x: x.key) else: options = self.options def format(opt, self=self, env=env): if opt.key in env: actual = env.subst('${%s}' % opt.key) else: actual = None return self.FormatVariableHelpText(env, opt.key, opt.help, opt.default, actual, opt.aliases) lines = [_f for _f in map(format, options) if _f] return ''.join(lines) format = '\n%s: %s\n default: %s\n actual: %s\n' format_ = '\n%s: %s\n default: %s\n actual: %s\n aliases: %s\n' def FormatVariableHelpText(self, env, key, help, default, actual, aliases=[]): # Don't display the key name itself as an alias. aliases = [a for a in aliases if a != key] if len(aliases)==0: return self.format % (key, help, default, actual) else: return self.format_ % (key, help, default, actual, aliases) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Variables/BoolVariable.py0000644000175000017500000000600412114661560024120 0ustar dktrkranzdktrkranz"""engine.SCons.Variables.BoolVariable This file defines the option type for SCons implementing true/false values. Usage example: opts = Variables() opts.Add(BoolVariable('embedded', 'build for an embedded system', 0)) ... if env['embedded'] == 1: ... """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Variables/BoolVariable.py 2013/03/03 09:48:35 garyo" __all__ = ['BoolVariable',] import SCons.Errors __true_strings = ('y', 'yes', 'true', 't', '1', 'on' , 'all' ) __false_strings = ('n', 'no', 'false', 'f', '0', 'off', 'none') def _text2bool(val): """ Converts strings to True/False depending on the 'truth' expressed by the string. If the string can't be converted, the original value will be returned. See '__true_strings' and '__false_strings' for values considered 'true' or 'false respectivly. This is usable as 'converter' for SCons' Variables. """ lval = val.lower() if lval in __true_strings: return True if lval in __false_strings: return False raise ValueError("Invalid value for boolean option: %s" % val) def _validator(key, val, env): """ Validates the given value to be either '0' or '1'. This is usable as 'validator' for SCons' Variables. """ if not env[key] in (True, False): raise SCons.Errors.UserError( 'Invalid value for boolean option %s: %s' % (key, env[key])) def BoolVariable(key, help, default): """ The input parameters describe a boolen option, thus they are returned with the correct converter and validator appended. The 'help' text will by appended by '(yes|no) to show the valid valued. The result is usable for input to opts.Add(). """ return (key, '%s (yes|no)' % help, default, _validator, _text2bool) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Node/0000755000175000017500000000000012114661557020170 5ustar dktrkranzdktrkranzscons-doc-2.3.0/src/engine/SCons/Node/FS.py0000644000175000017500000034434012114661557021062 0ustar dktrkranzdktrkranz"""scons.Node.FS File system nodes. These Nodes represent the canonical external objects that people think of when they think of building software: files and directories. This holds a "default_fs" variable that should be initialized with an FS that can be used by scripts or modules looking for the canonical default. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Node/FS.py 2013/03/03 09:48:35 garyo" import fnmatch import os import re import shutil import stat import sys import time import codecs import SCons.Action from SCons.Debug import logInstanceCreation import SCons.Errors import SCons.Memoize import SCons.Node import SCons.Node.Alias import SCons.Subst import SCons.Util import SCons.Warnings from SCons.Debug import Trace do_store_info = True print_duplicate = 0 class EntryProxyAttributeError(AttributeError): """ An AttributeError subclass for recording and displaying the name of the underlying Entry involved in an AttributeError exception. """ def __init__(self, entry_proxy, attribute): AttributeError.__init__(self) self.entry_proxy = entry_proxy self.attribute = attribute def __str__(self): entry = self.entry_proxy.get() fmt = "%s instance %s has no attribute %s" return fmt % (entry.__class__.__name__, repr(entry.name), repr(self.attribute)) # The max_drift value: by default, use a cached signature value for # any file that's been untouched for more than two days. default_max_drift = 2*24*60*60 # # We stringify these file system Nodes a lot. Turning a file system Node # into a string is non-trivial, because the final string representation # can depend on a lot of factors: whether it's a derived target or not, # whether it's linked to a repository or source directory, and whether # there's duplication going on. The normal technique for optimizing # calculations like this is to memoize (cache) the string value, so you # only have to do the calculation once. # # A number of the above factors, however, can be set after we've already # been asked to return a string for a Node, because a Repository() or # VariantDir() call or the like may not occur until later in SConscript # files. So this variable controls whether we bother trying to save # string values for Nodes. The wrapper interface can set this whenever # they're done mucking with Repository and VariantDir and the other stuff, # to let this module know it can start returning saved string values # for Nodes. # Save_Strings = None def save_strings(val): global Save_Strings Save_Strings = val # # Avoid unnecessary function calls by recording a Boolean value that # tells us whether or not os.path.splitdrive() actually does anything # on this system, and therefore whether we need to bother calling it # when looking up path names in various methods below. # do_splitdrive = None _my_splitdrive =None def initialize_do_splitdrive(): global do_splitdrive global has_unc drive, path = os.path.splitdrive('X:/foo') has_unc = hasattr(os.path, 'splitunc') do_splitdrive = not not drive or has_unc global _my_splitdrive if has_unc: def splitdrive(p): if p[1:2] == ':': return p[:2], p[2:] if p[0:2] == '//': # Note that we leave a leading slash in the path # because UNC paths are always absolute. return '//', p[1:] return '', p else: def splitdrive(p): if p[1:2] == ':': return p[:2], p[2:] return '', p _my_splitdrive = splitdrive # Keep some commonly used values in global variables to skip to # module look-up costs. global OS_SEP global UNC_PREFIX global os_sep_is_slash OS_SEP = os.sep UNC_PREFIX = OS_SEP + OS_SEP os_sep_is_slash = OS_SEP == '/' initialize_do_splitdrive() # Used to avoid invoking os.path.normpath if not necessary. needs_normpath_check = re.compile( r''' # We need to renormalize the path if it contains any consecutive # '/' characters. .*// | # We need to renormalize the path if it contains a '..' directory. # Note that we check for all the following cases: # # a) The path is a single '..' # b) The path starts with '..'. E.g. '../' or '../moredirs' # but we not match '..abc/'. # c) The path ends with '..'. E.g. '/..' or 'dirs/..' # d) The path contains a '..' in the middle. # E.g. dirs/../moredirs (.*/)?\.\.(?:/|$) | # We need to renormalize the path if it contains a '.' # directory, but NOT if it is a single '.' '/' characters. We # do not want to match a single '.' because this case is checked # for explicitely since this is common enough case. # # Note that we check for all the following cases: # # a) We don't match a single '.' # b) We match if the path starts with '.'. E.g. './' or # './moredirs' but we not match '.abc/'. # c) We match if the path ends with '.'. E.g. '/.' or # 'dirs/.' # d) We match if the path contains a '.' in the middle. # E.g. dirs/./moredirs \./|.*/\.(?:/|$) ''', re.VERBOSE ) needs_normpath_match = needs_normpath_check.match # # SCons.Action objects for interacting with the outside world. # # The Node.FS methods in this module should use these actions to # create and/or remove files and directories; they should *not* use # os.{link,symlink,unlink,mkdir}(), etc., directly. # # Using these SCons.Action objects ensures that descriptions of these # external activities are properly displayed, that the displays are # suppressed when the -s (silent) option is used, and (most importantly) # the actions are disabled when the the -n option is used, in which case # there should be *no* changes to the external file system(s)... # if hasattr(os, 'link'): def _hardlink_func(fs, src, dst): # If the source is a symlink, we can't just hard-link to it # because a relative symlink may point somewhere completely # different. We must disambiguate the symlink and then # hard-link the final destination file. while fs.islink(src): link = fs.readlink(src) if not os.path.isabs(link): src = link else: src = os.path.join(os.path.dirname(src), link) fs.link(src, dst) else: _hardlink_func = None if hasattr(os, 'symlink'): def _softlink_func(fs, src, dst): fs.symlink(src, dst) else: _softlink_func = None def _copy_func(fs, src, dest): shutil.copy2(src, dest) st = fs.stat(src) fs.chmod(dest, stat.S_IMODE(st[stat.ST_MODE]) | stat.S_IWRITE) Valid_Duplicates = ['hard-soft-copy', 'soft-hard-copy', 'hard-copy', 'soft-copy', 'copy'] Link_Funcs = [] # contains the callables of the specified duplication style def set_duplicate(duplicate): # Fill in the Link_Funcs list according to the argument # (discarding those not available on the platform). # Set up the dictionary that maps the argument names to the # underlying implementations. We do this inside this function, # not in the top-level module code, so that we can remap os.link # and os.symlink for testing purposes. link_dict = { 'hard' : _hardlink_func, 'soft' : _softlink_func, 'copy' : _copy_func } if not duplicate in Valid_Duplicates: raise SCons.Errors.InternalError("The argument of set_duplicate " "should be in Valid_Duplicates") global Link_Funcs Link_Funcs = [] for func in duplicate.split('-'): if link_dict[func]: Link_Funcs.append(link_dict[func]) def LinkFunc(target, source, env): # Relative paths cause problems with symbolic links, so # we use absolute paths, which may be a problem for people # who want to move their soft-linked src-trees around. Those # people should use the 'hard-copy' mode, softlinks cannot be # used for that; at least I have no idea how ... src = source[0].abspath dest = target[0].abspath dir, file = os.path.split(dest) if dir and not target[0].fs.isdir(dir): os.makedirs(dir) if not Link_Funcs: # Set a default order of link functions. set_duplicate('hard-soft-copy') fs = source[0].fs # Now link the files with the previously specified order. for func in Link_Funcs: try: func(fs, src, dest) break except (IOError, OSError): # An OSError indicates something happened like a permissions # problem or an attempt to symlink across file-system # boundaries. An IOError indicates something like the file # not existing. In either case, keeping trying additional # functions in the list and only raise an error if the last # one failed. if func == Link_Funcs[-1]: # exception of the last link method (copy) are fatal raise return 0 Link = SCons.Action.Action(LinkFunc, None) def LocalString(target, source, env): return 'Local copy of %s from %s' % (target[0], source[0]) LocalCopy = SCons.Action.Action(LinkFunc, LocalString) def UnlinkFunc(target, source, env): t = target[0] t.fs.unlink(t.abspath) return 0 Unlink = SCons.Action.Action(UnlinkFunc, None) def MkdirFunc(target, source, env): t = target[0] if not t.exists(): t.fs.mkdir(t.abspath) return 0 Mkdir = SCons.Action.Action(MkdirFunc, None, presub=None) MkdirBuilder = None def get_MkdirBuilder(): global MkdirBuilder if MkdirBuilder is None: import SCons.Builder import SCons.Defaults # "env" will get filled in by Executor.get_build_env() # calling SCons.Defaults.DefaultEnvironment() when necessary. MkdirBuilder = SCons.Builder.Builder(action = Mkdir, env = None, explain = None, is_explicit = None, target_scanner = SCons.Defaults.DirEntryScanner, name = "MkdirBuilder") return MkdirBuilder class _Null(object): pass _null = _Null() DefaultSCCSBuilder = None DefaultRCSBuilder = None def get_DefaultSCCSBuilder(): global DefaultSCCSBuilder if DefaultSCCSBuilder is None: import SCons.Builder # "env" will get filled in by Executor.get_build_env() # calling SCons.Defaults.DefaultEnvironment() when necessary. act = SCons.Action.Action('$SCCSCOM', '$SCCSCOMSTR') DefaultSCCSBuilder = SCons.Builder.Builder(action = act, env = None, name = "DefaultSCCSBuilder") return DefaultSCCSBuilder def get_DefaultRCSBuilder(): global DefaultRCSBuilder if DefaultRCSBuilder is None: import SCons.Builder # "env" will get filled in by Executor.get_build_env() # calling SCons.Defaults.DefaultEnvironment() when necessary. act = SCons.Action.Action('$RCS_COCOM', '$RCS_COCOMSTR') DefaultRCSBuilder = SCons.Builder.Builder(action = act, env = None, name = "DefaultRCSBuilder") return DefaultRCSBuilder # Cygwin's os.path.normcase pretends it's on a case-sensitive filesystem. _is_cygwin = sys.platform == "cygwin" if os.path.normcase("TeSt") == os.path.normpath("TeSt") and not _is_cygwin: def _my_normcase(x): return x else: def _my_normcase(x): return x.upper() class DiskChecker(object): def __init__(self, type, do, ignore): self.type = type self.do = do self.ignore = ignore self.func = do def __call__(self, *args, **kw): return self.func(*args, **kw) def set(self, list): if self.type in list: self.func = self.do else: self.func = self.ignore def do_diskcheck_match(node, predicate, errorfmt): result = predicate() try: # If calling the predicate() cached a None value from stat(), # remove it so it doesn't interfere with later attempts to # build this Node as we walk the DAG. (This isn't a great way # to do this, we're reaching into an interface that doesn't # really belong to us, but it's all about performance, so # for now we'll just document the dependency...) if node._memo['stat'] is None: del node._memo['stat'] except (AttributeError, KeyError): pass if result: raise TypeError(errorfmt % node.abspath) def ignore_diskcheck_match(node, predicate, errorfmt): pass def do_diskcheck_rcs(node, name): try: rcs_dir = node.rcs_dir except AttributeError: if node.entry_exists_on_disk('RCS'): rcs_dir = node.Dir('RCS') else: rcs_dir = None node.rcs_dir = rcs_dir if rcs_dir: return rcs_dir.entry_exists_on_disk(name+',v') return None def ignore_diskcheck_rcs(node, name): return None def do_diskcheck_sccs(node, name): try: sccs_dir = node.sccs_dir except AttributeError: if node.entry_exists_on_disk('SCCS'): sccs_dir = node.Dir('SCCS') else: sccs_dir = None node.sccs_dir = sccs_dir if sccs_dir: return sccs_dir.entry_exists_on_disk('s.'+name) return None def ignore_diskcheck_sccs(node, name): return None diskcheck_match = DiskChecker('match', do_diskcheck_match, ignore_diskcheck_match) diskcheck_rcs = DiskChecker('rcs', do_diskcheck_rcs, ignore_diskcheck_rcs) diskcheck_sccs = DiskChecker('sccs', do_diskcheck_sccs, ignore_diskcheck_sccs) diskcheckers = [ diskcheck_match, diskcheck_rcs, diskcheck_sccs, ] def set_diskcheck(list): for dc in diskcheckers: dc.set(list) def diskcheck_types(): return [dc.type for dc in diskcheckers] class EntryProxy(SCons.Util.Proxy): __str__ = SCons.Util.Delegate('__str__') def __get_abspath(self): entry = self.get() return SCons.Subst.SpecialAttrWrapper(entry.get_abspath(), entry.name + "_abspath") def __get_filebase(self): name = self.get().name return SCons.Subst.SpecialAttrWrapper(SCons.Util.splitext(name)[0], name + "_filebase") def __get_suffix(self): name = self.get().name return SCons.Subst.SpecialAttrWrapper(SCons.Util.splitext(name)[1], name + "_suffix") def __get_file(self): name = self.get().name return SCons.Subst.SpecialAttrWrapper(name, name + "_file") def __get_base_path(self): """Return the file's directory and file name, with the suffix stripped.""" entry = self.get() return SCons.Subst.SpecialAttrWrapper(SCons.Util.splitext(entry.get_path())[0], entry.name + "_base") def __get_posix_path(self): """Return the path with / as the path separator, regardless of platform.""" if os_sep_is_slash: return self else: entry = self.get() r = entry.get_path().replace(OS_SEP, '/') return SCons.Subst.SpecialAttrWrapper(r, entry.name + "_posix") def __get_windows_path(self): """Return the path with \ as the path separator, regardless of platform.""" if OS_SEP == '\\': return self else: entry = self.get() r = entry.get_path().replace(OS_SEP, '\\') return SCons.Subst.SpecialAttrWrapper(r, entry.name + "_windows") def __get_srcnode(self): return EntryProxy(self.get().srcnode()) def __get_srcdir(self): """Returns the directory containing the source node linked to this node via VariantDir(), or the directory of this node if not linked.""" return EntryProxy(self.get().srcnode().dir) def __get_rsrcnode(self): return EntryProxy(self.get().srcnode().rfile()) def __get_rsrcdir(self): """Returns the directory containing the source node linked to this node via VariantDir(), or the directory of this node if not linked.""" return EntryProxy(self.get().srcnode().rfile().dir) def __get_dir(self): return EntryProxy(self.get().dir) dictSpecialAttrs = { "base" : __get_base_path, "posix" : __get_posix_path, "windows" : __get_windows_path, "win32" : __get_windows_path, "srcpath" : __get_srcnode, "srcdir" : __get_srcdir, "dir" : __get_dir, "abspath" : __get_abspath, "filebase" : __get_filebase, "suffix" : __get_suffix, "file" : __get_file, "rsrcpath" : __get_rsrcnode, "rsrcdir" : __get_rsrcdir, } def __getattr__(self, name): # This is how we implement the "special" attributes # such as base, posix, srcdir, etc. try: attr_function = self.dictSpecialAttrs[name] except KeyError: try: attr = SCons.Util.Proxy.__getattr__(self, name) except AttributeError, e: # Raise our own AttributeError subclass with an # overridden __str__() method that identifies the # name of the entry that caused the exception. raise EntryProxyAttributeError(self, name) return attr else: return attr_function(self) class Base(SCons.Node.Node): """A generic class for file system entries. This class is for when we don't know yet whether the entry being looked up is a file or a directory. Instances of this class can morph into either Dir or File objects by a later, more precise lookup. Note: this class does not define __cmp__ and __hash__ for efficiency reasons. SCons does a lot of comparing of Node.FS.{Base,Entry,File,Dir} objects, so those operations must be as fast as possible, which means we want to use Python's built-in object identity comparisons. """ memoizer_counters = [] def __init__(self, name, directory, fs): """Initialize a generic Node.FS.Base object. Call the superclass initialization, take care of setting up our relative and absolute paths, identify our parent directory, and indicate that this node should use signatures.""" if __debug__: logInstanceCreation(self, 'Node.FS.Base') SCons.Node.Node.__init__(self) # Filenames and paths are probably reused and are intern'ed to # save some memory. #: Filename with extension as it was specified when the object was #: created; to obtain filesystem path, use Python str() function self.name = SCons.Util.silent_intern(name) #: Cached filename extension self.suffix = SCons.Util.silent_intern(SCons.Util.splitext(name)[1]) self.fs = fs #: Reference to parent Node.FS object assert directory, "A directory must be provided" self.abspath = SCons.Util.silent_intern(directory.entry_abspath(name)) self.labspath = SCons.Util.silent_intern(directory.entry_labspath(name)) if directory.path == '.': self.path = SCons.Util.silent_intern(name) else: self.path = SCons.Util.silent_intern(directory.entry_path(name)) if directory.tpath == '.': self.tpath = SCons.Util.silent_intern(name) else: self.tpath = SCons.Util.silent_intern(directory.entry_tpath(name)) self.path_elements = directory.path_elements + [self] self.dir = directory self.cwd = None # will hold the SConscript directory for target nodes self.duplicate = directory.duplicate def str_for_display(self): return '"' + self.__str__() + '"' def must_be_same(self, klass): """ This node, which already existed, is being looked up as the specified klass. Raise an exception if it isn't. """ if isinstance(self, klass) or klass is Entry: return raise TypeError("Tried to lookup %s '%s' as a %s." %\ (self.__class__.__name__, self.path, klass.__name__)) def get_dir(self): return self.dir def get_suffix(self): return self.suffix def rfile(self): return self def __str__(self): """A Node.FS.Base object's string representation is its path name.""" global Save_Strings if Save_Strings: return self._save_str() return self._get_str() memoizer_counters.append(SCons.Memoize.CountValue('_save_str')) def _save_str(self): try: return self._memo['_save_str'] except KeyError: pass result = sys.intern(self._get_str()) self._memo['_save_str'] = result return result def _get_str(self): global Save_Strings if self.duplicate or self.is_derived(): return self.get_path() srcnode = self.srcnode() if srcnode.stat() is None and self.stat() is not None: result = self.get_path() else: result = srcnode.get_path() if not Save_Strings: # We're not at the point where we're saving the string # representations of FS Nodes (because we haven't finished # reading the SConscript files and need to have str() return # things relative to them). That also means we can't yet # cache values returned (or not returned) by stat(), since # Python code in the SConscript files might still create # or otherwise affect the on-disk file. So get rid of the # values that the underlying stat() method saved. try: del self._memo['stat'] except KeyError: pass if self is not srcnode: try: del srcnode._memo['stat'] except KeyError: pass return result rstr = __str__ memoizer_counters.append(SCons.Memoize.CountValue('stat')) def stat(self): try: return self._memo['stat'] except KeyError: pass try: result = self.fs.stat(self.abspath) except os.error: result = None self._memo['stat'] = result return result def exists(self): return self.stat() is not None def rexists(self): return self.rfile().exists() def getmtime(self): st = self.stat() if st: return st[stat.ST_MTIME] else: return None def getsize(self): st = self.stat() if st: return st[stat.ST_SIZE] else: return None def isdir(self): st = self.stat() return st is not None and stat.S_ISDIR(st[stat.ST_MODE]) def isfile(self): st = self.stat() return st is not None and stat.S_ISREG(st[stat.ST_MODE]) if hasattr(os, 'symlink'): def islink(self): try: st = self.fs.lstat(self.abspath) except os.error: return 0 return stat.S_ISLNK(st[stat.ST_MODE]) else: def islink(self): return 0 # no symlinks def is_under(self, dir): if self is dir: return 1 else: return self.dir.is_under(dir) def set_local(self): self._local = 1 def srcnode(self): """If this node is in a build path, return the node corresponding to its source file. Otherwise, return ourself. """ srcdir_list = self.dir.srcdir_list() if srcdir_list: srcnode = srcdir_list[0].Entry(self.name) srcnode.must_be_same(self.__class__) return srcnode return self def get_path(self, dir=None): """Return path relative to the current working directory of the Node.FS.Base object that owns us.""" if not dir: dir = self.fs.getcwd() if self == dir: return '.' path_elems = self.path_elements pathname = '' try: i = path_elems.index(dir) except ValueError: for p in path_elems[:-1]: pathname += p.dirname else: for p in path_elems[i+1:-1]: pathname += p.dirname return pathname + path_elems[-1].name def set_src_builder(self, builder): """Set the source code builder for this node.""" self.sbuilder = builder if not self.has_builder(): self.builder_set(builder) def src_builder(self): """Fetch the source code builder for this node. If there isn't one, we cache the source code builder specified for the directory (which in turn will cache the value from its parent directory, and so on up to the file system root). """ try: scb = self.sbuilder except AttributeError: scb = self.dir.src_builder() self.sbuilder = scb return scb def get_abspath(self): """Get the absolute path of the file.""" return self.abspath def for_signature(self): # Return just our name. Even an absolute path would not work, # because that can change thanks to symlinks or remapped network # paths. return self.name def get_subst_proxy(self): try: return self._proxy except AttributeError: ret = EntryProxy(self) self._proxy = ret return ret def target_from_source(self, prefix, suffix, splitext=SCons.Util.splitext): """ Generates a target entry that corresponds to this entry (usually a source file) with the specified prefix and suffix. Note that this method can be overridden dynamically for generated files that need different behavior. See Tool/swig.py for an example. """ return self.dir.Entry(prefix + splitext(self.name)[0] + suffix) def _Rfindalldirs_key(self, pathlist): return pathlist memoizer_counters.append(SCons.Memoize.CountDict('Rfindalldirs', _Rfindalldirs_key)) def Rfindalldirs(self, pathlist): """ Return all of the directories for a given path list, including corresponding "backing" directories in any repositories. The Node lookups are relative to this Node (typically a directory), so memoizing result saves cycles from looking up the same path for each target in a given directory. """ try: memo_dict = self._memo['Rfindalldirs'] except KeyError: memo_dict = {} self._memo['Rfindalldirs'] = memo_dict else: try: return memo_dict[pathlist] except KeyError: pass create_dir_relative_to_self = self.Dir result = [] for path in pathlist: if isinstance(path, SCons.Node.Node): result.append(path) else: dir = create_dir_relative_to_self(path) result.extend(dir.get_all_rdirs()) memo_dict[pathlist] = result return result def RDirs(self, pathlist): """Search for a list of directories in the Repository list.""" cwd = self.cwd or self.fs._cwd return cwd.Rfindalldirs(pathlist) memoizer_counters.append(SCons.Memoize.CountValue('rentry')) def rentry(self): try: return self._memo['rentry'] except KeyError: pass result = self if not self.exists(): norm_name = _my_normcase(self.name) for dir in self.dir.get_all_rdirs(): try: node = dir.entries[norm_name] except KeyError: if dir.entry_exists_on_disk(self.name): result = dir.Entry(self.name) break self._memo['rentry'] = result return result def _glob1(self, pattern, ondisk=True, source=False, strings=False): return [] class Entry(Base): """This is the class for generic Node.FS entries--that is, things that could be a File or a Dir, but we're just not sure yet. Consequently, the methods in this class really exist just to transform their associated object into the right class when the time comes, and then call the same-named method in the transformed class.""" def diskcheck_match(self): pass def disambiguate(self, must_exist=None): """ """ if self.isdir(): self.__class__ = Dir self._morph() elif self.isfile(): self.__class__ = File self._morph() self.clear() else: # There was nothing on-disk at this location, so look in # the src directory. # # We can't just use self.srcnode() straight away because # that would create an actual Node for this file in the src # directory, and there might not be one. Instead, use the # dir_on_disk() method to see if there's something on-disk # with that name, in which case we can go ahead and call # self.srcnode() to create the right type of entry. srcdir = self.dir.srcnode() if srcdir != self.dir and \ srcdir.entry_exists_on_disk(self.name) and \ self.srcnode().isdir(): self.__class__ = Dir self._morph() elif must_exist: msg = "No such file or directory: '%s'" % self.abspath raise SCons.Errors.UserError(msg) else: self.__class__ = File self._morph() self.clear() return self def rfile(self): """We're a generic Entry, but the caller is actually looking for a File at this point, so morph into one.""" self.__class__ = File self._morph() self.clear() return File.rfile(self) def scanner_key(self): return self.get_suffix() def get_contents(self): """Fetch the contents of the entry. Returns the exact binary contents of the file.""" try: self = self.disambiguate(must_exist=1) except SCons.Errors.UserError: # There was nothing on disk with which to disambiguate # this entry. Leave it as an Entry, but return a null # string so calls to get_contents() in emitters and the # like (e.g. in qt.py) don't have to disambiguate by hand # or catch the exception. return '' else: return self.get_contents() def get_text_contents(self): """Fetch the decoded text contents of a Unicode encoded Entry. Since this should return the text contents from the file system, we check to see into what sort of subclass we should morph this Entry.""" try: self = self.disambiguate(must_exist=1) except SCons.Errors.UserError: # There was nothing on disk with which to disambiguate # this entry. Leave it as an Entry, but return a null # string so calls to get_text_contents() in emitters and # the like (e.g. in qt.py) don't have to disambiguate by # hand or catch the exception. return '' else: return self.get_text_contents() def must_be_same(self, klass): """Called to make sure a Node is a Dir. Since we're an Entry, we can morph into one.""" if self.__class__ is not klass: self.__class__ = klass self._morph() self.clear() # The following methods can get called before the Taskmaster has # had a chance to call disambiguate() directly to see if this Entry # should really be a Dir or a File. We therefore use these to call # disambiguate() transparently (from our caller's point of view). # # Right now, this minimal set of methods has been derived by just # looking at some of the methods that will obviously be called early # in any of the various Taskmasters' calling sequences, and then # empirically figuring out which additional methods are necessary # to make various tests pass. def exists(self): """Return if the Entry exists. Check the file system to see what we should turn into first. Assume a file if there's no directory.""" return self.disambiguate().exists() def rel_path(self, other): d = self.disambiguate() if d.__class__ is Entry: raise Exception("rel_path() could not disambiguate File/Dir") return d.rel_path(other) def new_ninfo(self): return self.disambiguate().new_ninfo() def changed_since_last_build(self, target, prev_ni): return self.disambiguate().changed_since_last_build(target, prev_ni) def _glob1(self, pattern, ondisk=True, source=False, strings=False): return self.disambiguate()._glob1(pattern, ondisk, source, strings) def get_subst_proxy(self): return self.disambiguate().get_subst_proxy() # This is for later so we can differentiate between Entry the class and Entry # the method of the FS class. _classEntry = Entry class LocalFS(object): if SCons.Memoize.use_memoizer: __metaclass__ = SCons.Memoize.Memoized_Metaclass # This class implements an abstraction layer for operations involving # a local file system. Essentially, this wraps any function in # the os, os.path or shutil modules that we use to actually go do # anything with or to the local file system. # # Note that there's a very good chance we'll refactor this part of # the architecture in some way as we really implement the interface(s) # for remote file system Nodes. For example, the right architecture # might be to have this be a subclass instead of a base class. # Nevertheless, we're using this as a first step in that direction. # # We're not using chdir() yet because the calling subclass method # needs to use os.chdir() directly to avoid recursion. Will we # really need this one? #def chdir(self, path): # return os.chdir(path) def chmod(self, path, mode): return os.chmod(path, mode) def copy(self, src, dst): return shutil.copy(src, dst) def copy2(self, src, dst): return shutil.copy2(src, dst) def exists(self, path): return os.path.exists(path) def getmtime(self, path): return os.path.getmtime(path) def getsize(self, path): return os.path.getsize(path) def isdir(self, path): return os.path.isdir(path) def isfile(self, path): return os.path.isfile(path) def link(self, src, dst): return os.link(src, dst) def lstat(self, path): return os.lstat(path) def listdir(self, path): return os.listdir(path) def makedirs(self, path): return os.makedirs(path) def mkdir(self, path): return os.mkdir(path) def rename(self, old, new): return os.rename(old, new) def stat(self, path): return os.stat(path) def symlink(self, src, dst): return os.symlink(src, dst) def open(self, path): return open(path) def unlink(self, path): return os.unlink(path) if hasattr(os, 'symlink'): def islink(self, path): return os.path.islink(path) else: def islink(self, path): return 0 # no symlinks if hasattr(os, 'readlink'): def readlink(self, file): return os.readlink(file) else: def readlink(self, file): return '' #class RemoteFS: # # Skeleton for the obvious methods we might need from the # # abstraction layer for a remote filesystem. # def upload(self, local_src, remote_dst): # pass # def download(self, remote_src, local_dst): # pass class FS(LocalFS): memoizer_counters = [] def __init__(self, path = None): """Initialize the Node.FS subsystem. The supplied path is the top of the source tree, where we expect to find the top-level build file. If no path is supplied, the current directory is the default. The path argument must be a valid absolute path. """ if __debug__: logInstanceCreation(self, 'Node.FS') self._memo = {} self.Root = {} self.SConstruct_dir = None self.max_drift = default_max_drift self.Top = None if path is None: self.pathTop = os.getcwd() else: self.pathTop = path self.defaultDrive = _my_normcase(_my_splitdrive(self.pathTop)[0]) self.Top = self.Dir(self.pathTop) self.Top.path = '.' self.Top.tpath = '.' self._cwd = self.Top DirNodeInfo.fs = self FileNodeInfo.fs = self def set_SConstruct_dir(self, dir): self.SConstruct_dir = dir def get_max_drift(self): return self.max_drift def set_max_drift(self, max_drift): self.max_drift = max_drift def getcwd(self): if hasattr(self, "_cwd"): return self._cwd else: return "" def chdir(self, dir, change_os_dir=0): """Change the current working directory for lookups. If change_os_dir is true, we will also change the "real" cwd to match. """ curr=self._cwd try: if dir is not None: self._cwd = dir if change_os_dir: os.chdir(dir.abspath) except OSError: self._cwd = curr raise def get_root(self, drive): """ Returns the root directory for the specified drive, creating it if necessary. """ drive = _my_normcase(drive) try: return self.Root[drive] except KeyError: root = RootDir(drive, self) self.Root[drive] = root if not drive: self.Root[self.defaultDrive] = root elif drive == self.defaultDrive: self.Root[''] = root return root def _lookup(self, p, directory, fsclass, create=1): """ The generic entry point for Node lookup with user-supplied data. This translates arbitrary input into a canonical Node.FS object of the specified fsclass. The general approach for strings is to turn it into a fully normalized absolute path and then call the root directory's lookup_abs() method for the heavy lifting. If the path name begins with '#', it is unconditionally interpreted relative to the top-level directory of this FS. '#' is treated as a synonym for the top-level SConstruct directory, much like '~' is treated as a synonym for the user's home directory in a UNIX shell. So both '#foo' and '#/foo' refer to the 'foo' subdirectory underneath the top-level SConstruct directory. If the path name is relative, then the path is looked up relative to the specified directory, or the current directory (self._cwd, typically the SConscript directory) if the specified directory is None. """ if isinstance(p, Base): # It's already a Node.FS object. Make sure it's the right # class and return. p.must_be_same(fsclass) return p # str(p) in case it's something like a proxy object p = str(p) if not os_sep_is_slash: p = p.replace(OS_SEP, '/') if p[0:1] == '#': # There was an initial '#', so we strip it and override # whatever directory they may have specified with the # top-level SConstruct directory. p = p[1:] directory = self.Top # There might be a drive letter following the # '#'. Although it is not described in the SCons man page, # the regression test suite explicitly tests for that # syntax. It seems to mean the following thing: # # Assuming the the SCons top dir is in C:/xxx/yyy, # '#X:/toto' means X:/xxx/yyy/toto. # # i.e. it assumes that the X: drive has a directory # structure similar to the one found on drive C:. if do_splitdrive: drive, p = _my_splitdrive(p) if drive: root = self.get_root(drive) else: root = directory.root else: root = directory.root # We can only strip trailing after splitting the drive # since the drive might the UNC '//' prefix. p = p.strip('/') needs_normpath = needs_normpath_match(p) # The path is relative to the top-level SCons directory. if p in ('', '.'): p = directory.labspath else: p = directory.labspath + '/' + p else: if do_splitdrive: drive, p = _my_splitdrive(p) if drive and not p: # This causes a naked drive letter to be treated # as a synonym for the root directory on that # drive. p = '/' else: drive = '' # We can only strip trailing '/' since the drive might the # UNC '//' prefix. if p != '/': p = p.rstrip('/') needs_normpath = needs_normpath_match(p) if p[0:1] == '/': # Absolute path root = self.get_root(drive) else: # This is a relative lookup or to the current directory # (the path name is not absolute). Add the string to the # appropriate directory lookup path, after which the whole # thing gets normalized. if directory: if not isinstance(directory, Dir): directory = self.Dir(directory) else: directory = self._cwd if p in ('', '.'): p = directory.labspath else: p = directory.labspath + '/' + p if drive: root = self.get_root(drive) else: root = directory.root if needs_normpath is not None: # Normalize a pathname. Will return the same result for # equivalent paths. # # We take advantage of the fact that we have an absolute # path here for sure. In addition, we know that the # components of lookup path are separated by slashes at # this point. Because of this, this code is about 2X # faster than calling os.path.normpath() followed by # replacing os.sep with '/' again. ins = p.split('/')[1:] outs = [] for d in ins: if d == '..': try: outs.pop() except IndexError: pass elif d not in ('', '.'): outs.append(d) p = '/' + '/'.join(outs) return root._lookup_abs(p, fsclass, create) def Entry(self, name, directory = None, create = 1): """Look up or create a generic Entry node with the specified name. If the name is a relative path (begins with ./, ../, or a file name), then it is looked up relative to the supplied directory node, or to the top level directory of the FS (supplied at construction time) if no directory is supplied. """ return self._lookup(name, directory, Entry, create) def File(self, name, directory = None, create = 1): """Look up or create a File node with the specified name. If the name is a relative path (begins with ./, ../, or a file name), then it is looked up relative to the supplied directory node, or to the top level directory of the FS (supplied at construction time) if no directory is supplied. This method will raise TypeError if a directory is found at the specified path. """ return self._lookup(name, directory, File, create) def Dir(self, name, directory = None, create = True): """Look up or create a Dir node with the specified name. If the name is a relative path (begins with ./, ../, or a file name), then it is looked up relative to the supplied directory node, or to the top level directory of the FS (supplied at construction time) if no directory is supplied. This method will raise TypeError if a normal file is found at the specified path. """ return self._lookup(name, directory, Dir, create) def VariantDir(self, variant_dir, src_dir, duplicate=1): """Link the supplied variant directory to the source directory for purposes of building files.""" if not isinstance(src_dir, SCons.Node.Node): src_dir = self.Dir(src_dir) if not isinstance(variant_dir, SCons.Node.Node): variant_dir = self.Dir(variant_dir) if src_dir.is_under(variant_dir): raise SCons.Errors.UserError("Source directory cannot be under variant directory.") if variant_dir.srcdir: if variant_dir.srcdir == src_dir: return # We already did this. raise SCons.Errors.UserError("'%s' already has a source directory: '%s'."%(variant_dir, variant_dir.srcdir)) variant_dir.link(src_dir, duplicate) def Repository(self, *dirs): """Specify Repository directories to search.""" for d in dirs: if not isinstance(d, SCons.Node.Node): d = self.Dir(d) self.Top.addRepository(d) def variant_dir_target_climb(self, orig, dir, tail): """Create targets in corresponding variant directories Climb the directory tree, and look up path names relative to any linked variant directories we find. Even though this loops and walks up the tree, we don't memoize the return value because this is really only used to process the command-line targets. """ targets = [] message = None fmt = "building associated VariantDir targets: %s" start_dir = dir while dir: for bd in dir.variant_dirs: if start_dir.is_under(bd): # If already in the build-dir location, don't reflect return [orig], fmt % str(orig) p = os.path.join(bd.path, *tail) targets.append(self.Entry(p)) tail = [dir.name] + tail dir = dir.up() if targets: message = fmt % ' '.join(map(str, targets)) return targets, message def Glob(self, pathname, ondisk=True, source=True, strings=False, cwd=None): """ Globs This is mainly a shim layer """ if cwd is None: cwd = self.getcwd() return cwd.glob(pathname, ondisk, source, strings) class DirNodeInfo(SCons.Node.NodeInfoBase): # This should get reset by the FS initialization. current_version_id = 1 fs = None def str_to_node(self, s): top = self.fs.Top root = top.root if do_splitdrive: drive, s = _my_splitdrive(s) if drive: root = self.fs.get_root(drive) if not os.path.isabs(s): s = top.labspath + '/' + s return root._lookup_abs(s, Entry) class DirBuildInfo(SCons.Node.BuildInfoBase): current_version_id = 1 glob_magic_check = re.compile('[*?[]') def has_glob_magic(s): return glob_magic_check.search(s) is not None class Dir(Base): """A class for directories in a file system. """ memoizer_counters = [] NodeInfo = DirNodeInfo BuildInfo = DirBuildInfo def __init__(self, name, directory, fs): if __debug__: logInstanceCreation(self, 'Node.FS.Dir') Base.__init__(self, name, directory, fs) self._morph() def _morph(self): """Turn a file system Node (either a freshly initialized directory object or a separate Entry object) into a proper directory object. Set up this directory's entries and hook it into the file system tree. Specify that directories (this Node) don't use signatures for calculating whether they're current. """ self.repositories = [] self.srcdir = None self.entries = {} self.entries['.'] = self self.entries['..'] = self.dir self.cwd = self self.searched = 0 self._sconsign = None self.variant_dirs = [] self.root = self.dir.root # For directories, we make a difference between the directory # 'name' and the directory 'dirname'. The 'name' attribute is # used when we need to print the 'name' of the directory or # when we it is used as the last part of a path. The 'dirname' # is used when the directory is not the last element of the # path. The main reason for making that distinction is that # for RoorDir's the dirname can not be easily inferred from # the name. For example, we have to add a '/' after a drive # letter but not after a UNC path prefix ('//'). self.dirname = self.name + OS_SEP # Don't just reset the executor, replace its action list, # because it might have some pre-or post-actions that need to # be preserved. # # But don't reset the executor if there is a non-null executor # attached already. The existing executor might have other # targets, in which case replacing the action list with a # Mkdir action is a big mistake. if not hasattr(self, 'executor'): self.builder = get_MkdirBuilder() self.get_executor().set_action_list(self.builder.action) else: # Prepend MkdirBuilder action to existing action list l = self.get_executor().action_list a = get_MkdirBuilder().action l.insert(0, a) self.get_executor().set_action_list(l) def diskcheck_match(self): diskcheck_match(self, self.isfile, "File %s found where directory expected.") def __clearRepositoryCache(self, duplicate=None): """Called when we change the repository(ies) for a directory. This clears any cached information that is invalidated by changing the repository.""" for node in self.entries.values(): if node != self.dir: if node != self and isinstance(node, Dir): node.__clearRepositoryCache(duplicate) else: node.clear() try: del node._srcreps except AttributeError: pass if duplicate is not None: node.duplicate=duplicate def __resetDuplicate(self, node): if node != self: node.duplicate = node.get_dir().duplicate def Entry(self, name): """ Looks up or creates an entry node named 'name' relative to this directory. """ return self.fs.Entry(name, self) def Dir(self, name, create=True): """ Looks up or creates a directory node named 'name' relative to this directory. """ return self.fs.Dir(name, self, create) def File(self, name): """ Looks up or creates a file node named 'name' relative to this directory. """ return self.fs.File(name, self) def link(self, srcdir, duplicate): """Set this directory as the variant directory for the supplied source directory.""" self.srcdir = srcdir self.duplicate = duplicate self.__clearRepositoryCache(duplicate) srcdir.variant_dirs.append(self) def getRepositories(self): """Returns a list of repositories for this directory. """ if self.srcdir and not self.duplicate: return self.srcdir.get_all_rdirs() + self.repositories return self.repositories memoizer_counters.append(SCons.Memoize.CountValue('get_all_rdirs')) def get_all_rdirs(self): try: return list(self._memo['get_all_rdirs']) except KeyError: pass result = [self] fname = '.' dir = self while dir: for rep in dir.getRepositories(): result.append(rep.Dir(fname)) if fname == '.': fname = dir.name else: fname = dir.name + OS_SEP + fname dir = dir.up() self._memo['get_all_rdirs'] = list(result) return result def addRepository(self, dir): if dir != self and not dir in self.repositories: self.repositories.append(dir) dir.tpath = '.' self.__clearRepositoryCache() def up(self): return self.dir def _rel_path_key(self, other): return str(other) memoizer_counters.append(SCons.Memoize.CountDict('rel_path', _rel_path_key)) def rel_path(self, other): """Return a path to "other" relative to this directory. """ # This complicated and expensive method, which constructs relative # paths between arbitrary Node.FS objects, is no longer used # by SCons itself. It was introduced to store dependency paths # in .sconsign files relative to the target, but that ended up # being significantly inefficient. # # We're continuing to support the method because some SConstruct # files out there started using it when it was available, and # we're all about backwards compatibility.. try: memo_dict = self._memo['rel_path'] except KeyError: memo_dict = {} self._memo['rel_path'] = memo_dict else: try: return memo_dict[other] except KeyError: pass if self is other: result = '.' elif not other in self.path_elements: try: other_dir = other.get_dir() except AttributeError: result = str(other) else: if other_dir is None: result = other.name else: dir_rel_path = self.rel_path(other_dir) if dir_rel_path == '.': result = other.name else: result = dir_rel_path + OS_SEP + other.name else: i = self.path_elements.index(other) + 1 path_elems = ['..'] * (len(self.path_elements) - i) \ + [n.name for n in other.path_elements[i:]] result = OS_SEP.join(path_elems) memo_dict[other] = result return result def get_env_scanner(self, env, kw={}): import SCons.Defaults return SCons.Defaults.DirEntryScanner def get_target_scanner(self): import SCons.Defaults return SCons.Defaults.DirEntryScanner def get_found_includes(self, env, scanner, path): """Return this directory's implicit dependencies. We don't bother caching the results because the scan typically shouldn't be requested more than once (as opposed to scanning .h file contents, which can be requested as many times as the files is #included by other files). """ if not scanner: return [] # Clear cached info for this Dir. If we already visited this # directory on our walk down the tree (because we didn't know at # that point it was being used as the source for another Node) # then we may have calculated build signature before realizing # we had to scan the disk. Now that we have to, though, we need # to invalidate the old calculated signature so that any node # dependent on our directory structure gets one that includes # info about everything on disk. self.clear() return scanner(self, env, path) # # Taskmaster interface subsystem # def prepare(self): pass def build(self, **kw): """A null "builder" for directories.""" global MkdirBuilder if self.builder is not MkdirBuilder: SCons.Node.Node.build(self, **kw) # # # def _create(self): """Create this directory, silently and without worrying about whether the builder is the default or not.""" listDirs = [] parent = self while parent: if parent.exists(): break listDirs.append(parent) p = parent.up() if p is None: # Don't use while: - else: for this condition because # if so, then parent is None and has no .path attribute. raise SCons.Errors.StopError(parent.path) parent = p listDirs.reverse() for dirnode in listDirs: try: # Don't call dirnode.build(), call the base Node method # directly because we definitely *must* create this # directory. The dirnode.build() method will suppress # the build if it's the default builder. SCons.Node.Node.build(dirnode) dirnode.get_executor().nullify() # The build() action may or may not have actually # created the directory, depending on whether the -n # option was used or not. Delete the _exists and # _rexists attributes so they can be reevaluated. dirnode.clear() except OSError: pass def multiple_side_effect_has_builder(self): global MkdirBuilder return self.builder is not MkdirBuilder and self.has_builder() def alter_targets(self): """Return any corresponding targets in a variant directory. """ return self.fs.variant_dir_target_climb(self, self, []) def scanner_key(self): """A directory does not get scanned.""" return None def get_text_contents(self): """We already emit things in text, so just return the binary version.""" return self.get_contents() def get_contents(self): """Return content signatures and names of all our children separated by new-lines. Ensure that the nodes are sorted.""" contents = [] for node in sorted(self.children(), key=lambda t: t.name): contents.append('%s %s\n' % (node.get_csig(), node.name)) return ''.join(contents) def get_csig(self): """Compute the content signature for Directory nodes. In general, this is not needed and the content signature is not stored in the DirNodeInfo. However, if get_contents on a Dir node is called which has a child directory, the child directory should return the hash of its contents.""" contents = self.get_contents() return SCons.Util.MD5signature(contents) def do_duplicate(self, src): pass changed_since_last_build = SCons.Node.Node.state_has_changed def is_up_to_date(self): """If any child is not up-to-date, then this directory isn't, either.""" if self.builder is not MkdirBuilder and not self.exists(): return 0 up_to_date = SCons.Node.up_to_date for kid in self.children(): if kid.get_state() > up_to_date: return 0 return 1 def rdir(self): if not self.exists(): norm_name = _my_normcase(self.name) for dir in self.dir.get_all_rdirs(): try: node = dir.entries[norm_name] except KeyError: node = dir.dir_on_disk(self.name) if node and node.exists() and \ (isinstance(dir, Dir) or isinstance(dir, Entry)): return node return self def sconsign(self): """Return the .sconsign file info for this directory, creating it first if necessary.""" if not self._sconsign: import SCons.SConsign self._sconsign = SCons.SConsign.ForDirectory(self) return self._sconsign def srcnode(self): """Dir has a special need for srcnode()...if we have a srcdir attribute set, then that *is* our srcnode.""" if self.srcdir: return self.srcdir return Base.srcnode(self) def get_timestamp(self): """Return the latest timestamp from among our children""" stamp = 0 for kid in self.children(): if kid.get_timestamp() > stamp: stamp = kid.get_timestamp() return stamp def entry_abspath(self, name): return self.abspath + OS_SEP + name def entry_labspath(self, name): return self.labspath + '/' + name def entry_path(self, name): return self.path + OS_SEP + name def entry_tpath(self, name): return self.tpath + OS_SEP + name def entry_exists_on_disk(self, name): try: d = self.on_disk_entries except AttributeError: d = {} try: entries = os.listdir(self.abspath) except OSError: pass else: for entry in map(_my_normcase, entries): d[entry] = True self.on_disk_entries = d if sys.platform == 'win32': name = _my_normcase(name) result = d.get(name) if result is None: # Belt-and-suspenders for Windows: check directly for # 8.3 file names that don't show up in os.listdir(). result = os.path.exists(self.abspath + OS_SEP + name) d[name] = result return result else: return name in d memoizer_counters.append(SCons.Memoize.CountValue('srcdir_list')) def srcdir_list(self): try: return self._memo['srcdir_list'] except KeyError: pass result = [] dirname = '.' dir = self while dir: if dir.srcdir: result.append(dir.srcdir.Dir(dirname)) dirname = dir.name + OS_SEP + dirname dir = dir.up() self._memo['srcdir_list'] = result return result def srcdir_duplicate(self, name): for dir in self.srcdir_list(): if self.is_under(dir): # We shouldn't source from something in the build path; # variant_dir is probably under src_dir, in which case # we are reflecting. break if dir.entry_exists_on_disk(name): srcnode = dir.Entry(name).disambiguate() if self.duplicate: node = self.Entry(name).disambiguate() node.do_duplicate(srcnode) return node else: return srcnode return None def _srcdir_find_file_key(self, filename): return filename memoizer_counters.append(SCons.Memoize.CountDict('srcdir_find_file', _srcdir_find_file_key)) def srcdir_find_file(self, filename): try: memo_dict = self._memo['srcdir_find_file'] except KeyError: memo_dict = {} self._memo['srcdir_find_file'] = memo_dict else: try: return memo_dict[filename] except KeyError: pass def func(node): if (isinstance(node, File) or isinstance(node, Entry)) and \ (node.is_derived() or node.exists()): return node return None norm_name = _my_normcase(filename) for rdir in self.get_all_rdirs(): try: node = rdir.entries[norm_name] except KeyError: node = rdir.file_on_disk(filename) else: node = func(node) if node: result = (node, self) memo_dict[filename] = result return result for srcdir in self.srcdir_list(): for rdir in srcdir.get_all_rdirs(): try: node = rdir.entries[norm_name] except KeyError: node = rdir.file_on_disk(filename) else: node = func(node) if node: result = (File(filename, self, self.fs), srcdir) memo_dict[filename] = result return result result = (None, None) memo_dict[filename] = result return result def dir_on_disk(self, name): if self.entry_exists_on_disk(name): try: return self.Dir(name) except TypeError: pass node = self.srcdir_duplicate(name) if isinstance(node, File): return None return node def file_on_disk(self, name): if self.entry_exists_on_disk(name) or \ diskcheck_rcs(self, name) or \ diskcheck_sccs(self, name): try: return self.File(name) except TypeError: pass node = self.srcdir_duplicate(name) if isinstance(node, Dir): return None return node def walk(self, func, arg): """ Walk this directory tree by calling the specified function for each directory in the tree. This behaves like the os.path.walk() function, but for in-memory Node.FS.Dir objects. The function takes the same arguments as the functions passed to os.path.walk(): func(arg, dirname, fnames) Except that "dirname" will actually be the directory *Node*, not the string. The '.' and '..' entries are excluded from fnames. The fnames list may be modified in-place to filter the subdirectories visited or otherwise impose a specific order. The "arg" argument is always passed to func() and may be used in any way (or ignored, passing None is common). """ entries = self.entries names = list(entries.keys()) names.remove('.') names.remove('..') func(arg, self, names) for dirname in [n for n in names if isinstance(entries[n], Dir)]: entries[dirname].walk(func, arg) def glob(self, pathname, ondisk=True, source=False, strings=False): """ Returns a list of Nodes (or strings) matching a specified pathname pattern. Pathname patterns follow UNIX shell semantics: * matches any-length strings of any characters, ? matches any character, and [] can enclose lists or ranges of characters. Matches do not span directory separators. The matches take into account Repositories, returning local Nodes if a corresponding entry exists in a Repository (either an in-memory Node or something on disk). By defafult, the glob() function matches entries that exist on-disk, in addition to in-memory Nodes. Setting the "ondisk" argument to False (or some other non-true value) causes the glob() function to only match in-memory Nodes. The default behavior is to return both the on-disk and in-memory Nodes. The "source" argument, when true, specifies that corresponding source Nodes must be returned if you're globbing in a build directory (initialized with VariantDir()). The default behavior is to return Nodes local to the VariantDir(). The "strings" argument, when true, returns the matches as strings, not Nodes. The strings are path names relative to this directory. The underlying algorithm is adapted from the glob.glob() function in the Python library (but heavily modified), and uses fnmatch() under the covers. """ dirname, basename = os.path.split(pathname) if not dirname: return sorted(self._glob1(basename, ondisk, source, strings), key=lambda t: str(t)) if has_glob_magic(dirname): list = self.glob(dirname, ondisk, source, strings=False) else: list = [self.Dir(dirname, create=True)] result = [] for dir in list: r = dir._glob1(basename, ondisk, source, strings) if strings: r = [os.path.join(str(dir), x) for x in r] result.extend(r) return sorted(result, key=lambda a: str(a)) def _glob1(self, pattern, ondisk=True, source=False, strings=False): """ Globs for and returns a list of entry names matching a single pattern in this directory. This searches any repositories and source directories for corresponding entries and returns a Node (or string) relative to the current directory if an entry is found anywhere. TODO: handle pattern with no wildcard """ search_dir_list = self.get_all_rdirs() for srcdir in self.srcdir_list(): search_dir_list.extend(srcdir.get_all_rdirs()) selfEntry = self.Entry names = [] for dir in search_dir_list: # We use the .name attribute from the Node because the keys of # the dir.entries dictionary are normalized (that is, all upper # case) on case-insensitive systems like Windows. node_names = [ v.name for k, v in dir.entries.items() if k not in ('.', '..') ] names.extend(node_names) if not strings: # Make sure the working directory (self) actually has # entries for all Nodes in repositories or variant dirs. for name in node_names: selfEntry(name) if ondisk: try: disk_names = os.listdir(dir.abspath) except os.error: continue names.extend(disk_names) if not strings: # We're going to return corresponding Nodes in # the local directory, so we need to make sure # those Nodes exist. We only want to create # Nodes for the entries that will match the # specified pattern, though, which means we # need to filter the list here, even though # the overall list will also be filtered later, # after we exit this loop. if pattern[0] != '.': #disk_names = [ d for d in disk_names if d[0] != '.' ] disk_names = [x for x in disk_names if x[0] != '.'] disk_names = fnmatch.filter(disk_names, pattern) dirEntry = dir.Entry for name in disk_names: # Add './' before disk filename so that '#' at # beginning of filename isn't interpreted. name = './' + name node = dirEntry(name).disambiguate() n = selfEntry(name) if n.__class__ != node.__class__: n.__class__ = node.__class__ n._morph() names = set(names) if pattern[0] != '.': #names = [ n for n in names if n[0] != '.' ] names = [x for x in names if x[0] != '.'] names = fnmatch.filter(names, pattern) if strings: return names #return [ self.entries[_my_normcase(n)] for n in names ] return [self.entries[_my_normcase(n)] for n in names] class RootDir(Dir): """A class for the root directory of a file system. This is the same as a Dir class, except that the path separator ('/' or '\\') is actually part of the name, so we don't need to add a separator when creating the path names of entries within this directory. """ def __init__(self, drive, fs): if __debug__: logInstanceCreation(self, 'Node.FS.RootDir') # We're going to be our own parent directory (".." entry and .dir # attribute) so we have to set up some values so Base.__init__() # won't gag won't it calls some of our methods. self.abspath = '' self.labspath = '' self.path = '' self.tpath = '' self.path_elements = [] self.duplicate = 0 self.root = self # Handle all the types of drives: if drive == '': # No drive, regular UNIX root or Windows default drive. name = OS_SEP dirname = OS_SEP elif drive == '//': # UNC path name = UNC_PREFIX dirname = UNC_PREFIX else: # Windows drive letter name = drive dirname = drive + OS_SEP Base.__init__(self, name, self, fs) # Now set our paths to what we really want them to be. The # name should already contain any necessary separators, such # as the initial drive letter (the name) plus the directory # separator, except for the "lookup abspath," which does not # have the drive letter. self.abspath = dirname self.labspath = '' self.path = dirname self.tpath = dirname self._morph() # Must be reset after Dir._morph() is invoked... self.dirname = dirname self._lookupDict = {} self._lookupDict[''] = self self._lookupDict['/'] = self # The // entry is necessary because os.path.normpath() # preserves double slashes at the beginning of a path on Posix # platforms. if not has_unc: self._lookupDict['//'] = self def must_be_same(self, klass): if klass is Dir: return Base.must_be_same(self, klass) def _lookup_abs(self, p, klass, create=1): """ Fast (?) lookup of a *normalized* absolute path. This method is intended for use by internal lookups with already-normalized path data. For general-purpose lookups, use the FS.Entry(), FS.Dir() or FS.File() methods. The caller is responsible for making sure we're passed a normalized absolute path; we merely let Python's dictionary look up and return the One True Node.FS object for the path. If a Node for the specified "p" doesn't already exist, and "create" is specified, the Node may be created after recursive invocation to find or create the parent directory or directories. """ k = _my_normcase(p) try: result = self._lookupDict[k] except KeyError: if not create: msg = "No such file or directory: '%s' in '%s' (and create is False)" % (p, str(self)) raise SCons.Errors.UserError(msg) # There is no Node for this path name, and we're allowed # to create it. # (note: would like to use p.rsplit('/',1) here but # that's not in python 2.3) # e.g.: dir_name, file_name = p.rsplit('/',1) last_slash = p.rindex('/') if (last_slash >= 0): dir_name = p[:last_slash] file_name = p[last_slash+1:] else: dir_name = p # shouldn't happen, just in case file_name = '' dir_node = self._lookup_abs(dir_name, Dir) result = klass(file_name, dir_node, self.fs) # Double-check on disk (as configured) that the Node we # created matches whatever is out there in the real world. result.diskcheck_match() self._lookupDict[k] = result dir_node.entries[_my_normcase(file_name)] = result dir_node.implicit = None else: # There is already a Node for this path name. Allow it to # complain if we were looking for an inappropriate type. result.must_be_same(klass) return result def __str__(self): return self.abspath def entry_abspath(self, name): return self.abspath + name def entry_labspath(self, name): return '/' + name def entry_path(self, name): return self.path + name def entry_tpath(self, name): return self.tpath + name def is_under(self, dir): if self is dir: return 1 else: return 0 def up(self): return None def get_dir(self): return None def src_builder(self): return _null class FileNodeInfo(SCons.Node.NodeInfoBase): current_version_id = 1 field_list = ['csig', 'timestamp', 'size'] # This should get reset by the FS initialization. fs = None def str_to_node(self, s): top = self.fs.Top root = top.root if do_splitdrive: drive, s = _my_splitdrive(s) if drive: root = self.fs.get_root(drive) if not os.path.isabs(s): s = top.labspath + '/' + s return root._lookup_abs(s, Entry) class FileBuildInfo(SCons.Node.BuildInfoBase): current_version_id = 1 def convert_to_sconsign(self): """ Converts this FileBuildInfo object for writing to a .sconsign file This replaces each Node in our various dependency lists with its usual string representation: relative to the top-level SConstruct directory, or an absolute path if it's outside. """ if os_sep_is_slash: node_to_str = str else: def node_to_str(n): try: s = n.path except AttributeError: s = str(n) else: s = s.replace(OS_SEP, '/') return s for attr in ['bsources', 'bdepends', 'bimplicit']: try: val = getattr(self, attr) except AttributeError: pass else: setattr(self, attr, list(map(node_to_str, val))) def convert_from_sconsign(self, dir, name): """ Converts a newly-read FileBuildInfo object for in-SCons use For normal up-to-date checking, we don't have any conversion to perform--but we're leaving this method here to make that clear. """ pass def prepare_dependencies(self): """ Prepares a FileBuildInfo object for explaining what changed The bsources, bdepends and bimplicit lists have all been stored on disk as paths relative to the top-level SConstruct directory. Convert the strings to actual Nodes (for use by the --debug=explain code and --implicit-cache). """ attrs = [ ('bsources', 'bsourcesigs'), ('bdepends', 'bdependsigs'), ('bimplicit', 'bimplicitsigs'), ] for (nattr, sattr) in attrs: try: strings = getattr(self, nattr) nodeinfos = getattr(self, sattr) except AttributeError: continue nodes = [] for s, ni in zip(strings, nodeinfos): if not isinstance(s, SCons.Node.Node): s = ni.str_to_node(s) nodes.append(s) setattr(self, nattr, nodes) def format(self, names=0): result = [] bkids = self.bsources + self.bdepends + self.bimplicit bkidsigs = self.bsourcesigs + self.bdependsigs + self.bimplicitsigs for bkid, bkidsig in zip(bkids, bkidsigs): result.append(str(bkid) + ': ' + ' '.join(bkidsig.format(names=names))) result.append('%s [%s]' % (self.bactsig, self.bact)) return '\n'.join(result) class File(Base): """A class for files in a file system. """ memoizer_counters = [] NodeInfo = FileNodeInfo BuildInfo = FileBuildInfo md5_chunksize = 64 def diskcheck_match(self): diskcheck_match(self, self.isdir, "Directory %s found where file expected.") def __init__(self, name, directory, fs): if __debug__: logInstanceCreation(self, 'Node.FS.File') Base.__init__(self, name, directory, fs) self._morph() def Entry(self, name): """Create an entry node named 'name' relative to the directory of this file.""" return self.dir.Entry(name) def Dir(self, name, create=True): """Create a directory node named 'name' relative to the directory of this file.""" return self.dir.Dir(name, create=create) def Dirs(self, pathlist): """Create a list of directories relative to the SConscript directory of this file.""" return [self.Dir(p) for p in pathlist] def File(self, name): """Create a file node named 'name' relative to the directory of this file.""" return self.dir.File(name) #def generate_build_dict(self): # """Return an appropriate dictionary of values for building # this File.""" # return {'Dir' : self.Dir, # 'File' : self.File, # 'RDirs' : self.RDirs} def _morph(self): """Turn a file system node into a File object.""" self.scanner_paths = {} if not hasattr(self, '_local'): self._local = 0 # If there was already a Builder set on this entry, then # we need to make sure we call the target-decider function, # not the source-decider. Reaching in and doing this by hand # is a little bogus. We'd prefer to handle this by adding # an Entry.builder_set() method that disambiguates like the # other methods, but that starts running into problems with the # fragile way we initialize Dir Nodes with their Mkdir builders, # yet still allow them to be overridden by the user. Since it's # not clear right now how to fix that, stick with what works # until it becomes clear... if self.has_builder(): self.changed_since_last_build = self.decide_target def scanner_key(self): return self.get_suffix() def get_contents(self): if not self.rexists(): return '' fname = self.rfile().abspath try: contents = open(fname, "rb").read() except EnvironmentError, e: if not e.filename: e.filename = fname raise return contents # This attempts to figure out what the encoding of the text is # based upon the BOM bytes, and then decodes the contents so that # it's a valid python string. def get_text_contents(self): contents = self.get_contents() # The behavior of various decode() methods and functions # w.r.t. the initial BOM bytes is different for different # encodings and/or Python versions. ('utf-8' does not strip # them, but has a 'utf-8-sig' which does; 'utf-16' seems to # strip them; etc.) Just sidestep all the complication by # explicitly stripping the BOM before we decode(). if contents.startswith(codecs.BOM_UTF8): return contents[len(codecs.BOM_UTF8):].decode('utf-8') if contents.startswith(codecs.BOM_UTF16_LE): return contents[len(codecs.BOM_UTF16_LE):].decode('utf-16-le') if contents.startswith(codecs.BOM_UTF16_BE): return contents[len(codecs.BOM_UTF16_BE):].decode('utf-16-be') return contents def get_content_hash(self): """ Compute and return the MD5 hash for this file. """ if not self.rexists(): return SCons.Util.MD5signature('') fname = self.rfile().abspath try: cs = SCons.Util.MD5filesignature(fname, chunksize=SCons.Node.FS.File.md5_chunksize*1024) except EnvironmentError, e: if not e.filename: e.filename = fname raise return cs memoizer_counters.append(SCons.Memoize.CountValue('get_size')) def get_size(self): try: return self._memo['get_size'] except KeyError: pass if self.rexists(): size = self.rfile().getsize() else: size = 0 self._memo['get_size'] = size return size memoizer_counters.append(SCons.Memoize.CountValue('get_timestamp')) def get_timestamp(self): try: return self._memo['get_timestamp'] except KeyError: pass if self.rexists(): timestamp = self.rfile().getmtime() else: timestamp = 0 self._memo['get_timestamp'] = timestamp return timestamp def store_info(self): # Merge our build information into the already-stored entry. # This accomodates "chained builds" where a file that's a target # in one build (SConstruct file) is a source in a different build. # See test/chained-build.py for the use case. if do_store_info: self.dir.sconsign().store_info(self.name, self) convert_copy_attrs = [ 'bsources', 'bimplicit', 'bdepends', 'bact', 'bactsig', 'ninfo', ] convert_sig_attrs = [ 'bsourcesigs', 'bimplicitsigs', 'bdependsigs', ] def convert_old_entry(self, old_entry): # Convert a .sconsign entry from before the Big Signature # Refactoring, doing what we can to convert its information # to the new .sconsign entry format. # # The old format looked essentially like this: # # BuildInfo # .ninfo (NodeInfo) # .bsig # .csig # .timestamp # .size # .bsources # .bsourcesigs ("signature" list) # .bdepends # .bdependsigs ("signature" list) # .bimplicit # .bimplicitsigs ("signature" list) # .bact # .bactsig # # The new format looks like this: # # .ninfo (NodeInfo) # .bsig # .csig # .timestamp # .size # .binfo (BuildInfo) # .bsources # .bsourcesigs (NodeInfo list) # .bsig # .csig # .timestamp # .size # .bdepends # .bdependsigs (NodeInfo list) # .bsig # .csig # .timestamp # .size # .bimplicit # .bimplicitsigs (NodeInfo list) # .bsig # .csig # .timestamp # .size # .bact # .bactsig # # The basic idea of the new structure is that a NodeInfo always # holds all available information about the state of a given Node # at a certain point in time. The various .b*sigs lists can just # be a list of pointers to the .ninfo attributes of the different # dependent nodes, without any copying of information until it's # time to pickle it for writing out to a .sconsign file. # # The complicating issue is that the *old* format only stored one # "signature" per dependency, based on however the *last* build # was configured. We don't know from just looking at it whether # it was a build signature, a content signature, or a timestamp # "signature". Since we no longer use build signatures, the # best we can do is look at the length and if it's thirty two, # assume that it was (or might have been) a content signature. # If it was actually a build signature, then it will cause a # rebuild anyway when it doesn't match the new content signature, # but that's probably the best we can do. import SCons.SConsign new_entry = SCons.SConsign.SConsignEntry() new_entry.binfo = self.new_binfo() binfo = new_entry.binfo for attr in self.convert_copy_attrs: try: value = getattr(old_entry, attr) except AttributeError: continue setattr(binfo, attr, value) delattr(old_entry, attr) for attr in self.convert_sig_attrs: try: sig_list = getattr(old_entry, attr) except AttributeError: continue value = [] for sig in sig_list: ninfo = self.new_ninfo() if len(sig) == 32: ninfo.csig = sig else: ninfo.timestamp = sig value.append(ninfo) setattr(binfo, attr, value) delattr(old_entry, attr) return new_entry memoizer_counters.append(SCons.Memoize.CountValue('get_stored_info')) def get_stored_info(self): try: return self._memo['get_stored_info'] except KeyError: pass try: sconsign_entry = self.dir.sconsign().get_entry(self.name) except (KeyError, EnvironmentError): import SCons.SConsign sconsign_entry = SCons.SConsign.SConsignEntry() sconsign_entry.binfo = self.new_binfo() sconsign_entry.ninfo = self.new_ninfo() else: if isinstance(sconsign_entry, FileBuildInfo): # This is a .sconsign file from before the Big Signature # Refactoring; convert it as best we can. sconsign_entry = self.convert_old_entry(sconsign_entry) try: delattr(sconsign_entry.ninfo, 'bsig') except AttributeError: pass self._memo['get_stored_info'] = sconsign_entry return sconsign_entry def get_stored_implicit(self): binfo = self.get_stored_info().binfo binfo.prepare_dependencies() try: return binfo.bimplicit except AttributeError: return None def rel_path(self, other): return self.dir.rel_path(other) def _get_found_includes_key(self, env, scanner, path): return (id(env), id(scanner), path) memoizer_counters.append(SCons.Memoize.CountDict('get_found_includes', _get_found_includes_key)) def get_found_includes(self, env, scanner, path): """Return the included implicit dependencies in this file. Cache results so we only scan the file once per path regardless of how many times this information is requested. """ memo_key = (id(env), id(scanner), path) try: memo_dict = self._memo['get_found_includes'] except KeyError: memo_dict = {} self._memo['get_found_includes'] = memo_dict else: try: return memo_dict[memo_key] except KeyError: pass if scanner: # result = [n.disambiguate() for n in scanner(self, env, path)] result = scanner(self, env, path) result = [N.disambiguate() for N in result] else: result = [] memo_dict[memo_key] = result return result def _createDir(self): # ensure that the directories for this node are # created. self.dir._create() def push_to_cache(self): """Try to push the node into a cache """ # This should get called before the Nodes' .built() method is # called, which would clear the build signature if the file has # a source scanner. # # We have to clear the local memoized values *before* we push # the node to cache so that the memoization of the self.exists() # return value doesn't interfere. if self.nocache: return self.clear_memoized_values() if self.exists(): self.get_build_env().get_CacheDir().push(self) def retrieve_from_cache(self): """Try to retrieve the node's content from a cache This method is called from multiple threads in a parallel build, so only do thread safe stuff here. Do thread unsafe stuff in built(). Returns true if the node was successfully retrieved. """ if self.nocache: return None if not self.is_derived(): return None return self.get_build_env().get_CacheDir().retrieve(self) def visited(self): if self.exists(): self.get_build_env().get_CacheDir().push_if_forced(self) ninfo = self.get_ninfo() csig = self.get_max_drift_csig() if csig: ninfo.csig = csig ninfo.timestamp = self.get_timestamp() ninfo.size = self.get_size() if not self.has_builder(): # This is a source file, but it might have been a target file # in another build that included more of the DAG. Copy # any build information that's stored in the .sconsign file # into our binfo object so it doesn't get lost. old = self.get_stored_info() self.get_binfo().__dict__.update(old.binfo.__dict__) self.store_info() def find_src_builder(self): if self.rexists(): return None scb = self.dir.src_builder() if scb is _null: if diskcheck_sccs(self.dir, self.name): scb = get_DefaultSCCSBuilder() elif diskcheck_rcs(self.dir, self.name): scb = get_DefaultRCSBuilder() else: scb = None if scb is not None: try: b = self.builder except AttributeError: b = None if b is None: self.builder_set(scb) return scb def has_src_builder(self): """Return whether this Node has a source builder or not. If this Node doesn't have an explicit source code builder, this is where we figure out, on the fly, if there's a transparent source code builder for it. Note that if we found a source builder, we also set the self.builder attribute, so that all of the methods that actually *build* this file don't have to do anything different. """ try: scb = self.sbuilder except AttributeError: scb = self.sbuilder = self.find_src_builder() return scb is not None def alter_targets(self): """Return any corresponding targets in a variant directory. """ if self.is_derived(): return [], None return self.fs.variant_dir_target_climb(self, self.dir, [self.name]) def _rmv_existing(self): self.clear_memoized_values() if print_duplicate: print "dup: removing existing target %s"%self e = Unlink(self, [], None) if isinstance(e, SCons.Errors.BuildError): raise e # # Taskmaster interface subsystem # def make_ready(self): self.has_src_builder() self.get_binfo() def prepare(self): """Prepare for this file to be created.""" SCons.Node.Node.prepare(self) if self.get_state() != SCons.Node.up_to_date: if self.exists(): if self.is_derived() and not self.precious: self._rmv_existing() else: try: self._createDir() except SCons.Errors.StopError, drive: desc = "No drive `%s' for target `%s'." % (drive, self) raise SCons.Errors.StopError(desc) # # # def remove(self): """Remove this file.""" if self.exists() or self.islink(): self.fs.unlink(self.path) return 1 return None def do_duplicate(self, src): self._createDir() if print_duplicate: print "dup: relinking variant '%s' from '%s'"%(self, src) Unlink(self, None, None) e = Link(self, src, None) if isinstance(e, SCons.Errors.BuildError): desc = "Cannot duplicate `%s' in `%s': %s." % (src.path, self.dir.path, e.errstr) raise SCons.Errors.StopError(desc) self.linked = 1 # The Link() action may or may not have actually # created the file, depending on whether the -n # option was used or not. Delete the _exists and # _rexists attributes so they can be reevaluated. self.clear() memoizer_counters.append(SCons.Memoize.CountValue('exists')) def exists(self): try: return self._memo['exists'] except KeyError: pass # Duplicate from source path if we are set up to do this. if self.duplicate and not self.is_derived() and not self.linked: src = self.srcnode() if src is not self: # At this point, src is meant to be copied in a variant directory. src = src.rfile() if src.abspath != self.abspath: if src.exists(): self.do_duplicate(src) # Can't return 1 here because the duplication might # not actually occur if the -n option is being used. else: # The source file does not exist. Make sure no old # copy remains in the variant directory. if print_duplicate: print "dup: no src for %s, unlinking old variant copy"%self if Base.exists(self) or self.islink(): self.fs.unlink(self.path) # Return None explicitly because the Base.exists() call # above will have cached its value if the file existed. self._memo['exists'] = None return None result = Base.exists(self) self._memo['exists'] = result return result # # SIGNATURE SUBSYSTEM # def get_max_drift_csig(self): """ Returns the content signature currently stored for this node if it's been unmodified longer than the max_drift value, or the max_drift value is 0. Returns None otherwise. """ old = self.get_stored_info() mtime = self.get_timestamp() max_drift = self.fs.max_drift if max_drift > 0: if (time.time() - mtime) > max_drift: try: n = old.ninfo if n.timestamp and n.csig and n.timestamp == mtime: return n.csig except AttributeError: pass elif max_drift == 0: try: return old.ninfo.csig except AttributeError: pass return None def get_csig(self): """ Generate a node's content signature, the digested signature of its content. node - the node cache - alternate node to use for the signature cache returns - the content signature """ ninfo = self.get_ninfo() try: return ninfo.csig except AttributeError: pass csig = self.get_max_drift_csig() if csig is None: try: if self.get_size() < SCons.Node.FS.File.md5_chunksize: contents = self.get_contents() else: csig = self.get_content_hash() except IOError: # This can happen if there's actually a directory on-disk, # which can be the case if they've disabled disk checks, # or if an action with a File target actually happens to # create a same-named directory by mistake. csig = '' else: if not csig: csig = SCons.Util.MD5signature(contents) ninfo.csig = csig return csig # # DECISION SUBSYSTEM # def builder_set(self, builder): SCons.Node.Node.builder_set(self, builder) self.changed_since_last_build = self.decide_target def changed_content(self, target, prev_ni): cur_csig = self.get_csig() try: return cur_csig != prev_ni.csig except AttributeError: return 1 def changed_state(self, target, prev_ni): return self.state != SCons.Node.up_to_date def changed_timestamp_then_content(self, target, prev_ni): if not self.changed_timestamp_match(target, prev_ni): try: self.get_ninfo().csig = prev_ni.csig except AttributeError: pass return False return self.changed_content(target, prev_ni) def changed_timestamp_newer(self, target, prev_ni): try: return self.get_timestamp() > target.get_timestamp() except AttributeError: return 1 def changed_timestamp_match(self, target, prev_ni): try: return self.get_timestamp() != prev_ni.timestamp except AttributeError: return 1 def decide_source(self, target, prev_ni): return target.get_build_env().decide_source(self, target, prev_ni) def decide_target(self, target, prev_ni): return target.get_build_env().decide_target(self, target, prev_ni) # Initialize this Node's decider function to decide_source() because # every file is a source file until it has a Builder attached... changed_since_last_build = decide_source def is_up_to_date(self): T = 0 if T: Trace('is_up_to_date(%s):' % self) if not self.exists(): if T: Trace(' not self.exists():') # The file doesn't exist locally... r = self.rfile() if r != self: # ...but there is one in a Repository... if not self.changed(r): if T: Trace(' changed(%s):' % r) # ...and it's even up-to-date... if self._local: # ...and they'd like a local copy. e = LocalCopy(self, r, None) if isinstance(e, SCons.Errors.BuildError): raise self.store_info() if T: Trace(' 1\n') return 1 self.changed() if T: Trace(' None\n') return None else: r = self.changed() if T: Trace(' self.exists(): %s\n' % r) return not r memoizer_counters.append(SCons.Memoize.CountValue('rfile')) def rfile(self): try: return self._memo['rfile'] except KeyError: pass result = self if not self.exists(): norm_name = _my_normcase(self.name) for dir in self.dir.get_all_rdirs(): try: node = dir.entries[norm_name] except KeyError: node = dir.file_on_disk(self.name) if node and node.exists() and \ (isinstance(node, File) or isinstance(node, Entry) \ or not node.is_derived()): result = node # Copy over our local attributes to the repository # Node so we identify shared object files in the # repository and don't assume they're static. # # This isn't perfect; the attribute would ideally # be attached to the object in the repository in # case it was built statically in the repository # and we changed it to shared locally, but that's # rarely the case and would only occur if you # intentionally used the same suffix for both # shared and static objects anyway. So this # should work well in practice. result.attributes = self.attributes break self._memo['rfile'] = result return result def rstr(self): return str(self.rfile()) def get_cachedir_csig(self): """ Fetch a Node's content signature for purposes of computing another Node's cachesig. This is a wrapper around the normal get_csig() method that handles the somewhat obscure case of using CacheDir with the -n option. Any files that don't exist would normally be "built" by fetching them from the cache, but the normal get_csig() method will try to open up the local file, which doesn't exist because the -n option meant we didn't actually pull the file from cachedir. But since the file *does* actually exist in the cachedir, we can use its contents for the csig. """ try: return self.cachedir_csig except AttributeError: pass cachedir, cachefile = self.get_build_env().get_CacheDir().cachepath(self) if not self.exists() and cachefile and os.path.exists(cachefile): self.cachedir_csig = SCons.Util.MD5filesignature(cachefile, \ SCons.Node.FS.File.md5_chunksize * 1024) else: self.cachedir_csig = self.get_csig() return self.cachedir_csig def get_cachedir_bsig(self): try: return self.cachesig except AttributeError: pass # Add the path to the cache signature, because multiple # targets built by the same action will all have the same # build signature, and we have to differentiate them somehow. children = self.children() executor = self.get_executor() # sigs = [n.get_cachedir_csig() for n in children] sigs = [n.get_cachedir_csig() for n in children] sigs.append(SCons.Util.MD5signature(executor.get_contents())) sigs.append(self.path) result = self.cachesig = SCons.Util.MD5collect(sigs) return result default_fs = None def get_default_fs(): global default_fs if not default_fs: default_fs = FS() return default_fs class FileFinder(object): """ """ if SCons.Memoize.use_memoizer: __metaclass__ = SCons.Memoize.Memoized_Metaclass memoizer_counters = [] def __init__(self): self._memo = {} def filedir_lookup(self, p, fd=None): """ A helper method for find_file() that looks up a directory for a file we're trying to find. This only creates the Dir Node if it exists on-disk, since if the directory doesn't exist we know we won't find any files in it... :-) It would be more compact to just use this as a nested function with a default keyword argument (see the commented-out version below), but that doesn't work unless you have nested scopes, so we define it here just so this work under Python 1.5.2. """ if fd is None: fd = self.default_filedir dir, name = os.path.split(fd) drive, d = _my_splitdrive(dir) if not name and d[:1] in ('/', OS_SEP): #return p.fs.get_root(drive).dir_on_disk(name) return p.fs.get_root(drive) if dir: p = self.filedir_lookup(p, dir) if not p: return None norm_name = _my_normcase(name) try: node = p.entries[norm_name] except KeyError: return p.dir_on_disk(name) if isinstance(node, Dir): return node if isinstance(node, Entry): node.must_be_same(Dir) return node return None def _find_file_key(self, filename, paths, verbose=None): return (filename, paths) memoizer_counters.append(SCons.Memoize.CountDict('find_file', _find_file_key)) def find_file(self, filename, paths, verbose=None): """ find_file(str, [Dir()]) -> [nodes] filename - a filename to find paths - a list of directory path *nodes* to search in. Can be represented as a list, a tuple, or a callable that is called with no arguments and returns the list or tuple. returns - the node created from the found file. Find a node corresponding to either a derived file or a file that exists already. Only the first file found is returned, and none is returned if no file is found. """ memo_key = self._find_file_key(filename, paths) try: memo_dict = self._memo['find_file'] except KeyError: memo_dict = {} self._memo['find_file'] = memo_dict else: try: return memo_dict[memo_key] except KeyError: pass if verbose and not callable(verbose): if not SCons.Util.is_String(verbose): verbose = "find_file" _verbose = u' %s: ' % verbose verbose = lambda s: sys.stdout.write(_verbose + s) filedir, filename = os.path.split(filename) if filedir: # More compact code that we can't use until we drop # support for Python 1.5.2: # #def filedir_lookup(p, fd=filedir): # """ # A helper function that looks up a directory for a file # we're trying to find. This only creates the Dir Node # if it exists on-disk, since if the directory doesn't # exist we know we won't find any files in it... :-) # """ # dir, name = os.path.split(fd) # if dir: # p = filedir_lookup(p, dir) # if not p: # return None # norm_name = _my_normcase(name) # try: # node = p.entries[norm_name] # except KeyError: # return p.dir_on_disk(name) # if isinstance(node, Dir): # return node # if isinstance(node, Entry): # node.must_be_same(Dir) # return node # if isinstance(node, Dir) or isinstance(node, Entry): # return node # return None #paths = [_f for _f in map(filedir_lookup, paths) if _f] self.default_filedir = filedir paths = [_f for _f in map(self.filedir_lookup, paths) if _f] result = None for dir in paths: if verbose: verbose("looking for '%s' in '%s' ...\n" % (filename, dir)) node, d = dir.srcdir_find_file(filename) if node: if verbose: verbose("... FOUND '%s' in '%s'\n" % (filename, d)) result = node break memo_dict[memo_key] = result return result find_file = FileFinder().find_file def invalidate_node_memos(targets): """ Invalidate the memoized values of all Nodes (files or directories) that are associated with the given entries. Has been added to clear the cache of nodes affected by a direct execution of an action (e.g. Delete/Copy/Chmod). Existing Node caches become inconsistent if the action is run through Execute(). The argument `targets` can be a single Node object or filename, or a sequence of Nodes/filenames. """ from traceback import extract_stack # First check if the cache really needs to be flushed. Only # actions run in the SConscript with Execute() seem to be # affected. XXX The way to check if Execute() is in the stacktrace # is a very dirty hack and should be replaced by a more sensible # solution. for f in extract_stack(): if f[2] == 'Execute' and f[0][-14:] == 'Environment.py': break else: # Dont have to invalidate, so return return if not SCons.Util.is_List(targets): targets = [targets] for entry in targets: # If the target is a Node object, clear the cache. If it is a # filename, look up potentially existing Node object first. try: entry.clear_memoized_values() except AttributeError: # Not a Node object, try to look up Node by filename. XXX # This creates Node objects even for those filenames which # do not correspond to an existing Node object. node = get_default_fs().Entry(entry) if node: node.clear_memoized_values() # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Node/NodeTests.py0000644000175000017500000011664012114661557022462 0ustar dktrkranzdktrkranz# # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Node/NodeTests.py 2013/03/03 09:48:35 garyo" import SCons.compat import collections import os import re import sys import unittest import SCons.Errors import SCons.Node import SCons.Util built_it = None built_target = None built_source = None cycle_detected = None built_order = 0 def _actionAppend(a1, a2): all = [] for curr_a in [a1, a2]: if isinstance(curr_a, MyAction): all.append(curr_a) elif isinstance(curr_a, MyListAction): all.extend(curr_a.list) elif isinstance(curr_a, list): all.extend(curr_a) else: raise Exception('Cannot Combine Actions') return MyListAction(all) class MyActionBase(object): def __add__(self, other): return _actionAppend(self, other) def __radd__(self, other): return _actionAppend(other, self) class MyAction(MyActionBase): def __init__(self): self.order = 0 def __call__(self, target, source, env, executor=None): global built_it, built_target, built_source, built_args, built_order if executor: target = executor.get_all_targets() source = executor.get_all_sources() built_it = 1 built_target = target built_source = source built_args = env built_order = built_order + 1 self.order = built_order return 0 def get_implicit_deps(self, target, source, env): return [] class MyExecutor(object): def __init__(self, env=None, targets=[], sources=[]): self.env = env self.targets = targets self.sources = sources def get_build_env(self): return self.env def get_build_scanner_path(self, scanner): return 'executor would call %s' % scanner def cleanup(self): self.cleaned_up = 1 def scan_targets(self, scanner): if not scanner: return d = scanner(self.targets) for t in self.targets: t.implicit.extend(d) def scan_sources(self, scanner): if not scanner: return d = scanner(self.sources) for t in self.targets: t.implicit.extend(d) class MyListAction(MyActionBase): def __init__(self, list): self.list = list def __call__(self, target, source, env): for A in self.list: A(target, source, env) class Environment(object): def __init__(self, **kw): self._dict = {} self._dict.update(kw) def __getitem__(self, key): return self._dict[key] def Dictionary(self, *args): return {} def Override(self, overrides): d = self._dict.copy() d.update(overrides) return Environment(**d) def _update(self, dict): self._dict.update(dict) def get_factory(self, factory): return factory or MyNode def get_scanner(self, scanner_key): return self._dict['SCANNERS'][0] class Builder(object): def __init__(self, env=None, is_explicit=1): if env is None: env = Environment() self.env = env self.overrides = {} self.action = MyAction() self.source_factory = MyNode self.is_explicit = is_explicit self.target_scanner = None self.source_scanner = None def targets(self, t): return [t] def get_actions(self): return [self.action] def get_contents(self, target, source, env): return 7 class NoneBuilder(Builder): def execute(self, target, source, env): Builder.execute(self, target, source, env) return None class ListBuilder(Builder): def __init__(self, *nodes): Builder.__init__(self) self.nodes = nodes def execute(self, target, source, env): if hasattr(self, 'status'): return self.status for n in self.nodes: n.prepare() target = self.nodes[0] self.status = Builder.execute(self, target, source, env) class FailBuilder(object): def execute(self, target, source, env): return 1 class ExceptBuilder(object): def execute(self, target, source, env): raise SCons.Errors.BuildError class ExceptBuilder2(object): def execute(self, target, source, env): raise Exception("foo") class Scanner(object): called = None def __call__(self, node): self.called = 1 return node.found_includes def path(self, env, dir, target=None, source=None): return () def select(self, node): return self def recurse_nodes(self, nodes): return nodes class MyNode(SCons.Node.Node): """The base Node class contains a number of do-nothing methods that we expect to be overridden by real, functional Node subclasses. So simulate a real, functional Node subclass. """ def __init__(self, name): SCons.Node.Node.__init__(self) self.name = name self.found_includes = [] def __str__(self): return self.name def get_found_includes(self, env, scanner, target): return scanner(self) class Calculator(object): def __init__(self, val): self.max_drift = 0 class M(object): def __init__(self, val): self.val = val def signature(self, args): return self.val def collect(self, args): result = self.val for a in args: result += a return result self.module = M(val) class NodeInfoBaseTestCase(unittest.TestCase): def test_merge(self): """Test merging NodeInfoBase attributes""" ni1 = SCons.Node.NodeInfoBase(SCons.Node.Node()) ni2 = SCons.Node.NodeInfoBase(SCons.Node.Node()) ni1.a1 = 1 ni1.a2 = 2 ni2.a2 = 222 ni2.a3 = 333 ni1.merge(ni2) expect = {'a1':1, 'a2':222, 'a3':333, '_version_id':1} assert ni1.__dict__ == expect, ni1.__dict__ def test_update(self): """Test the update() method""" ni = SCons.Node.NodeInfoBase(SCons.Node.Node()) ni.update(SCons.Node.Node()) def test_format(self): """Test the NodeInfoBase.format() method""" ni1 = SCons.Node.NodeInfoBase(SCons.Node.Node()) ni1.xxx = 'x' ni1.yyy = 'y' ni1.zzz = 'z' f = ni1.format() assert f == ['1', 'x', 'y', 'z'], f ni1.field_list = ['xxx', 'zzz', 'aaa'] f = ni1.format() assert f == ['x', 'z', 'None'], f class BuildInfoBaseTestCase(unittest.TestCase): def test___init__(self): """Test BuildInfoBase initialization""" n = SCons.Node.Node() bi = SCons.Node.BuildInfoBase(n) assert bi def test_merge(self): """Test merging BuildInfoBase attributes""" n1 = SCons.Node.Node() bi1 = SCons.Node.BuildInfoBase(n1) n2 = SCons.Node.Node() bi2 = SCons.Node.BuildInfoBase(n2) bi1.a1 = 1 bi1.a2 = 2 bi2.a2 = 222 bi2.a3 = 333 bi1.merge(bi2) assert bi1.a1 == 1, bi1.a1 assert bi1.a2 == 222, bi1.a2 assert bi1.a3 == 333, bi1.a3 class NodeTestCase(unittest.TestCase): def test_build(self): """Test building a node """ global built_it, built_order # Make sure it doesn't blow up if no builder is set. node = MyNode("www") node.build() assert built_it is None node.build(extra_kw_argument = 1) assert built_it is None node = MyNode("xxx") node.builder_set(Builder()) node.env_set(Environment()) node.path = "xxx" node.sources = ["yyy", "zzz"] node.build() assert built_it assert built_target == [node], built_target assert built_source == ["yyy", "zzz"], built_source built_it = None node = MyNode("qqq") node.builder_set(NoneBuilder()) node.env_set(Environment()) node.path = "qqq" node.sources = ["rrr", "sss"] node.builder.overrides = { "foo" : 1, "bar" : 2 } node.build() assert built_it assert built_target == [node], built_target assert built_source == ["rrr", "sss"], built_source assert built_args["foo"] == 1, built_args assert built_args["bar"] == 2, built_args fff = MyNode("fff") ggg = MyNode("ggg") lb = ListBuilder(fff, ggg) e = Environment() fff.builder_set(lb) fff.env_set(e) fff.path = "fff" ggg.builder_set(lb) ggg.env_set(e) ggg.path = "ggg" fff.sources = ["hhh", "iii"] ggg.sources = ["hhh", "iii"] # [Charles C. 1/7/2002] Uhhh, why are there no asserts here? # [SK, 15 May 2003] I dunno, let's add some... built_it = None fff.build() assert built_it assert built_target == [fff], built_target assert built_source == ["hhh", "iii"], built_source built_it = None ggg.build() assert built_it assert built_target == [ggg], built_target assert built_source == ["hhh", "iii"], built_source built_it = None jjj = MyNode("jjj") b = Builder() jjj.builder_set(b) # NOTE: No env_set()! We should pull the environment from the builder. b.env = Environment() b.overrides = { "on" : 3, "off" : 4 } e.builder = b jjj.build() assert built_it assert built_target[0] == jjj, built_target[0] assert built_source == [], built_source assert built_args["on"] == 3, built_args assert built_args["off"] == 4, built_args def test_get_build_scanner_path(self): """Test the get_build_scanner_path() method""" n = SCons.Node.Node() x = MyExecutor() n.set_executor(x) p = n.get_build_scanner_path('fake_scanner') assert p == "executor would call fake_scanner", p def test_get_executor(self): """Test the get_executor() method""" n = SCons.Node.Node() try: n.get_executor(0) except AttributeError: pass else: self.fail("did not catch expected AttributeError") class Builder(object): action = 'act' env = 'env1' overrides = {} n = SCons.Node.Node() n.builder_set(Builder()) x = n.get_executor() assert x.env == 'env1', x.env n = SCons.Node.Node() n.builder_set(Builder()) n.env_set('env2') x = n.get_executor() assert x.env == 'env2', x.env def test_set_executor(self): """Test the set_executor() method""" n = SCons.Node.Node() n.set_executor(1) assert n.executor == 1, n.executor def test_executor_cleanup(self): """Test letting the executor cleanup its cache""" n = SCons.Node.Node() x = MyExecutor() n.set_executor(x) n.executor_cleanup() assert x.cleaned_up def test_reset_executor(self): """Test the reset_executor() method""" n = SCons.Node.Node() n.set_executor(1) assert n.executor == 1, n.executor n.reset_executor() assert not hasattr(n, 'executor'), "unexpected executor attribute" def test_built(self): """Test the built() method""" class SubNodeInfo(SCons.Node.NodeInfoBase): def update(self, node): self.updated = 1 class SubNode(SCons.Node.Node): def clear(self): self.cleared = 1 n = SubNode() n.ninfo = SubNodeInfo(n) n.built() assert n.cleared, n.cleared assert n.ninfo.updated, n.ninfo.cleared def test_push_to_cache(self): """Test the base push_to_cache() method""" n = SCons.Node.Node() r = n.push_to_cache() assert r is None, r def test_retrieve_from_cache(self): """Test the base retrieve_from_cache() method""" n = SCons.Node.Node() r = n.retrieve_from_cache() assert r == 0, r def test_visited(self): """Test the base visited() method Just make sure it's there and we can call it. """ n = SCons.Node.Node() n.visited() def test_builder_set(self): """Test setting a Node's Builder """ node = SCons.Node.Node() b = Builder() node.builder_set(b) assert node.builder == b def test_has_builder(self): """Test the has_builder() method """ n1 = SCons.Node.Node() assert n1.has_builder() == 0 n1.builder_set(Builder()) assert n1.has_builder() == 1 def test_has_explicit_builder(self): """Test the has_explicit_builder() method """ n1 = SCons.Node.Node() assert not n1.has_explicit_builder() n1.set_explicit(1) assert n1.has_explicit_builder() n1.set_explicit(None) assert not n1.has_explicit_builder() def test_get_builder(self): """Test the get_builder() method""" n1 = SCons.Node.Node() b = n1.get_builder() assert b is None, b b = n1.get_builder(777) assert b == 777, b n1.builder_set(888) b = n1.get_builder() assert b == 888, b b = n1.get_builder(999) assert b == 888, b def test_multiple_side_effect_has_builder(self): """Test the multiple_side_effect_has_builder() method """ n1 = SCons.Node.Node() assert n1.multiple_side_effect_has_builder() == 0 n1.builder_set(Builder()) assert n1.multiple_side_effect_has_builder() == 1 def test_is_derived(self): """Test the is_derived() method """ n1 = SCons.Node.Node() n2 = SCons.Node.Node() n3 = SCons.Node.Node() n2.builder_set(Builder()) n3.side_effect = 1 assert n1.is_derived() == 0 assert n2.is_derived() == 1 assert n3.is_derived() == 1 def test_alter_targets(self): """Test the alter_targets() method """ n = SCons.Node.Node() t, m = n.alter_targets() assert t == [], t assert m is None, m def test_is_up_to_date(self): """Test the default is_up_to_date() method """ node = SCons.Node.Node() assert node.is_up_to_date() is None def test_children_are_up_to_date(self): """Test the children_are_up_to_date() method used by subclasses """ n1 = SCons.Node.Node() n2 = SCons.Node.Node() n1.add_source([n2]) assert n1.children_are_up_to_date(), "expected up to date" n2.set_state(SCons.Node.executed) assert not n1.children_are_up_to_date(), "expected not up to date" n2.set_state(SCons.Node.up_to_date) assert n1.children_are_up_to_date(), "expected up to date" n1.always_build = 1 assert not n1.children_are_up_to_date(), "expected not up to date" def test_env_set(self): """Test setting a Node's Environment """ node = SCons.Node.Node() e = Environment() node.env_set(e) assert node.env == e def test_get_actions(self): """Test fetching a Node's action list """ node = SCons.Node.Node() node.builder_set(Builder()) a = node.builder.get_actions() assert isinstance(a[0], MyAction), a[0] def test_get_csig(self): """Test generic content signature calculation """ node = SCons.Node.Node() node.get_contents = lambda: 444 result = node.get_csig() assert result == '550a141f12de6341fba65b0ad0433500', result def test_get_cachedir_csig(self): """Test content signature calculation for CacheDir """ node = SCons.Node.Node() node.get_contents = lambda: 555 result = node.get_cachedir_csig() assert result == '15de21c670ae7c3f6f3f1f37029303c9', result def test_get_binfo(self): """Test fetching/creating a build information structure """ node = SCons.Node.Node() binfo = node.get_binfo() assert isinstance(binfo, SCons.Node.BuildInfoBase), binfo node = SCons.Node.Node() d = SCons.Node.Node() d.get_ninfo().csig = 777 i = SCons.Node.Node() i.get_ninfo().csig = 888 node.depends = [d] node.implicit = [i] binfo = node.get_binfo() assert isinstance(binfo, SCons.Node.BuildInfoBase), binfo assert hasattr(binfo, 'bsources') assert hasattr(binfo, 'bsourcesigs') assert binfo.bdepends == [d] assert hasattr(binfo, 'bdependsigs') assert binfo.bimplicit == [i] assert hasattr(binfo, 'bimplicitsigs') def test_explain(self): """Test explaining why a Node must be rebuilt """ class testNode(SCons.Node.Node): def __str__(self): return 'xyzzy' node = testNode() node.exists = lambda: None # Can't do this with new-style classes (python bug #1066490) #node.__str__ = lambda: 'xyzzy' result = node.explain() assert result == "building `xyzzy' because it doesn't exist\n", result class testNode2(SCons.Node.Node): def __str__(self): return 'null_binfo' class FS(object): pass node = testNode2() node.fs = FS() node.fs.Top = SCons.Node.Node() result = node.explain() assert result is None, result def get_null_info(): class Null_SConsignEntry(object): class Null_BuildInfo(object): def prepare_dependencies(self): pass binfo = Null_BuildInfo() return Null_SConsignEntry() node.get_stored_info = get_null_info #see above: node.__str__ = lambda: 'null_binfo' result = node.explain() assert result == "Cannot explain why `null_binfo' is being rebuilt: No previous build information found\n", result # XXX additional tests for the guts of the functionality some day #def test_del_binfo(self): # """Test deleting the build information from a Node # """ # node = SCons.Node.Node() # node.binfo = None # node.del_binfo() # assert not hasattr(node, 'binfo'), node def test_store_info(self): """Test calling the method to store build information """ node = SCons.Node.Node() node.store_info() def test_get_stored_info(self): """Test calling the method to fetch stored build information """ node = SCons.Node.Node() result = node.get_stored_info() assert result is None, result def test_set_always_build(self): """Test setting a Node's always_build value """ node = SCons.Node.Node() node.set_always_build() assert node.always_build node.set_always_build(3) assert node.always_build == 3 def test_set_noclean(self): """Test setting a Node's noclean value """ node = SCons.Node.Node() node.set_noclean() assert node.noclean == 1, node.noclean node.set_noclean(7) assert node.noclean == 1, node.noclean node.set_noclean(0) assert node.noclean == 0, node.noclean node.set_noclean(None) assert node.noclean == 0, node.noclean def test_set_precious(self): """Test setting a Node's precious value """ node = SCons.Node.Node() node.set_precious() assert node.precious node.set_precious(7) assert node.precious == 7 def test_exists(self): """Test evaluating whether a Node exists. """ node = SCons.Node.Node() e = node.exists() assert e == 1, e def test_exists(self): """Test evaluating whether a Node exists locally or in a repository. """ node = SCons.Node.Node() e = node.rexists() assert e == 1, e class MyNode(SCons.Node.Node): def exists(self): return 'xyz' node = MyNode() e = node.rexists() assert e == 'xyz', e def test_prepare(self): """Test preparing a node to be built By extension, this also tests the missing() method. """ node = SCons.Node.Node() n1 = SCons.Node.Node() n1.builder_set(Builder()) node.implicit = [] node.implicit_set = set() node._add_child(node.implicit, node.implicit_set, [n1]) node.prepare() # should not throw an exception n2 = SCons.Node.Node() n2.linked = 1 node.implicit = [] node.implicit_set = set() node._add_child(node.implicit, node.implicit_set, [n2]) node.prepare() # should not throw an exception n3 = SCons.Node.Node() node.implicit = [] node.implicit_set = set() node._add_child(node.implicit, node.implicit_set, [n3]) node.prepare() # should not throw an exception class MyNode(SCons.Node.Node): def rexists(self): return None n4 = MyNode() node.implicit = [] node.implicit_set = set() node._add_child(node.implicit, node.implicit_set, [n4]) exc_caught = 0 try: node.prepare() except SCons.Errors.StopError: exc_caught = 1 assert exc_caught, "did not catch expected StopError" def test_add_dependency(self): """Test adding dependencies to a Node's list. """ node = SCons.Node.Node() assert node.depends == [] zero = SCons.Node.Node() one = SCons.Node.Node() two = SCons.Node.Node() three = SCons.Node.Node() four = SCons.Node.Node() five = SCons.Node.Node() six = SCons.Node.Node() node.add_dependency([zero]) assert node.depends == [zero] node.add_dependency([one]) assert node.depends == [zero, one] node.add_dependency([two, three]) assert node.depends == [zero, one, two, three] node.add_dependency([three, four, one]) assert node.depends == [zero, one, two, three, four] try: node.add_depends([[five, six]]) except: pass else: raise Exception("did not catch expected exception") assert node.depends == [zero, one, two, three, four] def test_add_source(self): """Test adding sources to a Node's list. """ node = SCons.Node.Node() assert node.sources == [] zero = SCons.Node.Node() one = SCons.Node.Node() two = SCons.Node.Node() three = SCons.Node.Node() four = SCons.Node.Node() five = SCons.Node.Node() six = SCons.Node.Node() node.add_source([zero]) assert node.sources == [zero] node.add_source([one]) assert node.sources == [zero, one] node.add_source([two, three]) assert node.sources == [zero, one, two, three] node.add_source([three, four, one]) assert node.sources == [zero, one, two, three, four] try: node.add_source([[five, six]]) except: pass else: raise Exception("did not catch expected exception") assert node.sources == [zero, one, two, three, four], node.sources def test_add_ignore(self): """Test adding files whose dependencies should be ignored. """ node = SCons.Node.Node() assert node.ignore == [] zero = SCons.Node.Node() one = SCons.Node.Node() two = SCons.Node.Node() three = SCons.Node.Node() four = SCons.Node.Node() five = SCons.Node.Node() six = SCons.Node.Node() node.add_ignore([zero]) assert node.ignore == [zero] node.add_ignore([one]) assert node.ignore == [zero, one] node.add_ignore([two, three]) assert node.ignore == [zero, one, two, three] node.add_ignore([three, four, one]) assert node.ignore == [zero, one, two, three, four] try: node.add_ignore([[five, six]]) except: pass else: raise Exception("did not catch expected exception") assert node.ignore == [zero, one, two, three, four] def test_get_found_includes(self): """Test the default get_found_includes() method """ node = SCons.Node.Node() target = SCons.Node.Node() e = Environment() deps = node.get_found_includes(e, None, target) assert deps == [], deps def test_get_implicit_deps(self): """Test get_implicit_deps() """ node = MyNode("nnn") target = MyNode("ttt") env = Environment() # No scanner at all returns [] deps = node.get_implicit_deps(env, None, target) assert deps == [], deps s = Scanner() d1 = MyNode("d1") d2 = MyNode("d2") node.found_includes = [d1, d2] # Simple return of the found includes deps = node.get_implicit_deps(env, s, target) assert deps == [d1, d2], deps # By default, our fake scanner recurses e = MyNode("eee") f = MyNode("fff") g = MyNode("ggg") d1.found_includes = [e, f] d2.found_includes = [e, f] f.found_includes = [g] deps = node.get_implicit_deps(env, s, target) assert deps == [d1, d2, e, f, g], list(map(str, deps)) # Recursive scanning eliminates duplicates e.found_includes = [f] deps = node.get_implicit_deps(env, s, target) assert deps == [d1, d2, e, f, g], list(map(str, deps)) # Scanner method can select specific nodes to recurse def no_fff(nodes): return [n for n in nodes if str(n)[0] != 'f'] s.recurse_nodes = no_fff deps = node.get_implicit_deps(env, s, target) assert deps == [d1, d2, e, f], list(map(str, deps)) # Scanner method can short-circuit recursing entirely s.recurse_nodes = lambda nodes: [] deps = node.get_implicit_deps(env, s, target) assert deps == [d1, d2], list(map(str, deps)) def test_get_env_scanner(self): """Test fetching the environment scanner for a Node """ node = SCons.Node.Node() scanner = Scanner() env = Environment(SCANNERS = [scanner]) s = node.get_env_scanner(env) assert s == scanner, s s = node.get_env_scanner(env, {'X':1}) assert s == scanner, s def test_get_target_scanner(self): """Test fetching the target scanner for a Node """ s = Scanner() b = Builder() b.target_scanner = s n = SCons.Node.Node() n.builder = b x = n.get_target_scanner() assert x is s, x def test_get_source_scanner(self): """Test fetching the source scanner for a Node """ target = SCons.Node.Node() source = SCons.Node.Node() s = target.get_source_scanner(source) assert isinstance(s, SCons.Util.Null), s ts1 = Scanner() ts2 = Scanner() ts3 = Scanner() class Builder1(Builder): def __call__(self, source): r = SCons.Node.Node() r.builder = self return [r] class Builder2(Builder1): def __init__(self, scanner): self.source_scanner = scanner builder = Builder2(ts1) targets = builder([source]) s = targets[0].get_source_scanner(source) assert s is ts1, s target.builder_set(Builder2(ts1)) target.builder.source_scanner = ts2 s = target.get_source_scanner(source) assert s is ts2, s builder = Builder1(env=Environment(SCANNERS = [ts3])) targets = builder([source]) s = targets[0].get_source_scanner(source) assert s is ts3, s def test_scan(self): """Test Scanner functionality """ env = Environment() node = MyNode("nnn") node.builder = Builder() node.env_set(env) x = MyExecutor(env, [node]) s = Scanner() d = MyNode("ddd") node.found_includes = [d] node.builder.target_scanner = s assert node.implicit is None node.scan() assert s.called assert node.implicit == [d], node.implicit # Check that scanning a node with some stored implicit # dependencies resets internal attributes appropriately # if the stored dependencies need recalculation. class StoredNode(MyNode): def get_stored_implicit(self): return [MyNode('implicit1'), MyNode('implicit2')] save_implicit_cache = SCons.Node.implicit_cache save_implicit_deps_changed = SCons.Node.implicit_deps_changed save_implicit_deps_unchanged = SCons.Node.implicit_deps_unchanged SCons.Node.implicit_cache = 1 SCons.Node.implicit_deps_changed = None SCons.Node.implicit_deps_unchanged = None try: sn = StoredNode("eee") sn.builder_set(Builder()) sn.builder.target_scanner = s sn.scan() assert sn.implicit == [], sn.implicit assert sn.children() == [], sn.children() finally: SCons.Node.implicit_cache = save_implicit_cache SCons.Node.implicit_deps_changed = save_implicit_deps_changed SCons.Node.implicit_deps_unchanged = save_implicit_deps_unchanged def test_scanner_key(self): """Test that a scanner_key() method exists""" assert SCons.Node.Node().scanner_key() is None def test_children(self): """Test fetching the non-ignored "children" of a Node. """ node = SCons.Node.Node() n1 = SCons.Node.Node() n2 = SCons.Node.Node() n3 = SCons.Node.Node() n4 = SCons.Node.Node() n5 = SCons.Node.Node() n6 = SCons.Node.Node() n7 = SCons.Node.Node() n8 = SCons.Node.Node() n9 = SCons.Node.Node() n10 = SCons.Node.Node() n11 = SCons.Node.Node() n12 = SCons.Node.Node() node.add_source([n1, n2, n3]) node.add_dependency([n4, n5, n6]) node.implicit = [] node.implicit_set = set() node._add_child(node.implicit, node.implicit_set, [n7, n8, n9]) node._add_child(node.implicit, node.implicit_set, [n10, n11, n12]) node.add_ignore([n2, n5, n8, n11]) kids = node.children() for kid in [n1, n3, n4, n6, n7, n9, n10, n12]: assert kid in kids, kid for kid in [n2, n5, n8, n11]: assert not kid in kids, kid def test_all_children(self): """Test fetching all the "children" of a Node. """ node = SCons.Node.Node() n1 = SCons.Node.Node() n2 = SCons.Node.Node() n3 = SCons.Node.Node() n4 = SCons.Node.Node() n5 = SCons.Node.Node() n6 = SCons.Node.Node() n7 = SCons.Node.Node() n8 = SCons.Node.Node() n9 = SCons.Node.Node() n10 = SCons.Node.Node() n11 = SCons.Node.Node() n12 = SCons.Node.Node() node.add_source([n1, n2, n3]) node.add_dependency([n4, n5, n6]) node.implicit = [] node.implicit_set = set() node._add_child(node.implicit, node.implicit_set, [n7, n8, n9]) node._add_child(node.implicit, node.implicit_set, [n10, n11, n12]) node.add_ignore([n2, n5, n8, n11]) kids = node.all_children() for kid in [n1, n2, n3, n4, n5, n6, n7, n8, n9, n10, n11, n12]: assert kid in kids, kid def test_state(self): """Test setting and getting the state of a node """ node = SCons.Node.Node() assert node.get_state() == SCons.Node.no_state node.set_state(SCons.Node.executing) assert node.get_state() == SCons.Node.executing assert SCons.Node.pending < SCons.Node.executing assert SCons.Node.executing < SCons.Node.up_to_date assert SCons.Node.up_to_date < SCons.Node.executed assert SCons.Node.executed < SCons.Node.failed def test_walker(self): """Test walking a Node tree. """ n1 = MyNode("n1") nw = SCons.Node.Walker(n1) assert not nw.is_done() assert nw.get_next().name == "n1" assert nw.is_done() assert nw.get_next() is None n2 = MyNode("n2") n3 = MyNode("n3") n1.add_source([n2, n3]) nw = SCons.Node.Walker(n1) n = nw.get_next() assert n.name == "n2", n.name n = nw.get_next() assert n.name == "n3", n.name n = nw.get_next() assert n.name == "n1", n.name n = nw.get_next() assert n is None, n n4 = MyNode("n4") n5 = MyNode("n5") n6 = MyNode("n6") n7 = MyNode("n7") n2.add_source([n4, n5]) n3.add_dependency([n6, n7]) nw = SCons.Node.Walker(n1) assert nw.get_next().name == "n4" assert nw.get_next().name == "n5" assert n2 in nw.history assert nw.get_next().name == "n2" assert nw.get_next().name == "n6" assert nw.get_next().name == "n7" assert n3 in nw.history assert nw.get_next().name == "n3" assert n1 in nw.history assert nw.get_next().name == "n1" assert nw.get_next() is None n8 = MyNode("n8") n8.add_dependency([n3]) n7.add_dependency([n8]) def cycle(node, stack): global cycle_detected cycle_detected = 1 global cycle_detected nw = SCons.Node.Walker(n3, cycle_func = cycle) n = nw.get_next() assert n.name == "n6", n.name n = nw.get_next() assert n.name == "n8", n.name assert cycle_detected cycle_detected = None n = nw.get_next() assert n.name == "n7", n.name n = nw.get_next() assert nw.get_next() is None def test_abspath(self): """Test the get_abspath() method.""" n = MyNode("foo") assert n.get_abspath() == str(n), n.get_abspath() def test_for_signature(self): """Test the for_signature() method.""" n = MyNode("foo") assert n.for_signature() == str(n), n.get_abspath() def test_get_string(self): """Test the get_string() method.""" class TestNode(MyNode): def __init__(self, name, sig): MyNode.__init__(self, name) self.sig = sig def for_signature(self): return self.sig n = TestNode("foo", "bar") assert n.get_string(0) == "foo", n.get_string(0) assert n.get_string(1) == "bar", n.get_string(1) def test_literal(self): """Test the is_literal() function.""" n=SCons.Node.Node() assert n.is_literal() def test_Annotate(self): """Test using an interface-specific Annotate function.""" def my_annotate(node, self=self): node.annotation = self.node_string save_Annotate = SCons.Node.Annotate SCons.Node.Annotate = my_annotate try: self.node_string = '#1' n = SCons.Node.Node() assert n.annotation == '#1', n.annotation self.node_string = '#2' n = SCons.Node.Node() assert n.annotation == '#2', n.annotation finally: SCons.Node.Annotate = save_Annotate def test_clear(self): """Test clearing all cached state information.""" n = SCons.Node.Node() n.set_state(3) n.binfo = 'xyz' n.includes = 'testincludes' n.found_include = {'testkey':'testvalue'} n.implicit = 'testimplicit' x = MyExecutor() n.set_executor(x) n.clear() assert n.includes is None, n.includes assert x.cleaned_up def test_get_subst_proxy(self): """Test the get_subst_proxy method.""" n = MyNode("test") assert n.get_subst_proxy() == n, n.get_subst_proxy() def test_new_binfo(self): """Test the new_binfo() method""" n = SCons.Node.Node() result = n.new_binfo() assert isinstance(result, SCons.Node.BuildInfoBase), result def test_get_suffix(self): """Test the base Node get_suffix() method""" n = SCons.Node.Node() s = n.get_suffix() assert s == '', s def test_postprocess(self): """Test calling the base Node postprocess() method""" n = SCons.Node.Node() n.waiting_parents = set( ['foo','bar'] ) n.postprocess() assert n.waiting_parents == set(), n.waiting_parents def test_add_to_waiting_parents(self): """Test the add_to_waiting_parents() method""" n1 = SCons.Node.Node() n2 = SCons.Node.Node() assert n1.waiting_parents == set(), n1.waiting_parents r = n1.add_to_waiting_parents(n2) assert r == 1, r assert n1.waiting_parents == set((n2,)), n1.waiting_parents r = n1.add_to_waiting_parents(n2) assert r == 0, r class NodeListTestCase(unittest.TestCase): def test___str__(self): """Test""" n1 = MyNode("n1") n2 = MyNode("n2") n3 = MyNode("n3") nl = SCons.Node.NodeList([n3, n2, n1]) l = [1] ul = collections.UserList([2]) s = str(nl) assert s == "['n3', 'n2', 'n1']", s r = repr(nl) r = re.sub('at (0[xX])?[0-9a-fA-F]+', 'at 0x', r) # Don't care about ancestry: just leaf value of MyNode r = re.sub('<.*?\.MyNode', '"]*3) assert r == '[%s]' % l, r if __name__ == "__main__": suite = unittest.TestSuite() tclasses = [ BuildInfoBaseTestCase, NodeInfoBaseTestCase, NodeTestCase, NodeListTestCase ] for tclass in tclasses: names = unittest.getTestCaseNames(tclass, 'test_') suite.addTests(list(map(tclass, names))) if not unittest.TextTestRunner().run(suite).wasSuccessful(): sys.exit(1) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Node/AliasTests.py0000644000175000017500000000772712114661557022633 0ustar dktrkranzdktrkranz# # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Node/AliasTests.py 2013/03/03 09:48:35 garyo" import sys import unittest import SCons.Errors import SCons.Node.Alias class AliasTestCase(unittest.TestCase): def test_AliasNameSpace(self): """Test creating an Alias name space """ ans = SCons.Node.Alias.AliasNameSpace() assert ans is not None, ans def test_ANS_Alias(self): """Test the Alias() factory """ ans = SCons.Node.Alias.AliasNameSpace() a1 = ans.Alias('a1') assert a1.name == 'a1', a1.name a2 = ans.Alias('a1') assert a1 is a2, (a1, a2) def test_get_contents(self): """Test the get_contents() method """ class DummyNode(object): def __init__(self, contents): self.contents = contents def get_csig(self): return self.contents def get_contents(self): return self.contents ans = SCons.Node.Alias.AliasNameSpace() ans.Alias('a1') a = ans.lookup('a1') a.sources = [ DummyNode('one'), DummyNode('two'), DummyNode('three') ] c = a.get_contents() assert c == 'onetwothree', c def test_lookup(self): """Test the lookup() method """ ans = SCons.Node.Alias.AliasNameSpace() ans.Alias('a1') a = ans.lookup('a1') assert a.name == 'a1', a.name a1 = ans.lookup('a1') assert a is a1, a1 a = ans.lookup('a2') assert a is None, a def test_Alias(self): """Test creating an Alias() object """ a1 = SCons.Node.Alias.Alias('a') assert a1.name == 'a', a1.name a2 = SCons.Node.Alias.Alias('a') assert a2.name == 'a', a2.name assert not a1 is a2 assert a1.name == a2.name class AliasNodeInfoTestCase(unittest.TestCase): def test___init__(self): """Test AliasNodeInfo initialization""" ans = SCons.Node.Alias.AliasNameSpace() aaa = ans.Alias('aaa') ni = SCons.Node.Alias.AliasNodeInfo(aaa) class AliasBuildInfoTestCase(unittest.TestCase): def test___init__(self): """Test AliasBuildInfo initialization""" ans = SCons.Node.Alias.AliasNameSpace() aaa = ans.Alias('aaa') bi = SCons.Node.Alias.AliasBuildInfo(aaa) if __name__ == "__main__": suite = unittest.TestSuite() tclasses = [ AliasTestCase, AliasBuildInfoTestCase, AliasNodeInfoTestCase, ] for tclass in tclasses: names = unittest.getTestCaseNames(tclass, 'test_') suite.addTests(list(map(tclass, names))) if not unittest.TextTestRunner().run(suite).wasSuccessful(): sys.exit(1) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Node/FSTests.py0000644000175000017500000036774412114661557022122 0ustar dktrkranzdktrkranz# # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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. # from __future__ import division __revision__ = "src/engine/SCons/Node/FSTests.py 2013/03/03 09:48:35 garyo" import SCons.compat import os import os.path import sys import time import unittest from TestCmd import TestCmd import shutil import stat import SCons.Errors import SCons.Node.FS import SCons.Util import SCons.Warnings built_it = None scanner_count = 0 class Scanner(object): def __init__(self, node=None): global scanner_count scanner_count = scanner_count + 1 self.hash = scanner_count self.node = node def path(self, env, dir, target=None, source=None): return () def __call__(self, node, env, path): return [self.node] def __hash__(self): return self.hash def select(self, node): return self def recurse_nodes(self, nodes): return nodes class Environment(object): def __init__(self): self.scanner = Scanner() def Dictionary(self, *args): return {} def autogenerate(self, **kw): return {} def get_scanner(self, skey): return self.scanner def Override(self, overrides): return self def _update(self, dict): pass class Action(object): def __call__(self, targets, sources, env, **kw): global built_it if kw.get('execute', 1): built_it = 1 return 0 def show(self, string): pass def get_contents(self, target, source, env): return "" def genstring(self, target, source, env): return "" def strfunction(self, targets, sources, env): return "" def get_implicit_deps(self, target, source, env): return [] class Builder(object): def __init__(self, factory, action=Action()): self.factory = factory self.env = Environment() self.overrides = {} self.action = action self.target_scanner = None self.source_scanner = None def targets(self, t): return [t] def source_factory(self, name): return self.factory(name) class _tempdirTestCase(unittest.TestCase): def setUp(self): self.save_cwd = os.getcwd() self.test = TestCmd(workdir='') # FS doesn't like the cwd to be something other than its root. os.chdir(self.test.workpath("")) self.fs = SCons.Node.FS.FS() def tearDown(self): os.chdir(self.save_cwd) class VariantDirTestCase(unittest.TestCase): def runTest(self): """Test variant dir functionality""" test=TestCmd(workdir='') fs = SCons.Node.FS.FS() f1 = fs.File('build/test1') fs.VariantDir('build', 'src') f2 = fs.File('build/test2') d1 = fs.Dir('build') assert f1.srcnode().path == os.path.normpath('src/test1'), f1.srcnode().path assert f2.srcnode().path == os.path.normpath('src/test2'), f2.srcnode().path assert d1.srcnode().path == 'src', d1.srcnode().path fs = SCons.Node.FS.FS() f1 = fs.File('build/test1') fs.VariantDir('build', '.') f2 = fs.File('build/test2') d1 = fs.Dir('build') assert f1.srcnode().path == 'test1', f1.srcnode().path assert f2.srcnode().path == 'test2', f2.srcnode().path assert d1.srcnode().path == '.', d1.srcnode().path fs = SCons.Node.FS.FS() fs.VariantDir('build/var1', 'src') fs.VariantDir('build/var2', 'src') f1 = fs.File('build/var1/test1') f2 = fs.File('build/var2/test1') assert f1.srcnode().path == os.path.normpath('src/test1'), f1.srcnode().path assert f2.srcnode().path == os.path.normpath('src/test1'), f2.srcnode().path fs = SCons.Node.FS.FS() fs.VariantDir('../var1', 'src') fs.VariantDir('../var2', 'src') f1 = fs.File('../var1/test1') f2 = fs.File('../var2/test1') assert f1.srcnode().path == os.path.normpath('src/test1'), f1.srcnode().path assert f2.srcnode().path == os.path.normpath('src/test1'), f2.srcnode().path # Set up some files test.subdir('work', ['work', 'src']) test.subdir(['work', 'build'], ['work', 'build', 'var1']) test.subdir(['work', 'build', 'var2']) test.subdir('rep1', ['rep1', 'src']) test.subdir(['rep1', 'build'], ['rep1', 'build', 'var1']) test.subdir(['rep1', 'build', 'var2']) # A source file in the source directory test.write([ 'work', 'src', 'test.in' ], 'test.in') # A source file in a subdir of the source directory test.subdir([ 'work', 'src', 'new_dir' ]) test.write([ 'work', 'src', 'new_dir', 'test9.out' ], 'test9.out\n') # A source file in the repository test.write([ 'rep1', 'src', 'test2.in' ], 'test2.in') # Some source files in the variant directory test.write([ 'work', 'build', 'var2', 'test.in' ], 'test.old') test.write([ 'work', 'build', 'var2', 'test2.in' ], 'test2.old') # An old derived file in the variant directories test.write([ 'work', 'build', 'var1', 'test.out' ], 'test.old') test.write([ 'work', 'build', 'var2', 'test.out' ], 'test.old') # And just in case we are weird, a derived file in the source # dir. test.write([ 'work', 'src', 'test.out' ], 'test.out.src') # A derived file in the repository test.write([ 'rep1', 'build', 'var1', 'test2.out' ], 'test2.out_rep') test.write([ 'rep1', 'build', 'var2', 'test2.out' ], 'test2.out_rep') os.chdir(test.workpath('work')) fs = SCons.Node.FS.FS(test.workpath('work')) fs.VariantDir('build/var1', 'src', duplicate=0) fs.VariantDir('build/var2', 'src') f1 = fs.File('build/var1/test.in') f1out = fs.File('build/var1/test.out') f1out.builder = 1 f1out_2 = fs.File('build/var1/test2.out') f1out_2.builder = 1 f2 = fs.File('build/var2/test.in') f2out = fs.File('build/var2/test.out') f2out.builder = 1 f2out_2 = fs.File('build/var2/test2.out') f2out_2.builder = 1 fs.Repository(test.workpath('rep1')) assert f1.srcnode().path == os.path.normpath('src/test.in'),\ f1.srcnode().path # str(node) returns source path for duplicate = 0 assert str(f1) == os.path.normpath('src/test.in'), str(f1) # Build path does not exist assert not f1.exists() # ...but the actual file is not there... assert not os.path.exists(f1.get_abspath()) # And duplicate=0 should also work just like a Repository assert f1.rexists() # rfile() should point to the source path assert f1.rfile().path == os.path.normpath('src/test.in'),\ f1.rfile().path assert f2.srcnode().path == os.path.normpath('src/test.in'),\ f2.srcnode().path # str(node) returns build path for duplicate = 1 assert str(f2) == os.path.normpath('build/var2/test.in'), str(f2) # Build path exists assert f2.exists() # ...and exists() should copy the file from src to build path assert test.read(['work', 'build', 'var2', 'test.in']) == 'test.in',\ test.read(['work', 'build', 'var2', 'test.in']) # Since exists() is true, so should rexists() be assert f2.rexists() f3 = fs.File('build/var1/test2.in') f4 = fs.File('build/var2/test2.in') assert f3.srcnode().path == os.path.normpath('src/test2.in'),\ f3.srcnode().path # str(node) returns source path for duplicate = 0 assert str(f3) == os.path.normpath('src/test2.in'), str(f3) # Build path does not exist assert not f3.exists() # Source path does not either assert not f3.srcnode().exists() # But we do have a file in the Repository assert f3.rexists() # rfile() should point to the source path assert f3.rfile().path == os.path.normpath(test.workpath('rep1/src/test2.in')),\ f3.rfile().path assert f4.srcnode().path == os.path.normpath('src/test2.in'),\ f4.srcnode().path # str(node) returns build path for duplicate = 1 assert str(f4) == os.path.normpath('build/var2/test2.in'), str(f4) # Build path should exist assert f4.exists() # ...and copy over the file into the local build path assert test.read(['work', 'build', 'var2', 'test2.in']) == 'test2.in' # should exist in repository, since exists() is true assert f4.rexists() # rfile() should point to ourselves assert f4.rfile().path == os.path.normpath('build/var2/test2.in'),\ f4.rfile().path f5 = fs.File('build/var1/test.out') f6 = fs.File('build/var2/test.out') assert f5.exists() # We should not copy the file from the source dir, since this is # a derived file. assert test.read(['work', 'build', 'var1', 'test.out']) == 'test.old' assert f6.exists() # We should not copy the file from the source dir, since this is # a derived file. assert test.read(['work', 'build', 'var2', 'test.out']) == 'test.old' f7 = fs.File('build/var1/test2.out') f8 = fs.File('build/var2/test2.out') assert not f7.exists() assert f7.rexists() r = f7.rfile().path expect = os.path.normpath(test.workpath('rep1/build/var1/test2.out')) assert r == expect, (repr(r), repr(expect)) assert not f8.exists() assert f8.rexists() assert f8.rfile().path == os.path.normpath(test.workpath('rep1/build/var2/test2.out')),\ f8.rfile().path # Verify the Mkdir and Link actions are called d9 = fs.Dir('build/var2/new_dir') f9 = fs.File('build/var2/new_dir/test9.out') class MkdirAction(Action): def __init__(self, dir_made): self.dir_made = dir_made def __call__(self, target, source, env, executor=None): if executor: target = executor.get_all_targets() source = executor.get_all_sources() self.dir_made.extend(target) save_Link = SCons.Node.FS.Link link_made = [] def link_func(target, source, env, link_made=link_made): link_made.append(target) SCons.Node.FS.Link = link_func try: dir_made = [] d9.builder = Builder(fs.Dir, action=MkdirAction(dir_made)) d9.reset_executor() f9.exists() expect = os.path.join('build', 'var2', 'new_dir') assert dir_made[0].path == expect, dir_made[0].path expect = os.path.join('build', 'var2', 'new_dir', 'test9.out') assert link_made[0].path == expect, link_made[0].path assert f9.linked finally: SCons.Node.FS.Link = save_Link # Test for an interesting pathological case...we have a source # file in a build path, but not in a source path. This can # happen if you switch from duplicate=1 to duplicate=0, then # delete a source file. At one time, this would cause exists() # to return a 1 but get_contents() to throw. test.write([ 'work', 'build', 'var1', 'asourcefile' ], 'stuff') f10 = fs.File('build/var1/asourcefile') assert f10.exists() assert f10.get_contents() == 'stuff', f10.get_contents() f11 = fs.File('src/file11') t, m = f11.alter_targets() bdt = [n.path for n in t] var1_file11 = os.path.normpath('build/var1/file11') var2_file11 = os.path.normpath('build/var2/file11') assert bdt == [var1_file11, var2_file11], bdt f12 = fs.File('src/file12') f12.builder = 1 bdt, m = f12.alter_targets() assert bdt == [], [n.path for n in bdt] d13 = fs.Dir('src/new_dir') t, m = d13.alter_targets() bdt = [n.path for n in t] var1_new_dir = os.path.normpath('build/var1/new_dir') var2_new_dir = os.path.normpath('build/var2/new_dir') assert bdt == [var1_new_dir, var2_new_dir], bdt # Test that an IOError trying to Link a src file # into a VariantDir ends up throwing a StopError. fIO = fs.File("build/var2/IOError") save_Link = SCons.Node.FS.Link def Link_IOError(target, source, env): raise IOError(17, "Link_IOError") SCons.Node.FS.Link = SCons.Action.Action(Link_IOError, None) test.write(['work', 'src', 'IOError'], "work/src/IOError\n") try: exc_caught = 0 try: fIO.exists() except SCons.Errors.StopError: exc_caught = 1 assert exc_caught, "Should have caught a StopError" finally: SCons.Node.FS.Link = save_Link # Test to see if Link() works... test.subdir('src','build') test.write('src/foo', 'src/foo\n') os.chmod(test.workpath('src/foo'), stat.S_IRUSR) SCons.Node.FS.Link(fs.File(test.workpath('build/foo')), fs.File(test.workpath('src/foo')), None) os.chmod(test.workpath('src/foo'), stat.S_IRUSR | stat.S_IWRITE) st=os.stat(test.workpath('build/foo')) assert (stat.S_IMODE(st[stat.ST_MODE]) & stat.S_IWRITE), \ stat.S_IMODE(st[stat.ST_MODE]) # This used to generate a UserError when we forbid the source # directory from being outside the top-level SConstruct dir. fs = SCons.Node.FS.FS() fs.VariantDir('build', '/test/foo') exc_caught = 0 try: try: fs = SCons.Node.FS.FS() fs.VariantDir('build', 'build/src') except SCons.Errors.UserError: exc_caught = 1 assert exc_caught, "Should have caught a UserError." finally: test.unlink( "src/foo" ) test.unlink( "build/foo" ) fs = SCons.Node.FS.FS() fs.VariantDir('build', 'src1') # Calling the same VariantDir twice should work fine. fs.VariantDir('build', 'src1') # Trying to move a variant dir to a second source dir # should blow up try: fs.VariantDir('build', 'src2') except SCons.Errors.UserError: pass else: assert 0, "Should have caught a UserError." # Test against a former bug. Make sure we can get a repository # path for the variant directory itself! fs=SCons.Node.FS.FS(test.workpath('work')) test.subdir('work') fs.VariantDir('build/var3', 'src', duplicate=0) d1 = fs.Dir('build/var3') r = d1.rdir() assert r == d1, "%s != %s" % (r, d1) # verify the link creation attempts in file_link() class LinkSimulator (object): """A class to intercept os.[sym]link() calls and track them.""" def __init__( self, duplicate, link, symlink, copy ) : self.duplicate = duplicate self.have = {} self.have['hard'] = link self.have['soft'] = symlink self.have['copy'] = copy self.links_to_be_called = [] for link in self.duplicate.split('-'): if self.have[link]: self.links_to_be_called.append(link) def link_fail( self , src , dest ) : next_link = self.links_to_be_called.pop(0) assert next_link == "hard", \ "Wrong link order: expected %s to be called "\ "instead of hard" % next_link raise OSError( "Simulating hard link creation error." ) def symlink_fail( self , src , dest ) : next_link = self.links_to_be_called.pop(0) assert next_link == "soft", \ "Wrong link order: expected %s to be called "\ "instead of soft" % next_link raise OSError( "Simulating symlink creation error." ) def copy( self , src , dest ) : next_link = self.links_to_be_called.pop(0) assert next_link == "copy", \ "Wrong link order: expected %s to be called "\ "instead of copy" % next_link # copy succeeds, but use the real copy self.have['copy'](src, dest) # end class LinkSimulator try: SCons.Node.FS.set_duplicate("no-link-order") assert 0, "Expected exception when passing an invalid duplicate to set_duplicate" except SCons.Errors.InternalError: pass for duplicate in SCons.Node.FS.Valid_Duplicates: # save the real functions for later restoration try: real_link = os.link except AttributeError: real_link = None try: real_symlink = os.symlink except AttributeError: real_symlink = None real_copy = shutil.copy2 simulator = LinkSimulator(duplicate, real_link, real_symlink, real_copy) # override the real functions with our simulation os.link = simulator.link_fail os.symlink = simulator.symlink_fail shutil.copy2 = simulator.copy try: SCons.Node.FS.set_duplicate(duplicate) src_foo = test.workpath('src', 'foo') build_foo = test.workpath('build', 'foo') test.write(src_foo, 'src/foo\n') os.chmod(src_foo, stat.S_IRUSR) try: SCons.Node.FS.Link(fs.File(build_foo), fs.File(src_foo), None) finally: os.chmod(src_foo, stat.S_IRUSR | stat.S_IWRITE) test.unlink(src_foo) test.unlink(build_foo) finally: # restore the real functions if real_link: os.link = real_link else: delattr(os, 'link') if real_symlink: os.symlink = real_symlink else: delattr(os, 'symlink') shutil.copy2 = real_copy # Test VariantDir "reflection," where a same-named subdirectory # exists underneath a variant_dir. fs = SCons.Node.FS.FS() fs.VariantDir('work/src/b1/b2', 'work/src') dir_list = [ 'work/src', 'work/src/b1', 'work/src/b1/b2', 'work/src/b1/b2/b1', 'work/src/b1/b2/b1/b2', 'work/src/b1/b2/b1/b2/b1', 'work/src/b1/b2/b1/b2/b1/b2', ] srcnode_map = { 'work/src/b1/b2' : 'work/src', 'work/src/b1/b2/f' : 'work/src/f', 'work/src/b1/b2/b1' : 'work/src/b1/', 'work/src/b1/b2/b1/f' : 'work/src/b1/f', 'work/src/b1/b2/b1/b2' : 'work/src/b1/b2', 'work/src/b1/b2/b1/b2/f' : 'work/src/b1/b2/f', 'work/src/b1/b2/b1/b2/b1' : 'work/src/b1/b2/b1', 'work/src/b1/b2/b1/b2/b1/f' : 'work/src/b1/b2/b1/f', 'work/src/b1/b2/b1/b2/b1/b2' : 'work/src/b1/b2/b1/b2', 'work/src/b1/b2/b1/b2/b1/b2/f' : 'work/src/b1/b2/b1/b2/f', } alter_map = { 'work/src' : 'work/src/b1/b2', 'work/src/f' : 'work/src/b1/b2/f', 'work/src/b1' : 'work/src/b1/b2/b1', 'work/src/b1/f' : 'work/src/b1/b2/b1/f', } errors = 0 for dir in dir_list: dnode = fs.Dir(dir) f = dir + '/f' fnode = fs.File(dir + '/f') dp = dnode.srcnode().path expect = os.path.normpath(srcnode_map.get(dir, dir)) if dp != expect: print "Dir `%s' srcnode() `%s' != expected `%s'" % (dir, dp, expect) errors = errors + 1 fp = fnode.srcnode().path expect = os.path.normpath(srcnode_map.get(f, f)) if fp != expect: print "File `%s' srcnode() `%s' != expected `%s'" % (f, fp, expect) errors = errors + 1 for dir in dir_list: dnode = fs.Dir(dir) f = dir + '/f' fnode = fs.File(dir + '/f') t, m = dnode.alter_targets() tp = t[0].path expect = os.path.normpath(alter_map.get(dir, dir)) if tp != expect: print "Dir `%s' alter_targets() `%s' != expected `%s'" % (dir, tp, expect) errors = errors + 1 t, m = fnode.alter_targets() tp = t[0].path expect = os.path.normpath(alter_map.get(f, f)) if tp != expect: print "File `%s' alter_targets() `%s' != expected `%s'" % (f, tp, expect) errors = errors + 1 self.failIf(errors) class BaseTestCase(_tempdirTestCase): def test_stat(self): """Test the Base.stat() method""" test = self.test test.write("e1", "e1\n") fs = SCons.Node.FS.FS() e1 = fs.Entry('e1') s = e1.stat() assert s is not None, s e2 = fs.Entry('e2') s = e2.stat() assert s is None, s def test_getmtime(self): """Test the Base.getmtime() method""" test = self.test test.write("file", "file\n") fs = SCons.Node.FS.FS() file = fs.Entry('file') assert file.getmtime() file = fs.Entry('nonexistent') mtime = file.getmtime() assert mtime is None, mtime def test_getsize(self): """Test the Base.getsize() method""" test = self.test test.write("file", "file\n") fs = SCons.Node.FS.FS() file = fs.Entry('file') size = file.getsize() assert size == 5, size file = fs.Entry('nonexistent') size = file.getsize() assert size is None, size def test_isdir(self): """Test the Base.isdir() method""" test = self.test test.subdir('dir') test.write("file", "file\n") fs = SCons.Node.FS.FS() dir = fs.Entry('dir') assert dir.isdir() file = fs.Entry('file') assert not file.isdir() nonexistent = fs.Entry('nonexistent') assert not nonexistent.isdir() def test_isfile(self): """Test the Base.isfile() method""" test = self.test test.subdir('dir') test.write("file", "file\n") fs = SCons.Node.FS.FS() dir = fs.Entry('dir') assert not dir.isfile() file = fs.Entry('file') assert file.isfile() nonexistent = fs.Entry('nonexistent') assert not nonexistent.isfile() if hasattr(os, 'symlink'): def test_islink(self): """Test the Base.islink() method""" test = self.test test.subdir('dir') test.write("file", "file\n") test.symlink("symlink", "symlink") fs = SCons.Node.FS.FS() dir = fs.Entry('dir') assert not dir.islink() file = fs.Entry('file') assert not file.islink() symlink = fs.Entry('symlink') assert symlink.islink() nonexistent = fs.Entry('nonexistent') assert not nonexistent.islink() class DirNodeInfoTestCase(_tempdirTestCase): def test___init__(self): """Test DirNodeInfo initialization""" ddd = self.fs.Dir('ddd') ni = SCons.Node.FS.DirNodeInfo(ddd) class DirBuildInfoTestCase(_tempdirTestCase): def test___init__(self): """Test DirBuildInfo initialization""" ddd = self.fs.Dir('ddd') bi = SCons.Node.FS.DirBuildInfo(ddd) class FileNodeInfoTestCase(_tempdirTestCase): def test___init__(self): """Test FileNodeInfo initialization""" fff = self.fs.File('fff') ni = SCons.Node.FS.FileNodeInfo(fff) assert isinstance(ni, SCons.Node.FS.FileNodeInfo) def test_update(self): """Test updating a File.NodeInfo with on-disk information""" test = self.test fff = self.fs.File('fff') ni = SCons.Node.FS.FileNodeInfo(fff) test.write('fff', "fff\n") st = os.stat('fff') ni.update(fff) assert hasattr(ni, 'timestamp') assert hasattr(ni, 'size') ni.timestamp = 0 ni.size = 0 ni.update(fff) mtime = st[stat.ST_MTIME] assert ni.timestamp == mtime, (ni.timestamp, mtime) size = st[stat.ST_SIZE] assert ni.size == size, (ni.size, size) import time time.sleep(2) test.write('fff', "fff longer size, different time stamp\n") st = os.stat('fff') mtime = st[stat.ST_MTIME] assert ni.timestamp != mtime, (ni.timestamp, mtime) size = st[stat.ST_SIZE] assert ni.size != size, (ni.size, size) #fff.clear() #ni.update(fff) #st = os.stat('fff') #mtime = st[stat.ST_MTIME] #assert ni.timestamp == mtime, (ni.timestamp, mtime) #size = st[stat.ST_SIZE] #assert ni.size == size, (ni.size, size) class FileBuildInfoTestCase(_tempdirTestCase): def test___init__(self): """Test File.BuildInfo initialization""" fff = self.fs.File('fff') bi = SCons.Node.FS.FileBuildInfo(fff) assert bi, bi def test_convert_to_sconsign(self): """Test converting to .sconsign file format""" fff = self.fs.File('fff') bi = SCons.Node.FS.FileBuildInfo(fff) assert hasattr(bi, 'convert_to_sconsign') def test_convert_from_sconsign(self): """Test converting from .sconsign file format""" fff = self.fs.File('fff') bi = SCons.Node.FS.FileBuildInfo(fff) assert hasattr(bi, 'convert_from_sconsign') def test_prepare_dependencies(self): """Test that we have a prepare_dependencies() method""" fff = self.fs.File('fff') bi = SCons.Node.FS.FileBuildInfo(fff) bi.prepare_dependencies() def test_format(self): """Test the format() method""" f1 = self.fs.File('f1') bi1 = SCons.Node.FS.FileBuildInfo(f1) s1sig = SCons.Node.FS.FileNodeInfo(self.fs.File('n1')) s1sig.csig = 1 d1sig = SCons.Node.FS.FileNodeInfo(self.fs.File('n2')) d1sig.timestamp = 2 i1sig = SCons.Node.FS.FileNodeInfo(self.fs.File('n3')) i1sig.size = 3 bi1.bsources = [self.fs.File('s1')] bi1.bdepends = [self.fs.File('d1')] bi1.bimplicit = [self.fs.File('i1')] bi1.bsourcesigs = [s1sig] bi1.bdependsigs = [d1sig] bi1.bimplicitsigs = [i1sig] bi1.bact = 'action' bi1.bactsig = 'actionsig' expect_lines = [ 's1: 1 None None', 'd1: None 2 None', 'i1: None None 3', 'actionsig [action]', ] expect = '\n'.join(expect_lines) format = bi1.format() assert format == expect, (repr(expect), repr(format)) class FSTestCase(_tempdirTestCase): def test_needs_normpath(self): """Test the needs_normpath Regular expression This test case verifies that the regular expression used to determine whether a path needs normalization works as expected. """ needs_normpath_match = SCons.Node.FS.needs_normpath_match do_not_need_normpath = [ ".", "/", "/a", "/aa", "/a/", "/aa/", "/a/b", "/aa/bb", "/a/b/", "/aa/bb/", "", "a", "aa", "a/", "aa/", "a/b", "aa/bb", "a/b/", "aa/bb/", "a.", "a..", "/a.", "/a..", "a./", "a../", "/a./", "/a../", ".a", "..a", "/.a", "/..a", ".a/", "..a/", "/.a/", "/..a/", ] for p in do_not_need_normpath: assert needs_normpath_match(p) is None, p needs_normpath = [ "//", "//a", "//aa", "//a/", "//a/", "/aa//", "//a/b", "//aa/bb", "//a/b/", "//aa/bb/", "/a//b", "/aa//bb", "/a/b//", "/aa/bb//", "/a/b//", "/aa/bb//", "a//", "aa//", "a//b", "aa//bb", "a//b/", "aa//bb/", "a/b//", "aa/bb//", "..", "/.", "/..", "./", "../", "/./", "/../", "a/.", "a/..", "./a", "../a", "a/./a", "a/../a", ] for p in needs_normpath: assert needs_normpath_match(p) is not None, p def test_runTest(self): """Test FS (file system) Node operations This test case handles all of the file system node tests in one environment, so we don't have to set up a complicated directory structure for each test individually. """ test = self.test test.subdir('sub', ['sub', 'dir']) wp = test.workpath('') sub = test.workpath('sub', '') sub_dir = test.workpath('sub', 'dir', '') sub_dir_foo = test.workpath('sub', 'dir', 'foo', '') sub_dir_foo_bar = test.workpath('sub', 'dir', 'foo', 'bar', '') sub_foo = test.workpath('sub', 'foo', '') os.chdir(sub_dir) fs = SCons.Node.FS.FS() e1 = fs.Entry('e1') assert isinstance(e1, SCons.Node.FS.Entry) d1 = fs.Dir('d1') assert isinstance(d1, SCons.Node.FS.Dir) assert d1.cwd is d1, d1 f1 = fs.File('f1', directory = d1) assert isinstance(f1, SCons.Node.FS.File) d1_f1 = os.path.join('d1', 'f1') assert f1.path == d1_f1, "f1.path %s != %s" % (f1.path, d1_f1) assert str(f1) == d1_f1, "str(f1) %s != %s" % (str(f1), d1_f1) x1 = d1.File('x1') assert isinstance(x1, SCons.Node.FS.File) assert str(x1) == os.path.join('d1', 'x1') x2 = d1.Dir('x2') assert isinstance(x2, SCons.Node.FS.Dir) assert str(x2) == os.path.join('d1', 'x2') x3 = d1.Entry('x3') assert isinstance(x3, SCons.Node.FS.Entry) assert str(x3) == os.path.join('d1', 'x3') assert d1.File(x1) == x1 assert d1.Dir(x2) == x2 assert d1.Entry(x3) == x3 x1.cwd = d1 x4 = x1.File('x4') assert str(x4) == os.path.join('d1', 'x4') x5 = x1.Dir('x5') assert str(x5) == os.path.join('d1', 'x5') x6 = x1.Entry('x6') assert str(x6) == os.path.join('d1', 'x6') x7 = x1.Entry('x7') assert str(x7) == os.path.join('d1', 'x7') assert x1.File(x4) == x4 assert x1.Dir(x5) == x5 assert x1.Entry(x6) == x6 assert x1.Entry(x7) == x7 assert x1.Entry(x5) == x5 try: x1.File(x5) except TypeError: pass else: raise Exception("did not catch expected TypeError") assert x1.Entry(x4) == x4 try: x1.Dir(x4) except TypeError: pass else: raise Exception("did not catch expected TypeError") x6 = x1.File(x6) assert isinstance(x6, SCons.Node.FS.File) x7 = x1.Dir(x7) assert isinstance(x7, SCons.Node.FS.Dir) seps = [os.sep] if os.sep != '/': seps = seps + ['/'] drive, path = os.path.splitdrive(os.getcwd()) def _do_Dir_test(lpath, path_, abspath_, up_path_, sep, fileSys=fs, drive=drive): dir = fileSys.Dir(lpath.replace('/', sep)) if os.sep != '/': path_ = path_.replace('/', os.sep) abspath_ = abspath_.replace('/', os.sep) up_path_ = up_path_.replace('/', os.sep) def strip_slash(p, drive=drive): if p[-1] == os.sep and len(p) > 1: p = p[:-1] if p[0] == os.sep: p = drive + p return p path = strip_slash(path_) abspath = strip_slash(abspath_) up_path = strip_slash(up_path_) name = abspath.split(os.sep)[-1] if not name: if drive: name = drive else: name = os.sep if dir.up() is None: dir_up_path = dir.path else: dir_up_path = dir.up().path assert dir.name == name, \ "dir.name %s != expected name %s" % \ (dir.name, name) assert dir.path == path, \ "dir.path %s != expected path %s" % \ (dir.path, path) assert str(dir) == path, \ "str(dir) %s != expected path %s" % \ (str(dir), path) assert dir.get_abspath() == abspath, \ "dir.abspath %s != expected absolute path %s" % \ (dir.get_abspath(), abspath) assert dir_up_path == up_path, \ "dir.up().path %s != expected parent path %s" % \ (dir_up_path, up_path) for sep in seps: def Dir_test(lpath, path_, abspath_, up_path_, sep=sep, func=_do_Dir_test): return func(lpath, path_, abspath_, up_path_, sep) Dir_test('/', '/', '/', '/') Dir_test('', './', sub_dir, sub) Dir_test('foo', 'foo/', sub_dir_foo, './') Dir_test('foo/bar', 'foo/bar/', sub_dir_foo_bar, 'foo/') Dir_test('/foo', '/foo/', '/foo/', '/') Dir_test('/foo/bar', '/foo/bar/', '/foo/bar/', '/foo/') Dir_test('..', sub, sub, wp) Dir_test('foo/..', './', sub_dir, sub) Dir_test('../foo', sub_foo, sub_foo, sub) Dir_test('.', './', sub_dir, sub) Dir_test('./.', './', sub_dir, sub) Dir_test('foo/./bar', 'foo/bar/', sub_dir_foo_bar, 'foo/') Dir_test('#../foo', sub_foo, sub_foo, sub) Dir_test('#/../foo', sub_foo, sub_foo, sub) Dir_test('#foo/bar', 'foo/bar/', sub_dir_foo_bar, 'foo/') Dir_test('#/foo/bar', 'foo/bar/', sub_dir_foo_bar, 'foo/') Dir_test('#', './', sub_dir, sub) try: f2 = fs.File(sep.join(['f1', 'f2']), directory = d1) except TypeError, x: assert str(x) == ("Tried to lookup File '%s' as a Dir." % d1_f1), x except: raise try: dir = fs.Dir(sep.join(['d1', 'f1'])) except TypeError, x: assert str(x) == ("Tried to lookup File '%s' as a Dir." % d1_f1), x except: raise try: f2 = fs.File('d1') except TypeError, x: assert str(x) == ("Tried to lookup Dir '%s' as a File." % 'd1'), x except: raise # Test that just specifying the drive works to identify # its root directory. p = os.path.abspath(test.workpath('root_file')) drive, path = os.path.splitdrive(p) if drive: # The assert below probably isn't correct for the general # case, but it works for Windows, which covers a lot # of ground... dir = fs.Dir(drive) assert str(dir) == drive + os.sep, str(dir) # Make sure that lookups with and without the drive are # equivalent. p = os.path.abspath(test.workpath('some/file')) drive, path = os.path.splitdrive(p) e1 = fs.Entry(p) e2 = fs.Entry(path) assert e1 is e2, (e1, e2) assert str(e1) is str(e2), (str(e1), str(e2)) # Test for a bug in 0.04 that did not like looking up # dirs with a trailing slash on Windows. d=fs.Dir('./') assert d.path == '.', d.abspath d=fs.Dir('foo/') assert d.path == 'foo', d.abspath # Test for sub-classing of node building. global built_it built_it = None assert not built_it d1.add_source([SCons.Node.Node()]) # XXX FAKE SUBCLASS ATTRIBUTE d1.builder_set(Builder(fs.File)) d1.reset_executor() d1.env_set(Environment()) d1.build() assert built_it built_it = None assert not built_it f1.add_source([SCons.Node.Node()]) # XXX FAKE SUBCLASS ATTRIBUTE f1.builder_set(Builder(fs.File)) f1.reset_executor() f1.env_set(Environment()) f1.build() assert built_it def match(path, expect): expect = expect.replace('/', os.sep) assert path == expect, "path %s != expected %s" % (path, expect) e1 = fs.Entry("d1") assert e1.__class__.__name__ == 'Dir' match(e1.path, "d1") match(e1.dir.path, ".") e2 = fs.Entry("d1/f1") assert e2.__class__.__name__ == 'File' match(e2.path, "d1/f1") match(e2.dir.path, "d1") e3 = fs.Entry("e3") assert e3.__class__.__name__ == 'Entry' match(e3.path, "e3") match(e3.dir.path, ".") e4 = fs.Entry("d1/e4") assert e4.__class__.__name__ == 'Entry' match(e4.path, "d1/e4") match(e4.dir.path, "d1") e5 = fs.Entry("e3/e5") assert e3.__class__.__name__ == 'Dir' match(e3.path, "e3") match(e3.dir.path, ".") assert e5.__class__.__name__ == 'Entry' match(e5.path, "e3/e5") match(e5.dir.path, "e3") e6 = fs.Dir("d1/e4") assert e6 is e4 assert e4.__class__.__name__ == 'Dir' match(e4.path, "d1/e4") match(e4.dir.path, "d1") e7 = fs.File("e3/e5") assert e7 is e5 assert e5.__class__.__name__ == 'File' match(e5.path, "e3/e5") match(e5.dir.path, "e3") fs.chdir(fs.Dir('subdir')) f11 = fs.File("f11") match(f11.path, "subdir/f11") d12 = fs.Dir("d12") e13 = fs.Entry("subdir/e13") match(e13.path, "subdir/subdir/e13") fs.chdir(fs.Dir('..')) # Test scanning f1.builder_set(Builder(fs.File)) f1.env_set(Environment()) xyz = fs.File("xyz") f1.builder.target_scanner = Scanner(xyz) f1.scan() assert f1.implicit[0].path == "xyz" f1.implicit = [] f1.scan() assert f1.implicit == [] f1.implicit = None f1.scan() assert f1.implicit[0].path == "xyz" # Test underlying scanning functionality in get_found_includes() env = Environment() f12 = fs.File("f12") t1 = fs.File("t1") deps = f12.get_found_includes(env, None, t1) assert deps == [], deps class MyScanner(Scanner): call_count = 0 def __call__(self, node, env, path): self.call_count = self.call_count + 1 return Scanner.__call__(self, node, env, path) s = MyScanner(xyz) deps = f12.get_found_includes(env, s, t1) assert deps == [xyz], deps assert s.call_count == 1, s.call_count f12.built() deps = f12.get_found_includes(env, s, t1) assert deps == [xyz], deps assert s.call_count == 2, s.call_count env2 = Environment() deps = f12.get_found_includes(env2, s, t1) assert deps == [xyz], deps assert s.call_count == 3, s.call_count # Make sure we can scan this file even if the target isn't # a file that has a scanner (it might be an Alias, e.g.). class DummyNode(object): pass deps = f12.get_found_includes(env, s, DummyNode()) assert deps == [xyz], deps # Test building a file whose directory is not there yet... f1 = fs.File(test.workpath("foo/bar/baz/ack")) assert not f1.dir.exists() f1.prepare() f1.build() assert f1.dir.exists() os.chdir('..') # Test getcwd() fs = SCons.Node.FS.FS() assert str(fs.getcwd()) == ".", str(fs.getcwd()) fs.chdir(fs.Dir('subdir')) # The cwd's path is always "." assert str(fs.getcwd()) == ".", str(fs.getcwd()) assert fs.getcwd().path == 'subdir', fs.getcwd().path fs.chdir(fs.Dir('../..')) assert fs.getcwd().path == test.workdir, fs.getcwd().path f1 = fs.File(test.workpath("do_i_exist")) assert not f1.exists() test.write("do_i_exist","\n") assert not f1.exists(), "exists() call not cached" f1.built() assert f1.exists(), "exists() call caching not reset" test.unlink("do_i_exist") assert f1.exists() f1.built() assert not f1.exists() # For some reason, in Windows, the \x1a character terminates # the reading of files in text mode. This tests that # get_contents() returns the binary contents. test.write("binary_file", "Foo\x1aBar") f1 = fs.File(test.workpath("binary_file")) assert f1.get_contents() == "Foo\x1aBar", f1.get_contents() # This tests to make sure we can decode UTF-8 text files. test_string = u"Foo\x1aBar" test.write("utf8_file", test_string.encode('utf-8')) f1 = fs.File(test.workpath("utf8_file")) assert eval('f1.get_text_contents() == u"Foo\x1aBar"'), \ f1.get_text_contents() def nonexistent(method, s): try: x = method(s, create = 0) except SCons.Errors.UserError: pass else: raise Exception("did not catch expected UserError") nonexistent(fs.Entry, 'nonexistent') nonexistent(fs.Entry, 'nonexistent/foo') nonexistent(fs.File, 'nonexistent') nonexistent(fs.File, 'nonexistent/foo') nonexistent(fs.Dir, 'nonexistent') nonexistent(fs.Dir, 'nonexistent/foo') test.write("preserve_me", "\n") assert os.path.exists(test.workpath("preserve_me")) f1 = fs.File(test.workpath("preserve_me")) f1.prepare() assert os.path.exists(test.workpath("preserve_me")) test.write("remove_me", "\n") assert os.path.exists(test.workpath("remove_me")) f1 = fs.File(test.workpath("remove_me")) f1.builder = Builder(fs.File) f1.env_set(Environment()) f1.prepare() assert not os.path.exists(test.workpath("remove_me")) e = fs.Entry('e_local') assert not hasattr(e, '_local') e.set_local() assert e._local == 1 f = fs.File('e_local') assert f._local == 1 f = fs.File('f_local') assert f._local == 0 #XXX test_is_up_to_date() for directories #XXX test_sconsign() for directories #XXX test_set_signature() for directories #XXX test_build() for directories #XXX test_root() # test Entry.get_contents() e = fs.Entry('does_not_exist') c = e.get_contents() assert c == "", c assert e.__class__ == SCons.Node.FS.Entry test.write("file", "file\n") try: e = fs.Entry('file') c = e.get_contents() assert c == "file\n", c assert e.__class__ == SCons.Node.FS.File finally: test.unlink("file") # test Entry.get_text_contents() e = fs.Entry('does_not_exist') c = e.get_text_contents() assert c == "", c assert e.__class__ == SCons.Node.FS.Entry test.write("file", "file\n") try: e = fs.Entry('file') c = e.get_text_contents() assert c == "file\n", c assert e.__class__ == SCons.Node.FS.File finally: test.unlink("file") test.subdir("dir") e = fs.Entry('dir') c = e.get_contents() assert c == "", c assert e.__class__ == SCons.Node.FS.Dir c = e.get_text_contents() try: eval('assert c == u"", c') except SyntaxError: assert c == "" if hasattr(os, 'symlink'): os.symlink('nonexistent', test.workpath('dangling_symlink')) e = fs.Entry('dangling_symlink') c = e.get_contents() assert e.__class__ == SCons.Node.FS.Entry, e.__class__ assert c == "", c c = e.get_text_contents() try: eval('assert c == u"", c') except SyntaxError: assert c == "", c test.write("tstamp", "tstamp\n") try: # Okay, *this* manipulation accomodates Windows FAT file systems # that only have two-second granularity on their timestamps. # We round down the current time to the nearest even integer # value, subtract two to make sure the timestamp is not "now," # and then convert it back to a float. tstamp = float(int(time.time() // 2) * 2) - 2.0 os.utime(test.workpath("tstamp"), (tstamp - 2.0, tstamp)) f = fs.File("tstamp") t = f.get_timestamp() assert t == tstamp, "expected %f, got %f" % (tstamp, t) finally: test.unlink("tstamp") test.subdir('tdir1') d = fs.Dir('tdir1') t = d.get_timestamp() assert t == 0, "expected 0, got %s" % str(t) test.subdir('tdir2') f1 = test.workpath('tdir2', 'file1') f2 = test.workpath('tdir2', 'file2') test.write(f1, 'file1\n') test.write(f2, 'file2\n') current_time = float(int(time.time() // 2) * 2) t1 = current_time - 4.0 t2 = current_time - 2.0 os.utime(f1, (t1 - 2.0, t1)) os.utime(f2, (t2 - 2.0, t2)) d = fs.Dir('tdir2') fs.File(f1) fs.File(f2) t = d.get_timestamp() assert t == t2, "expected %f, got %f" % (t2, t) skey = fs.Entry('eee.x').scanner_key() assert skey == '.x', skey skey = fs.Entry('eee.xyz').scanner_key() assert skey == '.xyz', skey skey = fs.File('fff.x').scanner_key() assert skey == '.x', skey skey = fs.File('fff.xyz').scanner_key() assert skey == '.xyz', skey skey = fs.Dir('ddd.x').scanner_key() assert skey is None, skey test.write("i_am_not_a_directory", "\n") try: exc_caught = 0 try: fs.Dir(test.workpath("i_am_not_a_directory")) except TypeError: exc_caught = 1 assert exc_caught, "Should have caught a TypeError" finally: test.unlink("i_am_not_a_directory") exc_caught = 0 try: fs.File(sub_dir) except TypeError: exc_caught = 1 assert exc_caught, "Should have caught a TypeError" # XXX test_is_up_to_date() d = fs.Dir('dir') r = d.remove() assert r is None, r f = fs.File('does_not_exist') r = f.remove() assert r is None, r test.write('exists', "exists\n") f = fs.File('exists') r = f.remove() assert r, r assert not os.path.exists(test.workpath('exists')), "exists was not removed" symlink = test.workpath('symlink') try: os.symlink(test.workpath('does_not_exist'), symlink) assert os.path.islink(symlink) f = fs.File('symlink') r = f.remove() assert r, r assert not os.path.islink(symlink), "symlink was not removed" except AttributeError: pass test.write('can_not_remove', "can_not_remove\n") test.writable(test.workpath('.'), 0) fp = open(test.workpath('can_not_remove')) f = fs.File('can_not_remove') exc_caught = 0 try: r = f.remove() except OSError: exc_caught = 1 fp.close() assert exc_caught, "Should have caught an OSError, r = " + str(r) f = fs.Entry('foo/bar/baz') assert f.for_signature() == 'baz', f.for_signature() assert f.get_string(0) == os.path.normpath('foo/bar/baz'), \ f.get_string(0) assert f.get_string(1) == 'baz', f.get_string(1) def test_drive_letters(self): """Test drive-letter look-ups""" test = self.test test.subdir('sub', ['sub', 'dir']) def drive_workpath(dirs, test=test): x = test.workpath(*dirs) drive, path = os.path.splitdrive(x) return 'X:' + path wp = drive_workpath(['']) if wp[-1] in (os.sep, '/'): tmp = os.path.split(wp[:-1])[0] else: tmp = os.path.split(wp)[0] parent_tmp = os.path.split(tmp)[0] if parent_tmp == 'X:': parent_tmp = 'X:' + os.sep tmp_foo = os.path.join(tmp, 'foo') foo = drive_workpath(['foo']) foo_bar = drive_workpath(['foo', 'bar']) sub = drive_workpath(['sub', '']) sub_dir = drive_workpath(['sub', 'dir', '']) sub_dir_foo = drive_workpath(['sub', 'dir', 'foo', '']) sub_dir_foo_bar = drive_workpath(['sub', 'dir', 'foo', 'bar', '']) sub_foo = drive_workpath(['sub', 'foo', '']) fs = SCons.Node.FS.FS() seps = [os.sep] if os.sep != '/': seps = seps + ['/'] def _do_Dir_test(lpath, path_, up_path_, sep, fileSys=fs): dir = fileSys.Dir(lpath.replace('/', sep)) if os.sep != '/': path_ = path_.replace('/', os.sep) up_path_ = up_path_.replace('/', os.sep) def strip_slash(p): if p[-1] == os.sep and len(p) > 3: p = p[:-1] return p path = strip_slash(path_) up_path = strip_slash(up_path_) name = path.split(os.sep)[-1] assert dir.name == name, \ "dir.name %s != expected name %s" % \ (dir.name, name) assert dir.path == path, \ "dir.path %s != expected path %s" % \ (dir.path, path) assert str(dir) == path, \ "str(dir) %s != expected path %s" % \ (str(dir), path) assert dir.up().path == up_path, \ "dir.up().path %s != expected parent path %s" % \ (dir.up().path, up_path) save_os_path = os.path save_os_sep = os.sep try: import ntpath os.path = ntpath os.sep = '\\' SCons.Node.FS.initialize_do_splitdrive() for sep in seps: def Dir_test(lpath, path_, up_path_, sep=sep, func=_do_Dir_test): return func(lpath, path_, up_path_, sep) Dir_test('#X:', wp, tmp) Dir_test('X:foo', foo, wp) Dir_test('X:foo/bar', foo_bar, foo) Dir_test('X:/foo', 'X:/foo', 'X:/') Dir_test('X:/foo/bar', 'X:/foo/bar/', 'X:/foo/') Dir_test('X:..', tmp, parent_tmp) Dir_test('X:foo/..', wp, tmp) Dir_test('X:../foo', tmp_foo, tmp) Dir_test('X:.', wp, tmp) Dir_test('X:./.', wp, tmp) Dir_test('X:foo/./bar', foo_bar, foo) Dir_test('#X:../foo', tmp_foo, tmp) Dir_test('#X:/../foo', tmp_foo, tmp) Dir_test('#X:foo/bar', foo_bar, foo) Dir_test('#X:/foo/bar', foo_bar, foo) Dir_test('#X:/', wp, tmp) finally: os.path = save_os_path os.sep = save_os_sep SCons.Node.FS.initialize_do_splitdrive() def test_unc_path(self): """Test UNC path look-ups""" test = self.test test.subdir('sub', ['sub', 'dir']) def strip_slash(p): if p[-1] == os.sep and len(p) > 3: p = p[:-1] return p def unc_workpath(dirs, test=test): import ntpath x = apply(test.workpath, dirs) drive, path = ntpath.splitdrive(x) unc, path = ntpath.splitunc(path) path = strip_slash(path) return '//' + path[1:] wp = unc_workpath(['']) if wp[-1] in (os.sep, '/'): tmp = os.path.split(wp[:-1])[0] else: tmp = os.path.split(wp)[0] parent_tmp = os.path.split(tmp)[0] tmp_foo = os.path.join(tmp, 'foo') foo = unc_workpath(['foo']) foo_bar = unc_workpath(['foo', 'bar']) sub = unc_workpath(['sub', '']) sub_dir = unc_workpath(['sub', 'dir', '']) sub_dir_foo = unc_workpath(['sub', 'dir', 'foo', '']) sub_dir_foo_bar = unc_workpath(['sub', 'dir', 'foo', 'bar', '']) sub_foo = unc_workpath(['sub', 'foo', '']) fs = SCons.Node.FS.FS() seps = [os.sep] if os.sep != '/': seps = seps + ['/'] def _do_Dir_test(lpath, path, up_path, sep, fileSys=fs): dir = fileSys.Dir(lpath.replace('/', sep)) if os.sep != '/': path = path.replace('/', os.sep) up_path = up_path.replace('/', os.sep) if path == os.sep + os.sep: name = os.sep + os.sep else: name = path.split(os.sep)[-1] if dir.up() is None: dir_up_path = dir.path else: dir_up_path = dir.up().path assert dir.name == name, \ "dir.name %s != expected name %s" % \ (dir.name, name) assert dir.path == path, \ "dir.path %s != expected path %s" % \ (dir.path, path) assert str(dir) == path, \ "str(dir) %s != expected path %s" % \ (str(dir), path) assert dir_up_path == up_path, \ "dir.up().path %s != expected parent path %s" % \ (dir.up().path, up_path) save_os_path = os.path save_os_sep = os.sep try: import ntpath os.path = ntpath os.sep = '\\' SCons.Node.FS.initialize_do_splitdrive() for sep in seps: def Dir_test(lpath, path_, up_path_, sep=sep, func=_do_Dir_test): return func(lpath, path_, up_path_, sep) Dir_test('//foo', '//foo', '//') Dir_test('//foo/bar', '//foo/bar', '//foo') Dir_test('//', '//', '//') Dir_test('//..', '//', '//') Dir_test('//foo/..', '//', '//') Dir_test('//../foo', '//foo', '//') Dir_test('//.', '//', '//') Dir_test('//./.', '//', '//') Dir_test('//foo/./bar', '//foo/bar', '//foo') Dir_test('//foo/../bar', '//bar', '//') Dir_test('//foo/../../bar', '//bar', '//') Dir_test('//foo/bar/../..', '//', '//') Dir_test('#//', wp, tmp) Dir_test('#//../foo', tmp_foo, tmp) Dir_test('#//../foo', tmp_foo, tmp) Dir_test('#//foo/bar', foo_bar, foo) Dir_test('#//foo/bar', foo_bar, foo) Dir_test('#//', wp, tmp) finally: os.path = save_os_path os.sep = save_os_sep SCons.Node.FS.initialize_do_splitdrive() def test_target_from_source(self): """Test the method for generating target nodes from sources""" fs = self.fs x = fs.File('x.c') t = x.target_from_source('pre-', '-suf') assert str(t) == 'pre-x-suf', str(t) assert t.__class__ == SCons.Node.FS.Entry y = fs.File('dir/y') t = y.target_from_source('pre-', '-suf') assert str(t) == os.path.join('dir', 'pre-y-suf'), str(t) assert t.__class__ == SCons.Node.FS.Entry z = fs.File('zz') t = z.target_from_source('pre-', '-suf', lambda x: x[:-1]) assert str(t) == 'pre-z-suf', str(t) assert t.__class__ == SCons.Node.FS.Entry d = fs.Dir('ddd') t = d.target_from_source('pre-', '-suf') assert str(t) == 'pre-ddd-suf', str(t) assert t.__class__ == SCons.Node.FS.Entry e = fs.Entry('eee') t = e.target_from_source('pre-', '-suf') assert str(t) == 'pre-eee-suf', str(t) assert t.__class__ == SCons.Node.FS.Entry def test_same_name(self): """Test that a local same-named file isn't found for a Dir lookup""" test = self.test fs = self.fs test.subdir('subdir') test.write(['subdir', 'build'], "subdir/build\n") subdir = fs.Dir('subdir') fs.chdir(subdir, change_os_dir=1) self.fs._lookup('#build/file', subdir, SCons.Node.FS.File) def test_above_root(self): """Testing looking up a path above the root directory""" test = self.test fs = self.fs d1 = fs.Dir('d1') d2 = d1.Dir('d2') dirs = os.path.normpath(d2.abspath).split(os.sep) above_path = os.path.join(*['..']*len(dirs) + ['above']) above = d2.Dir(above_path) def test_lookup_abs(self): """Exercise the _lookup_abs function""" test = self.test fs = self.fs root = fs.Dir('/') d = root._lookup_abs('/tmp/foo-nonexistent/nonexistent-dir', SCons.Node.FS.Dir) assert d.__class__ == SCons.Node.FS.Dir, str(d.__class__) def test_lookup_uncpath(self): """Testing looking up a UNC path on Windows""" if sys.platform not in ('win32',): return test = self.test fs = self.fs path='//servername/C$/foo' f = self.fs._lookup('//servername/C$/foo', fs.Dir('#'), SCons.Node.FS.File) # before the fix in this commit, this returned 'C:\servername\C$\foo' # Should be a normalized Windows UNC path as below. assert str(f) == r'\\servername\C$\foo', \ 'UNC path %s got looked up as %s'%(path, f) def test_unc_drive_letter(self): """Test drive-letter lookup for windows UNC-style directories""" if sys.platform not in ('win32',): return share = self.fs.Dir(r'\\SERVER\SHARE\Directory') assert str(share) == r'\\SERVER\SHARE\Directory', str(share) def test_UNC_dirs_2689(self): """Test some UNC dirs that printed incorrectly and/or caused infinite recursion errors prior to r5180 (SCons 2.1).""" fs = self.fs if sys.platform not in ('win32',): return p = fs.Dir(r"\\computername\sharename").abspath assert p == r"\\computername\sharename", p p = fs.Dir(r"\\\computername\sharename").abspath assert p == r"\\computername\sharename", p def test_rel_path(self): """Test the rel_path() method""" test = self.test fs = self.fs d1 = fs.Dir('d1') d1_f = d1.File('f') d1_d2 = d1.Dir('d2') d1_d2_f = d1_d2.File('f') d3 = fs.Dir('d3') d3_f = d3.File('f') d3_d4 = d3.Dir('d4') d3_d4_f = d3_d4.File('f') cases = [ d1, d1, '.', d1, d1_f, 'f', d1, d1_d2, 'd2', d1, d1_d2_f, 'd2/f', d1, d3, '../d3', d1, d3_f, '../d3/f', d1, d3_d4, '../d3/d4', d1, d3_d4_f, '../d3/d4/f', d1_f, d1, '.', d1_f, d1_f, 'f', d1_f, d1_d2, 'd2', d1_f, d1_d2_f, 'd2/f', d1_f, d3, '../d3', d1_f, d3_f, '../d3/f', d1_f, d3_d4, '../d3/d4', d1_f, d3_d4_f, '../d3/d4/f', d1_d2, d1, '..', d1_d2, d1_f, '../f', d1_d2, d1_d2, '.', d1_d2, d1_d2_f, 'f', d1_d2, d3, '../../d3', d1_d2, d3_f, '../../d3/f', d1_d2, d3_d4, '../../d3/d4', d1_d2, d3_d4_f, '../../d3/d4/f', d1_d2_f, d1, '..', d1_d2_f, d1_f, '../f', d1_d2_f, d1_d2, '.', d1_d2_f, d1_d2_f, 'f', d1_d2_f, d3, '../../d3', d1_d2_f, d3_f, '../../d3/f', d1_d2_f, d3_d4, '../../d3/d4', d1_d2_f, d3_d4_f, '../../d3/d4/f', ] if sys.platform in ('win32',): x_d1 = fs.Dir(r'X:\d1') x_d1_d2 = x_d1.Dir('d2') y_d1 = fs.Dir(r'Y:\d1') y_d1_d2 = y_d1.Dir('d2') y_d2 = fs.Dir(r'Y:\d2') win32_cases = [ x_d1, x_d1, '.', x_d1, x_d1_d2, 'd2', x_d1, y_d1, r'Y:\d1', x_d1, y_d1_d2, r'Y:\d1\d2', x_d1, y_d2, r'Y:\d2', ] cases.extend(win32_cases) failed = 0 while cases: dir, other, expect = cases[:3] expect = os.path.normpath(expect) del cases[:3] result = dir.rel_path(other) if result != expect: if failed == 0: print fmt = " dir_path(%(dir)s, %(other)s) => '%(result)s' did not match '%(expect)s'" print fmt % locals() failed = failed + 1 assert failed == 0, "%d rel_path() cases failed" % failed def test_proxy(self): """Test a Node.FS object wrapped in a proxy instance""" f1 = self.fs.File('fff') class MyProxy(SCons.Util.Proxy): __str__ = SCons.Util.Delegate('__str__') p = MyProxy(f1) f2 = self.fs.Entry(p) assert f1 is f2, (f1, str(f1), f2, str(f2)) class DirTestCase(_tempdirTestCase): def test__morph(self): """Test handling of actions when morphing an Entry into a Dir""" test = self.test e = self.fs.Entry('eee') x = e.get_executor() x.add_pre_action('pre') x.add_post_action('post') e.must_be_same(SCons.Node.FS.Dir) a = x.get_action_list() assert 'pre' in a, a assert 'post' in a, a def test_subclass(self): """Test looking up subclass of Dir nodes""" class DirSubclass(SCons.Node.FS.Dir): pass sd = self.fs._lookup('special_dir', None, DirSubclass, create=1) sd.must_be_same(SCons.Node.FS.Dir) def test_get_env_scanner(self): """Test the Dir.get_env_scanner() method """ import SCons.Defaults d = self.fs.Dir('ddd') s = d.get_env_scanner(Environment()) assert s is SCons.Defaults.DirEntryScanner, s def test_get_target_scanner(self): """Test the Dir.get_target_scanner() method """ import SCons.Defaults d = self.fs.Dir('ddd') s = d.get_target_scanner() assert s is SCons.Defaults.DirEntryScanner, s def test_scan(self): """Test scanning a directory for in-memory entries """ fs = self.fs dir = fs.Dir('ddd') fs.File(os.path.join('ddd', 'f1')) fs.File(os.path.join('ddd', 'f2')) fs.File(os.path.join('ddd', 'f3')) fs.Dir(os.path.join('ddd', 'd1')) fs.Dir(os.path.join('ddd', 'd1', 'f4')) fs.Dir(os.path.join('ddd', 'd1', 'f5')) dir.scan() kids = sorted([x.path for x in dir.children(None)]) assert kids == [os.path.join('ddd', 'd1'), os.path.join('ddd', 'f1'), os.path.join('ddd', 'f2'), os.path.join('ddd', 'f3')], kids def test_get_contents(self): """Test getting the contents for a directory. """ test = self.test test.subdir('d') test.write(['d', 'g'], "67890\n") test.write(['d', 'f'], "12345\n") test.subdir(['d','sub']) test.write(['d', 'sub','h'], "abcdef\n") test.subdir(['d','empty']) d = self.fs.Dir('d') g = self.fs.File(os.path.join('d', 'g')) f = self.fs.File(os.path.join('d', 'f')) h = self.fs.File(os.path.join('d', 'sub', 'h')) e = self.fs.Dir(os.path.join('d', 'empty')) s = self.fs.Dir(os.path.join('d', 'sub')) files = d.get_contents().split('\n') assert e.get_contents() == '', e.get_contents() assert e.get_text_contents() == '', e.get_text_contents() assert e.get_csig()+" empty" == files[0], files assert f.get_csig()+" f" == files[1], files assert g.get_csig()+" g" == files[2], files assert s.get_csig()+" sub" == files[3], files def test_implicit_re_scans(self): """Test that adding entries causes a directory to be re-scanned """ fs = self.fs dir = fs.Dir('ddd') fs.File(os.path.join('ddd', 'f1')) dir.scan() kids = sorted([x.path for x in dir.children()]) assert kids == [os.path.join('ddd', 'f1')], kids fs.File(os.path.join('ddd', 'f2')) dir.scan() kids = sorted([x.path for x in dir.children()]) assert kids == [os.path.join('ddd', 'f1'), os.path.join('ddd', 'f2')], kids def test_entry_exists_on_disk(self): """Test the Dir.entry_exists_on_disk() method """ test = self.test does_not_exist = self.fs.Dir('does_not_exist') assert not does_not_exist.entry_exists_on_disk('foo') test.subdir('d') test.write(['d', 'exists'], "d/exists\n") test.write(['d', 'Case-Insensitive'], "d/Case-Insensitive\n") d = self.fs.Dir('d') assert d.entry_exists_on_disk('exists') assert not d.entry_exists_on_disk('does_not_exist') if os.path.normcase("TeSt") != os.path.normpath("TeSt") or sys.platform == "cygwin": assert d.entry_exists_on_disk('case-insensitive') def test_srcdir_list(self): """Test the Dir.srcdir_list() method """ src = self.fs.Dir('src') bld = self.fs.Dir('bld') sub1 = bld.Dir('sub') sub2 = sub1.Dir('sub') sub3 = sub2.Dir('sub') self.fs.VariantDir(bld, src, duplicate=0) self.fs.VariantDir(sub2, src, duplicate=0) def check(result, expect): result = list(map(str, result)) expect = list(map(os.path.normpath, expect)) assert result == expect, result s = src.srcdir_list() check(s, []) s = bld.srcdir_list() check(s, ['src']) s = sub1.srcdir_list() check(s, ['src/sub']) s = sub2.srcdir_list() check(s, ['src', 'src/sub/sub']) s = sub3.srcdir_list() check(s, ['src/sub', 'src/sub/sub/sub']) self.fs.VariantDir('src/b1/b2', 'src') b1 = src.Dir('b1') b1_b2 = b1.Dir('b2') b1_b2_b1 = b1_b2.Dir('b1') b1_b2_b1_b2 = b1_b2_b1.Dir('b2') b1_b2_b1_b2_sub = b1_b2_b1_b2.Dir('sub') s = b1.srcdir_list() check(s, []) s = b1_b2.srcdir_list() check(s, ['src']) s = b1_b2_b1.srcdir_list() check(s, ['src/b1']) s = b1_b2_b1_b2.srcdir_list() check(s, ['src/b1/b2']) s = b1_b2_b1_b2_sub.srcdir_list() check(s, ['src/b1/b2/sub']) def test_srcdir_duplicate(self): """Test the Dir.srcdir_duplicate() method """ test = self.test test.subdir('src0') test.write(['src0', 'exists'], "src0/exists\n") bld0 = self.fs.Dir('bld0') src0 = self.fs.Dir('src0') self.fs.VariantDir(bld0, src0, duplicate=0) n = bld0.srcdir_duplicate('does_not_exist') assert n is None, n assert not os.path.exists(test.workpath('bld0', 'does_not_exist')) n = bld0.srcdir_duplicate('exists') assert str(n) == os.path.normpath('src0/exists'), str(n) assert not os.path.exists(test.workpath('bld0', 'exists')) test.subdir('src1') test.write(['src1', 'exists'], "src0/exists\n") bld1 = self.fs.Dir('bld1') src1 = self.fs.Dir('src1') self.fs.VariantDir(bld1, src1, duplicate=1) n = bld1.srcdir_duplicate('does_not_exist') assert n is None, n assert not os.path.exists(test.workpath('bld1', 'does_not_exist')) n = bld1.srcdir_duplicate('exists') assert str(n) == os.path.normpath('bld1/exists'), str(n) assert os.path.exists(test.workpath('bld1', 'exists')) def test_srcdir_find_file(self): """Test the Dir.srcdir_find_file() method """ test = self.test return_true = lambda: 1 test.subdir('src0') test.write(['src0', 'on-disk-f1'], "src0/on-disk-f1\n") test.write(['src0', 'on-disk-f2'], "src0/on-disk-f2\n") test.write(['src0', 'on-disk-e1'], "src0/on-disk-e1\n") test.write(['src0', 'on-disk-e2'], "src0/on-disk-e2\n") bld0 = self.fs.Dir('bld0') src0 = self.fs.Dir('src0') self.fs.VariantDir(bld0, src0, duplicate=0) derived_f = src0.File('derived-f') derived_f.is_derived = return_true exists_f = src0.File('exists-f') exists_f.exists = return_true derived_e = src0.Entry('derived-e') derived_e.is_derived = return_true exists_e = src0.Entry('exists-e') exists_e.exists = return_true def check(result, expect): result = list(map(str, result)) expect = list(map(os.path.normpath, expect)) assert result == expect, result # First check from the source directory. n = src0.srcdir_find_file('does_not_exist') assert n == (None, None), n n = src0.srcdir_find_file('derived-f') check(n, ['src0/derived-f', 'src0']) n = src0.srcdir_find_file('exists-f') check(n, ['src0/exists-f', 'src0']) n = src0.srcdir_find_file('on-disk-f1') check(n, ['src0/on-disk-f1', 'src0']) n = src0.srcdir_find_file('derived-e') check(n, ['src0/derived-e', 'src0']) n = src0.srcdir_find_file('exists-e') check(n, ['src0/exists-e', 'src0']) n = src0.srcdir_find_file('on-disk-e1') check(n, ['src0/on-disk-e1', 'src0']) # Now check from the variant directory. n = bld0.srcdir_find_file('does_not_exist') assert n == (None, None), n n = bld0.srcdir_find_file('derived-f') check(n, ['src0/derived-f', 'bld0']) n = bld0.srcdir_find_file('exists-f') check(n, ['src0/exists-f', 'bld0']) n = bld0.srcdir_find_file('on-disk-f2') check(n, ['src0/on-disk-f2', 'bld0']) n = bld0.srcdir_find_file('derived-e') check(n, ['src0/derived-e', 'bld0']) n = bld0.srcdir_find_file('exists-e') check(n, ['src0/exists-e', 'bld0']) n = bld0.srcdir_find_file('on-disk-e2') check(n, ['src0/on-disk-e2', 'bld0']) test.subdir('src1') test.write(['src1', 'on-disk-f1'], "src1/on-disk-f1\n") test.write(['src1', 'on-disk-f2'], "src1/on-disk-f2\n") test.write(['src1', 'on-disk-e1'], "src1/on-disk-e1\n") test.write(['src1', 'on-disk-e2'], "src1/on-disk-e2\n") bld1 = self.fs.Dir('bld1') src1 = self.fs.Dir('src1') self.fs.VariantDir(bld1, src1, duplicate=1) derived_f = src1.File('derived-f') derived_f.is_derived = return_true exists_f = src1.File('exists-f') exists_f.exists = return_true derived_e = src1.Entry('derived-e') derived_e.is_derived = return_true exists_e = src1.Entry('exists-e') exists_e.exists = return_true # First check from the source directory. n = src1.srcdir_find_file('does_not_exist') assert n == (None, None), n n = src1.srcdir_find_file('derived-f') check(n, ['src1/derived-f', 'src1']) n = src1.srcdir_find_file('exists-f') check(n, ['src1/exists-f', 'src1']) n = src1.srcdir_find_file('on-disk-f1') check(n, ['src1/on-disk-f1', 'src1']) n = src1.srcdir_find_file('derived-e') check(n, ['src1/derived-e', 'src1']) n = src1.srcdir_find_file('exists-e') check(n, ['src1/exists-e', 'src1']) n = src1.srcdir_find_file('on-disk-e1') check(n, ['src1/on-disk-e1', 'src1']) # Now check from the variant directory. n = bld1.srcdir_find_file('does_not_exist') assert n == (None, None), n n = bld1.srcdir_find_file('derived-f') check(n, ['bld1/derived-f', 'src1']) n = bld1.srcdir_find_file('exists-f') check(n, ['bld1/exists-f', 'src1']) n = bld1.srcdir_find_file('on-disk-f2') check(n, ['bld1/on-disk-f2', 'bld1']) n = bld1.srcdir_find_file('derived-e') check(n, ['bld1/derived-e', 'src1']) n = bld1.srcdir_find_file('exists-e') check(n, ['bld1/exists-e', 'src1']) n = bld1.srcdir_find_file('on-disk-e2') check(n, ['bld1/on-disk-e2', 'bld1']) def test_dir_on_disk(self): """Test the Dir.dir_on_disk() method""" self.test.subdir('sub', ['sub', 'exists']) self.test.write(['sub', 'file'], "self/file\n") sub = self.fs.Dir('sub') r = sub.dir_on_disk('does_not_exist') assert not r, r r = sub.dir_on_disk('exists') assert r, r r = sub.dir_on_disk('file') assert not r, r def test_file_on_disk(self): """Test the Dir.file_on_disk() method""" self.test.subdir('sub', ['sub', 'dir']) self.test.write(['sub', 'exists'], "self/exists\n") sub = self.fs.Dir('sub') r = sub.file_on_disk('does_not_exist') assert not r, r r = sub.file_on_disk('exists') assert r, r r = sub.file_on_disk('dir') assert not r, r class EntryTestCase(_tempdirTestCase): def test_runTest(self): """Test methods specific to the Entry sub-class. """ test = TestCmd(workdir='') # FS doesn't like the cwd to be something other than its root. os.chdir(test.workpath("")) fs = SCons.Node.FS.FS() e1 = fs.Entry('e1') e1.rfile() assert e1.__class__ is SCons.Node.FS.File, e1.__class__ test.subdir('e3d') test.write('e3f', "e3f\n") e3d = fs.Entry('e3d') e3d.get_contents() assert e3d.__class__ is SCons.Node.FS.Dir, e3d.__class__ e3f = fs.Entry('e3f') e3f.get_contents() assert e3f.__class__ is SCons.Node.FS.File, e3f.__class__ e3n = fs.Entry('e3n') e3n.get_contents() assert e3n.__class__ is SCons.Node.FS.Entry, e3n.__class__ test.subdir('e4d') test.write('e4f', "e4f\n") e4d = fs.Entry('e4d') exists = e4d.exists() assert e4d.__class__ is SCons.Node.FS.Dir, e4d.__class__ assert exists, "e4d does not exist?" e4f = fs.Entry('e4f') exists = e4f.exists() assert e4f.__class__ is SCons.Node.FS.File, e4f.__class__ assert exists, "e4f does not exist?" e4n = fs.Entry('e4n') exists = e4n.exists() assert e4n.__class__ is SCons.Node.FS.File, e4n.__class__ assert not exists, "e4n exists?" class MyCalc(object): def __init__(self, val): self.max_drift = 0 class M(object): def __init__(self, val): self.val = val def collect(self, args): result = 0 for a in args: result += a return result def signature(self, executor): return self.val + 222 self.module = M(val) test.subdir('e5d') test.write('e5f', "e5f\n") def test_Entry_Entry_lookup(self): """Test looking up an Entry within another Entry""" self.fs.Entry('#topdir') self.fs.Entry('#topdir/a/b/c') class FileTestCase(_tempdirTestCase): def test_subclass(self): """Test looking up subclass of File nodes""" class FileSubclass(SCons.Node.FS.File): pass sd = self.fs._lookup('special_file', None, FileSubclass, create=1) sd.must_be_same(SCons.Node.FS.File) def test_Dirs(self): """Test the File.Dirs() method""" fff = self.fs.File('subdir/fff') # This simulates that the SConscript file that defined # fff is in subdir/. fff.cwd = self.fs.Dir('subdir') d1 = self.fs.Dir('subdir/d1') d2 = self.fs.Dir('subdir/d2') dirs = fff.Dirs(['d1', 'd2']) assert dirs == [d1, d2], list(map(str, dirs)) def test_exists(self): """Test the File.exists() method""" fs = self.fs test = self.test src_f1 = fs.File('src/f1') assert not src_f1.exists(), "%s apparently exists?" % src_f1 test.subdir('src') test.write(['src', 'f1'], "src/f1\n") assert not src_f1.exists(), "%s did not cache previous exists() value" % src_f1 src_f1.clear() assert src_f1.exists(), "%s apparently does not exist?" % src_f1 test.subdir('build') fs.VariantDir('build', 'src') build_f1 = fs.File('build/f1') assert build_f1.exists(), "%s did not realize that %s exists" % (build_f1, src_f1) assert os.path.exists(build_f1.abspath), "%s did not get duplicated on disk" % build_f1.abspath test.unlink(['src', 'f1']) src_f1.clear() # so the next exists() call will look on disk again assert build_f1.exists(), "%s did not cache previous exists() value" % build_f1 build_f1.clear() build_f1.linked = None assert not build_f1.exists(), "%s did not realize that %s disappeared" % (build_f1, src_f1) assert not os.path.exists(build_f1.abspath), "%s did not get removed after %s was removed" % (build_f1, src_f1) class GlobTestCase(_tempdirTestCase): def setUp(self): _tempdirTestCase.setUp(self) fs = SCons.Node.FS.FS() self.fs = fs # Make entries on disk that will not have Nodes, so we can verify # the behavior of looking for things on disk. self.test.write('disk-bbb', "disk-bbb\n") self.test.write('disk-aaa', "disk-aaa\n") self.test.write('disk-ccc', "disk-ccc\n") self.test.write('#disk-hash', "#disk-hash\n") self.test.subdir('disk-sub') self.test.write(['disk-sub', 'disk-ddd'], "disk-sub/disk-ddd\n") self.test.write(['disk-sub', 'disk-eee'], "disk-sub/disk-eee\n") self.test.write(['disk-sub', 'disk-fff'], "disk-sub/disk-fff\n") # Make some entries that have both Nodes and on-disk entries, # so we can verify what we do with self.test.write('both-aaa', "both-aaa\n") self.test.write('both-bbb', "both-bbb\n") self.test.write('both-ccc', "both-ccc\n") self.test.write('#both-hash', "#both-hash\n") self.test.subdir('both-sub1') self.test.write(['both-sub1', 'both-ddd'], "both-sub1/both-ddd\n") self.test.write(['both-sub1', 'both-eee'], "both-sub1/both-eee\n") self.test.write(['both-sub1', 'both-fff'], "both-sub1/both-fff\n") self.test.subdir('both-sub2') self.test.write(['both-sub2', 'both-ddd'], "both-sub2/both-ddd\n") self.test.write(['both-sub2', 'both-eee'], "both-sub2/both-eee\n") self.test.write(['both-sub2', 'both-fff'], "both-sub2/both-fff\n") self.both_aaa = fs.File('both-aaa') self.both_bbb = fs.File('both-bbb') self.both_ccc = fs.File('both-ccc') self._both_hash = fs.File('./#both-hash') self.both_sub1 = fs.Dir('both-sub1') self.both_sub1_both_ddd = self.both_sub1.File('both-ddd') self.both_sub1_both_eee = self.both_sub1.File('both-eee') self.both_sub1_both_fff = self.both_sub1.File('both-fff') self.both_sub2 = fs.Dir('both-sub2') self.both_sub2_both_ddd = self.both_sub2.File('both-ddd') self.both_sub2_both_eee = self.both_sub2.File('both-eee') self.both_sub2_both_fff = self.both_sub2.File('both-fff') # Make various Nodes (that don't have on-disk entries) so we # can verify how we match them. self.ggg = fs.File('ggg') self.hhh = fs.File('hhh') self.iii = fs.File('iii') self._hash = fs.File('./#hash') self.subdir1 = fs.Dir('subdir1') self.subdir1_lll = self.subdir1.File('lll') self.subdir1_jjj = self.subdir1.File('jjj') self.subdir1_kkk = self.subdir1.File('kkk') self.subdir2 = fs.Dir('subdir2') self.subdir2_lll = self.subdir2.File('lll') self.subdir2_kkk = self.subdir2.File('kkk') self.subdir2_jjj = self.subdir2.File('jjj') self.sub = fs.Dir('sub') self.sub_dir3 = self.sub.Dir('dir3') self.sub_dir3_kkk = self.sub_dir3.File('kkk') self.sub_dir3_jjj = self.sub_dir3.File('jjj') self.sub_dir3_lll = self.sub_dir3.File('lll') def do_cases(self, cases, **kwargs): # First, execute all of the cases with string=True and verify # that we get the expected strings returned. We do this first # so the Glob() calls don't add Nodes to the self.fs file system # hierarchy. import copy strings_kwargs = copy.copy(kwargs) strings_kwargs['strings'] = True for input, string_expect, node_expect in cases: r = sorted(self.fs.Glob(input, **strings_kwargs)) assert r == string_expect, "Glob(%s, strings=True) expected %s, got %s" % (input, string_expect, r) # Now execute all of the cases without string=True and look for # the expected Nodes to be returned. If we don't have a list of # actual expected Nodes, that means we're expecting a search for # on-disk-only files to have returned some newly-created nodes. # Verify those by running the list through str() before comparing # them with the expected list of strings. for input, string_expect, node_expect in cases: r = self.fs.Glob(input, **kwargs) if node_expect: r = sorted(r, key=lambda a: a.path) result = [] for n in node_expect: if isinstance(n, str): n = self.fs.Entry(n) result.append(n) fmt = lambda n: "%s %s" % (repr(n), repr(str(n))) else: r = sorted(map(str, r)) result = string_expect fmt = lambda n: n if r != result: import pprint print "Glob(%s) expected:" % repr(input) pprint.pprint(list(map(fmt, result))) print "Glob(%s) got:" % repr(input) pprint.pprint(list(map(fmt, r))) self.fail() def test_exact_match(self): """Test globbing for exact Node matches""" join = os.path.join cases = ( ('ggg', ['ggg'], [self.ggg]), ('subdir1', ['subdir1'], [self.subdir1]), ('subdir1/jjj', [join('subdir1', 'jjj')], [self.subdir1_jjj]), ('disk-aaa', ['disk-aaa'], None), ('disk-sub', ['disk-sub'], None), ('both-aaa', ['both-aaa'], []), ) self.do_cases(cases) def test_subdir_matches(self): """Test globbing for exact Node matches in subdirectories""" join = os.path.join cases = ( ('*/jjj', [join('subdir1', 'jjj'), join('subdir2', 'jjj')], [self.subdir1_jjj, self.subdir2_jjj]), ('*/disk-ddd', [join('disk-sub', 'disk-ddd')], None), ) self.do_cases(cases) def test_asterisk1(self): """Test globbing for simple asterisk Node matches (1)""" cases = ( ('h*', ['hhh'], [self.hhh]), ('*', ['#both-hash', '#hash', 'both-aaa', 'both-bbb', 'both-ccc', 'both-sub1', 'both-sub2', 'ggg', 'hhh', 'iii', 'sub', 'subdir1', 'subdir2'], [self._both_hash, self._hash, self.both_aaa, self.both_bbb, self.both_ccc, 'both-hash', self.both_sub1, self.both_sub2, self.ggg, 'hash', self.hhh, self.iii, self.sub, self.subdir1, self.subdir2]), ) self.do_cases(cases, ondisk=False) def test_asterisk2(self): """Test globbing for simple asterisk Node matches (2)""" cases = ( ('disk-b*', ['disk-bbb'], None), ('*', ['#both-hash', '#disk-hash', '#hash', 'both-aaa', 'both-bbb', 'both-ccc', 'both-sub1', 'both-sub2', 'disk-aaa', 'disk-bbb', 'disk-ccc', 'disk-sub', 'ggg', 'hhh', 'iii', 'sub', 'subdir1', 'subdir2'], ['./#both-hash', './#disk-hash', './#hash', 'both-aaa', 'both-bbb', 'both-ccc', 'both-hash', 'both-sub1', 'both-sub2', 'disk-aaa', 'disk-bbb', 'disk-ccc', 'disk-sub', 'ggg', 'hash', 'hhh', 'iii', 'sub', 'subdir1', 'subdir2']), ) self.do_cases(cases) def test_question_mark(self): """Test globbing for simple question-mark Node matches""" join = os.path.join cases = ( ('ii?', ['iii'], [self.iii]), ('both-sub?/both-eee', [join('both-sub1', 'both-eee'), join('both-sub2', 'both-eee')], [self.both_sub1_both_eee, self.both_sub2_both_eee]), ('subdir?/jjj', [join('subdir1', 'jjj'), join('subdir2', 'jjj')], [self.subdir1_jjj, self.subdir2_jjj]), ('disk-cc?', ['disk-ccc'], None), ) self.do_cases(cases) def test_does_not_exist(self): """Test globbing for things that don't exist""" cases = ( ('does_not_exist', [], []), ('no_subdir/*', [], []), ('subdir?/no_file', [], []), ) self.do_cases(cases) def test_subdir_asterisk(self): """Test globbing for asterisk Node matches in subdirectories""" join = os.path.join cases = ( ('*/k*', [join('subdir1', 'kkk'), join('subdir2', 'kkk')], [self.subdir1_kkk, self.subdir2_kkk]), ('both-sub?/*', [join('both-sub1', 'both-ddd'), join('both-sub1', 'both-eee'), join('both-sub1', 'both-fff'), join('both-sub2', 'both-ddd'), join('both-sub2', 'both-eee'), join('both-sub2', 'both-fff')], [self.both_sub1_both_ddd, self.both_sub1_both_eee, self.both_sub1_both_fff, self.both_sub2_both_ddd, self.both_sub2_both_eee, self.both_sub2_both_fff], ), ('subdir?/*', [join('subdir1', 'jjj'), join('subdir1', 'kkk'), join('subdir1', 'lll'), join('subdir2', 'jjj'), join('subdir2', 'kkk'), join('subdir2', 'lll')], [self.subdir1_jjj, self.subdir1_kkk, self.subdir1_lll, self.subdir2_jjj, self.subdir2_kkk, self.subdir2_lll]), ('sub/*/*', [join('sub', 'dir3', 'jjj'), join('sub', 'dir3', 'kkk'), join('sub', 'dir3', 'lll')], [self.sub_dir3_jjj, self.sub_dir3_kkk, self.sub_dir3_lll]), ('*/k*', [join('subdir1', 'kkk'), join('subdir2', 'kkk')], None), ('subdir?/*', [join('subdir1', 'jjj'), join('subdir1', 'kkk'), join('subdir1', 'lll'), join('subdir2', 'jjj'), join('subdir2', 'kkk'), join('subdir2', 'lll')], None), ('sub/*/*', [join('sub', 'dir3', 'jjj'), join('sub', 'dir3', 'kkk'), join('sub', 'dir3', 'lll')], None), ) self.do_cases(cases) def test_subdir_question(self): """Test globbing for question-mark Node matches in subdirectories""" join = os.path.join cases = ( ('*/?kk', [join('subdir1', 'kkk'), join('subdir2', 'kkk')], [self.subdir1_kkk, self.subdir2_kkk]), ('subdir?/l?l', [join('subdir1', 'lll'), join('subdir2', 'lll')], [self.subdir1_lll, self.subdir2_lll]), ('*/disk-?ff', [join('disk-sub', 'disk-fff')], None), ('subdir?/l?l', [join('subdir1', 'lll'), join('subdir2', 'lll')], None), ) self.do_cases(cases) def test_sort(self): """Test whether globbing sorts""" join = os.path.join # At least sometimes this should return out-of-order items # if Glob doesn't sort. # It's not a very good test though since it depends on the # order returned by glob, which might already be sorted. g = self.fs.Glob('disk-sub/*', strings=True) expect = [ os.path.join('disk-sub', 'disk-ddd'), os.path.join('disk-sub', 'disk-eee'), os.path.join('disk-sub', 'disk-fff'), ] assert g == expect, str(g) + " is not sorted, but should be!" g = self.fs.Glob('disk-*', strings=True) expect = [ 'disk-aaa', 'disk-bbb', 'disk-ccc', 'disk-sub' ] assert g == expect, str(g) + " is not sorted, but should be!" class RepositoryTestCase(_tempdirTestCase): def setUp(self): _tempdirTestCase.setUp(self) self.test.subdir('rep1', 'rep2', 'rep3', 'work') self.rep1 = self.test.workpath('rep1') self.rep2 = self.test.workpath('rep2') self.rep3 = self.test.workpath('rep3') os.chdir(self.test.workpath('work')) self.fs = SCons.Node.FS.FS() self.fs.Repository(self.rep1, self.rep2, self.rep3) def test_getRepositories(self): """Test the Dir.getRepositories() method""" self.fs.Repository('foo') self.fs.Repository(os.path.join('foo', 'bar')) self.fs.Repository('bar/foo') self.fs.Repository('bar') expect = [ self.rep1, self.rep2, self.rep3, 'foo', os.path.join('foo', 'bar'), os.path.join('bar', 'foo'), 'bar' ] rep = self.fs.Dir('#').getRepositories() r = [os.path.normpath(str(x)) for x in rep] assert r == expect, r def test_get_all_rdirs(self): """Test the Dir.get_all_rdirs() method""" self.fs.Repository('foo') self.fs.Repository(os.path.join('foo', 'bar')) self.fs.Repository('bar/foo') self.fs.Repository('bar') expect = [ '.', self.rep1, self.rep2, self.rep3, 'foo', os.path.join('foo', 'bar'), os.path.join('bar', 'foo'), 'bar' ] rep = self.fs.Dir('#').get_all_rdirs() r = [os.path.normpath(str(x)) for x in rep] assert r == expect, r def test_rentry(self): """Test the Base.entry() method""" return_true = lambda: 1 return_false = lambda: 0 d1 = self.fs.Dir('d1') d2 = self.fs.Dir('d2') d3 = self.fs.Dir('d3') e1 = self.fs.Entry('e1') e2 = self.fs.Entry('e2') e3 = self.fs.Entry('e3') f1 = self.fs.File('f1') f2 = self.fs.File('f2') f3 = self.fs.File('f3') self.test.write([self.rep1, 'd2'], "") self.test.subdir([self.rep2, 'd3']) self.test.write([self.rep3, 'd3'], "") self.test.write([self.rep1, 'e2'], "") self.test.subdir([self.rep2, 'e3']) self.test.write([self.rep3, 'e3'], "") self.test.write([self.rep1, 'f2'], "") self.test.subdir([self.rep2, 'f3']) self.test.write([self.rep3, 'f3'], "") r = d1.rentry() assert r is d1, r r = d2.rentry() assert not r is d2, r r = str(r) assert r == os.path.join(self.rep1, 'd2'), r r = d3.rentry() assert not r is d3, r r = str(r) assert r == os.path.join(self.rep2, 'd3'), r r = e1.rentry() assert r is e1, r r = e2.rentry() assert not r is e2, r r = str(r) assert r == os.path.join(self.rep1, 'e2'), r r = e3.rentry() assert not r is e3, r r = str(r) assert r == os.path.join(self.rep2, 'e3'), r r = f1.rentry() assert r is f1, r r = f2.rentry() assert not r is f2, r r = str(r) assert r == os.path.join(self.rep1, 'f2'), r r = f3.rentry() assert not r is f3, r r = str(r) assert r == os.path.join(self.rep2, 'f3'), r def test_rdir(self): """Test the Dir.rdir() method""" return_true = lambda: 1 return_false = lambda: 0 d1 = self.fs.Dir('d1') d2 = self.fs.Dir('d2') d3 = self.fs.Dir('d3') self.test.subdir([self.rep1, 'd2']) self.test.write([self.rep2, 'd3'], "") self.test.subdir([self.rep3, 'd3']) r = d1.rdir() assert r is d1, r r = d2.rdir() assert not r is d2, r r = str(r) assert r == os.path.join(self.rep1, 'd2'), r r = d3.rdir() assert not r is d3, r r = str(r) assert r == os.path.join(self.rep3, 'd3'), r e1 = self.fs.Dir('e1') e1.exists = return_false e2 = self.fs.Dir('e2') e2.exists = return_false # Make sure we match entries in repositories, # regardless of whether they're derived or not. re1 = self.fs.Entry(os.path.join(self.rep1, 'e1')) re1.exists = return_true re1.is_derived = return_true re2 = self.fs.Entry(os.path.join(self.rep2, 'e2')) re2.exists = return_true re2.is_derived = return_false r = e1.rdir() assert r is re1, r r = e2.rdir() assert r is re2, r def test_rfile(self): """Test the File.rfile() method""" return_true = lambda: 1 return_false = lambda: 0 f1 = self.fs.File('f1') f2 = self.fs.File('f2') f3 = self.fs.File('f3') self.test.write([self.rep1, 'f2'], "") self.test.subdir([self.rep2, 'f3']) self.test.write([self.rep3, 'f3'], "") r = f1.rfile() assert r is f1, r r = f2.rfile() assert not r is f2, r r = str(r) assert r == os.path.join(self.rep1, 'f2'), r r = f3.rfile() assert not r is f3, r r = f3.rstr() assert r == os.path.join(self.rep3, 'f3'), r e1 = self.fs.File('e1') e1.exists = return_false e2 = self.fs.File('e2') e2.exists = return_false # Make sure we match entries in repositories, # regardless of whether they're derived or not. re1 = self.fs.Entry(os.path.join(self.rep1, 'e1')) re1.exists = return_true re1.is_derived = return_true re2 = self.fs.Entry(os.path.join(self.rep2, 'e2')) re2.exists = return_true re2.is_derived = return_false r = e1.rfile() assert r is re1, r r = e2.rfile() assert r is re2, r def test_Rfindalldirs(self): """Test the Rfindalldirs() methods""" fs = self.fs test = self.test d1 = fs.Dir('d1') d2 = fs.Dir('d2') rep1_d1 = fs.Dir(test.workpath('rep1', 'd1')) rep2_d1 = fs.Dir(test.workpath('rep2', 'd1')) rep3_d1 = fs.Dir(test.workpath('rep3', 'd1')) sub = fs.Dir('sub') sub_d1 = sub.Dir('d1') rep1_sub_d1 = fs.Dir(test.workpath('rep1', 'sub', 'd1')) rep2_sub_d1 = fs.Dir(test.workpath('rep2', 'sub', 'd1')) rep3_sub_d1 = fs.Dir(test.workpath('rep3', 'sub', 'd1')) r = fs.Top.Rfindalldirs((d1,)) assert r == [d1], list(map(str, r)) r = fs.Top.Rfindalldirs((d1, d2)) assert r == [d1, d2], list(map(str, r)) r = fs.Top.Rfindalldirs(('d1',)) assert r == [d1, rep1_d1, rep2_d1, rep3_d1], list(map(str, r)) r = fs.Top.Rfindalldirs(('#d1',)) assert r == [d1, rep1_d1, rep2_d1, rep3_d1], list(map(str, r)) r = sub.Rfindalldirs(('d1',)) assert r == [sub_d1, rep1_sub_d1, rep2_sub_d1, rep3_sub_d1], list(map(str, r)) r = sub.Rfindalldirs(('#d1',)) assert r == [d1, rep1_d1, rep2_d1, rep3_d1], list(map(str, r)) r = fs.Top.Rfindalldirs(('d1', d2)) assert r == [d1, rep1_d1, rep2_d1, rep3_d1, d2], list(map(str, r)) def test_rexists(self): """Test the Entry.rexists() method""" fs = self.fs test = self.test test.write([self.rep1, 'f2'], "") test.write([self.rep2, "i_exist"], "\n") test.write(["work", "i_exist_too"], "\n") fs.VariantDir('build', '.') f = fs.File(test.workpath("work", "i_do_not_exist")) assert not f.rexists() f = fs.File(test.workpath("work", "i_exist")) assert f.rexists() f = fs.File(test.workpath("work", "i_exist_too")) assert f.rexists() f1 = fs.File(os.path.join('build', 'f1')) assert not f1.rexists() f2 = fs.File(os.path.join('build', 'f2')) assert f2.rexists() def test_FAT_timestamps(self): """Test repository timestamps on FAT file systems""" fs = self.fs test = self.test test.write(["rep2", "tstamp"], "tstamp\n") try: # Okay, *this* manipulation accomodates Windows FAT file systems # that only have two-second granularity on their timestamps. # We round down the current time to the nearest even integer # value, subtract two to make sure the timestamp is not "now," # and then convert it back to a float. tstamp = float(int(time.time() // 2) * 2) - 2.0 os.utime(test.workpath("rep2", "tstamp"), (tstamp - 2.0, tstamp)) f = fs.File("tstamp") t = f.get_timestamp() assert t == tstamp, "expected %f, got %f" % (tstamp, t) finally: test.unlink(["rep2", "tstamp"]) def test_get_contents(self): """Ensure get_contents() returns binary contents from Repositories""" fs = self.fs test = self.test test.write(["rep3", "contents"], "Con\x1aTents\n") try: c = fs.File("contents").get_contents() assert c == "Con\x1aTents\n", "got '%s'" % c finally: test.unlink(["rep3", "contents"]) def test_get_text_contents(self): """Ensure get_text_contents() returns text contents from Repositories""" fs = self.fs test = self.test # Use a test string that has a file terminator in it to make # sure we read the entire file, regardless of its contents. try: eval('test_string = u"Con\x1aTents\n"') except SyntaxError: import collections class FakeUnicodeString(collections.UserString): def encode(self, encoding): return str(self) test_string = FakeUnicodeString("Con\x1aTents\n") # Test with ASCII. test.write(["rep3", "contents"], test_string.encode('ascii')) try: c = fs.File("contents").get_text_contents() assert test_string == c, "got %s" % repr(c) finally: test.unlink(["rep3", "contents"]) # Test with utf-8 test.write(["rep3", "contents"], test_string.encode('utf-8')) try: c = fs.File("contents").get_text_contents() assert test_string == c, "got %s" % repr(c) finally: test.unlink(["rep3", "contents"]) # Test with utf-16 test.write(["rep3", "contents"], test_string.encode('utf-16')) try: c = fs.File("contents").get_text_contents() assert test_string == c, "got %s" % repr(c) finally: test.unlink(["rep3", "contents"]) #def test_is_up_to_date(self): class find_fileTestCase(unittest.TestCase): def runTest(self): """Testing find_file function""" test = TestCmd(workdir = '') test.write('./foo', 'Some file\n') test.write('./foo2', 'Another file\n') test.subdir('same') test.subdir('bar') test.write(['bar', 'on_disk'], 'Another file\n') test.write(['bar', 'same'], 'bar/same\n') fs = SCons.Node.FS.FS(test.workpath("")) # FS doesn't like the cwd to be something other than its root. os.chdir(test.workpath("")) node_derived = fs.File(test.workpath('bar/baz')) node_derived.builder_set(1) # Any non-zero value. node_pseudo = fs.File(test.workpath('pseudo')) node_pseudo.set_src_builder(1) # Any non-zero value. paths = tuple(map(fs.Dir, ['.', 'same', './bar'])) nodes = [SCons.Node.FS.find_file('foo', paths)] nodes.append(SCons.Node.FS.find_file('baz', paths)) nodes.append(SCons.Node.FS.find_file('pseudo', paths)) nodes.append(SCons.Node.FS.find_file('same', paths)) file_names = list(map(str, nodes)) file_names = list(map(os.path.normpath, file_names)) expect = ['./foo', './bar/baz', './pseudo', './bar/same'] expect = list(map(os.path.normpath, expect)) assert file_names == expect, file_names # Make sure we don't blow up if there's already a File in place # of a directory that we'd otherwise try to search. If this # is broken, we'll see an exception like "Tried to lookup File # 'bar/baz' as a Dir. SCons.Node.FS.find_file('baz/no_file_here', paths) import io save_sys_stdout = sys.stdout try: sio = io.StringIO() sys.stdout = sio SCons.Node.FS.find_file('foo2', paths, verbose="xyz") expect = " xyz: looking for 'foo2' in '.' ...\n" + \ " xyz: ... FOUND 'foo2' in '.'\n" c = sio.getvalue() assert c == expect, c sio = io.StringIO() sys.stdout = sio SCons.Node.FS.find_file('baz2', paths, verbose=1) expect = " find_file: looking for 'baz2' in '.' ...\n" + \ " find_file: looking for 'baz2' in 'same' ...\n" + \ " find_file: looking for 'baz2' in 'bar' ...\n" c = sio.getvalue() assert c == expect, c sio = io.StringIO() sys.stdout = sio SCons.Node.FS.find_file('on_disk', paths, verbose=1) expect = " find_file: looking for 'on_disk' in '.' ...\n" + \ " find_file: looking for 'on_disk' in 'same' ...\n" + \ " find_file: looking for 'on_disk' in 'bar' ...\n" + \ " find_file: ... FOUND 'on_disk' in 'bar'\n" c = sio.getvalue() assert c == expect, c finally: sys.stdout = save_sys_stdout class StringDirTestCase(unittest.TestCase): def runTest(self): """Test using a string as the second argument of File() and Dir()""" test = TestCmd(workdir = '') test.subdir('sub') fs = SCons.Node.FS.FS(test.workpath('')) d = fs.Dir('sub', '.') assert str(d) == 'sub', str(d) assert d.exists() f = fs.File('file', 'sub') assert str(f) == os.path.join('sub', 'file') assert not f.exists() class stored_infoTestCase(unittest.TestCase): def runTest(self): """Test how we store build information""" test = TestCmd(workdir = '') test.subdir('sub') fs = SCons.Node.FS.FS(test.workpath('')) d = fs.Dir('sub') f = fs.File('file1', d) bi = f.get_stored_info() assert hasattr(bi, 'ninfo') class MySConsign(object): class Null(object): def __init__(self): self.xyzzy = 7 def get_entry(self, name): return self.Null() f = fs.File('file2', d) f.dir.sconsign = MySConsign bi = f.get_stored_info() assert bi.xyzzy == 7, bi class has_src_builderTestCase(unittest.TestCase): def runTest(self): """Test the has_src_builder() method""" test = TestCmd(workdir = '') fs = SCons.Node.FS.FS(test.workpath('')) os.chdir(test.workpath('')) test.subdir('sub1') test.subdir('sub2', ['sub2', 'SCCS'], ['sub2', 'RCS']) sub1 = fs.Dir('sub1', '.') f1 = fs.File('f1', sub1) f2 = fs.File('f2', sub1) f3 = fs.File('f3', sub1) sub2 = fs.Dir('sub2', '.') f4 = fs.File('f4', sub2) f5 = fs.File('f5', sub2) f6 = fs.File('f6', sub2) f7 = fs.File('f7', sub2) f8 = fs.File('f8', sub2) h = f1.has_src_builder() assert not h, h h = f1.has_builder() assert not h, h b1 = Builder(fs.File) sub1.set_src_builder(b1) test.write(['sub1', 'f2'], "sub1/f2\n") h = f1.has_src_builder() # cached from previous call assert not h, h h = f1.has_builder() # cached from previous call assert not h, h h = f2.has_src_builder() assert not h, h h = f2.has_builder() assert not h, h h = f3.has_src_builder() assert h, h h = f3.has_builder() assert h, h assert f3.builder is b1, f3.builder f7.set_src_builder(b1) f8.builder_set(b1) test.write(['sub2', 'SCCS', 's.f5'], "sub2/SCCS/s.f5\n") test.write(['sub2', 'RCS', 'f6,v'], "sub2/RCS/f6,v\n") h = f4.has_src_builder() assert not h, h h = f4.has_builder() assert not h, h h = f5.has_src_builder() assert h, h h = f5.has_builder() assert h, h h = f6.has_src_builder() assert h, h h = f6.has_builder() assert h, h h = f7.has_src_builder() assert h, h h = f7.has_builder() assert h, h h = f8.has_src_builder() assert not h, h h = f8.has_builder() assert h, h class prepareTestCase(unittest.TestCase): def runTest(self): """Test the prepare() method""" class MyFile(SCons.Node.FS.File): def _createDir(self, update=None): raise SCons.Errors.StopError def exists(self): return None fs = SCons.Node.FS.FS() file = MyFile('foo', fs.Dir('.'), fs) exc_caught = 0 try: file.prepare() except SCons.Errors.StopError: exc_caught = 1 assert exc_caught, "Should have caught a StopError." class MkdirAction(Action): def __init__(self, dir_made): self.dir_made = dir_made def __call__(self, target, source, env, executor=None): if executor: target = executor.get_all_targets() source = executor.get_all_sources() self.dir_made.extend(target) dir_made = [] new_dir = fs.Dir("new_dir") new_dir.builder = Builder(fs.Dir, action=MkdirAction(dir_made)) new_dir.reset_executor() xyz = fs.File(os.path.join("new_dir", "xyz")) xyz.set_state(SCons.Node.up_to_date) xyz.prepare() assert dir_made == [], dir_made xyz.set_state(0) xyz.prepare() assert dir_made[0].path == "new_dir", dir_made[0] dir = fs.Dir("dir") dir.prepare() class SConstruct_dirTestCase(unittest.TestCase): def runTest(self): """Test setting the SConstruct directory""" fs = SCons.Node.FS.FS() fs.set_SConstruct_dir(fs.Dir('xxx')) assert fs.SConstruct_dir.path == 'xxx' class CacheDirTestCase(unittest.TestCase): def test_get_cachedir_csig(self): fs = SCons.Node.FS.FS() f9 = fs.File('f9') r = f9.get_cachedir_csig() assert r == 'd41d8cd98f00b204e9800998ecf8427e', r class clearTestCase(unittest.TestCase): def runTest(self): """Test clearing FS nodes of cached data.""" fs = SCons.Node.FS.FS() test = TestCmd(workdir='') e = fs.Entry('e') assert not e.exists() assert not e.rexists() assert str(e) == 'e', str(d) e.clear() assert not e.exists() assert not e.rexists() assert str(e) == 'e', str(d) d = fs.Dir(test.workpath('d')) test.subdir('d') assert d.exists() assert d.rexists() assert str(d) == test.workpath('d'), str(d) fs.rename(test.workpath('d'), test.workpath('gone')) # Verify caching is active assert d.exists(), 'caching not active' assert d.rexists() assert str(d) == test.workpath('d'), str(d) # Now verify clear() resets the cache d.clear() assert not d.exists() assert not d.rexists() assert str(d) == test.workpath('d'), str(d) f = fs.File(test.workpath('f')) test.write(test.workpath('f'), 'file f') assert f.exists() assert f.rexists() assert str(f) == test.workpath('f'), str(f) # Verify caching is active test.unlink(test.workpath('f')) assert f.exists() assert f.rexists() assert str(f) == test.workpath('f'), str(f) # Now verify clear() resets the cache f.clear() assert not f.exists() assert not f.rexists() assert str(f) == test.workpath('f'), str(f) class disambiguateTestCase(unittest.TestCase): def runTest(self): """Test calling the disambiguate() method.""" test = TestCmd(workdir='') fs = SCons.Node.FS.FS() ddd = fs.Dir('ddd') d = ddd.disambiguate() assert d is ddd, d fff = fs.File('fff') f = fff.disambiguate() assert f is fff, f test.subdir('edir') test.write('efile', "efile\n") edir = fs.Entry(test.workpath('edir')) d = edir.disambiguate() assert d.__class__ is ddd.__class__, d.__class__ efile = fs.Entry(test.workpath('efile')) f = efile.disambiguate() assert f.__class__ is fff.__class__, f.__class__ test.subdir('build') test.subdir(['build', 'bdir']) test.write(['build', 'bfile'], "build/bfile\n") test.subdir('src') test.write(['src', 'bdir'], "src/bdir\n") test.subdir(['src', 'bfile']) test.subdir(['src', 'edir']) test.write(['src', 'efile'], "src/efile\n") fs.VariantDir(test.workpath('build'), test.workpath('src')) build_bdir = fs.Entry(test.workpath('build/bdir')) d = build_bdir.disambiguate() assert d is build_bdir, d assert d.__class__ is ddd.__class__, d.__class__ build_bfile = fs.Entry(test.workpath('build/bfile')) f = build_bfile.disambiguate() assert f is build_bfile, f assert f.__class__ is fff.__class__, f.__class__ build_edir = fs.Entry(test.workpath('build/edir')) d = build_edir.disambiguate() assert d.__class__ is ddd.__class__, d.__class__ build_efile = fs.Entry(test.workpath('build/efile')) f = build_efile.disambiguate() assert f.__class__ is fff.__class__, f.__class__ build_nonexistant = fs.Entry(test.workpath('build/nonexistant')) f = build_nonexistant.disambiguate() assert f.__class__ is fff.__class__, f.__class__ class postprocessTestCase(unittest.TestCase): def runTest(self): """Test calling the postprocess() method.""" fs = SCons.Node.FS.FS() e = fs.Entry('e') e.postprocess() d = fs.Dir('d') d.postprocess() f = fs.File('f') f.postprocess() class SpecialAttrTestCase(unittest.TestCase): def runTest(self): """Test special attributes of file nodes.""" test=TestCmd(workdir='') fs = SCons.Node.FS.FS(test.workpath('work')) f = fs.Entry('foo/bar/baz.blat').get_subst_proxy() s = str(f.dir) assert s == os.path.normpath('foo/bar'), s assert f.dir.is_literal(), f.dir for_sig = f.dir.for_signature() assert for_sig == 'bar', for_sig s = str(f.file) assert s == 'baz.blat', s assert f.file.is_literal(), f.file for_sig = f.file.for_signature() assert for_sig == 'baz.blat_file', for_sig s = str(f.base) assert s == os.path.normpath('foo/bar/baz'), s assert f.base.is_literal(), f.base for_sig = f.base.for_signature() assert for_sig == 'baz.blat_base', for_sig s = str(f.filebase) assert s == 'baz', s assert f.filebase.is_literal(), f.filebase for_sig = f.filebase.for_signature() assert for_sig == 'baz.blat_filebase', for_sig s = str(f.suffix) assert s == '.blat', s assert f.suffix.is_literal(), f.suffix for_sig = f.suffix.for_signature() assert for_sig == 'baz.blat_suffix', for_sig s = str(f.abspath) assert s == test.workpath('work', 'foo', 'bar', 'baz.blat'), s assert f.abspath.is_literal(), f.abspath for_sig = f.abspath.for_signature() assert for_sig == 'baz.blat_abspath', for_sig s = str(f.posix) assert s == 'foo/bar/baz.blat', s assert f.posix.is_literal(), f.posix if f.posix != f: for_sig = f.posix.for_signature() assert for_sig == 'baz.blat_posix', for_sig s = str(f.windows) assert s == 'foo\\bar\\baz.blat', repr(s) assert f.windows.is_literal(), f.windows if f.windows != f: for_sig = f.windows.for_signature() assert for_sig == 'baz.blat_windows', for_sig # Deprecated synonym for the .windows suffix. s = str(f.win32) assert s == 'foo\\bar\\baz.blat', repr(s) assert f.win32.is_literal(), f.win32 if f.win32 != f: for_sig = f.win32.for_signature() assert for_sig == 'baz.blat_windows', for_sig # And now, combinations!!! s = str(f.srcpath.base) assert s == os.path.normpath('foo/bar/baz'), s s = str(f.srcpath.dir) assert s == str(f.srcdir), s s = str(f.srcpath.posix) assert s == 'foo/bar/baz.blat', s s = str(f.srcpath.windows) assert s == 'foo\\bar\\baz.blat', s s = str(f.srcpath.win32) assert s == 'foo\\bar\\baz.blat', s # Test what happens with VariantDir() fs.VariantDir('foo', 'baz') s = str(f.srcpath) assert s == os.path.normpath('baz/bar/baz.blat'), s assert f.srcpath.is_literal(), f.srcpath g = f.srcpath.get() assert isinstance(g, SCons.Node.FS.File), g.__class__ s = str(f.srcdir) assert s == os.path.normpath('baz/bar'), s assert f.srcdir.is_literal(), f.srcdir g = f.srcdir.get() assert isinstance(g, SCons.Node.FS.Dir), g.__class__ # And now what happens with VariantDir() + Repository() fs.Repository(test.workpath('repository')) f = fs.Entry('foo/sub/file.suffix').get_subst_proxy() test.subdir('repository', ['repository', 'baz'], ['repository', 'baz', 'sub']) rd = test.workpath('repository', 'baz', 'sub') rf = test.workpath('repository', 'baz', 'sub', 'file.suffix') test.write(rf, "\n") s = str(f.srcpath) assert s == os.path.normpath('baz/sub/file.suffix'), s assert f.srcpath.is_literal(), f.srcpath g = f.srcpath.get() # Gets disambiguated to SCons.Node.FS.File by get_subst_proxy(). assert isinstance(g, SCons.Node.FS.File), g.__class__ s = str(f.srcdir) assert s == os.path.normpath('baz/sub'), s assert f.srcdir.is_literal(), f.srcdir g = f.srcdir.get() assert isinstance(g, SCons.Node.FS.Dir), g.__class__ s = str(f.rsrcpath) assert s == rf, s assert f.rsrcpath.is_literal(), f.rsrcpath g = f.rsrcpath.get() assert isinstance(g, SCons.Node.FS.File), g.__class__ s = str(f.rsrcdir) assert s == rd, s assert f.rsrcdir.is_literal(), f.rsrcdir g = f.rsrcdir.get() assert isinstance(g, SCons.Node.FS.Dir), g.__class__ # Check that attempts to access non-existent attributes of the # subst proxy generate the right exceptions and messages. caught = None try: fs.Dir('ddd').get_subst_proxy().no_such_attr except AttributeError, e: assert str(e) == "Dir instance 'ddd' has no attribute 'no_such_attr'", e caught = 1 assert caught, "did not catch expected AttributeError" caught = None try: fs.Entry('eee').get_subst_proxy().no_such_attr except AttributeError, e: # Gets disambiguated to File instance by get_subst_proxy(). assert str(e) == "File instance 'eee' has no attribute 'no_such_attr'", e caught = 1 assert caught, "did not catch expected AttributeError" caught = None try: fs.File('fff').get_subst_proxy().no_such_attr except AttributeError, e: assert str(e) == "File instance 'fff' has no attribute 'no_such_attr'", e caught = 1 assert caught, "did not catch expected AttributeError" class SaveStringsTestCase(unittest.TestCase): def runTest(self): """Test caching string values of nodes.""" test=TestCmd(workdir='') def setup(fs): fs.Dir('src') fs.Dir('d0') fs.Dir('d1') d0_f = fs.File('d0/f') d1_f = fs.File('d1/f') d0_b = fs.File('d0/b') d1_b = fs.File('d1/b') d1_f.duplicate = 1 d1_b.duplicate = 1 d0_b.builder = 1 d1_b.builder = 1 return [d0_f, d1_f, d0_b, d1_b] def modify(nodes): d0_f, d1_f, d0_b, d1_b = nodes d1_f.duplicate = 0 d1_b.duplicate = 0 d0_b.builder = 0 d1_b.builder = 0 fs1 = SCons.Node.FS.FS(test.workpath('fs1')) nodes = setup(fs1) fs1.VariantDir('d0', 'src', duplicate=0) fs1.VariantDir('d1', 'src', duplicate=1) s = list(map(str, nodes)) expect = list(map(os.path.normpath, ['src/f', 'd1/f', 'd0/b', 'd1/b'])) assert s == expect, s modify(nodes) s = list(map(str, nodes)) expect = list(map(os.path.normpath, ['src/f', 'src/f', 'd0/b', 'd1/b'])) assert s == expect, s SCons.Node.FS.save_strings(1) fs2 = SCons.Node.FS.FS(test.workpath('fs2')) nodes = setup(fs2) fs2.VariantDir('d0', 'src', duplicate=0) fs2.VariantDir('d1', 'src', duplicate=1) s = list(map(str, nodes)) expect = list(map(os.path.normpath, ['src/f', 'd1/f', 'd0/b', 'd1/b'])) assert s == expect, s modify(nodes) s = list(map(str, nodes)) expect = list(map(os.path.normpath, ['src/f', 'd1/f', 'd0/b', 'd1/b'])) assert s == expect, 'node str() not cached: %s'%s class AbsolutePathTestCase(unittest.TestCase): def test_root_lookup_equivalence(self): """Test looking up /fff vs. fff in the / directory""" test=TestCmd(workdir='') fs = SCons.Node.FS.FS('/') save_cwd = os.getcwd() try: os.chdir('/') fff1 = fs.File('fff') fff2 = fs.File('/fff') assert fff1 is fff2, "fff and /fff returned different Nodes!" finally: os.chdir(save_cwd) if __name__ == "__main__": suite = unittest.TestSuite() suite.addTest(VariantDirTestCase()) suite.addTest(find_fileTestCase()) suite.addTest(StringDirTestCase()) suite.addTest(stored_infoTestCase()) suite.addTest(has_src_builderTestCase()) suite.addTest(prepareTestCase()) suite.addTest(SConstruct_dirTestCase()) suite.addTest(clearTestCase()) suite.addTest(disambiguateTestCase()) suite.addTest(postprocessTestCase()) suite.addTest(SpecialAttrTestCase()) suite.addTest(SaveStringsTestCase()) tclasses = [ AbsolutePathTestCase, BaseTestCase, CacheDirTestCase, DirTestCase, DirBuildInfoTestCase, DirNodeInfoTestCase, EntryTestCase, FileTestCase, FileBuildInfoTestCase, FileNodeInfoTestCase, FSTestCase, GlobTestCase, RepositoryTestCase, ] for tclass in tclasses: names = unittest.getTestCaseNames(tclass, 'test_') suite.addTests(list(map(tclass, names))) if not unittest.TextTestRunner().run(suite).wasSuccessful(): sys.exit(1) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Node/PythonTests.py0000644000175000017500000001022612114661557023047 0ustar dktrkranzdktrkranz# # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Node/PythonTests.py 2013/03/03 09:48:35 garyo" import sys import unittest import SCons.Errors import SCons.Node.Python class ValueTestCase(unittest.TestCase): def test_Value(self): """Test creating a Value() object """ v1 = SCons.Node.Python.Value('a') assert v1.value == 'a', v1.value value2 = 'a' v2 = SCons.Node.Python.Value(value2) assert v2.value == value2, v2.value assert v2.value is value2, v2.value assert not v1 is v2 assert v1.value == v2.value v3 = SCons.Node.Python.Value('c', 'cb') assert v3.built_value == 'cb' def test_build(self): """Test "building" a Value Node """ class fake_executor(object): def __call__(self, node): node.write('faked') v1 = SCons.Node.Python.Value('b', 'built') v1.executor = fake_executor() v1.build() assert v1.built_value == 'built', v1.built_value v2 = SCons.Node.Python.Value('b') v2.executor = fake_executor() v2.build() assert v2.built_value == 'faked', v2.built_value def test_read(self): """Test the Value.read() method """ v1 = SCons.Node.Python.Value('a') x = v1.read() assert x == 'a', x def test_write(self): """Test the Value.write() method """ v1 = SCons.Node.Python.Value('a') assert v1.value == 'a', v1.value assert not hasattr(v1, 'built_value') v1.write('new') assert v1.value == 'a', v1.value assert v1.built_value == 'new', v1.built_value def test_get_csig(self): """Test calculating the content signature of a Value() object """ v1 = SCons.Node.Python.Value('aaa') csig = v1.get_csig(None) assert csig == 'aaa', csig v2 = SCons.Node.Python.Value(7) csig = v2.get_csig(None) assert csig == '7', csig v3 = SCons.Node.Python.Value(None) csig = v3.get_csig(None) assert csig == 'None', csig class ValueNodeInfoTestCase(unittest.TestCase): def test___init__(self): """Test ValueNodeInfo initialization""" vvv = SCons.Node.Python.Value('vvv') ni = SCons.Node.Python.ValueNodeInfo(vvv) class ValueBuildInfoTestCase(unittest.TestCase): def test___init__(self): """Test ValueBuildInfo initialization""" vvv = SCons.Node.Python.Value('vvv') bi = SCons.Node.Python.ValueBuildInfo(vvv) if __name__ == "__main__": suite = unittest.TestSuite() tclasses = [ ValueTestCase, ValueBuildInfoTestCase, ValueNodeInfoTestCase, ] for tclass in tclasses: names = unittest.getTestCaseNames(tclass, 'test_') suite.addTests(list(map(tclass, names))) if not unittest.TextTestRunner().run(suite).wasSuccessful(): sys.exit(1) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Node/Alias.py0000644000175000017500000001024612114661557021576 0ustar dktrkranzdktrkranz """scons.Node.Alias Alias nodes. This creates a hash of global Aliases (dummy targets). """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Node/Alias.py 2013/03/03 09:48:35 garyo" import collections import SCons.Errors import SCons.Node import SCons.Util class AliasNameSpace(collections.UserDict): def Alias(self, name, **kw): if isinstance(name, SCons.Node.Alias.Alias): return name try: a = self[name] except KeyError: a = SCons.Node.Alias.Alias(name, **kw) self[name] = a return a def lookup(self, name, **kw): try: return self[name] except KeyError: return None class AliasNodeInfo(SCons.Node.NodeInfoBase): current_version_id = 1 field_list = ['csig'] def str_to_node(self, s): return default_ans.Alias(s) class AliasBuildInfo(SCons.Node.BuildInfoBase): current_version_id = 1 class Alias(SCons.Node.Node): NodeInfo = AliasNodeInfo BuildInfo = AliasBuildInfo def __init__(self, name): SCons.Node.Node.__init__(self) self.name = name def str_for_display(self): return '"' + self.__str__() + '"' def __str__(self): return self.name def make_ready(self): self.get_csig() really_build = SCons.Node.Node.build is_up_to_date = SCons.Node.Node.children_are_up_to_date def is_under(self, dir): # Make Alias nodes get built regardless of # what directory scons was run from. Alias nodes # are outside the filesystem: return 1 def get_contents(self): """The contents of an alias is the concatenation of the content signatures of all its sources.""" childsigs = [n.get_csig() for n in self.children()] return ''.join(childsigs) def sconsign(self): """An Alias is not recorded in .sconsign files""" pass # # # def changed_since_last_build(self, target, prev_ni): cur_csig = self.get_csig() try: return cur_csig != prev_ni.csig except AttributeError: return 1 def build(self): """A "builder" for aliases.""" pass def convert(self): try: del self.builder except AttributeError: pass self.reset_executor() self.build = self.really_build def get_csig(self): """ Generate a node's content signature, the digested signature of its content. node - the node cache - alternate node to use for the signature cache returns - the content signature """ try: return self.ninfo.csig except AttributeError: pass contents = self.get_contents() csig = SCons.Util.MD5signature(contents) self.get_ninfo().csig = csig return csig default_ans = AliasNameSpace() SCons.Node.arg2nodes_lookups.append(default_ans.lookup) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Node/Python.py0000644000175000017500000001020712114661557022023 0ustar dktrkranzdktrkranz"""scons.Node.Python Python nodes. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Node/Python.py 2013/03/03 09:48:35 garyo" import SCons.Node class ValueNodeInfo(SCons.Node.NodeInfoBase): current_version_id = 1 field_list = ['csig'] def str_to_node(self, s): return Value(s) class ValueBuildInfo(SCons.Node.BuildInfoBase): current_version_id = 1 class Value(SCons.Node.Node): """A class for Python variables, typically passed on the command line or generated by a script, but not from a file or some other source. """ NodeInfo = ValueNodeInfo BuildInfo = ValueBuildInfo def __init__(self, value, built_value=None): SCons.Node.Node.__init__(self) self.value = value if built_value is not None: self.built_value = built_value def str_for_display(self): return repr(self.value) def __str__(self): return str(self.value) def make_ready(self): self.get_csig() def build(self, **kw): if not hasattr(self, 'built_value'): SCons.Node.Node.build(self, **kw) is_up_to_date = SCons.Node.Node.children_are_up_to_date def is_under(self, dir): # Make Value nodes get built regardless of # what directory scons was run from. Value nodes # are outside the filesystem: return 1 def write(self, built_value): """Set the value of the node.""" self.built_value = built_value def read(self): """Return the value. If necessary, the value is built.""" self.build() if not hasattr(self, 'built_value'): self.built_value = self.value return self.built_value def get_text_contents(self): """By the assumption that the node.built_value is a deterministic product of the sources, the contents of a Value are the concatenation of all the contents of its sources. As the value need not be built when get_contents() is called, we cannot use the actual node.built_value.""" ###TODO: something reasonable about universal newlines contents = str(self.value) for kid in self.children(None): contents = contents + kid.get_contents() return contents get_contents = get_text_contents ###TODO should return 'bytes' value def changed_since_last_build(self, target, prev_ni): cur_csig = self.get_csig() try: return cur_csig != prev_ni.csig except AttributeError: return 1 def get_csig(self, calc=None): """Because we're a Python value node and don't have a real timestamp, we get to ignore the calculator and just use the value contents.""" try: return self.ninfo.csig except AttributeError: pass contents = self.get_contents() self.get_ninfo().csig = contents return contents # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Node/__init__.py0000644000175000017500000013507212114661557022311 0ustar dktrkranzdktrkranz"""SCons.Node The Node package for the SCons software construction utility. This is, in many ways, the heart of SCons. A Node is where we encapsulate all of the dependency information about any thing that SCons can build, or about any thing which SCons can use to build some other thing. The canonical "thing," of course, is a file, but a Node can also represent something remote (like a web page) or something completely abstract (like an Alias). Each specific type of "thing" is specifically represented by a subclass of the Node base class: Node.FS.File for files, Node.Alias for aliases, etc. Dependency information is kept here in the base class, and information specific to files/aliases/etc. is in the subclass. The goal, if we've done this correctly, is that any type of "thing" should be able to depend on any other type of "thing." """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Node/__init__.py 2013/03/03 09:48:35 garyo" import collections import copy from itertools import chain from SCons.Debug import logInstanceCreation import SCons.Executor import SCons.Memoize import SCons.Util from SCons.Debug import Trace def classname(obj): return str(obj.__class__).split('.')[-1] # Node states # # These are in "priority" order, so that the maximum value for any # child/dependency of a node represents the state of that node if # it has no builder of its own. The canonical example is a file # system directory, which is only up to date if all of its children # were up to date. no_state = 0 pending = 1 executing = 2 up_to_date = 3 executed = 4 failed = 5 StateString = { 0 : "no_state", 1 : "pending", 2 : "executing", 3 : "up_to_date", 4 : "executed", 5 : "failed", } # controls whether implicit dependencies are cached: implicit_cache = 0 # controls whether implicit dep changes are ignored: implicit_deps_unchanged = 0 # controls whether the cached implicit deps are ignored: implicit_deps_changed = 0 # A variable that can be set to an interface-specific function be called # to annotate a Node with information about its creation. def do_nothing(node): pass Annotate = do_nothing # Classes for signature info for Nodes. class NodeInfoBase(object): """ The generic base class for signature information for a Node. Node subclasses should subclass NodeInfoBase to provide their own logic for dealing with their own Node-specific signature information. """ current_version_id = 1 def __init__(self, node=None): # Create an object attribute from the class attribute so it ends up # in the pickled data in the .sconsign file. self._version_id = self.current_version_id def update(self, node): try: field_list = self.field_list except AttributeError: return for f in field_list: try: delattr(self, f) except AttributeError: pass try: func = getattr(node, 'get_' + f) except AttributeError: pass else: setattr(self, f, func()) def convert(self, node, val): pass def merge(self, other): self.__dict__.update(other.__dict__) def format(self, field_list=None, names=0): if field_list is None: try: field_list = self.field_list except AttributeError: field_list = sorted(self.__dict__.keys()) fields = [] for field in field_list: try: f = getattr(self, field) except AttributeError: f = None f = str(f) if names: f = field + ': ' + f fields.append(f) return fields class BuildInfoBase(object): """ The generic base class for build information for a Node. This is what gets stored in a .sconsign file for each target file. It contains a NodeInfo instance for this node (signature information that's specific to the type of Node) and direct attributes for the generic build stuff we have to track: sources, explicit dependencies, implicit dependencies, and action information. """ current_version_id = 1 def __init__(self, node=None): # Create an object attribute from the class attribute so it ends up # in the pickled data in the .sconsign file. self._version_id = self.current_version_id self.bsourcesigs = [] self.bdependsigs = [] self.bimplicitsigs = [] self.bactsig = None def merge(self, other): self.__dict__.update(other.__dict__) class Node(object): """The base Node class, for entities that we know how to build, or use to build other Nodes. """ if SCons.Memoize.use_memoizer: __metaclass__ = SCons.Memoize.Memoized_Metaclass memoizer_counters = [] class Attrs(object): pass def __init__(self): if __debug__: logInstanceCreation(self, 'Node.Node') # Note that we no longer explicitly initialize a self.builder # attribute to None here. That's because the self.builder # attribute may be created on-the-fly later by a subclass (the # canonical example being a builder to fetch a file from a # source code system like CVS or Subversion). # Each list of children that we maintain is accompanied by a # dictionary used to look up quickly whether a node is already # present in the list. Empirical tests showed that it was # fastest to maintain them as side-by-side Node attributes in # this way, instead of wrapping up each list+dictionary pair in # a class. (Of course, we could always still do that in the # future if we had a good reason to...). self.sources = [] # source files used to build node self.sources_set = set() self._specific_sources = False self.depends = [] # explicit dependencies (from Depends) self.depends_set = set() self.ignore = [] # dependencies to ignore self.ignore_set = set() self.prerequisites = SCons.Util.UniqueList() self.implicit = None # implicit (scanned) dependencies (None means not scanned yet) self.waiting_parents = set() self.waiting_s_e = set() self.ref_count = 0 self.wkids = None # Kids yet to walk, when it's an array self.env = None self.state = no_state self.precious = None self.noclean = 0 self.nocache = 0 self.cached = 0 # is this node pulled from cache? self.always_build = None self.includes = None self.attributes = self.Attrs() # Generic place to stick information about the Node. self.side_effect = 0 # true iff this node is a side effect self.side_effects = [] # the side effects of building this target self.linked = 0 # is this node linked to the variant directory? self.clear_memoized_values() # Let the interface in which the build engine is embedded # annotate this Node with its own info (like a description of # what line in what file created the node, for example). Annotate(self) def disambiguate(self, must_exist=None): return self def get_suffix(self): return '' memoizer_counters.append(SCons.Memoize.CountValue('get_build_env')) def get_build_env(self): """Fetch the appropriate Environment to build this node. """ try: return self._memo['get_build_env'] except KeyError: pass result = self.get_executor().get_build_env() self._memo['get_build_env'] = result return result def get_build_scanner_path(self, scanner): """Fetch the appropriate scanner path for this node.""" return self.get_executor().get_build_scanner_path(scanner) def set_executor(self, executor): """Set the action executor for this node.""" self.executor = executor def get_executor(self, create=1): """Fetch the action executor for this node. Create one if there isn't already one, and requested to do so.""" try: executor = self.executor except AttributeError: if not create: raise try: act = self.builder.action except AttributeError: executor = SCons.Executor.Null(targets=[self]) else: executor = SCons.Executor.Executor(act, self.env or self.builder.env, [self.builder.overrides], [self], self.sources) self.executor = executor return executor def executor_cleanup(self): """Let the executor clean up any cached information.""" try: executor = self.get_executor(create=None) except AttributeError: pass else: executor.cleanup() def reset_executor(self): "Remove cached executor; forces recompute when needed." try: delattr(self, 'executor') except AttributeError: pass def push_to_cache(self): """Try to push a node into a cache """ pass def retrieve_from_cache(self): """Try to retrieve the node's content from a cache This method is called from multiple threads in a parallel build, so only do thread safe stuff here. Do thread unsafe stuff in built(). Returns true if the node was successfully retrieved. """ return 0 # # Taskmaster interface subsystem # def make_ready(self): """Get a Node ready for evaluation. This is called before the Taskmaster decides if the Node is up-to-date or not. Overriding this method allows for a Node subclass to be disambiguated if necessary, or for an implicit source builder to be attached. """ pass def prepare(self): """Prepare for this Node to be built. This is called after the Taskmaster has decided that the Node is out-of-date and must be rebuilt, but before actually calling the method to build the Node. This default implementation checks that explicit or implicit dependencies either exist or are derived, and initializes the BuildInfo structure that will hold the information about how this node is, uh, built. (The existence of source files is checked separately by the Executor, which aggregates checks for all of the targets built by a specific action.) Overriding this method allows for for a Node subclass to remove the underlying file from the file system. Note that subclass methods should call this base class method to get the child check and the BuildInfo structure. """ for d in self.depends: if d.missing(): msg = "Explicit dependency `%s' not found, needed by target `%s'." raise SCons.Errors.StopError(msg % (d, self)) if self.implicit is not None: for i in self.implicit: if i.missing(): msg = "Implicit dependency `%s' not found, needed by target `%s'." raise SCons.Errors.StopError(msg % (i, self)) self.binfo = self.get_binfo() def build(self, **kw): """Actually build the node. This is called by the Taskmaster after it's decided that the Node is out-of-date and must be rebuilt, and after the prepare() method has gotten everything, uh, prepared. This method is called from multiple threads in a parallel build, so only do thread safe stuff here. Do thread unsafe stuff in built(). """ try: self.get_executor()(self, **kw) except SCons.Errors.BuildError, e: e.node = self raise def built(self): """Called just after this node is successfully built.""" # Clear the implicit dependency caches of any Nodes # waiting for this Node to be built. for parent in self.waiting_parents: parent.implicit = None self.clear() self.ninfo.update(self) def visited(self): """Called just after this node has been visited (with or without a build).""" try: binfo = self.binfo except AttributeError: # Apparently this node doesn't need build info, so # don't bother calculating or storing it. pass else: self.ninfo.update(self) self.store_info() # # # def add_to_waiting_s_e(self, node): self.waiting_s_e.add(node) def add_to_waiting_parents(self, node): """ Returns the number of nodes added to our waiting parents list: 1 if we add a unique waiting parent, 0 if not. (Note that the returned values are intended to be used to increment a reference count, so don't think you can "clean up" this function by using True and False instead...) """ wp = self.waiting_parents if node in wp: return 0 wp.add(node) return 1 def postprocess(self): """Clean up anything we don't need to hang onto after we've been built.""" self.executor_cleanup() self.waiting_parents = set() def clear(self): """Completely clear a Node of all its cached state (so that it can be re-evaluated by interfaces that do continuous integration builds). """ # The del_binfo() call here isn't necessary for normal execution, # but is for interactive mode, where we might rebuild the same # target and need to start from scratch. self.del_binfo() self.clear_memoized_values() self.ninfo = self.new_ninfo() self.executor_cleanup() try: delattr(self, '_calculated_sig') except AttributeError: pass self.includes = None def clear_memoized_values(self): self._memo = {} def builder_set(self, builder): self.builder = builder try: del self.executor except AttributeError: pass def has_builder(self): """Return whether this Node has a builder or not. In Boolean tests, this turns out to be a *lot* more efficient than simply examining the builder attribute directly ("if node.builder: ..."). When the builder attribute is examined directly, it ends up calling __getattr__ for both the __len__ and __nonzero__ attributes on instances of our Builder Proxy class(es), generating a bazillion extra calls and slowing things down immensely. """ try: b = self.builder except AttributeError: # There was no explicit builder for this Node, so initialize # the self.builder attribute to None now. b = self.builder = None return b is not None def set_explicit(self, is_explicit): self.is_explicit = is_explicit def has_explicit_builder(self): """Return whether this Node has an explicit builder This allows an internal Builder created by SCons to be marked non-explicit, so that it can be overridden by an explicit builder that the user supplies (the canonical example being directories).""" try: return self.is_explicit except AttributeError: self.is_explicit = None return self.is_explicit def get_builder(self, default_builder=None): """Return the set builder, or a specified default value""" try: return self.builder except AttributeError: return default_builder multiple_side_effect_has_builder = has_builder def is_derived(self): """ Returns true iff this node is derived (i.e. built). This should return true only for nodes whose path should be in the variant directory when duplicate=0 and should contribute their build signatures when they are used as source files to other derived files. For example: source with source builders are not derived in this sense, and hence should not return true. """ return self.has_builder() or self.side_effect def alter_targets(self): """Return a list of alternate targets for this Node. """ return [], None def get_found_includes(self, env, scanner, path): """Return the scanned include lines (implicit dependencies) found in this node. The default is no implicit dependencies. We expect this method to be overridden by any subclass that can be scanned for implicit dependencies. """ return [] def get_implicit_deps(self, env, scanner, path): """Return a list of implicit dependencies for this node. This method exists to handle recursive invocation of the scanner on the implicit dependencies returned by the scanner, if the scanner's recursive flag says that we should. """ if not scanner: return [] # Give the scanner a chance to select a more specific scanner # for this Node. #scanner = scanner.select(self) nodes = [self] seen = {} seen[self] = 1 deps = [] while nodes: n = nodes.pop(0) d = [x for x in n.get_found_includes(env, scanner, path) if x not in seen] if d: deps.extend(d) for n in d: seen[n] = 1 nodes.extend(scanner.recurse_nodes(d)) return deps def get_env_scanner(self, env, kw={}): return env.get_scanner(self.scanner_key()) def get_target_scanner(self): return self.builder.target_scanner def get_source_scanner(self, node): """Fetch the source scanner for the specified node NOTE: "self" is the target being built, "node" is the source file for which we want to fetch the scanner. Implies self.has_builder() is true; again, expect to only be called from locations where this is already verified. This function may be called very often; it attempts to cache the scanner found to improve performance. """ scanner = None try: scanner = self.builder.source_scanner except AttributeError: pass if not scanner: # The builder didn't have an explicit scanner, so go look up # a scanner from env['SCANNERS'] based on the node's scanner # key (usually the file extension). scanner = self.get_env_scanner(self.get_build_env()) if scanner: scanner = scanner.select(node) return scanner def add_to_implicit(self, deps): if not hasattr(self, 'implicit') or self.implicit is None: self.implicit = [] self.implicit_set = set() self._children_reset() self._add_child(self.implicit, self.implicit_set, deps) def scan(self): """Scan this node's dependents for implicit dependencies.""" # Don't bother scanning non-derived files, because we don't # care what their dependencies are. # Don't scan again, if we already have scanned. if self.implicit is not None: return self.implicit = [] self.implicit_set = set() self._children_reset() if not self.has_builder(): return build_env = self.get_build_env() executor = self.get_executor() # Here's where we implement --implicit-cache. if implicit_cache and not implicit_deps_changed: implicit = self.get_stored_implicit() if implicit is not None: # We now add the implicit dependencies returned from the # stored .sconsign entry to have already been converted # to Nodes for us. (We used to run them through a # source_factory function here.) # Update all of the targets with them. This # essentially short-circuits an N*M scan of the # sources for each individual target, which is a hell # of a lot more efficient. for tgt in executor.get_all_targets(): tgt.add_to_implicit(implicit) if implicit_deps_unchanged or self.is_up_to_date(): return # one of this node's sources has changed, # so we must recalculate the implicit deps for all targets for tgt in executor.get_all_targets(): tgt.implicit = [] tgt.implicit_set = set() # Have the executor scan the sources. executor.scan_sources(self.builder.source_scanner) # If there's a target scanner, have the executor scan the target # node itself and associated targets that might be built. scanner = self.get_target_scanner() if scanner: executor.scan_targets(scanner) def scanner_key(self): return None def select_scanner(self, scanner): """Selects a scanner for this Node. This is a separate method so it can be overridden by Node subclasses (specifically, Node.FS.Dir) that *must* use their own Scanner and don't select one the Scanner.Selector that's configured for the target. """ return scanner.select(self) def env_set(self, env, safe=0): if safe and self.env: return self.env = env # # SIGNATURE SUBSYSTEM # NodeInfo = NodeInfoBase BuildInfo = BuildInfoBase def new_ninfo(self): ninfo = self.NodeInfo(self) return ninfo def get_ninfo(self): try: return self.ninfo except AttributeError: self.ninfo = self.new_ninfo() return self.ninfo def new_binfo(self): binfo = self.BuildInfo(self) return binfo def get_binfo(self): """ Fetch a node's build information. node - the node whose sources will be collected cache - alternate node to use for the signature cache returns - the build signature This no longer handles the recursive descent of the node's children's signatures. We expect that they're already built and updated by someone else, if that's what's wanted. """ try: return self.binfo except AttributeError: pass binfo = self.new_binfo() self.binfo = binfo executor = self.get_executor() ignore_set = self.ignore_set if self.has_builder(): binfo.bact = str(executor) binfo.bactsig = SCons.Util.MD5signature(executor.get_contents()) if self._specific_sources: sources = [] for s in self.sources: if s not in ignore_set: sources.append(s) else: sources = executor.get_unignored_sources(self, self.ignore) seen = set() bsources = [] bsourcesigs = [] for s in sources: if not s in seen: seen.add(s) bsources.append(s) bsourcesigs.append(s.get_ninfo()) binfo.bsources = bsources binfo.bsourcesigs = bsourcesigs depends = self.depends dependsigs = [] for d in depends: if d not in ignore_set: dependsigs.append(d.get_ninfo()) binfo.bdepends = depends binfo.bdependsigs = dependsigs implicit = self.implicit or [] implicitsigs = [] for i in implicit: if i not in ignore_set: implicitsigs.append(i.get_ninfo()) binfo.bimplicit = implicit binfo.bimplicitsigs = implicitsigs return binfo def del_binfo(self): """Delete the build info from this node.""" try: delattr(self, 'binfo') except AttributeError: pass def get_csig(self): try: return self.ninfo.csig except AttributeError: ninfo = self.get_ninfo() ninfo.csig = SCons.Util.MD5signature(self.get_contents()) return self.ninfo.csig def get_cachedir_csig(self): return self.get_csig() def store_info(self): """Make the build signature permanent (that is, store it in the .sconsign file or equivalent).""" pass def do_not_store_info(self): pass def get_stored_info(self): return None def get_stored_implicit(self): """Fetch the stored implicit dependencies""" return None # # # def set_precious(self, precious = 1): """Set the Node's precious value.""" self.precious = precious def set_noclean(self, noclean = 1): """Set the Node's noclean value.""" # Make sure noclean is an integer so the --debug=stree # output in Util.py can use it as an index. self.noclean = noclean and 1 or 0 def set_nocache(self, nocache = 1): """Set the Node's nocache value.""" # Make sure nocache is an integer so the --debug=stree # output in Util.py can use it as an index. self.nocache = nocache and 1 or 0 def set_always_build(self, always_build = 1): """Set the Node's always_build value.""" self.always_build = always_build def exists(self): """Does this node exists?""" # All node exist by default: return 1 def rexists(self): """Does this node exist locally or in a repositiory?""" # There are no repositories by default: return self.exists() def missing(self): return not self.is_derived() and \ not self.linked and \ not self.rexists() def remove(self): """Remove this Node: no-op by default.""" return None def add_dependency(self, depend): """Adds dependencies.""" try: self._add_child(self.depends, self.depends_set, depend) except TypeError, e: e = e.args[0] if SCons.Util.is_List(e): s = list(map(str, e)) else: s = str(e) raise SCons.Errors.UserError("attempted to add a non-Node dependency to %s:\n\t%s is a %s, not a Node" % (str(self), s, type(e))) def add_prerequisite(self, prerequisite): """Adds prerequisites""" self.prerequisites.extend(prerequisite) self._children_reset() def add_ignore(self, depend): """Adds dependencies to ignore.""" try: self._add_child(self.ignore, self.ignore_set, depend) except TypeError, e: e = e.args[0] if SCons.Util.is_List(e): s = list(map(str, e)) else: s = str(e) raise SCons.Errors.UserError("attempted to ignore a non-Node dependency of %s:\n\t%s is a %s, not a Node" % (str(self), s, type(e))) def add_source(self, source): """Adds sources.""" if self._specific_sources: return try: self._add_child(self.sources, self.sources_set, source) except TypeError, e: e = e.args[0] if SCons.Util.is_List(e): s = list(map(str, e)) else: s = str(e) raise SCons.Errors.UserError("attempted to add a non-Node as source of %s:\n\t%s is a %s, not a Node" % (str(self), s, type(e))) def _add_child(self, collection, set, child): """Adds 'child' to 'collection', first checking 'set' to see if it's already present.""" #if type(child) is not type([]): # child = [child] #for c in child: # if not isinstance(c, Node): # raise TypeError, c added = None for c in child: if c not in set: set.add(c) collection.append(c) added = 1 if added: self._children_reset() def set_specific_source(self, source): self.add_source(source) self._specific_sources = True def add_wkid(self, wkid): """Add a node to the list of kids waiting to be evaluated""" if self.wkids is not None: self.wkids.append(wkid) def _children_reset(self): self.clear_memoized_values() # We need to let the Executor clear out any calculated # build info that it's cached so we can re-calculate it. self.executor_cleanup() memoizer_counters.append(SCons.Memoize.CountValue('_children_get')) def _children_get(self): try: return self._memo['children_get'] except KeyError: pass # The return list may contain duplicate Nodes, especially in # source trees where there are a lot of repeated #includes # of a tangle of .h files. Profiling shows, however, that # eliminating the duplicates with a brute-force approach that # preserves the order (that is, something like: # # u = [] # for n in list: # if n not in u: # u.append(n)" # # takes more cycles than just letting the underlying methods # hand back cached values if a Node's information is requested # multiple times. (Other methods of removing duplicates, like # using dictionary keys, lose the order, and the only ordered # dictionary patterns I found all ended up using "not in" # internally anyway...) if self.ignore_set: if self.implicit is None: iter = chain(self.sources,self.depends) else: iter = chain(self.sources, self.depends, self.implicit) children = [] for i in iter: if i not in self.ignore_set: children.append(i) else: if self.implicit is None: children = self.sources + self.depends else: children = self.sources + self.depends + self.implicit self._memo['children_get'] = children return children def all_children(self, scan=1): """Return a list of all the node's direct children.""" if scan: self.scan() # The return list may contain duplicate Nodes, especially in # source trees where there are a lot of repeated #includes # of a tangle of .h files. Profiling shows, however, that # eliminating the duplicates with a brute-force approach that # preserves the order (that is, something like: # # u = [] # for n in list: # if n not in u: # u.append(n)" # # takes more cycles than just letting the underlying methods # hand back cached values if a Node's information is requested # multiple times. (Other methods of removing duplicates, like # using dictionary keys, lose the order, and the only ordered # dictionary patterns I found all ended up using "not in" # internally anyway...) if self.implicit is None: return self.sources + self.depends else: return self.sources + self.depends + self.implicit def children(self, scan=1): """Return a list of the node's direct children, minus those that are ignored by this node.""" if scan: self.scan() return self._children_get() def set_state(self, state): self.state = state def get_state(self): return self.state def state_has_changed(self, target, prev_ni): return (self.state != SCons.Node.up_to_date) def get_env(self): env = self.env if not env: import SCons.Defaults env = SCons.Defaults.DefaultEnvironment() return env def changed_since_last_build(self, target, prev_ni): """ Must be overridden in a specific subclass to return True if this Node (a dependency) has changed since the last time it was used to build the specified target. prev_ni is this Node's state (for example, its file timestamp, length, maybe content signature) as of the last time the target was built. Note that this method is called through the dependency, not the target, because a dependency Node must be able to use its own logic to decide if it changed. For example, File Nodes need to obey if we're configured to use timestamps, but Python Value Nodes never use timestamps and always use the content. If this method were called through the target, then each Node's implementation of this method would have to have more complicated logic to handle all the different Node types on which it might depend. """ raise NotImplementedError def Decider(self, function): SCons.Util.AddMethod(self, function, 'changed_since_last_build') def changed(self, node=None): """ Returns if the node is up-to-date with respect to the BuildInfo stored last time it was built. The default behavior is to compare it against our own previously stored BuildInfo, but the stored BuildInfo from another Node (typically one in a Repository) can be used instead. Note that we now *always* check every dependency. We used to short-circuit the check by returning as soon as we detected any difference, but we now rely on checking every dependency to make sure that any necessary Node information (for example, the content signature of an #included .h file) is updated. """ t = 0 if t: Trace('changed(%s [%s], %s)' % (self, classname(self), node)) if node is None: node = self result = False bi = node.get_stored_info().binfo then = bi.bsourcesigs + bi.bdependsigs + bi.bimplicitsigs children = self.children() diff = len(children) - len(then) if diff: # The old and new dependency lists are different lengths. # This always indicates that the Node must be rebuilt. # We also extend the old dependency list with enough None # entries to equal the new dependency list, for the benefit # of the loop below that updates node information. then.extend([None] * diff) if t: Trace(': old %s new %s' % (len(then), len(children))) result = True for child, prev_ni in zip(children, then): if child.changed_since_last_build(self, prev_ni): if t: Trace(': %s changed' % child) result = True contents = self.get_executor().get_contents() if self.has_builder(): import SCons.Util newsig = SCons.Util.MD5signature(contents) if bi.bactsig != newsig: if t: Trace(': bactsig %s != newsig %s' % (bi.bactsig, newsig)) result = True if not result: if t: Trace(': up to date') if t: Trace('\n') return result def is_up_to_date(self): """Default check for whether the Node is current: unknown Node subtypes are always out of date, so they will always get built.""" return None def children_are_up_to_date(self): """Alternate check for whether the Node is current: If all of our children were up-to-date, then this Node was up-to-date, too. The SCons.Node.Alias and SCons.Node.Python.Value subclasses rebind their current() method to this method.""" # Allow the children to calculate their signatures. self.binfo = self.get_binfo() if self.always_build: return None state = 0 for kid in self.children(None): s = kid.get_state() if s and (not state or s > state): state = s return (state == 0 or state == SCons.Node.up_to_date) def is_literal(self): """Always pass the string representation of a Node to the command interpreter literally.""" return 1 def render_include_tree(self): """ Return a text representation, suitable for displaying to the user, of the include tree for the sources of this node. """ if self.is_derived() and self.env: env = self.get_build_env() for s in self.sources: scanner = self.get_source_scanner(s) if scanner: path = self.get_build_scanner_path(scanner) else: path = None def f(node, env=env, scanner=scanner, path=path): return node.get_found_includes(env, scanner, path) return SCons.Util.render_tree(s, f, 1) else: return None def get_abspath(self): """ Return an absolute path to the Node. This will return simply str(Node) by default, but for Node types that have a concept of relative path, this might return something different. """ return str(self) def for_signature(self): """ Return a string representation of the Node that will always be the same for this particular Node, no matter what. This is by contrast to the __str__() method, which might, for instance, return a relative path for a file Node. The purpose of this method is to generate a value to be used in signature calculation for the command line used to build a target, and we use this method instead of str() to avoid unnecessary rebuilds. This method does not need to return something that would actually work in a command line; it can return any kind of nonsense, so long as it does not change. """ return str(self) def get_string(self, for_signature): """This is a convenience function designed primarily to be used in command generators (i.e., CommandGeneratorActions or Environment variables that are callable), which are called with a for_signature argument that is nonzero if the command generator is being called to generate a signature for the command line, which determines if we should rebuild or not. Such command generators should use this method in preference to str(Node) when converting a Node to a string, passing in the for_signature parameter, such that we will call Node.for_signature() or str(Node) properly, depending on whether we are calculating a signature or actually constructing a command line.""" if for_signature: return self.for_signature() return str(self) def get_subst_proxy(self): """ This method is expected to return an object that will function exactly like this Node, except that it implements any additional special features that we would like to be in effect for Environment variable substitution. The principle use is that some Nodes would like to implement a __getattr__() method, but putting that in the Node type itself has a tendency to kill performance. We instead put it in a proxy and return it from this method. It is legal for this method to return self if no new functionality is needed for Environment substitution. """ return self def explain(self): 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 = old.binfo old.prepare_dependencies() try: 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() new_bkids = new.bsources + new.bdepends + new.bimplicit new_bkidsigs = new.bsourcesigs + new.bdependsigs + new.bimplicitsigs 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 # report them relative to the top-level SConstruct directory, # so we only print them after running them through this lambda # to turn them into the right relative Node and then return # its string. def stringify( s, E=self.dir.Entry ) : if hasattr( s, 'dir' ) : return str(E(s)) return str(s) lines = [] removed = [x for x in old_bkids if not x in new_bkids] if removed: removed = list(map(stringify, removed)) fmt = "`%s' is no longer a dependency\n" lines.extend([fmt % s for s in removed]) for k in new_bkids: if not k in old_bkids: lines.append("`%s' is a new dependency\n" % stringify(k)) elif k.changed_since_last_build(self, osig[k]): lines.append("`%s' changed\n" % stringify(k)) if len(lines) == 0 and old_bkids != new_bkids: lines.append("the dependency order changed:\n" + "%sold: %s\n" % (' '*15, list(map(stringify, old_bkids))) + "%snew: %s\n" % (' '*15, list(map(stringify, new_bkids)))) if len(lines) == 0: def fmt_with_title(title, strlines): lines = strlines.split('\n') sep = '\n' + ' '*(15 + len(title)) return ' '*15 + title + sep.join(lines) + '\n' if old.bactsig != new.bactsig: if old.bact == new.bact: lines.append("the contents of the build action changed\n" + fmt_with_title('action: ', new.bact)) else: lines.append("the build action changed:\n" + fmt_with_title('old: ', old.bact) + fmt_with_title('new: ', new.bact)) if len(lines) == 0: return "rebuilding `%s' for unknown reasons\n" % self preamble = "rebuilding `%s' because" % self if len(lines) == 1: return "%s %s" % (preamble, lines[0]) else: lines = ["%s:\n" % preamble] + lines return ( ' '*11).join(lines) class NodeList(collections.UserList): def __str__(self): return str(list(map(str, self.data))) def get_children(node, parent): return node.children() def ignore_cycle(node, stack): pass def do_nothing(node, parent): pass class Walker(object): """An iterator for walking a Node tree. This is depth-first, children are visited before the parent. The Walker object can be initialized with any node, and returns the next node on the descent with each get_next() call. 'kids_func' is an optional function that will be called to get the children of a node instead of calling 'children'. 'cycle_func' is an optional function that will be called when a cycle is detected. This class does not get caught in node cycles caused, for example, by C header file include loops. """ def __init__(self, node, kids_func=get_children, cycle_func=ignore_cycle, eval_func=do_nothing): self.kids_func = kids_func self.cycle_func = cycle_func self.eval_func = eval_func node.wkids = copy.copy(kids_func(node, None)) self.stack = [node] self.history = {} # used to efficiently detect and avoid cycles self.history[node] = None def get_next(self): """Return the next node for this walk of the tree. This function is intentionally iterative, not recursive, to sidestep any issues of stack size limitations. """ while self.stack: if self.stack[-1].wkids: node = self.stack[-1].wkids.pop(0) if not self.stack[-1].wkids: self.stack[-1].wkids = None if node in self.history: self.cycle_func(node, self.stack) else: node.wkids = copy.copy(self.kids_func(node, self.stack[-1])) self.stack.append(node) self.history[node] = None else: node = self.stack.pop() del self.history[node] if node: if self.stack: parent = self.stack[-1] else: parent = None self.eval_func(node, parent) return node return None def is_done(self): return not self.stack arg2nodes_lookups = [] # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/exitfuncs.py0000644000175000017500000000417412114661560021665 0ustar dktrkranzdktrkranz"""SCons.exitfuncs Register functions which are executed when SCons exits for any reason. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/exitfuncs.py 2013/03/03 09:48:35 garyo" import atexit _exithandlers = [] def _run_exitfuncs(): """run any registered exit functions _exithandlers is traversed in reverse order so functions are executed last in, first out. """ while _exithandlers: func, targs, kargs = _exithandlers.pop() func(*targs, **kargs) def register(func, *targs, **kargs): """register a function to be executed upon normal program termination func - function to be called at exit targs - optional arguments to pass to func kargs - optional keyword arguments to pass to func """ _exithandlers.append((func, targs, kargs)) # make our exit function get run by python when it exits atexit.register(_run_exitfuncs) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/ErrorsTests.py0000644000175000017500000000772712114661557022171 0ustar dktrkranzdktrkranz# # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/ErrorsTests.py 2013/03/03 09:48:35 garyo" import sys import unittest import SCons.Errors class ErrorsTestCase(unittest.TestCase): def test_BuildError(self): """Test the BuildError exception.""" try: raise SCons.Errors.BuildError( errstr = "foo", status=57, filename="file", exc_info=(1,2,3), node = "n", executor="e", action="a", command="c") except SCons.Errors.BuildError, e: assert e.errstr == "foo" assert e.status == 57 assert e.exitstatus == 2, e.exitstatus assert e.filename == "file" assert e.exc_info == (1,2,3) assert e.node == "n" assert e.executor == "e" assert e.action == "a" assert e.command == "c" try: raise SCons.Errors.BuildError("n", "foo", 57, 3, "file", "e", "a", "c", (1,2,3)) except SCons.Errors.BuildError, e: assert e.errstr == "foo", e.errstr assert e.status == 57, e.status assert e.exitstatus == 3, e.exitstatus assert e.filename == "file", e.filename assert e.exc_info == (1,2,3), e.exc_info assert e.node == "n" assert e.executor == "e" assert e.action == "a" assert e.command == "c" try: raise SCons.Errors.BuildError() except SCons.Errors.BuildError, e: assert e.errstr == "Unknown error" assert e.status == 2 assert e.exitstatus == 2 assert e.filename is None assert e.exc_info == (None, None, None) assert e.node is None assert e.executor is None assert e.action is None assert e.command is None def test_InternalError(self): """Test the InternalError exception.""" try: raise SCons.Errors.InternalError("test internal error") except SCons.Errors.InternalError, e: assert e.args == ("test internal error",) def test_UserError(self): """Test the UserError exception.""" try: raise SCons.Errors.UserError("test user error") except SCons.Errors.UserError, e: assert e.args == ("test user error",) def test_ExplicitExit(self): """Test the ExplicitExit exception.""" try: raise SCons.Errors.ExplicitExit("node") except SCons.Errors.ExplicitExit, e: assert e.node == "node" if __name__ == "__main__": suite = unittest.makeSuite(ErrorsTestCase, 'test_') if not unittest.TextTestRunner().run(suite).wasSuccessful(): sys.exit(1) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/EnvironmentTests.py0000644000175000017500000043373012114661557023216 0ustar dktrkranzdktrkranz# # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/EnvironmentTests.py 2013/03/03 09:48:35 garyo" import SCons.compat import copy import io import os import sys import TestCmd import unittest from collections import UserDict as UD, UserList as UL from SCons.Environment import * import SCons.Warnings def diff_env(env1, env2): s1 = "env1 = {\n" s2 = "env2 = {\n" d = {} for k in list(env1._dict.keys()) + list(env2._dict.keys()): d[k] = None for k in sorted(d.keys()): if k in env1: if k in env2: if env1[k] != env2[k]: s1 = s1 + " " + repr(k) + " : " + repr(env1[k]) + "\n" s2 = s2 + " " + repr(k) + " : " + repr(env2[k]) + "\n" else: s1 = s1 + " " + repr(k) + " : " + repr(env1[k]) + "\n" elif k in env2: s2 = s2 + " " + repr(k) + " : " + repr(env2[k]) + "\n" s1 = s1 + "}\n" s2 = s2 + "}\n" return s1 + s2 def diff_dict(d1, d2): s1 = "d1 = {\n" s2 = "d2 = {\n" d = {} for k in list(d1.keys()) + list(d2.keys()): d[k] = None for k in sorted(d.keys()): if k in d1: if k in d2: if d1[k] != d2[k]: s1 = s1 + " " + repr(k) + " : " + repr(d1[k]) + "\n" s2 = s2 + " " + repr(k) + " : " + repr(d2[k]) + "\n" else: s1 = s1 + " " + repr(k) + " : " + repr(d1[k]) + "\n" elif k in env2: s2 = s2 + " " + repr(k) + " : " + repr(d2[k]) + "\n" s1 = s1 + "}\n" s2 = s2 + "}\n" return s1 + s2 called_it = {} built_it = {} class Builder(SCons.Builder.BuilderBase): """A dummy Builder class for testing purposes. "Building" a target is simply setting a value in the dictionary. """ def __init__(self, name = None): self.name = name def __call__(self, env, target=None, source=None, **kw): global called_it called_it['target'] = target called_it['source'] = source called_it.update(kw) def execute(self, target = None, **kw): global built_it built_it[target] = 1 scanned_it = {} class Scanner(object): """A dummy Scanner class for testing purposes. "Scanning" a target is simply setting a value in the dictionary. """ def __init__(self, name, skeys=[]): self.name = name self.skeys = skeys def __call__(self, filename): global scanned_it scanned_it[filename] = 1 def __cmp__(self, other): try: return cmp(self.__dict__, other.__dict__) except AttributeError: return 1 def get_skeys(self, env): return self.skeys def __str__(self): return self.name class CLVar(UL): def __init__(self, seq): if isinstance(seq, str): seq = seq.split() UL.__init__(self, seq) def __add__(self, other): return UL.__add__(self, CLVar(other)) def __radd__(self, other): return UL.__radd__(self, CLVar(other)) def __coerce__(self, other): return (self, CLVar(other)) class DummyNode(object): def __init__(self, name): self.name = name def __str__(self): return self.name def rfile(self): return self def get_subst_proxy(self): return self def test_tool( env ): env['_F77INCFLAGS'] = '$( ${_concat(INCPREFIX, F77PATH, INCSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)' class TestEnvironmentFixture(object): def TestEnvironment(self, *args, **kw): if not kw or 'tools' not in kw: kw['tools'] = [test_tool] default_keys = { 'CC' : 'cc', 'CCFLAGS' : '-DNDEBUG', 'ENV' : { 'TMP' : '/tmp' } } for key, value in default_keys.items(): if key not in kw: kw[key] = value if 'BUILDERS' not in kw: static_obj = SCons.Builder.Builder(action = {}, emitter = {}, suffix = '.o', single_source = 1) kw['BUILDERS'] = {'Object' : static_obj} static_obj.add_action('.cpp', 'fake action') env = Environment(*args, **kw) return env class SubstitutionTestCase(unittest.TestCase): def test___init__(self): """Test initializing a SubstitutionEnvironment """ env = SubstitutionEnvironment() assert '__env__' not in env def test___cmp__(self): """Test comparing SubstitutionEnvironments """ env1 = SubstitutionEnvironment(XXX = 'x') env2 = SubstitutionEnvironment(XXX = 'x') env3 = SubstitutionEnvironment(XXX = 'xxx') env4 = SubstitutionEnvironment(XXX = 'x', YYY = 'x') assert env1 == env2 assert env1 != env3 assert env1 != env4 def test___delitem__(self): """Test deleting a variable from a SubstitutionEnvironment """ env1 = SubstitutionEnvironment(XXX = 'x', YYY = 'y') env2 = SubstitutionEnvironment(XXX = 'x') del env1['YYY'] assert env1 == env2 def test___getitem__(self): """Test fetching a variable from a SubstitutionEnvironment """ env = SubstitutionEnvironment(XXX = 'x') assert env['XXX'] == 'x', env['XXX'] def test___setitem__(self): """Test setting a variable in a SubstitutionEnvironment """ env1 = SubstitutionEnvironment(XXX = 'x') env2 = SubstitutionEnvironment(XXX = 'x', YYY = 'y') env1['YYY'] = 'y' assert env1 == env2 def test_get(self): """Test the SubstitutionEnvironment get() method """ env = SubstitutionEnvironment(XXX = 'x') assert env.get('XXX') == 'x', env.get('XXX') assert env.get('YYY') is None, env.get('YYY') def test_has_key(self): """Test the SubstitutionEnvironment has_key() method """ env = SubstitutionEnvironment(XXX = 'x') assert 'XXX' in env assert 'YYY' not in env def test_contains(self): """Test the SubstitutionEnvironment __contains__() method """ env = SubstitutionEnvironment(XXX = 'x') assert 'XXX' in env assert not 'YYY' in env def test_items(self): """Test the SubstitutionEnvironment items() method """ env = SubstitutionEnvironment(XXX = 'x', YYY = 'y') items = list(env.items()) assert items == [('XXX','x'), ('YYY','y')], items def test_arg2nodes(self): """Test the arg2nodes method """ env = SubstitutionEnvironment() dict = {} class X(SCons.Node.Node): pass def Factory(name, directory = None, create = 1, dict=dict, X=X): if name not in dict: dict[name] = X() dict[name].name = name return dict[name] nodes = env.arg2nodes("Util.py UtilTests.py", Factory) assert len(nodes) == 1, nodes assert isinstance(nodes[0], X) assert nodes[0].name == "Util.py UtilTests.py" try: unicode except NameError: pass else: code = """if 1: nodes = env.arg2nodes(u"Util.py UtilTests.py", Factory) assert len(nodes) == 1, nodes assert isinstance(nodes[0], X) assert nodes[0].name == u"Util.py UtilTests.py" \n""" exec code in globals(), locals() nodes = env.arg2nodes(["Util.py", "UtilTests.py"], Factory) assert len(nodes) == 2, nodes assert isinstance(nodes[0], X) assert isinstance(nodes[1], X) assert nodes[0].name == "Util.py" assert nodes[1].name == "UtilTests.py" n1 = Factory("Util.py") nodes = env.arg2nodes([n1, "UtilTests.py"], Factory) assert len(nodes) == 2, nodes assert isinstance(nodes[0], X) assert isinstance(nodes[1], X) assert nodes[0].name == "Util.py" assert nodes[1].name == "UtilTests.py" class SConsNode(SCons.Node.Node): pass nodes = env.arg2nodes(SConsNode()) assert len(nodes) == 1, nodes assert isinstance(nodes[0], SConsNode), node class OtherNode(object): pass nodes = env.arg2nodes(OtherNode()) assert len(nodes) == 1, nodes assert isinstance(nodes[0], OtherNode), node def lookup_a(str, F=Factory): if str[0] == 'a': n = F(str) n.a = 1 return n else: return None def lookup_b(str, F=Factory): if str[0] == 'b': n = F(str) n.b = 1 return n else: return None env_ll = SubstitutionEnvironment() env_ll.lookup_list = [lookup_a, lookup_b] nodes = env_ll.arg2nodes(['aaa', 'bbb', 'ccc'], Factory) assert len(nodes) == 3, nodes assert nodes[0].name == 'aaa', nodes[0] assert nodes[0].a == 1, nodes[0] assert not hasattr(nodes[0], 'b'), nodes[0] assert nodes[1].name == 'bbb' assert not hasattr(nodes[1], 'a'), nodes[1] assert nodes[1].b == 1, nodes[1] assert nodes[2].name == 'ccc' assert not hasattr(nodes[2], 'a'), nodes[1] assert not hasattr(nodes[2], 'b'), nodes[1] def lookup_bbbb(str, F=Factory): if str == 'bbbb': n = F(str) n.bbbb = 1 return n else: return None def lookup_c(str, F=Factory): if str[0] == 'c': n = F(str) n.c = 1 return n else: return None nodes = env.arg2nodes(['bbbb', 'ccc'], Factory, [lookup_c, lookup_bbbb, lookup_b]) assert len(nodes) == 2, nodes assert nodes[0].name == 'bbbb' assert not hasattr(nodes[0], 'a'), nodes[1] assert not hasattr(nodes[0], 'b'), nodes[1] assert nodes[0].bbbb == 1, nodes[1] assert not hasattr(nodes[0], 'c'), nodes[0] assert nodes[1].name == 'ccc' assert not hasattr(nodes[1], 'a'), nodes[1] assert not hasattr(nodes[1], 'b'), nodes[1] 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 = [n.name for n in nodes] assert names == ['t1-a', 's1-b', 't2-c', 's2-d'], names def test_gvars(self): """Test the base class gvars() method""" env = SubstitutionEnvironment() gvars = env.gvars() assert gvars == {}, gvars def test_lvars(self): """Test the base class lvars() method""" env = SubstitutionEnvironment() lvars = env.lvars() assert lvars == {}, lvars def test_subst(self): """Test substituting construction variables within strings Check various combinations, including recursive expansion of variables into other variables. """ env = SubstitutionEnvironment(AAA = 'a', BBB = 'b') mystr = env.subst("$AAA ${AAA}A $BBBB $BBB") assert mystr == "a aA b", mystr # Changed the tests below to reflect a bug fix in # subst() env = SubstitutionEnvironment(AAA = '$BBB', BBB = 'b', BBBA = 'foo') mystr = env.subst("$AAA ${AAA}A ${AAA}B $BBB") assert mystr == "b bA bB b", mystr env = SubstitutionEnvironment(AAA = '$BBB', BBB = '$CCC', CCC = 'c') mystr = env.subst("$AAA ${AAA}A ${AAA}B $BBB") assert mystr == "c cA cB c", mystr # Lists: env = SubstitutionEnvironment(AAA = ['a', 'aa', 'aaa']) mystr = env.subst("$AAA") assert mystr == "a aa aaa", mystr # Tuples: env = SubstitutionEnvironment(AAA = ('a', 'aa', 'aaa')) mystr = env.subst("$AAA") assert mystr == "a aa aaa", mystr t1 = DummyNode('t1') t2 = DummyNode('t2') s1 = DummyNode('s1') s2 = DummyNode('s2') env = SubstitutionEnvironment(AAA = 'aaa') s = env.subst('$AAA $TARGET $SOURCES', target=[t1, t2], source=[s1, s2]) assert s == "aaa t1 s1 s2", s s = env.subst('$AAA $TARGETS $SOURCE', target=[t1, t2], source=[s1, s2]) assert s == "aaa t1 t2 s1", s # Test callables in the SubstitutionEnvironment def foo(target, source, env, for_signature): assert str(target) == 't', target assert str(source) == 's', source return env["FOO"] env = SubstitutionEnvironment(BAR=foo, FOO='baz') t = DummyNode('t') s = DummyNode('s') subst = env.subst('test $BAR', target=t, source=s) assert subst == 'test baz', subst # Test not calling callables in the SubstitutionEnvironment if 0: # This will take some serious surgery to subst() and # subst_list(), so just leave these tests out until we can # do that. def bar(arg): pass env = SubstitutionEnvironment(BAR=bar, FOO='$BAR') subst = env.subst('$BAR', call=None) assert subst is bar, subst subst = env.subst('$FOO', call=None) assert subst is bar, subst def test_subst_kw(self): """Test substituting construction variables within dictionaries""" env = SubstitutionEnvironment(AAA = 'a', BBB = 'b') kw = env.subst_kw({'$AAA' : 'aaa', 'bbb' : '$BBB'}) assert len(kw) == 2, kw assert kw['a'] == 'aaa', kw['a'] assert kw['bbb'] == 'b', kw['bbb'] def test_subst_list(self): """Test substituting construction variables in command lists """ env = SubstitutionEnvironment(AAA = 'a', BBB = 'b') l = env.subst_list("$AAA ${AAA}A $BBBB $BBB") assert l == [["a", "aA", "b"]], l # Changed the tests below to reflect a bug fix in # subst() env = SubstitutionEnvironment(AAA = '$BBB', BBB = 'b', BBBA = 'foo') l = env.subst_list("$AAA ${AAA}A ${AAA}B $BBB") assert l == [["b", "bA", "bB", "b"]], l env = SubstitutionEnvironment(AAA = '$BBB', BBB = '$CCC', CCC = 'c') l = env.subst_list("$AAA ${AAA}A ${AAA}B $BBB") assert l == [["c", "cA", "cB", "c"]], mystr env = SubstitutionEnvironment(AAA = '$BBB', BBB = '$CCC', CCC = [ 'a', 'b\nc' ]) lst = env.subst_list([ "$AAA", "B $CCC" ]) assert lst == [[ "a", "b"], ["c", "B a", "b"], ["c"]], lst t1 = DummyNode('t1') t2 = DummyNode('t2') s1 = DummyNode('s1') s2 = DummyNode('s2') env = SubstitutionEnvironment(AAA = 'aaa') s = env.subst_list('$AAA $TARGET $SOURCES', target=[t1, t2], source=[s1, s2]) assert s == [["aaa", "t1", "s1", "s2"]], s s = env.subst_list('$AAA $TARGETS $SOURCE', target=[t1, t2], source=[s1, s2]) assert s == [["aaa", "t1", "t2", "s1"]], s # Test callables in the SubstitutionEnvironment def foo(target, source, env, for_signature): assert str(target) == 't', target assert str(source) == 's', source return env["FOO"] env = SubstitutionEnvironment(BAR=foo, FOO='baz') t = DummyNode('t') s = DummyNode('s') lst = env.subst_list('test $BAR', target=t, source=s) assert lst == [['test', 'baz']], lst # Test not calling callables in the SubstitutionEnvironment if 0: # This will take some serious surgery to subst() and # subst_list(), so just leave these tests out until we can # do that. def bar(arg): pass env = SubstitutionEnvironment(BAR=bar, FOO='$BAR') subst = env.subst_list('$BAR', call=None) assert subst is bar, subst subst = env.subst_list('$FOO', call=None) assert subst is bar, subst def test_subst_path(self): """Test substituting a path list """ class MyProxy(object): def __init__(self, val): self.val = val def get(self): return self.val + '-proxy' class MyNode(object): def __init__(self, val): self.val = val def get_subst_proxy(self): return self def __str__(self): return self.val class MyObj(object): def get(self): return self env = SubstitutionEnvironment(FOO='foo', BAR='bar', LIST=['one', 'two'], PROXY=MyProxy('my1')) r = env.subst_path('$FOO') assert r == ['foo'], r r = env.subst_path(['$FOO', 'xxx', '$BAR']) assert r == ['foo', 'xxx', 'bar'], r r = env.subst_path(['$FOO', '$LIST', '$BAR']) assert list(map(str, r)) == ['foo', 'one two', 'bar'], r r = env.subst_path(['$FOO', '$TARGET', '$SOURCE', '$BAR']) assert r == ['foo', '', '', 'bar'], r r = env.subst_path(['$FOO', '$TARGET', '$BAR'], target=MyNode('ttt')) assert list(map(str, r)) == ['foo', 'ttt', 'bar'], r r = env.subst_path(['$FOO', '$SOURCE', '$BAR'], source=MyNode('sss')) assert list(map(str, r)) == ['foo', 'sss', 'bar'], r n = MyObj() r = env.subst_path(['$PROXY', MyProxy('my2'), n]) assert r == ['my1-proxy', 'my2-proxy', n], r class StringableObj(object): def __init__(self, s): self.s = s def __str__(self): return self.s env = SubstitutionEnvironment(FOO=StringableObj("foo"), BAR=StringableObj("bar")) r = env.subst_path([ "${FOO}/bar", "${BAR}/baz" ]) assert r == [ "foo/bar", "bar/baz" ], r r = env.subst_path([ "bar/${FOO}", "baz/${BAR}" ]) assert r == [ "bar/foo", "baz/bar" ], r r = env.subst_path([ "bar/${FOO}/bar", "baz/${BAR}/baz" ]) assert r == [ "bar/foo/bar", "baz/bar/baz" ], r def test_subst_target_source(self): """Test the base environment subst_target_source() method""" env = SubstitutionEnvironment(AAA = 'a', BBB = 'b') mystr = env.subst_target_source("$AAA ${AAA}A $BBBB $BBB") assert mystr == "a aA b", mystr def test_backtick(self): """Test the backtick() method for capturing command output""" env = SubstitutionEnvironment() test = TestCmd.TestCmd(workdir = '') test.write('stdout.py', """\ import sys sys.stdout.write('this came from stdout.py\\n') sys.exit(0) """) test.write('stderr.py', """\ import sys sys.stderr.write('this came from stderr.py\\n') sys.exit(0) """) test.write('fail.py', """\ import sys sys.exit(1) """) test.write('echo.py', """\ import os, sys sys.stdout.write(os.environ['ECHO'] + '\\n') sys.exit(0) """) save_stderr = sys.stderr python = '"' + sys.executable + '"' try: sys.stderr = io.StringIO() cmd = '%s %s' % (python, test.workpath('stdout.py')) output = env.backtick(cmd) errout = sys.stderr.getvalue() assert output == 'this came from stdout.py\n', output assert errout == '', errout sys.stderr = io.StringIO() cmd = '%s %s' % (python, test.workpath('stderr.py')) output = env.backtick(cmd) errout = sys.stderr.getvalue() assert output == '', output assert errout == 'this came from stderr.py\n', errout sys.stderr = io.StringIO() cmd = '%s %s' % (python, test.workpath('fail.py')) try: env.backtick(cmd) except OSError, e: assert str(e) == "'%s' exited 1" % cmd, str(e) else: self.fail("did not catch expected OSError") sys.stderr = io.StringIO() cmd = '%s %s' % (python, test.workpath('echo.py')) env['ENV'] = os.environ.copy() env['ENV']['ECHO'] = 'this came from ECHO' output = env.backtick(cmd) errout = sys.stderr.getvalue() assert output == 'this came from ECHO\n', output assert errout == '', errout 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 # Test that clones of clones correctly re-bind added methods. env1 = Environment(FOO = '1') env1.AddMethod(func2) env2 = env1.Clone(FOO = '2') env3 = env2.Clone(FOO = '3') env4 = env3.Clone(FOO = '4') r = env1.func2() assert r == 'func2-1', r r = env2.func2() assert r == 'func2-2', r r = env3.func2() assert r == 'func2-3', r r = env4.func2() assert r == 'func2-4', r # Test that clones don't re-bind an attribute that the user env1 = Environment(FOO = '1') env1.AddMethod(func2) def replace_func2(): return 'replace_func2' env1.func2 = replace_func2 env2 = env1.Clone(FOO = '2') r = env2.func2() assert r == 'replace_func2', r def test_Override(self): "Test overriding construction variables" env = SubstitutionEnvironment(ONE=1, TWO=2, THREE=3, FOUR=4) assert env['ONE'] == 1, env['ONE'] assert env['TWO'] == 2, env['TWO'] assert env['THREE'] == 3, env['THREE'] assert env['FOUR'] == 4, env['FOUR'] env2 = env.Override({'TWO' : '10', 'THREE' :'x $THREE y', 'FOUR' : ['x', '$FOUR', 'y']}) assert env2['ONE'] == 1, env2['ONE'] assert env2['TWO'] == '10', env2['TWO'] assert env2['THREE'] == 'x 3 y', env2['THREE'] assert env2['FOUR'] == ['x', 4, 'y'], env2['FOUR'] assert env['ONE'] == 1, env['ONE'] assert env['TWO'] == 2, env['TWO'] assert env['THREE'] == 3, env['THREE'] assert env['FOUR'] == 4, env['FOUR'] env2.Replace(ONE = "won") assert env2['ONE'] == "won", env2['ONE'] assert env['ONE'] == 1, env['ONE'] def test_ParseFlags(self): """Test the ParseFlags() method """ env = SubstitutionEnvironment() empty = { 'ASFLAGS' : [], 'CFLAGS' : [], 'CCFLAGS' : [], 'CXXFLAGS' : [], 'CPPDEFINES' : [], 'CPPFLAGS' : [], 'CPPPATH' : [], 'FRAMEWORKPATH' : [], 'FRAMEWORKS' : [], 'LIBPATH' : [], 'LIBS' : [], 'LINKFLAGS' : [], 'RPATH' : [], } d = env.ParseFlags(None) assert d == empty, d d = env.ParseFlags('') assert d == empty, d d = env.ParseFlags([]) assert d == empty, d s = "-I/usr/include/fum -I bar -X\n" + \ '-I"C:\\Program Files\\ASCEND\\include" ' + \ "-L/usr/fax -L foo -lxxx -l yyy " + \ '-L"C:\\Program Files\\ASCEND" -lascend ' + \ "-Wa,-as -Wl,-link " + \ "-Wl,-rpath=rpath1 " + \ "-Wl,-R,rpath2 " + \ "-Wl,-Rrpath3 " + \ "-Wp,-cpp " + \ "-std=c99 " + \ "-std=c++0x " + \ "-framework Carbon " + \ "-frameworkdir=fwd1 " + \ "-Ffwd2 " + \ "-F fwd3 " + \ "-dylib_file foo-dylib " + \ "-pthread " + \ "-fopenmp " + \ "-mno-cygwin -mwindows " + \ "-arch i386 -isysroot /tmp +DD64 " + \ "-DFOO -DBAR=value -D BAZ " d = env.ParseFlags(s) assert d['ASFLAGS'] == ['-as'], d['ASFLAGS'] assert d['CFLAGS'] == ['-std=c99'] assert d['CCFLAGS'] == ['-X', '-Wa,-as', '-pthread', '-fopenmp', '-mno-cygwin', ('-arch', 'i386'), ('-isysroot', '/tmp'), '+DD64'], repr(d['CCFLAGS']) assert d['CXXFLAGS'] == ['-std=c++0x'], repr(d['CXXFLAGS']) assert d['CPPDEFINES'] == ['FOO', ['BAR', 'value'], 'BAZ'], d['CPPDEFINES'] assert d['CPPFLAGS'] == ['-Wp,-cpp'], d['CPPFLAGS'] assert d['CPPPATH'] == ['/usr/include/fum', 'bar', 'C:\\Program Files\\ASCEND\\include'], d['CPPPATH'] assert d['FRAMEWORKPATH'] == ['fwd1', 'fwd2', 'fwd3'], d['FRAMEWORKPATH'] assert d['FRAMEWORKS'] == ['Carbon'], d['FRAMEWORKS'] assert d['LIBPATH'] == ['/usr/fax', 'foo', 'C:\\Program Files\\ASCEND'], d['LIBPATH'] LIBS = list(map(str, d['LIBS'])) assert LIBS == ['xxx', 'yyy', 'ascend'], (d['LIBS'], LIBS) assert d['LINKFLAGS'] == ['-Wl,-link', '-dylib_file', 'foo-dylib', '-pthread', '-fopenmp', '-mno-cygwin', '-mwindows', ('-arch', 'i386'), ('-isysroot', '/tmp'), '+DD64'], repr(d['LINKFLAGS']) assert d['RPATH'] == ['rpath1', 'rpath2', 'rpath3'], d['RPATH'] def test_MergeFlags(self): """Test the MergeFlags() method """ env = SubstitutionEnvironment() env.MergeFlags('') assert 'CCFLAGS' not in env, env['CCFLAGS'] env.MergeFlags('-X') assert env['CCFLAGS'] == ['-X'], env['CCFLAGS'] env.MergeFlags('-X') assert env['CCFLAGS'] == ['-X'], env['CCFLAGS'] env = SubstitutionEnvironment(CCFLAGS=None) env.MergeFlags('-Y') assert env['CCFLAGS'] == ['-Y'], env['CCFLAGS'] env = SubstitutionEnvironment() env.MergeFlags({'A':['aaa'], 'B':['bbb']}) assert env['A'] == ['aaa'], env['A'] assert env['B'] == ['bbb'], env['B'] # def test_MergeShellPaths(self): # """Test the MergeShellPaths() method # """ # env = Environment() # env.MergeShellPaths({}) # assert not env['ENV'].has_key('INCLUDE'), env['INCLUDE'] # env.MergeShellPaths({'INCLUDE': r'c:\Program Files\Stuff'}) # assert env['ENV']['INCLUDE'] == r'c:\Program Files\Stuff', env['ENV']['INCLUDE'] # env.MergeShellPaths({'INCLUDE': r'c:\Program Files\Stuff'}) # assert env['ENV']['INCLUDE'] == r'c:\Program Files\Stuff', env['ENV']['INCLUDE'] # env.MergeShellPaths({'INCLUDE': r'xyz'}) # assert env['ENV']['INCLUDE'] == r'xyz%sc:\Program Files\Stuff'%os.pathsep, env['ENV']['INCLUDE'] # env = Environment() # env['ENV']['INCLUDE'] = 'xyz' # env.MergeShellPaths({'INCLUDE':['c:/inc1', 'c:/inc2']} ) # assert env['ENV']['INCLUDE'] == r'c:/inc1%sc:/inc2%sxyz'%(os.pathsep, os.pathsep), env['ENV']['INCLUDE'] # # test prepend=0 # env = Environment() # env.MergeShellPaths({'INCLUDE': r'c:\Program Files\Stuff'}, prepend=0) # assert env['ENV']['INCLUDE'] == r'c:\Program Files\Stuff', env['ENV']['INCLUDE'] # env.MergeShellPaths({'INCLUDE': r'xyz'}, prepend=0) # assert env['ENV']['INCLUDE'] == r'c:\Program Files\Stuff%sxyz'%os.pathsep, env['ENV']['INCLUDE'] class BaseTestCase(unittest.TestCase,TestEnvironmentFixture): reserved_variables = [ 'CHANGED_SOURCES', 'CHANGED_TARGETS', 'SOURCE', 'SOURCES', 'TARGET', 'TARGETS', 'UNCHANGED_SOURCES', 'UNCHANGED_TARGETS', ] def test___init__(self): """Test construction Environment creation Create two with identical arguments and check that they compare the same. """ env1 = self.TestEnvironment(XXX = 'x', YYY = 'y') env2 = self.TestEnvironment(XXX = 'x', YYY = 'y') assert env1 == env2, diff_env(env1, env2) assert '__env__' not in env1 assert '__env__' not in env2 def test_variables(self): """Test that variables only get applied once.""" class FakeOptions(object): def __init__(self, key, val): self.calls = 0 self.key = key self.val = val def keys(self): return [self.key] def Update(self, env): env[self.key] = self.val self.calls = self.calls + 1 o = FakeOptions('AAA', 'fake_opt') env = Environment(variables=o, AAA='keyword_arg') assert o.calls == 1, o.calls assert env['AAA'] == 'fake_opt', env['AAA'] def test_get(self): """Test the get() method.""" env = self.TestEnvironment(aaa = 'AAA') x = env.get('aaa') assert x == 'AAA', x x = env.get('aaa', 'XXX') assert x == 'AAA', x x = env.get('bbb') assert x is None, x x = env.get('bbb', 'XXX') assert x == 'XXX', x def test_Builder_calls(self): """Test Builder calls through different environments """ global called_it b1 = Builder() b2 = Builder() env = Environment() env.Replace(BUILDERS = { 'builder1' : b1, 'builder2' : b2 }) called_it = {} env.builder1('in1') assert called_it['target'] is None, called_it assert called_it['source'] == ['in1'], called_it called_it = {} env.builder2(source = 'in2', xyzzy = 1) assert called_it['target'] is None, called_it assert called_it['source'] == ['in2'], called_it assert called_it['xyzzy'] == 1, called_it called_it = {} env.builder1(foo = 'bar') assert called_it['foo'] == 'bar', called_it assert called_it['target'] is None, called_it assert called_it['source'] is None, called_it def test_BuilderWrapper_attributes(self): """Test getting and setting of BuilderWrapper attributes """ b1 = Builder() b2 = Builder() e1 = Environment() e2 = Environment() e1.Replace(BUILDERS = {'b' : b1}) bw = e1.b assert bw.env is e1 bw.env = e2 assert bw.env is e2 assert bw.builder is b1 bw.builder = b2 assert bw.builder is b2 self.assertRaises(AttributeError, getattr, bw, 'foobar') bw.foobar = 42 assert bw.foobar is 42 # This unit test is currently disabled because we don't think the # underlying method it tests (Environment.BuilderWrapper.execute()) # is necessary, but we're leaving the code here for now in case # that's mistaken. def _DO_NOT_test_Builder_execs(self): """Test Builder execution through different environments One environment is initialized with a single Builder object, one with a list of a single Builder object, and one with a list of two Builder objects. """ global built_it b1 = Builder() b2 = Builder() built_it = {} env3 = Environment() env3.Replace(BUILDERS = { 'builder1' : b1, 'builder2' : b2 }) env3.builder1.execute(target = 'out1') env3.builder2.execute(target = 'out2') env3.builder1.execute(target = 'out3') assert built_it['out1'] assert built_it['out2'] assert built_it['out3'] env4 = env3.Clone() assert env4.builder1.env is env4, "builder1.env (%s) == env3 (%s)?" % ( env4.builder1.env, env3) assert env4.builder2.env is env4, "builder2.env (%s) == env3 (%s)?" % ( env4.builder1.env, env3) # Now test BUILDERS as a dictionary. built_it = {} env5 = self.TestEnvironment(BUILDERS={ 'foo' : b1 }) env5['BUILDERS']['bar'] = b2 env5.foo.execute(target='out1') env5.bar.execute(target='out2') assert built_it['out1'] assert built_it['out2'] built_it = {} env6 = Environment() env6['BUILDERS'] = { 'foo' : b1, 'bar' : b2 } env6.foo.execute(target='out1') env6.bar.execute(target='out2') assert built_it['out1'] assert built_it['out2'] def test_Scanners(self): """Test setting SCANNERS in various ways One environment is initialized with a single Scanner object, one with a list of a single Scanner object, and one with a list of two Scanner objects. """ global scanned_it s1 = Scanner(name = 'scanner1', skeys = [".c", ".cc"]) s2 = Scanner(name = 'scanner2', skeys = [".m4"]) s3 = Scanner(name = 'scanner3', skeys = [".m4", ".m5"]) s4 = Scanner(name = 'scanner4', skeys = [None]) # XXX Tests for scanner execution through different environments, # XXX if we ever want to do that some day # scanned_it = {} # env1 = self.TestEnvironment(SCANNERS = s1) # env1.scanner1(filename = 'out1') # assert scanned_it['out1'] # # scanned_it = {} # env2 = self.TestEnvironment(SCANNERS = [s1]) # env1.scanner1(filename = 'out1') # assert scanned_it['out1'] # # scanned_it = {} # env3 = Environment() # env3.Replace(SCANNERS = [s1]) # env3.scanner1(filename = 'out1') # env3.scanner2(filename = 'out2') # env3.scanner1(filename = 'out3') # assert scanned_it['out1'] # assert scanned_it['out2'] # assert scanned_it['out3'] suffixes = [".c", ".cc", ".cxx", ".m4", ".m5"] env = Environment() try: del env['SCANNERS'] except KeyError: pass s = list(map(env.get_scanner, suffixes)) assert s == [None, None, None, None, None], s env = self.TestEnvironment(SCANNERS = []) s = list(map(env.get_scanner, suffixes)) assert s == [None, None, None, None, None], s env.Replace(SCANNERS = [s1]) s = list(map(env.get_scanner, suffixes)) assert s == [s1, s1, None, None, None], s env.Append(SCANNERS = [s2]) s = list(map(env.get_scanner, suffixes)) assert s == [s1, s1, None, s2, None], s env.AppendUnique(SCANNERS = [s3]) s = list(map(env.get_scanner, suffixes)) assert s == [s1, s1, None, s2, s3], s env = env.Clone(SCANNERS = [s2]) s = list(map(env.get_scanner, suffixes)) assert s == [None, None, None, s2, None], s env['SCANNERS'] = [s1] s = list(map(env.get_scanner, suffixes)) assert s == [s1, s1, None, None, None], s env.PrependUnique(SCANNERS = [s2, s1]) s = list(map(env.get_scanner, suffixes)) assert s == [s1, s1, None, s2, None], s env.Prepend(SCANNERS = [s3]) s = list(map(env.get_scanner, suffixes)) assert s == [s1, s1, None, s3, s3], s # Verify behavior of case-insensitive suffix matches on Windows. uc_suffixes = [_.upper() for _ in suffixes] env = Environment(SCANNERS = [s1, s2, s3], PLATFORM = 'linux') s = list(map(env.get_scanner, suffixes)) assert s == [s1, s1, None, s2, s3], s s = list(map(env.get_scanner, uc_suffixes)) assert s == [None, None, None, None, None], s env['PLATFORM'] = 'win32' s = list(map(env.get_scanner, uc_suffixes)) assert s == [s1, s1, None, s2, s3], s # Verify behavior for a scanner returning None (on Windows # where we might try to perform case manipulation on None). env.Replace(SCANNERS = [s4]) s = list(map(env.get_scanner, suffixes)) assert s == [None, None, None, None, None], s def test_ENV(self): """Test setting the external ENV in Environments """ env = Environment() assert 'ENV' in env.Dictionary() env = self.TestEnvironment(ENV = { 'PATH' : '/foo:/bar' }) assert env.Dictionary('ENV')['PATH'] == '/foo:/bar' def test_ReservedVariables(self): """Test warning generation when reserved variable names are set""" reserved_variables = [ 'CHANGED_SOURCES', 'CHANGED_TARGETS', 'SOURCE', 'SOURCES', 'TARGET', 'TARGETS', 'UNCHANGED_SOURCES', 'UNCHANGED_TARGETS', ] warning = SCons.Warnings.ReservedVariableWarning SCons.Warnings.enableWarningClass(warning) old = SCons.Warnings.warningAsException(1) try: env4 = Environment() for kw in self.reserved_variables: exc_caught = None try: env4[kw] = 'xyzzy' except warning: exc_caught = 1 assert exc_caught, "Did not catch ReservedVariableWarning for `%s'" % kw assert kw not in env4, "`%s' variable was incorrectly set" % kw finally: SCons.Warnings.warningAsException(old) def test_FutureReservedVariables(self): """Test warning generation when future reserved variable names are set""" future_reserved_variables = [] warning = SCons.Warnings.FutureReservedVariableWarning SCons.Warnings.enableWarningClass(warning) old = SCons.Warnings.warningAsException(1) try: env4 = Environment() for kw in future_reserved_variables: exc_caught = None try: env4[kw] = 'xyzzy' except warning: exc_caught = 1 assert exc_caught, "Did not catch FutureReservedVariableWarning for `%s'" % kw assert kw in env4, "`%s' variable was not set" % kw finally: SCons.Warnings.warningAsException(old) def test_IllegalVariables(self): """Test that use of illegal variables raises an exception""" env = Environment() def test_it(var, env=env): exc_caught = None try: env[var] = 1 except SCons.Errors.UserError: exc_caught = 1 assert exc_caught, "did not catch UserError for '%s'" % var env['aaa'] = 1 assert env['aaa'] == 1, env['aaa'] test_it('foo/bar') test_it('foo.bar') test_it('foo-bar') def test_autogenerate(dict): """Test autogenerating variables in a dictionary.""" drive, p = os.path.splitdrive(os.getcwd()) def normalize_path(path, drive=drive): if path[0] in '\\/': path = drive + path path = os.path.normpath(path) drive, path = os.path.splitdrive(path) return drive.lower() + path env = dict.TestEnvironment(LIBS = [ 'foo', 'bar', 'baz' ], LIBLINKPREFIX = 'foo', LIBLINKSUFFIX = 'bar') def RDirs(pathlist, fs=env.fs): return fs.Dir('xx').Rfindalldirs(pathlist) env['RDirs'] = RDirs flags = env.subst_list('$_LIBFLAGS', 1)[0] assert flags == ['foobar', 'foobar', 'foobazbar'], flags blat = env.fs.Dir('blat') env.Replace(CPPPATH = [ 'foo', '$FOO/bar', blat ], INCPREFIX = 'foo ', INCSUFFIX = 'bar', FOO = 'baz') flags = env.subst_list('$_CPPINCFLAGS', 1)[0] expect = [ '$(', normalize_path('foo'), normalize_path('xx/foobar'), normalize_path('foo'), normalize_path('xx/baz/bar'), normalize_path('foo'), normalize_path('blatbar'), '$)', ] assert flags == expect, flags env.Replace(F77PATH = [ 'foo', '$FOO/bar', blat ], INCPREFIX = 'foo ', INCSUFFIX = 'bar', FOO = 'baz') flags = env.subst_list('$_F77INCFLAGS', 1)[0] expect = [ '$(', normalize_path('foo'), normalize_path('xx/foobar'), normalize_path('foo'), normalize_path('xx/baz/bar'), normalize_path('foo'), normalize_path('blatbar'), '$)', ] assert flags == expect, flags env.Replace(CPPPATH = '', F77PATH = '', LIBPATH = '') l = env.subst_list('$_CPPINCFLAGS') assert l == [[]], l l = env.subst_list('$_F77INCFLAGS') assert l == [[]], l l = env.subst_list('$_LIBDIRFLAGS') assert l == [[]], l env.fs.Repository('/rep1') env.fs.Repository('/rep2') env.Replace(CPPPATH = [ 'foo', '/__a__/b', '$FOO/bar', blat], INCPREFIX = '-I ', INCSUFFIX = 'XXX', FOO = 'baz') flags = env.subst_list('$_CPPINCFLAGS', 1)[0] expect = [ '$(', '-I', normalize_path('xx/fooXXX'), '-I', normalize_path('/rep1/xx/fooXXX'), '-I', normalize_path('/rep2/xx/fooXXX'), '-I', normalize_path('/__a__/bXXX'), '-I', normalize_path('xx/baz/barXXX'), '-I', normalize_path('/rep1/xx/baz/barXXX'), '-I', normalize_path('/rep2/xx/baz/barXXX'), '-I', normalize_path('blatXXX'), '$)' ] def normalize_if_path(arg, np=normalize_path): if arg not in ('$(','$)','-I'): return np(str(arg)) return arg flags = list(map(normalize_if_path, flags)) assert flags == expect, flags def test_platform(self): """Test specifying a platform callable when instantiating.""" class platform(object): def __str__(self): return "TestPlatform" def __call__(self, env): env['XYZZY'] = 777 def tool(env): env['SET_TOOL'] = 'initialized' assert env['PLATFORM'] == "TestPlatform" env = self.TestEnvironment(platform = platform(), tools = [tool]) assert env['XYZZY'] == 777, env assert env['PLATFORM'] == "TestPlatform" assert env['SET_TOOL'] == "initialized" def test_Default_PLATFORM(self): """Test overriding the default PLATFORM variable""" class platform(object): def __str__(self): return "DefaultTestPlatform" def __call__(self, env): env['XYZZY'] = 888 def tool(env): env['SET_TOOL'] = 'abcde' assert env['PLATFORM'] == "DefaultTestPlatform" import SCons.Defaults save = SCons.Defaults.ConstructionEnvironment.copy() try: import SCons.Defaults SCons.Defaults.ConstructionEnvironment.update({ 'PLATFORM' : platform(), }) env = self.TestEnvironment(tools = [tool]) assert env['XYZZY'] == 888, env assert env['PLATFORM'] == "DefaultTestPlatform" assert env['SET_TOOL'] == "abcde" finally: SCons.Defaults.ConstructionEnvironment = save def test_tools(self): """Test specifying a tool callable when instantiating.""" def t1(env): env['TOOL1'] = 111 def t2(env): env['TOOL2'] = 222 def t3(env): env['AAA'] = env['XYZ'] def t4(env): env['TOOL4'] = 444 env = self.TestEnvironment(tools = [t1, t2, t3], XYZ = 'aaa') assert env['TOOL1'] == 111, env['TOOL1'] assert env['TOOL2'] == 222, env assert env['AAA'] == 'aaa', env t4(env) assert env['TOOL4'] == 444, env test = TestCmd.TestCmd(workdir = '') test.write('faketool.py', """\ def generate(env, **kw): for k, v in kw.items(): env[k] = v def exists(env): return 1 """) env = self.TestEnvironment(tools = [('faketool', {'a':1, 'b':2, 'c':3})], toolpath = [test.workpath('')]) assert env['a'] == 1, env['a'] assert env['b'] == 2, env['b'] assert env['c'] == 3, env['c'] def test_Default_TOOLS(self): """Test overriding the default TOOLS variable""" def t5(env): env['TOOL5'] = 555 def t6(env): env['TOOL6'] = 666 def t7(env): env['BBB'] = env['XYZ'] def t8(env): env['TOOL8'] = 888 import SCons.Defaults save = SCons.Defaults.ConstructionEnvironment.copy() try: SCons.Defaults.ConstructionEnvironment.update({ 'TOOLS' : [t5, t6, t7], }) env = Environment(XYZ = 'bbb') assert env['TOOL5'] == 555, env['TOOL5'] assert env['TOOL6'] == 666, env assert env['BBB'] == 'bbb', env t8(env) assert env['TOOL8'] == 888, env finally: SCons.Defaults.ConstructionEnvironment = save def test_null_tools(self): """Test specifying a tool of None is OK.""" def t1(env): env['TOOL1'] = 111 def t2(env): env['TOOL2'] = 222 env = self.TestEnvironment(tools = [t1, None, t2], XYZ = 'aaa') assert env['TOOL1'] == 111, env['TOOL1'] assert env['TOOL2'] == 222, env assert env['XYZ'] == 'aaa', env env = self.TestEnvironment(tools = [None], XYZ = 'xyz') assert env['XYZ'] == 'xyz', env env = self.TestEnvironment(tools = [t1, '', t2], XYZ = 'ddd') assert env['TOOL1'] == 111, env['TOOL1'] assert env['TOOL2'] == 222, env assert env['XYZ'] == 'ddd', env def test_concat(self): "Test _concat()" e1 = self.TestEnvironment(PRE='pre', SUF='suf', STR='a b', LIST=['a', 'b']) s = e1.subst x = s("${_concat('', '', '', __env__)}") assert x == '', x x = s("${_concat('', [], '', __env__)}") assert x == '', x x = s("${_concat(PRE, '', SUF, __env__)}") assert x == '', x x = s("${_concat(PRE, STR, SUF, __env__)}") assert x == 'prea bsuf', x x = s("${_concat(PRE, LIST, SUF, __env__)}") assert x == 'preasuf prebsuf', x def test_concat_nested(self): "Test _concat() on a nested substitution strings." e = self.TestEnvironment(PRE='pre', SUF='suf', L1=['a', 'b'], L2=['c', 'd'], L3=['$L2']) x = e.subst('$( ${_concat(PRE, L1, SUF, __env__)} $)') assert x == 'preasuf prebsuf', x e.AppendUnique(L1 = ['$L2']) x = e.subst('$( ${_concat(PRE, L1, SUF, __env__)} $)') assert x == 'preasuf prebsuf precsuf predsuf', x e.AppendUnique(L1 = ['$L3']) x = e.subst('$( ${_concat(PRE, L1, SUF, __env__)} $)') assert x == 'preasuf prebsuf precsuf predsuf precsuf predsuf', x def test_gvars(self): """Test the Environment gvars() method""" env = self.TestEnvironment(XXX = 'x', YYY = 'y', ZZZ = 'z') gvars = env.gvars() assert gvars['XXX'] == 'x', gvars['XXX'] assert gvars['YYY'] == 'y', gvars['YYY'] assert gvars['ZZZ'] == 'z', gvars['ZZZ'] def test__update(self): """Test the _update() method""" env = self.TestEnvironment(X = 'x', Y = 'y', Z = 'z') assert env['X'] == 'x', env['X'] assert env['Y'] == 'y', env['Y'] assert env['Z'] == 'z', env['Z'] env._update({'X' : 'xxx', 'TARGET' : 't', 'TARGETS' : 'ttt', 'SOURCE' : 's', 'SOURCES' : 'sss', 'Z' : 'zzz'}) assert env['X'] == 'xxx', env['X'] assert env['Y'] == 'y', env['Y'] assert env['Z'] == 'zzz', env['Z'] assert env['TARGET'] == 't', env['TARGET'] assert env['TARGETS'] == 'ttt', env['TARGETS'] assert env['SOURCE'] == 's', env['SOURCE'] assert env['SOURCES'] == 'sss', env['SOURCES'] def test_Append(self): """Test appending to construction variables in an Environment """ b1 = Environment()['BUILDERS'] b2 = Environment()['BUILDERS'] assert b1 == b2, diff_dict(b1, b2) cases = [ 'a1', 'A1', 'a1A1', 'a2', ['A2'], ['a2', 'A2'], 'a3', UL(['A3']), UL(['a', '3', 'A3']), 'a4', '', 'a4', 'a5', [], ['a5'], 'a6', UL([]), UL(['a', '6']), 'a7', [''], ['a7', ''], 'a8', UL(['']), UL(['a', '8', '']), ['e1'], 'E1', ['e1', 'E1'], ['e2'], ['E2'], ['e2', 'E2'], ['e3'], UL(['E3']), UL(['e3', 'E3']), ['e4'], '', ['e4'], ['e5'], [], ['e5'], ['e6'], UL([]), UL(['e6']), ['e7'], [''], ['e7', ''], ['e8'], UL(['']), UL(['e8', '']), UL(['i1']), 'I1', UL(['i1', 'I', '1']), UL(['i2']), ['I2'], UL(['i2', 'I2']), UL(['i3']), UL(['I3']), UL(['i3', 'I3']), UL(['i4']), '', UL(['i4']), UL(['i5']), [], UL(['i5']), UL(['i6']), UL([]), UL(['i6']), UL(['i7']), [''], UL(['i7', '']), UL(['i8']), UL(['']), UL(['i8', '']), {'d1':1}, 'D1', {'d1':1, 'D1':None}, {'d2':1}, ['D2'], {'d2':1, 'D2':None}, {'d3':1}, UL(['D3']), {'d3':1, 'D3':None}, {'d4':1}, {'D4':1}, {'d4':1, 'D4':1}, {'d5':1}, UD({'D5':1}), UD({'d5':1, 'D5':1}), UD({'u1':1}), 'U1', UD({'u1':1, 'U1':None}), UD({'u2':1}), ['U2'], UD({'u2':1, 'U2':None}), UD({'u3':1}), UL(['U3']), UD({'u3':1, 'U3':None}), UD({'u4':1}), {'U4':1}, UD({'u4':1, 'U4':1}), UD({'u5':1}), UD({'U5':1}), UD({'u5':1, 'U5':1}), '', 'M1', 'M1', '', ['M2'], ['M2'], '', UL(['M3']), UL(['M3']), '', '', '', '', [], [], '', UL([]), UL([]), '', [''], [''], '', UL(['']), UL(['']), [], 'N1', ['N1'], [], ['N2'], ['N2'], [], UL(['N3']), UL(['N3']), [], '', [], [], [], [], [], UL([]), UL([]), [], [''], [''], [], UL(['']), UL(['']), UL([]), 'O1', ['O', '1'], UL([]), ['O2'], ['O2'], UL([]), UL(['O3']), UL(['O3']), UL([]), '', UL([]), UL([]), [], UL([]), UL([]), UL([]), UL([]), UL([]), [''], UL(['']), UL([]), UL(['']), UL(['']), [''], 'P1', ['', 'P1'], [''], ['P2'], ['', 'P2'], [''], UL(['P3']), UL(['', 'P3']), [''], '', [''], [''], [], [''], [''], UL([]), UL(['']), [''], [''], ['', ''], [''], UL(['']), UL(['', '']), UL(['']), 'Q1', ['', 'Q', '1'], UL(['']), ['Q2'], ['', 'Q2'], UL(['']), UL(['Q3']), UL(['', 'Q3']), UL(['']), '', UL(['']), UL(['']), [], UL(['']), UL(['']), UL([]), UL(['']), UL(['']), [''], UL(['', '']), UL(['']), UL(['']), UL(['', '']), ] env = Environment() failed = 0 while cases: input, append, expect = cases[:3] env['XXX'] = copy.copy(input) try: env.Append(XXX = append) except Exception, e: if failed == 0: print print " %s Append %s exception: %s" % \ (repr(input), repr(append), e) failed = failed + 1 else: result = env['XXX'] if result != expect: if failed == 0: print print " %s Append %s => %s did not match %s" % \ (repr(input), repr(append), repr(result), repr(expect)) failed = failed + 1 del cases[:3] assert failed == 0, "%d Append() cases failed" % failed env['UL'] = UL(['foo']) env.Append(UL = 'bar') result = env['UL'] assert isinstance(result, UL), repr(result) assert result == ['foo', 'b', 'a', 'r'], result env['CLVar'] = CLVar(['foo']) env.Append(CLVar = 'bar') result = env['CLVar'] assert isinstance(result, CLVar), repr(result) assert result == ['foo', 'bar'], result class C(object): def __init__(self, name): self.name = name def __str__(self): return self.name def __cmp__(self, other): raise Exception("should not compare") ccc = C('ccc') env2 = self.TestEnvironment(CCC1 = ['c1'], CCC2 = ccc) env2.Append(CCC1 = ccc, CCC2 = ['c2']) assert env2['CCC1'][0] == 'c1', env2['CCC1'] assert env2['CCC1'][1] is ccc, env2['CCC1'] assert env2['CCC2'][0] is ccc, env2['CCC2'] assert env2['CCC2'][1] == 'c2', env2['CCC2'] env3 = self.TestEnvironment(X = {'x1' : 7}) env3.Append(X = {'x1' : 8, 'x2' : 9}, Y = {'y1' : 10}) assert env3['X'] == {'x1': 8, 'x2': 9}, env3['X'] assert env3['Y'] == {'y1': 10}, env3['Y'] z1 = Builder() z2 = Builder() env4 = self.TestEnvironment(BUILDERS = {'z1' : z1}) env4.Append(BUILDERS = {'z2' : z2}) assert env4['BUILDERS'] == {'z1' : z1, 'z2' : z2}, env4['BUILDERS'] assert hasattr(env4, 'z1') assert hasattr(env4, 'z2') def test_AppendENVPath(self): """Test appending to an ENV path.""" env1 = self.TestEnvironment(ENV = {'PATH': r'C:\dir\num\one;C:\dir\num\two'}, MYENV = {'MYPATH': r'C:\mydir\num\one;C:\mydir\num\two'}) # have to include the pathsep here so that the test will work on UNIX too. env1.AppendENVPath('PATH',r'C:\dir\num\two', sep = ';') env1.AppendENVPath('PATH',r'C:\dir\num\three', sep = ';') env1.AppendENVPath('MYPATH',r'C:\mydir\num\three','MYENV', sep = ';') env1.AppendENVPath('MYPATH',r'C:\mydir\num\one','MYENV', sep = ';') # this should do nothing since delete_existing is 0 env1.AppendENVPath('MYPATH',r'C:\mydir\num\three','MYENV', sep = ';', delete_existing=0) assert(env1['ENV']['PATH'] == r'C:\dir\num\one;C:\dir\num\two;C:\dir\num\three') assert(env1['MYENV']['MYPATH'] == r'C:\mydir\num\two;C:\mydir\num\three;C:\mydir\num\one') test = TestCmd.TestCmd(workdir = '') test.subdir('sub1', 'sub2') p=env1['ENV']['PATH'] env1.AppendENVPath('PATH','#sub1', sep = ';') env1.AppendENVPath('PATH',env1.fs.Dir('sub2'), sep = ';') assert env1['ENV']['PATH'] == p + ';sub1;sub2', env1['ENV']['PATH'] def test_AppendUnique(self): """Test appending to unique values to construction variables This strips values that are already present when lists are involved.""" env = self.TestEnvironment(AAA1 = 'a1', AAA2 = 'a2', AAA3 = 'a3', AAA4 = 'a4', AAA5 = 'a5', BBB1 = ['b1'], BBB2 = ['b2'], BBB3 = ['b3'], BBB4 = ['b4'], BBB5 = ['b5'], CCC1 = '', CCC2 = '', DDD1 = ['a', 'b', 'c']) env.AppendUnique(AAA1 = 'a1', AAA2 = ['a2'], AAA3 = ['a3', 'b', 'c', 'c', 'b', 'a3'], # ignore dups AAA4 = 'a4.new', AAA5 = ['a5.new'], BBB1 = 'b1', BBB2 = ['b2'], BBB3 = ['b3', 'c', 'd', 'c', 'b3'], BBB4 = 'b4.new', BBB5 = ['b5.new'], CCC1 = 'c1', CCC2 = ['c2'], DDD1 = 'b') assert env['AAA1'] == 'a1a1', env['AAA1'] assert env['AAA2'] == ['a2'], env['AAA2'] assert env['AAA3'] == ['a3', 'b', 'c'], env['AAA3'] assert env['AAA4'] == 'a4a4.new', env['AAA4'] assert env['AAA5'] == ['a5', 'a5.new'], env['AAA5'] assert env['BBB1'] == ['b1'], env['BBB1'] assert env['BBB2'] == ['b2'], env['BBB2'] assert env['BBB3'] == ['b3', 'c', 'd'], env['BBB3'] assert env['BBB4'] == ['b4', 'b4.new'], env['BBB4'] assert env['BBB5'] == ['b5', 'b5.new'], env['BBB5'] assert env['CCC1'] == 'c1', env['CCC1'] assert env['CCC2'] == ['c2'], env['CCC2'] assert env['DDD1'] == ['a', 'b', 'c'], env['DDD1'] env.AppendUnique(DDD1 = 'b', delete_existing=1) assert env['DDD1'] == ['a', 'c', 'b'], env['DDD1'] # b moves to end env.AppendUnique(DDD1 = ['a','b'], delete_existing=1) assert env['DDD1'] == ['c', 'a', 'b'], env['DDD1'] # a & b move to end env.AppendUnique(DDD1 = ['e','f', 'e'], delete_existing=1) assert env['DDD1'] == ['c', 'a', 'b', 'f', 'e'], env['DDD1'] # add last env['CLVar'] = CLVar([]) env.AppendUnique(CLVar = 'bar') result = env['CLVar'] assert isinstance(result, CLVar), repr(result) assert result == ['bar'], result env['CLVar'] = CLVar(['abc']) env.AppendUnique(CLVar = 'bar') result = env['CLVar'] assert isinstance(result, CLVar), repr(result) assert result == ['abc', 'bar'], result env['CLVar'] = CLVar(['bar']) env.AppendUnique(CLVar = 'bar') result = env['CLVar'] assert isinstance(result, CLVar), repr(result) assert result == ['bar'], result def test_Clone(self): """Test construction environment copying Update the copy independently afterwards and check that the original remains intact (that is, no dangling references point to objects in the copied environment). Clone the original with some construction variable updates and check that the original remains intact and the copy has the updated values. """ env1 = self.TestEnvironment(XXX = 'x', YYY = 'y') env2 = env1.Clone() env1copy = env1.Clone() assert env1copy == env1copy assert env2 == env2 env2.Replace(YYY = 'yyy') assert env2 == env2 assert env1 != env2 assert env1 == env1copy env3 = env1.Clone(XXX = 'x3', ZZZ = 'z3') assert env3 == env3 assert env3.Dictionary('XXX') == 'x3' assert env3.Dictionary('YYY') == 'y' assert env3.Dictionary('ZZZ') == 'z3' assert env1 == env1copy # Ensure that lists and dictionaries are # deep copied, but not instances. class TestA(object): pass env1 = self.TestEnvironment(XXX=TestA(), YYY = [ 1, 2, 3 ], ZZZ = { 1:2, 3:4 }) env2=env1.Clone() env2.Dictionary('YYY').append(4) env2.Dictionary('ZZZ')[5] = 6 assert env1.Dictionary('XXX') is env2.Dictionary('XXX') assert 4 in env2.Dictionary('YYY') assert not 4 in env1.Dictionary('YYY') assert 5 in env2.Dictionary('ZZZ') assert 5 not in env1.Dictionary('ZZZ') # env1 = self.TestEnvironment(BUILDERS = {'b1' : Builder()}) assert hasattr(env1, 'b1'), "env1.b1 was not set" assert env1.b1.object == env1, "b1.object doesn't point to env1" env2 = env1.Clone(BUILDERS = {'b2' : Builder()}) assert env2 is env2 assert env2 == env2 assert hasattr(env1, 'b1'), "b1 was mistakenly cleared from env1" assert env1.b1.object == env1, "b1.object was changed" assert not hasattr(env2, 'b1'), "b1 was not cleared from env2" assert hasattr(env2, 'b2'), "env2.b2 was not set" assert env2.b2.object == env2, "b2.object doesn't point to env2" # Ensure that specifying new tools in a copied environment # works. def foo(env): env['FOO'] = 1 def bar(env): env['BAR'] = 2 def baz(env): env['BAZ'] = 3 env1 = self.TestEnvironment(tools=[foo]) env2 = env1.Clone() env3 = env1.Clone(tools=[bar, baz]) assert env1.get('FOO') is 1 assert env1.get('BAR') is None assert env1.get('BAZ') is None assert env2.get('FOO') is 1 assert env2.get('BAR') is None assert env2.get('BAZ') is None assert env3.get('FOO') is 1 assert env3.get('BAR') is 2 assert env3.get('BAZ') is 3 # Ensure that recursive variable substitution when copying # environments works properly. env1 = self.TestEnvironment(CCFLAGS = '-DFOO', XYZ = '-DXYZ') env2 = env1.Clone(CCFLAGS = '$CCFLAGS -DBAR', XYZ = ['-DABC', 'x $XYZ y', '-DDEF']) x = env2.get('CCFLAGS') assert x == '-DFOO -DBAR', x x = env2.get('XYZ') assert x == ['-DABC', 'x -DXYZ y', '-DDEF'], x # Ensure that special properties of a class don't get # lost on copying. env1 = self.TestEnvironment(FLAGS = CLVar('flag1 flag2')) x = env1.get('FLAGS') assert x == ['flag1', 'flag2'], x env2 = env1.Clone() env2.Append(FLAGS = 'flag3 flag4') x = env2.get('FLAGS') assert x == ['flag1', 'flag2', 'flag3', 'flag4'], x x = env1.get('FLAGS') assert x == ['flag1', 'flag2'], x # Ensure that appending directly to a copied CLVar # doesn't modify the original. env1 = self.TestEnvironment(FLAGS = CLVar('flag1 flag2')) x = env1.get('FLAGS') assert x == ['flag1', 'flag2'], x env2 = env1.Clone() env2['FLAGS'] += ['flag3', 'flag4'] x = env2.get('FLAGS') assert x == ['flag1', 'flag2', 'flag3', 'flag4'], x x = env1.get('FLAGS') assert x == ['flag1', 'flag2'], x # Test that the environment stores the toolpath and # re-uses it for copies. test = TestCmd.TestCmd(workdir = '') test.write('xxx.py', """\ def exists(env): 1 def generate(env): env['XXX'] = 'one' """) test.write('yyy.py', """\ def exists(env): 1 def generate(env): env['YYY'] = 'two' """) env = self.TestEnvironment(tools=['xxx'], toolpath=[test.workpath('')]) assert env['XXX'] == 'one', env['XXX'] env = env.Clone(tools=['yyy']) assert env['YYY'] == 'two', env['YYY'] # Test that real_value = [4] def my_tool(env, rv=real_value): assert env['KEY_THAT_I_WANT'] == rv[0] env['KEY_THAT_I_WANT'] = rv[0] + 1 env = self.TestEnvironment() real_value[0] = 5 env = env.Clone(KEY_THAT_I_WANT=5, tools=[my_tool]) assert env['KEY_THAT_I_WANT'] == real_value[0], env['KEY_THAT_I_WANT'] real_value[0] = 6 env = env.Clone(KEY_THAT_I_WANT=6, tools=[my_tool]) assert env['KEY_THAT_I_WANT'] == real_value[0], env['KEY_THAT_I_WANT'] def test_Copy(self): """Test copying using the old env.Copy() method""" env1 = self.TestEnvironment(XXX = 'x', YYY = 'y') env2 = env1.Copy() env1copy = env1.Copy() assert env1copy == env1copy assert env2 == env2 env2.Replace(YYY = 'yyy') assert env2 == env2 assert env1 != env2 assert env1 == env1copy def test_Detect(self): """Test Detect()ing tools""" test = TestCmd.TestCmd(workdir = '') test.subdir('sub1', 'sub2') sub1 = test.workpath('sub1') sub2 = test.workpath('sub2') if sys.platform == 'win32': test.write(['sub1', 'xxx'], "sub1/xxx\n") test.write(['sub2', 'xxx'], "sub2/xxx\n") env = self.TestEnvironment(ENV = { 'PATH' : [sub1, sub2] }) x = env.Detect('xxx.exe') assert x is None, x test.write(['sub2', 'xxx.exe'], "sub2/xxx.exe\n") env = self.TestEnvironment(ENV = { 'PATH' : [sub1, sub2] }) x = env.Detect('xxx.exe') assert x == 'xxx.exe', x test.write(['sub1', 'xxx.exe'], "sub1/xxx.exe\n") x = env.Detect('xxx.exe') assert x == 'xxx.exe', x else: test.write(['sub1', 'xxx.exe'], "sub1/xxx.exe\n") test.write(['sub2', 'xxx.exe'], "sub2/xxx.exe\n") env = self.TestEnvironment(ENV = { 'PATH' : [sub1, sub2] }) x = env.Detect('xxx.exe') assert x is None, x sub2_xxx_exe = test.workpath('sub2', 'xxx.exe') os.chmod(sub2_xxx_exe, 0755) env = self.TestEnvironment(ENV = { 'PATH' : [sub1, sub2] }) x = env.Detect('xxx.exe') assert x == 'xxx.exe', x sub1_xxx_exe = test.workpath('sub1', 'xxx.exe') os.chmod(sub1_xxx_exe, 0755) x = env.Detect('xxx.exe') assert x == 'xxx.exe', x env = self.TestEnvironment(ENV = { 'PATH' : [] }) x = env.Detect('xxx.exe') assert x is None, x def test_Dictionary(self): """Test retrieval of known construction variables Fetch them from the Dictionary and check for well-known defaults that get inserted. """ env = self.TestEnvironment(XXX = 'x', YYY = 'y', ZZZ = 'z') assert env.Dictionary('XXX') == 'x' assert env.Dictionary('YYY') == 'y' assert env.Dictionary('XXX', 'ZZZ') == ['x', 'z'] xxx, zzz = env.Dictionary('XXX', 'ZZZ') assert xxx == 'x' assert zzz == 'z' assert 'BUILDERS' in env.Dictionary() assert 'CC' in env.Dictionary() assert 'CCFLAGS' in env.Dictionary() assert 'ENV' in env.Dictionary() assert env['XXX'] == 'x' env['XXX'] = 'foo' assert env.Dictionary('XXX') == 'foo' del env['XXX'] assert 'XXX' not in env.Dictionary() def test_FindIxes(self): "Test FindIxes()" env = self.TestEnvironment(LIBPREFIX='lib', LIBSUFFIX='.a', SHLIBPREFIX='lib', SHLIBSUFFIX='.so', PREFIX='pre', SUFFIX='post') paths = [os.path.join('dir', 'libfoo.a'), os.path.join('dir', 'libfoo.so')] assert paths[0] == env.FindIxes(paths, 'LIBPREFIX', 'LIBSUFFIX') assert paths[1] == env.FindIxes(paths, 'SHLIBPREFIX', 'SHLIBSUFFIX') assert None is env.FindIxes(paths, 'PREFIX', 'POST') paths = ['libfoo.a', 'prefoopost'] assert paths[0] == env.FindIxes(paths, 'LIBPREFIX', 'LIBSUFFIX') assert None is env.FindIxes(paths, 'SHLIBPREFIX', 'SHLIBSUFFIX') assert paths[1] == env.FindIxes(paths, 'PREFIX', 'SUFFIX') def test_ParseConfig(self): """Test the ParseConfig() method""" env = self.TestEnvironment(COMMAND='command', ASFLAGS='assembler', CCFLAGS=[''], CPPDEFINES=[], CPPFLAGS=[''], CPPPATH='string', FRAMEWORKPATH=[], FRAMEWORKS=[], LIBPATH=['list'], LIBS='', LINKFLAGS=[''], RPATH=[]) orig_backtick = env.backtick class my_backtick(object): def __init__(self, save_command, output): self.save_command = save_command self.output = output def __call__(self, command): self.save_command.append(command) return self.output try: save_command = [] env.backtick = my_backtick(save_command, "-I/usr/include/fum -I bar -X\n" + \ "-L/usr/fax -L foo -lxxx -l yyy " + \ "-Wa,-as -Wl,-link " + \ "-Wl,-rpath=rpath1 " + \ "-Wl,-R,rpath2 " + \ "-Wl,-Rrpath3 " + \ "-Wp,-cpp abc " + \ "-framework Carbon " + \ "-frameworkdir=fwd1 " + \ "-Ffwd2 " + \ "-F fwd3 " + \ "-pthread " + \ "-mno-cygwin -mwindows " + \ "-arch i386 -isysroot /tmp +DD64 " + \ "-DFOO -DBAR=value") env.ParseConfig("fake $COMMAND") assert save_command == ['fake command'], save_command assert env['ASFLAGS'] == ['assembler', '-as'], env['ASFLAGS'] assert env['CCFLAGS'] == ['', '-X', '-Wa,-as', '-pthread', '-mno-cygwin', ('-arch', 'i386'), ('-isysroot', '/tmp'), '+DD64'], env['CCFLAGS'] assert env['CPPDEFINES'] == ['FOO', ['BAR', 'value']], env['CPPDEFINES'] assert env['CPPFLAGS'] == ['', '-Wp,-cpp'], env['CPPFLAGS'] assert env['CPPPATH'] == ['string', '/usr/include/fum', 'bar'], env['CPPPATH'] assert env['FRAMEWORKPATH'] == ['fwd1', 'fwd2', 'fwd3'], env['FRAMEWORKPATH'] assert env['FRAMEWORKS'] == ['Carbon'], env['FRAMEWORKS'] assert env['LIBPATH'] == ['list', '/usr/fax', 'foo'], env['LIBPATH'] assert env['LIBS'] == ['xxx', 'yyy', env.File('abc')], env['LIBS'] assert env['LINKFLAGS'] == ['', '-Wl,-link', '-pthread', '-mno-cygwin', '-mwindows', ('-arch', 'i386'), ('-isysroot', '/tmp'), '+DD64'], env['LINKFLAGS'] assert env['RPATH'] == ['rpath1', 'rpath2', 'rpath3'], env['RPATH'] env.backtick = my_backtick([], "-Ibar") env.ParseConfig("fake2") assert env['CPPPATH'] == ['string', '/usr/include/fum', 'bar'], env['CPPPATH'] env.ParseConfig("fake2", unique=0) assert env['CPPPATH'] == ['string', '/usr/include/fum', 'bar', 'bar'], env['CPPPATH'] finally: env.backtick = orig_backtick def test_ParseDepends(self): """Test the ParseDepends() method""" test = TestCmd.TestCmd(workdir = '') test.write('single', """ #file: dependency f0: \ d1 \ d2 \ d3 \ """) test.write('multiple', """ f1: foo f2 f3: bar f4: abc def #file: dependency f5: \ ghi \ jkl \ mno \ """) env = self.TestEnvironment(SINGLE = test.workpath('single')) tlist = [] dlist = [] def my_depends(target, dependency, tlist=tlist, dlist=dlist): tlist.extend(target) dlist.extend(dependency) env.Depends = my_depends env.ParseDepends(test.workpath('does_not_exist')) exc_caught = None try: env.ParseDepends(test.workpath('does_not_exist'), must_exist=1) except IOError: exc_caught = 1 assert exc_caught, "did not catch expected IOError" del tlist[:] del dlist[:] env.ParseDepends('$SINGLE', only_one=1) t = list(map(str, tlist)) d = list(map(str, dlist)) assert t == ['f0'], t assert d == ['d1', 'd2', 'd3'], d del tlist[:] del dlist[:] env.ParseDepends(test.workpath('multiple')) t = list(map(str, tlist)) d = list(map(str, dlist)) assert t == ['f1', 'f2', 'f3', 'f4', 'f5'], t assert d == ['foo', 'bar', 'abc', 'def', 'ghi', 'jkl', 'mno'], d exc_caught = None try: env.ParseDepends(test.workpath('multiple'), only_one=1) except SCons.Errors.UserError: exc_caught = 1 assert exc_caught, "did not catch expected UserError" def test_Platform(self): """Test the Platform() method""" env = self.TestEnvironment(WIN32='win32', NONE='no-such-platform') exc_caught = None try: env.Platform('does_not_exist') except SCons.Errors.UserError: exc_caught = 1 assert exc_caught, "did not catch expected UserError" exc_caught = None try: env.Platform('$NONE') except SCons.Errors.UserError: exc_caught = 1 assert exc_caught, "did not catch expected UserError" env.Platform('posix') assert env['OBJSUFFIX'] == '.o', env['OBJSUFFIX'] env.Platform('$WIN32') assert env['OBJSUFFIX'] == '.obj', env['OBJSUFFIX'] def test_Prepend(self): """Test prepending to construction variables in an Environment """ cases = [ 'a1', 'A1', 'A1a1', 'a2', ['A2'], ['A2', 'a2'], 'a3', UL(['A3']), UL(['A3', 'a', '3']), 'a4', '', 'a4', 'a5', [], ['a5'], 'a6', UL([]), UL(['a', '6']), 'a7', [''], ['', 'a7'], 'a8', UL(['']), UL(['', 'a', '8']), ['e1'], 'E1', ['E1', 'e1'], ['e2'], ['E2'], ['E2', 'e2'], ['e3'], UL(['E3']), UL(['E3', 'e3']), ['e4'], '', ['e4'], ['e5'], [], ['e5'], ['e6'], UL([]), UL(['e6']), ['e7'], [''], ['', 'e7'], ['e8'], UL(['']), UL(['', 'e8']), UL(['i1']), 'I1', UL(['I', '1', 'i1']), UL(['i2']), ['I2'], UL(['I2', 'i2']), UL(['i3']), UL(['I3']), UL(['I3', 'i3']), UL(['i4']), '', UL(['i4']), UL(['i5']), [], UL(['i5']), UL(['i6']), UL([]), UL(['i6']), UL(['i7']), [''], UL(['', 'i7']), UL(['i8']), UL(['']), UL(['', 'i8']), {'d1':1}, 'D1', {'d1':1, 'D1':None}, {'d2':1}, ['D2'], {'d2':1, 'D2':None}, {'d3':1}, UL(['D3']), {'d3':1, 'D3':None}, {'d4':1}, {'D4':1}, {'d4':1, 'D4':1}, {'d5':1}, UD({'D5':1}), UD({'d5':1, 'D5':1}), UD({'u1':1}), 'U1', UD({'u1':1, 'U1':None}), UD({'u2':1}), ['U2'], UD({'u2':1, 'U2':None}), UD({'u3':1}), UL(['U3']), UD({'u3':1, 'U3':None}), UD({'u4':1}), {'U4':1}, UD({'u4':1, 'U4':1}), UD({'u5':1}), UD({'U5':1}), UD({'u5':1, 'U5':1}), '', 'M1', 'M1', '', ['M2'], ['M2'], '', UL(['M3']), UL(['M3']), '', '', '', '', [], [], '', UL([]), UL([]), '', [''], [''], '', UL(['']), UL(['']), [], 'N1', ['N1'], [], ['N2'], ['N2'], [], UL(['N3']), UL(['N3']), [], '', [], [], [], [], [], UL([]), UL([]), [], [''], [''], [], UL(['']), UL(['']), UL([]), 'O1', UL(['O', '1']), UL([]), ['O2'], UL(['O2']), UL([]), UL(['O3']), UL(['O3']), UL([]), '', UL([]), UL([]), [], UL([]), UL([]), UL([]), UL([]), UL([]), [''], UL(['']), UL([]), UL(['']), UL(['']), [''], 'P1', ['P1', ''], [''], ['P2'], ['P2', ''], [''], UL(['P3']), UL(['P3', '']), [''], '', [''], [''], [], [''], [''], UL([]), UL(['']), [''], [''], ['', ''], [''], UL(['']), UL(['', '']), UL(['']), 'Q1', UL(['Q', '1', '']), UL(['']), ['Q2'], UL(['Q2', '']), UL(['']), UL(['Q3']), UL(['Q3', '']), UL(['']), '', UL(['']), UL(['']), [], UL(['']), UL(['']), UL([]), UL(['']), UL(['']), [''], UL(['', '']), UL(['']), UL(['']), UL(['', '']), ] env = Environment() failed = 0 while cases: input, prepend, expect = cases[:3] env['XXX'] = copy.copy(input) try: env.Prepend(XXX = prepend) except Exception, e: if failed == 0: print print " %s Prepend %s exception: %s" % \ (repr(input), repr(prepend), e) failed = failed + 1 else: result = env['XXX'] if result != expect: if failed == 0: print print " %s Prepend %s => %s did not match %s" % \ (repr(input), repr(prepend), repr(result), repr(expect)) failed = failed + 1 del cases[:3] assert failed == 0, "%d Prepend() cases failed" % failed env['UL'] = UL(['foo']) env.Prepend(UL = 'bar') result = env['UL'] assert isinstance(result, UL), repr(result) assert result == ['b', 'a', 'r', 'foo'], result env['CLVar'] = CLVar(['foo']) env.Prepend(CLVar = 'bar') result = env['CLVar'] assert isinstance(result, CLVar), repr(result) assert result == ['bar', 'foo'], result env3 = self.TestEnvironment(X = {'x1' : 7}) env3.Prepend(X = {'x1' : 8, 'x2' : 9}, Y = {'y1' : 10}) assert env3['X'] == {'x1': 8, 'x2' : 9}, env3['X'] assert env3['Y'] == {'y1': 10}, env3['Y'] z1 = Builder() z2 = Builder() env4 = self.TestEnvironment(BUILDERS = {'z1' : z1}) env4.Prepend(BUILDERS = {'z2' : z2}) assert env4['BUILDERS'] == {'z1' : z1, 'z2' : z2}, env4['BUILDERS'] assert hasattr(env4, 'z1') assert hasattr(env4, 'z2') def test_PrependENVPath(self): """Test prepending to an ENV path.""" env1 = self.TestEnvironment(ENV = {'PATH': r'C:\dir\num\one;C:\dir\num\two'}, MYENV = {'MYPATH': r'C:\mydir\num\one;C:\mydir\num\two'}) # have to include the pathsep here so that the test will work on UNIX too. env1.PrependENVPath('PATH',r'C:\dir\num\two',sep = ';') env1.PrependENVPath('PATH',r'C:\dir\num\three',sep = ';') env1.PrependENVPath('MYPATH',r'C:\mydir\num\three','MYENV',sep = ';') env1.PrependENVPath('MYPATH',r'C:\mydir\num\one','MYENV',sep = ';') # this should do nothing since delete_existing is 0 env1.PrependENVPath('MYPATH',r'C:\mydir\num\three','MYENV', sep = ';', delete_existing=0) assert(env1['ENV']['PATH'] == r'C:\dir\num\three;C:\dir\num\two;C:\dir\num\one') assert(env1['MYENV']['MYPATH'] == r'C:\mydir\num\one;C:\mydir\num\three;C:\mydir\num\two') test = TestCmd.TestCmd(workdir = '') test.subdir('sub1', 'sub2') p=env1['ENV']['PATH'] env1.PrependENVPath('PATH','#sub1', sep = ';') env1.PrependENVPath('PATH',env1.fs.Dir('sub2'), sep = ';') assert env1['ENV']['PATH'] == 'sub2;sub1;' + p, env1['ENV']['PATH'] def test_PrependUnique(self): """Test prepending unique values to construction variables This strips values that are already present when lists are involved.""" env = self.TestEnvironment(AAA1 = 'a1', AAA2 = 'a2', AAA3 = 'a3', AAA4 = 'a4', AAA5 = 'a5', BBB1 = ['b1'], BBB2 = ['b2'], BBB3 = ['b3'], BBB4 = ['b4'], BBB5 = ['b5'], CCC1 = '', CCC2 = '', DDD1 = ['a', 'b', 'c']) env.PrependUnique(AAA1 = 'a1', AAA2 = ['a2'], AAA3 = ['a3', 'b', 'c', 'b', 'a3'], # ignore dups AAA4 = 'a4.new', AAA5 = ['a5.new'], BBB1 = 'b1', BBB2 = ['b2'], BBB3 = ['b3', 'b', 'c', 'b3'], BBB4 = 'b4.new', BBB5 = ['b5.new'], CCC1 = 'c1', CCC2 = ['c2'], DDD1 = 'b') assert env['AAA1'] == 'a1a1', env['AAA1'] assert env['AAA2'] == ['a2'], env['AAA2'] assert env['AAA3'] == ['c', 'b', 'a3'], env['AAA3'] assert env['AAA4'] == 'a4.newa4', env['AAA4'] assert env['AAA5'] == ['a5.new', 'a5'], env['AAA5'] assert env['BBB1'] == ['b1'], env['BBB1'] assert env['BBB2'] == ['b2'], env['BBB2'] assert env['BBB3'] == ['b', 'c', 'b3'], env['BBB3'] assert env['BBB4'] == ['b4.new', 'b4'], env['BBB4'] assert env['BBB5'] == ['b5.new', 'b5'], env['BBB5'] assert env['CCC1'] == 'c1', env['CCC1'] assert env['CCC2'] == ['c2'], env['CCC2'] assert env['DDD1'] == ['a', 'b', 'c'], env['DDD1'] env.PrependUnique(DDD1 = 'b', delete_existing=1) assert env['DDD1'] == ['b', 'a', 'c'], env['DDD1'] # b moves to front env.PrependUnique(DDD1 = ['a','c'], delete_existing=1) assert env['DDD1'] == ['a', 'c', 'b'], env['DDD1'] # a & c move to front env.PrependUnique(DDD1 = ['d','e','d'], delete_existing=1) assert env['DDD1'] == ['d', 'e', 'a', 'c', 'b'], env['DDD1'] env['CLVar'] = CLVar([]) env.PrependUnique(CLVar = 'bar') result = env['CLVar'] assert isinstance(result, CLVar), repr(result) assert result == ['bar'], result env['CLVar'] = CLVar(['abc']) env.PrependUnique(CLVar = 'bar') result = env['CLVar'] assert isinstance(result, CLVar), repr(result) assert result == ['bar', 'abc'], result env['CLVar'] = CLVar(['bar']) env.PrependUnique(CLVar = 'bar') result = env['CLVar'] assert isinstance(result, CLVar), repr(result) assert result == ['bar'], result def test_Replace(self): """Test replacing construction variables in an Environment After creation of the Environment, of course. """ env1 = self.TestEnvironment(AAA = 'a', BBB = 'b') env1.Replace(BBB = 'bbb', CCC = 'ccc') env2 = self.TestEnvironment(AAA = 'a', BBB = 'bbb', CCC = 'ccc') assert env1 == env2, diff_env(env1, env2) b1 = Builder() b2 = Builder() env3 = self.TestEnvironment(BUILDERS = {'b1' : b1}) assert hasattr(env3, 'b1'), "b1 was not set" env3.Replace(BUILDERS = {'b2' : b2}) assert not hasattr(env3, 'b1'), "b1 was not cleared" assert hasattr(env3, 'b2'), "b2 was not set" def test_ReplaceIxes(self): "Test ReplaceIxes()" env = self.TestEnvironment(LIBPREFIX='lib', LIBSUFFIX='.a', SHLIBPREFIX='lib', SHLIBSUFFIX='.so', PREFIX='pre', SUFFIX='post') assert 'libfoo.a' == env.ReplaceIxes('libfoo.so', 'SHLIBPREFIX', 'SHLIBSUFFIX', 'LIBPREFIX', 'LIBSUFFIX') assert os.path.join('dir', 'libfoo.a') == env.ReplaceIxes(os.path.join('dir', 'libfoo.so'), 'SHLIBPREFIX', 'SHLIBSUFFIX', 'LIBPREFIX', 'LIBSUFFIX') assert 'libfoo.a' == env.ReplaceIxes('prefoopost', 'PREFIX', 'SUFFIX', 'LIBPREFIX', 'LIBSUFFIX') def test_SetDefault(self): """Test the SetDefault method""" env = self.TestEnvironment(tools = []) env.SetDefault(V1 = 1) env.SetDefault(V1 = 2) assert env['V1'] == 1 env['V2'] = 2 env.SetDefault(V2 = 1) assert env['V2'] == 2 def test_Tool(self): """Test the Tool() method""" env = self.TestEnvironment(LINK='link', NONE='no-such-tool') exc_caught = None try: env.Tool('does_not_exist') except SCons.Errors.EnvironmentError: exc_caught = 1 assert exc_caught, "did not catch expected EnvironmentError" exc_caught = None try: env.Tool('$NONE') except SCons.Errors.EnvironmentError: exc_caught = 1 assert exc_caught, "did not catch expected EnvironmentError" # Use a non-existent toolpath directory just to make sure we # can call Tool() with the keyword argument. env.Tool('cc', toolpath=['/no/such/directory']) assert env['CC'] == 'cc', env['CC'] env.Tool('$LINK') assert env['LINK'] == '$SMARTLINK', env['LINK'] # Test that the environment stores the toolpath and # re-uses it for later calls. test = TestCmd.TestCmd(workdir = '') test.write('xxx.py', """\ def exists(env): 1 def generate(env): env['XXX'] = 'one' """) test.write('yyy.py', """\ def exists(env): 1 def generate(env): env['YYY'] = 'two' """) env = self.TestEnvironment(tools=['xxx'], toolpath=[test.workpath('')]) assert env['XXX'] == 'one', env['XXX'] env.Tool('yyy') assert env['YYY'] == 'two', env['YYY'] def test_WhereIs(self): """Test the WhereIs() method""" test = TestCmd.TestCmd(workdir = '') sub1_xxx_exe = test.workpath('sub1', 'xxx.exe') sub2_xxx_exe = test.workpath('sub2', 'xxx.exe') sub3_xxx_exe = test.workpath('sub3', 'xxx.exe') sub4_xxx_exe = test.workpath('sub4', 'xxx.exe') test.subdir('subdir', 'sub1', 'sub2', 'sub3', 'sub4') if sys.platform != 'win32': test.write(sub1_xxx_exe, "\n") os.mkdir(sub2_xxx_exe) test.write(sub3_xxx_exe, "\n") os.chmod(sub3_xxx_exe, 0777) test.write(sub4_xxx_exe, "\n") os.chmod(sub4_xxx_exe, 0777) env_path = os.environ['PATH'] pathdirs_1234 = [ test.workpath('sub1'), test.workpath('sub2'), test.workpath('sub3'), test.workpath('sub4'), ] + env_path.split(os.pathsep) pathdirs_1243 = [ test.workpath('sub1'), test.workpath('sub2'), test.workpath('sub4'), test.workpath('sub3'), ] + env_path.split(os.pathsep) path = os.pathsep.join(pathdirs_1234) env = self.TestEnvironment(ENV = {'PATH' : path}) wi = env.WhereIs('xxx.exe') assert wi == test.workpath(sub3_xxx_exe), wi wi = env.WhereIs('xxx.exe', pathdirs_1243) assert wi == test.workpath(sub4_xxx_exe), wi wi = env.WhereIs('xxx.exe', os.pathsep.join(pathdirs_1243)) assert wi == test.workpath(sub4_xxx_exe), wi wi = env.WhereIs('xxx.exe', reject = sub3_xxx_exe) assert wi == test.workpath(sub4_xxx_exe), wi wi = env.WhereIs('xxx.exe', pathdirs_1243, reject = sub3_xxx_exe) assert wi == test.workpath(sub4_xxx_exe), wi path = os.pathsep.join(pathdirs_1243) env = self.TestEnvironment(ENV = {'PATH' : path}) wi = env.WhereIs('xxx.exe') assert wi == test.workpath(sub4_xxx_exe), wi wi = env.WhereIs('xxx.exe', pathdirs_1234) assert wi == test.workpath(sub3_xxx_exe), wi wi = env.WhereIs('xxx.exe', os.pathsep.join(pathdirs_1234)) assert wi == test.workpath(sub3_xxx_exe), wi if sys.platform == 'win32': wi = env.WhereIs('xxx', pathext = '') assert wi is None, wi wi = env.WhereIs('xxx', pathext = '.exe') assert wi == test.workpath(sub4_xxx_exe), wi wi = env.WhereIs('xxx', path = pathdirs_1234, pathext = '.BAT;.EXE') assert wi.lower() == test.workpath(sub3_xxx_exe).lower(), wi # Test that we return a normalized path even when # the path contains forward slashes. forward_slash = test.workpath('') + '/sub3' wi = env.WhereIs('xxx', path = forward_slash, pathext = '.EXE') assert wi.lower() == test.workpath(sub3_xxx_exe).lower(), wi def test_Action(self): """Test the Action() method""" import SCons.Action env = self.TestEnvironment(FOO = 'xyzzy') a = env.Action('foo') assert a, a assert a.__class__ is SCons.Action.CommandAction, a.__class__ a = env.Action('$FOO') assert a, a assert a.__class__ is SCons.Action.CommandAction, a.__class__ a = env.Action('$$FOO') assert a, a assert a.__class__ is SCons.Action.LazyAction, a.__class__ a = env.Action(['$FOO', 'foo']) assert a, a assert a.__class__ is SCons.Action.ListAction, a.__class__ def func(arg): pass a = env.Action(func) assert a, a assert a.__class__ is SCons.Action.FunctionAction, a.__class__ def test_AddPostAction(self): """Test the AddPostAction() method""" env = self.TestEnvironment(FOO='fff', BAR='bbb') n = env.AddPostAction('$FOO', lambda x: x) assert str(n[0]) == 'fff', n[0] n = env.AddPostAction(['ggg', '$BAR'], lambda x: x) assert str(n[0]) == 'ggg', n[0] assert str(n[1]) == 'bbb', n[1] def test_AddPreAction(self): """Test the AddPreAction() method""" env = self.TestEnvironment(FOO='fff', BAR='bbb') n = env.AddPreAction('$FOO', lambda x: x) assert str(n[0]) == 'fff', n[0] n = env.AddPreAction(['ggg', '$BAR'], lambda x: x) assert str(n[0]) == 'ggg', n[0] assert str(n[1]) == 'bbb', n[1] def test_Alias(self): """Test the Alias() method""" env = self.TestEnvironment(FOO='kkk', BAR='lll', EA='export_alias') tgt = env.Alias('new_alias')[0] assert str(tgt) == 'new_alias', tgt assert tgt.sources == [], tgt.sources assert not hasattr(tgt, 'builder'), tgt.builder tgt = env.Alias('None_alias', None)[0] assert str(tgt) == 'None_alias', tgt assert tgt.sources == [], tgt.sources tgt = env.Alias('empty_list', [])[0] assert str(tgt) == 'empty_list', tgt assert tgt.sources == [], tgt.sources tgt = env.Alias('export_alias', [ 'asrc1', '$FOO' ])[0] assert str(tgt) == 'export_alias', tgt assert len(tgt.sources) == 2, list(map(str, tgt.sources)) assert str(tgt.sources[0]) == 'asrc1', list(map(str, tgt.sources)) assert str(tgt.sources[1]) == 'kkk', list(map(str, tgt.sources)) n = env.Alias(tgt, source = ['$BAR', 'asrc4'])[0] assert n is tgt, n assert len(tgt.sources) == 4, list(map(str, tgt.sources)) assert str(tgt.sources[2]) == 'lll', list(map(str, tgt.sources)) assert str(tgt.sources[3]) == 'asrc4', list(map(str, tgt.sources)) n = env.Alias('$EA', 'asrc5')[0] assert n is tgt, n assert len(tgt.sources) == 5, list(map(str, tgt.sources)) assert str(tgt.sources[4]) == 'asrc5', list(map(str, tgt.sources)) t1, t2 = env.Alias(['t1', 't2'], ['asrc6', 'asrc7']) assert str(t1) == 't1', t1 assert str(t2) == 't2', t2 assert len(t1.sources) == 2, list(map(str, t1.sources)) assert str(t1.sources[0]) == 'asrc6', list(map(str, t1.sources)) assert str(t1.sources[1]) == 'asrc7', list(map(str, t1.sources)) assert len(t2.sources) == 2, list(map(str, t2.sources)) assert str(t2.sources[0]) == 'asrc6', list(map(str, t2.sources)) assert str(t2.sources[1]) == 'asrc7', list(map(str, t2.sources)) tgt = env.Alias('add', 's1') tgt = env.Alias('add', 's2')[0] s = list(map(str, tgt.sources)) assert s == ['s1', 's2'], s tgt = env.Alias(tgt, 's3')[0] s = list(map(str, tgt.sources)) assert s == ['s1', 's2', 's3'], s tgt = env.Alias('act', None, "action1")[0] s = str(tgt.builder.action) assert s == "action1", s tgt = env.Alias('act', None, "action2")[0] s = str(tgt.builder.action) assert s == "action1\naction2", s tgt = env.Alias(tgt, None, "action3")[0] s = str(tgt.builder.action) assert s == "action1\naction2\naction3", s def test_AlwaysBuild(self): """Test the AlwaysBuild() method""" env = self.TestEnvironment(FOO='fff', BAR='bbb') 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__ == 'Entry' assert t[1].path == 'bfff' assert t[1].always_build assert t[2].__class__.__name__ == 'Entry' assert t[2].path == 'c' assert t[2].always_build assert t[3].__class__.__name__ == 'Entry' assert t[3].path == 'd' assert t[3].always_build 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_VariantDir(self): """Test the VariantDir() method""" class MyFS(object): def Dir(self, name): return name def VariantDir(self, variant_dir, src_dir, duplicate): self.variant_dir = variant_dir self.src_dir = src_dir self.duplicate = duplicate env = self.TestEnvironment(FOO = 'fff', BAR = 'bbb') env.fs = MyFS() env.VariantDir('build', 'src') assert env.fs.variant_dir == 'build', env.fs.variant_dir assert env.fs.src_dir == 'src', env.fs.src_dir assert env.fs.duplicate == 1, env.fs.duplicate env.VariantDir('build${FOO}', '${BAR}src', 0) assert env.fs.variant_dir == 'buildfff', env.fs.variant_dir assert env.fs.src_dir == 'bbbsrc', env.fs.src_dir assert env.fs.duplicate == 0, env.fs.duplicate def test_Builder(self): """Test the Builder() method""" env = self.TestEnvironment(FOO = 'xyzzy') b = env.Builder(action = 'foo') assert b is not None, b b = env.Builder(action = '$FOO') assert b is not None, b b = env.Builder(action = ['$FOO', 'foo']) assert b is not None, b def func(arg): pass b = env.Builder(action = func) assert b is not None, b b = env.Builder(generator = func) assert b is not None, b def test_CacheDir(self): """Test the CacheDir() method""" env = self.TestEnvironment(CD = 'CacheDir') env.CacheDir('foo') assert env._CacheDir_path == 'foo', env._CacheDir_path env.CacheDir('$CD') assert env._CacheDir_path == 'CacheDir', env._CacheDir_path def test_Clean(self): """Test the Clean() method""" env = self.TestEnvironment(FOO = 'fff', BAR = 'bbb') CT = SCons.Environment.CleanTargets foo = env.arg2nodes('foo')[0] fff = env.arg2nodes('fff')[0] t = env.Clean('foo', 'aaa') l = list(map(str, CT[foo])) assert l == ['aaa'], l t = env.Clean(foo, ['$BAR', 'ccc']) l = list(map(str, CT[foo])) assert l == ['aaa', 'bbb', 'ccc'], l eee = env.arg2nodes('eee')[0] t = env.Clean('$FOO', 'ddd') l = list(map(str, CT[fff])) assert l == ['ddd'], l t = env.Clean(fff, [eee, 'fff']) l = list(map(str, CT[fff])) assert l == ['ddd', 'eee', 'fff'], l def test_Command(self): """Test the Command() method.""" env = Environment() t = env.Command(target='foo.out', source=['foo1.in', 'foo2.in'], action='buildfoo $target $source')[0] assert t.builder is not None assert t.builder.action.__class__.__name__ == 'CommandAction' assert t.builder.action.cmd_list == 'buildfoo $target $source' assert 'foo1.in' in [x.path for x in t.sources] assert 'foo2.in' in [x.path for x in t.sources] sub = env.fs.Dir('sub') t = env.Command(target='bar.out', source='sub', action='buildbar $target $source')[0] assert 'sub' in [x.path for x in t.sources] def testFunc(env, target, source): assert str(target[0]) == 'foo.out' assert 'foo1.in' in list(map(str, source)) and 'foo2.in' in list(map(str, source)), list(map(str, source)) return 0 t = env.Command(target='foo.out', source=['foo1.in','foo2.in'], action=testFunc)[0] assert t.builder is not None assert t.builder.action.__class__.__name__ == 'FunctionAction' t.build() assert 'foo1.in' in [x.path for x in t.sources] assert 'foo2.in' in [x.path for x in t.sources] x = [] def test2(baz, x=x): x.append(baz) env = self.TestEnvironment(TEST2 = test2) t = env.Command(target='baz.out', source='baz.in', action='${TEST2(XYZ)}', XYZ='magic word')[0] assert t.builder is not None t.build() assert x[0] == 'magic word', x t = env.Command(target='${X}.out', source='${X}.in', action = 'foo', X = 'xxx')[0] assert str(t) == 'xxx.out', str(t) assert 'xxx.in' in [x.path for x in t.sources] env = self.TestEnvironment(source_scanner = 'should_not_find_this') t = env.Command(target='file.out', source='file.in', action = 'foo', source_scanner = 'fake')[0] assert t.builder.source_scanner == 'fake', t.builder.source_scanner def test_Configure(self): """Test the Configure() method""" # Configure() will write to a local temporary file. test = TestCmd.TestCmd(workdir = '') save = os.getcwd() try: os.chdir(test.workpath()) env = self.TestEnvironment(FOO = 'xyzzy') def func(arg): pass c = env.Configure() assert c is not None, c c.Finish() c = env.Configure(custom_tests = {'foo' : func, '$FOO' : func}) assert c is not None, c assert hasattr(c, 'foo') assert hasattr(c, 'xyzzy') c.Finish() finally: os.chdir(save) def test_Depends(self): """Test the explicit Depends method.""" env = self.TestEnvironment(FOO = 'xxx', BAR='yyy') env.Dir('dir1') env.Dir('dir2') env.File('xxx.py') env.File('yyy.py') t = env.Depends(target='EnvironmentTest.py', dependency='Environment.py')[0] assert t.__class__.__name__ == 'Entry', t.__class__.__name__ assert t.path == 'EnvironmentTest.py' assert len(t.depends) == 1 d = t.depends[0] assert d.__class__.__name__ == 'Entry', d.__class__.__name__ assert d.path == 'Environment.py' t = env.Depends(target='${FOO}.py', dependency='${BAR}.py')[0] assert t.__class__.__name__ == 'File', t.__class__.__name__ assert t.path == 'xxx.py' assert len(t.depends) == 1 d = t.depends[0] assert d.__class__.__name__ == 'File', d.__class__.__name__ assert d.path == 'yyy.py' t = env.Depends(target='dir1', dependency='dir2')[0] assert t.__class__.__name__ == 'Dir', t.__class__.__name__ assert t.path == 'dir1' assert len(t.depends) == 1 d = t.depends[0] assert d.__class__.__name__ == 'Dir', d.__class__.__name__ assert d.path == 'dir2' def test_Dir(self): """Test the Dir() method""" class MyFS(object): def Dir(self, name): return 'Dir(%s)' % name env = self.TestEnvironment(FOO = 'foodir', BAR = 'bardir') env.fs = MyFS() d = env.Dir('d') assert d == 'Dir(d)', d d = env.Dir('$FOO') assert d == 'Dir(foodir)', d d = env.Dir('${BAR}_$BAR') assert d == 'Dir(bardir_bardir)', d d = env.Dir(['dir1']) assert d == ['Dir(dir1)'], d d = env.Dir(['dir1', 'dir2']) assert d == ['Dir(dir1)', 'Dir(dir2)'], d def test_NoClean(self): """Test the NoClean() method""" env = self.TestEnvironment(FOO='ggg', BAR='hhh') env.Dir('p_hhhb') env.File('p_d') t = env.NoClean('p_a', 'p_${BAR}b', ['p_c', 'p_d'], 'p_$FOO') assert t[0].__class__.__name__ == 'Entry', t[0].__class__.__name__ assert t[0].path == 'p_a' assert t[0].noclean assert t[1].__class__.__name__ == 'Dir', t[1].__class__.__name__ assert t[1].path == 'p_hhhb' assert t[1].noclean assert t[2].__class__.__name__ == 'Entry', t[2].__class__.__name__ assert t[2].path == 'p_c' assert t[2].noclean assert t[3].__class__.__name__ == 'File', t[3].__class__.__name__ assert t[3].path == 'p_d' assert t[3].noclean assert t[4].__class__.__name__ == 'Entry', t[4].__class__.__name__ assert t[4].path == 'p_ggg' assert t[4].noclean def test_Dump(self): """Test the Dump() method""" env = self.TestEnvironment(FOO = 'foo') assert env.Dump('FOO') == "'foo'", env.Dump('FOO') assert len(env.Dump()) > 200, env.Dump() # no args version def test_Environment(self): """Test the Environment() method""" env = self.TestEnvironment(FOO = 'xxx', BAR = 'yyy') e2 = env.Environment(X = '$FOO', Y = '$BAR') assert e2['X'] == 'xxx', e2['X'] assert e2['Y'] == 'yyy', e2['Y'] def test_Execute(self): """Test the Execute() method""" class MyAction(object): def __init__(self, *args, **kw): self.args = args def __call__(self, target, source, env): return "%s executed" % self.args env = Environment() env.Action = MyAction result = env.Execute("foo") assert result == "foo executed", result def test_Entry(self): """Test the Entry() method""" class MyFS(object): def Entry(self, name): return 'Entry(%s)' % name env = self.TestEnvironment(FOO = 'fooentry', BAR = 'barentry') env.fs = MyFS() e = env.Entry('e') assert e == 'Entry(e)', e e = env.Entry('$FOO') assert e == 'Entry(fooentry)', e e = env.Entry('${BAR}_$BAR') assert e == 'Entry(barentry_barentry)', e e = env.Entry(['entry1']) assert e == ['Entry(entry1)'], e e = env.Entry(['entry1', 'entry2']) assert e == ['Entry(entry1)', 'Entry(entry2)'], e def test_File(self): """Test the File() method""" class MyFS(object): def File(self, name): return 'File(%s)' % name env = self.TestEnvironment(FOO = 'foofile', BAR = 'barfile') env.fs = MyFS() f = env.File('f') assert f == 'File(f)', f f = env.File('$FOO') assert f == 'File(foofile)', f f = env.File('${BAR}_$BAR') assert f == 'File(barfile_barfile)', f f = env.File(['file1']) assert f == ['File(file1)'], f f = env.File(['file1', 'file2']) assert f == ['File(file1)', 'File(file2)'], f def test_FindFile(self): """Test the FindFile() method""" env = self.TestEnvironment(FOO = 'fff', BAR = 'bbb') r = env.FindFile('foo', ['no_such_directory']) assert r is None, r # XXX def test_Flatten(self): """Test the Flatten() method""" env = Environment() l = env.Flatten([1]) assert l == [1] l = env.Flatten([1, [2, [3, [4]]]]) assert l == [1, 2, 3, 4], l def test_GetBuildPath(self): """Test the GetBuildPath() method.""" env = self.TestEnvironment(MAGIC = 'xyzzy') p = env.GetBuildPath('foo') assert p == 'foo', p p = env.GetBuildPath('$MAGIC') assert p == 'xyzzy', p def test_Ignore(self): """Test the explicit Ignore method.""" env = self.TestEnvironment(FOO='yyy', BAR='zzz') env.Dir('dir1') env.Dir('dir2') env.File('yyyzzz') env.File('zzzyyy') t = env.Ignore(target='targ.py', dependency='dep.py')[0] assert t.__class__.__name__ == 'Entry', t.__class__.__name__ assert t.path == 'targ.py' assert len(t.ignore) == 1 i = t.ignore[0] assert i.__class__.__name__ == 'Entry', i.__class__.__name__ assert i.path == 'dep.py' t = env.Ignore(target='$FOO$BAR', dependency='$BAR$FOO')[0] assert t.__class__.__name__ == 'File', t.__class__.__name__ assert t.path == 'yyyzzz' assert len(t.ignore) == 1 i = t.ignore[0] assert i.__class__.__name__ == 'File', i.__class__.__name__ assert i.path == 'zzzyyy' t = env.Ignore(target='dir1', dependency='dir2')[0] assert t.__class__.__name__ == 'Dir', t.__class__.__name__ assert t.path == 'dir1' assert len(t.ignore) == 1 i = t.ignore[0] assert i.__class__.__name__ == 'Dir', i.__class__.__name__ assert i.path == 'dir2' def test_Literal(self): """Test the Literal() method""" env = self.TestEnvironment(FOO='fff', BAR='bbb') list = env.subst_list([env.Literal('$FOO'), '$BAR'])[0] assert list == ['$FOO', 'bbb'], list list = env.subst_list(['$FOO', env.Literal('$BAR')])[0] assert list == ['fff', '$BAR'], list def test_Local(self): """Test the Local() method.""" env = self.TestEnvironment(FOO='lll') l = env.Local(env.fs.File('fff')) assert str(l[0]) == 'fff', l[0] l = env.Local('ggg', '$FOO') assert str(l[0]) == 'ggg', l[0] assert str(l[1]) == 'lll', l[1] def test_Precious(self): """Test the Precious() method""" env = self.TestEnvironment(FOO='ggg', BAR='hhh') env.Dir('p_hhhb') env.File('p_d') t = env.Precious('p_a', 'p_${BAR}b', ['p_c', 'p_d'], 'p_$FOO') assert t[0].__class__.__name__ == 'Entry', t[0].__class__.__name__ assert t[0].path == 'p_a' assert t[0].precious assert t[1].__class__.__name__ == 'Dir', t[1].__class__.__name__ assert t[1].path == 'p_hhhb' assert t[1].precious assert t[2].__class__.__name__ == 'Entry', t[2].__class__.__name__ assert t[2].path == 'p_c' assert t[2].precious assert t[3].__class__.__name__ == 'File', t[3].__class__.__name__ assert t[3].path == 'p_d' assert t[3].precious assert t[4].__class__.__name__ == 'Entry', t[4].__class__.__name__ assert t[4].path == 'p_ggg' assert t[4].precious def test_Repository(self): """Test the Repository() method.""" class MyFS(object): def __init__(self): self.list = [] def Repository(self, *dirs): self.list.extend(list(dirs)) def Dir(self, name): return name env = self.TestEnvironment(FOO='rrr', BAR='sss') env.fs = MyFS() env.Repository('/tmp/foo') env.Repository('/tmp/$FOO', '/tmp/$BAR/foo') expect = ['/tmp/foo', '/tmp/rrr', '/tmp/sss/foo'] assert env.fs.list == expect, env.fs.list def test_Scanner(self): """Test the Scanner() method""" def scan(node, env, target, arg): pass env = self.TestEnvironment(FOO = scan) s = env.Scanner('foo') assert s is not None, s s = env.Scanner(function = 'foo') assert s is not None, s if 0: s = env.Scanner('$FOO') assert s is not None, s s = env.Scanner(function = '$FOO') assert s is not None, s def test_SConsignFile(self): """Test the SConsignFile() method""" import SCons.SConsign class MyFS(object): SConstruct_dir = os.sep + 'dir' env = self.TestEnvironment(FOO = 'SConsign', BAR = os.path.join(os.sep, 'File')) env.fs = MyFS() env.Execute = lambda action: None try: fnames = [] dbms = [] def capture(name, dbm_module, fnames=fnames, dbms=dbms): fnames.append(name) dbms.append(dbm_module) save_SConsign_File = SCons.SConsign.File SCons.SConsign.File = capture env.SConsignFile('foo') assert fnames[-1] == os.path.join(os.sep, 'dir', 'foo'), fnames assert dbms[-1] is None, dbms env.SConsignFile('$FOO') assert fnames[-1] == os.path.join(os.sep, 'dir', 'SConsign'), fnames assert dbms[-1] is None, dbms env.SConsignFile('/$FOO') assert fnames[-1] == os.sep + 'SConsign', fnames assert dbms[-1] is None, dbms env.SConsignFile(os.sep + '$FOO') assert fnames[-1] == os.sep + 'SConsign', fnames assert dbms[-1] is None, dbms env.SConsignFile('$BAR', 'x') assert fnames[-1] == os.path.join(os.sep, 'File'), fnames assert dbms[-1] == 'x', dbms env.SConsignFile('__$BAR', 7) assert fnames[-1] == os.path.join(os.sep, 'dir', '__', 'File'), fnames assert dbms[-1] == 7, dbms env.SConsignFile() assert fnames[-1] == os.path.join(os.sep, 'dir', '.sconsign'), fnames assert dbms[-1] is None, dbms env.SConsignFile(None) assert fnames[-1] is None, fnames assert dbms[-1] is None, dbms finally: SCons.SConsign.File = save_SConsign_File def test_SideEffect(self): """Test the SideEffect() method""" env = self.TestEnvironment(LIB='lll', FOO='fff', BAR='bbb') env.File('mylll.pdb') env.Dir('mymmm.pdb') foo = env.Object('foo.obj', 'foo.cpp')[0] bar = env.Object('bar.obj', 'bar.cpp')[0] s = env.SideEffect('mylib.pdb', ['foo.obj', 'bar.obj'])[0] assert s.__class__.__name__ == 'Entry', s.__class__.__name__ assert s.path == 'mylib.pdb' assert s.side_effect assert foo.side_effects == [s] assert bar.side_effects == [s] fff = env.Object('fff.obj', 'fff.cpp')[0] bbb = env.Object('bbb.obj', 'bbb.cpp')[0] s = env.SideEffect('my${LIB}.pdb', ['${FOO}.obj', '${BAR}.obj'])[0] assert s.__class__.__name__ == 'File', s.__class__.__name__ assert s.path == 'mylll.pdb' assert s.side_effect assert fff.side_effects == [s], fff.side_effects assert bbb.side_effects == [s], bbb.side_effects ggg = env.Object('ggg.obj', 'ggg.cpp')[0] ccc = env.Object('ccc.obj', 'ccc.cpp')[0] s = env.SideEffect('mymmm.pdb', ['ggg.obj', 'ccc.obj'])[0] assert s.__class__.__name__ == 'Dir', s.__class__.__name__ assert s.path == 'mymmm.pdb' assert s.side_effect assert ggg.side_effects == [s], ggg.side_effects assert ccc.side_effects == [s], ccc.side_effects def test_SourceCode(self): """Test the SourceCode() method.""" env = self.TestEnvironment(FOO='mmm', BAR='nnn') e = env.SourceCode('foo', None)[0] assert e.path == 'foo' s = e.src_builder() assert s is None, s b = Builder() e = env.SourceCode(e, b)[0] assert e.path == 'foo' s = e.src_builder() assert s is b, s e = env.SourceCode('$BAR$FOO', None)[0] assert e.path == 'nnnmmm' s = e.src_builder() assert s is None, s def test_SourceSignatures(type): """Test the SourceSignatures() method""" import SCons.Errors env = type.TestEnvironment(M = 'MD5', T = 'timestamp') exc_caught = None try: env.SourceSignatures('invalid_type') except SCons.Errors.UserError: exc_caught = 1 assert exc_caught, "did not catch expected UserError" env.SourceSignatures('MD5') assert env.src_sig_type == 'MD5', env.src_sig_type env.SourceSignatures('$M') assert env.src_sig_type == 'MD5', env.src_sig_type env.SourceSignatures('timestamp') assert env.src_sig_type == 'timestamp', env.src_sig_type env.SourceSignatures('$T') assert env.src_sig_type == 'timestamp', env.src_sig_type try: import SCons.Util save_md5 = SCons.Util.md5 SCons.Util.md5 = None try: env.SourceSignatures('MD5') except SCons.Errors.UserError: pass else: self.fail('Did not catch expected UserError') finally: SCons.Util.md5 = save_md5 def test_Split(self): """Test the Split() method""" env = self.TestEnvironment(FOO='fff', BAR='bbb') s = env.Split("foo bar") assert s == ["foo", "bar"], s s = env.Split("$FOO bar") assert s == ["fff", "bar"], s s = env.Split(["foo", "bar"]) assert s == ["foo", "bar"], s s = env.Split(["foo", "${BAR}-bbb"]) assert s == ["foo", "bbb-bbb"], s s = env.Split("foo") assert s == ["foo"], s s = env.Split("$FOO$BAR") assert s == ["fffbbb"], s def test_TargetSignatures(type): """Test the TargetSignatures() method""" import SCons.Errors env = type.TestEnvironment(B = 'build', C = 'content') exc_caught = None try: env.TargetSignatures('invalid_type') except SCons.Errors.UserError: exc_caught = 1 assert exc_caught, "did not catch expected UserError" assert not hasattr(env, '_build_signature') env.TargetSignatures('build') assert env.tgt_sig_type == 'build', env.tgt_sig_type env.TargetSignatures('$B') assert env.tgt_sig_type == 'build', env.tgt_sig_type env.TargetSignatures('content') assert env.tgt_sig_type == 'content', env.tgt_sig_type env.TargetSignatures('$C') assert env.tgt_sig_type == 'content', env.tgt_sig_type env.TargetSignatures('MD5') assert env.tgt_sig_type == 'MD5', env.tgt_sig_type env.TargetSignatures('timestamp') assert env.tgt_sig_type == 'timestamp', env.tgt_sig_type try: import SCons.Util save_md5 = SCons.Util.md5 SCons.Util.md5 = None try: env.TargetSignatures('MD5') except SCons.Errors.UserError: pass else: self.fail('Did not catch expected UserError') try: env.TargetSignatures('content') except SCons.Errors.UserError: pass else: self.fail('Did not catch expected UserError') finally: SCons.Util.md5 = save_md5 def test_Value(self): """Test creating a Value() object """ env = Environment() v1 = env.Value('a') assert v1.value == 'a', v1.value value2 = 'a' v2 = env.Value(value2) assert v2.value == value2, v2.value assert v2.value is value2, v2.value assert not v1 is v2 assert v1.value == v2.value v3 = env.Value('c', 'build-c') assert v3.value == 'c', v3.value def test_Environment_global_variable(type): """Test setting Environment variable to an Environment.Base subclass""" class MyEnv(SCons.Environment.Base): def xxx(self, string): return self.subst(string) SCons.Environment.Environment = MyEnv env = SCons.Environment.Environment(FOO = 'foo') f = env.subst('$FOO') assert f == 'foo', f f = env.xxx('$FOO') assert f == 'foo', f def test_bad_keywords(self): """Test trying to use reserved keywords in an Environment""" added = [] env = self.TestEnvironment(TARGETS = 'targets', SOURCES = 'sources', SOURCE = 'source', TARGET = 'target', CHANGED_SOURCES = 'changed_sources', CHANGED_TARGETS = 'changed_targets', UNCHANGED_SOURCES = 'unchanged_sources', UNCHANGED_TARGETS = 'unchanged_targets', INIT = 'init') bad_msg = '%s is not reserved, but got omitted; see Environment.construction_var_name_ok' added.append('INIT') for x in self.reserved_variables: assert x not in env, env[x] for x in added: assert x in env, bad_msg % x env.Append(TARGETS = 'targets', SOURCES = 'sources', SOURCE = 'source', TARGET = 'target', CHANGED_SOURCES = 'changed_sources', CHANGED_TARGETS = 'changed_targets', UNCHANGED_SOURCES = 'unchanged_sources', UNCHANGED_TARGETS = 'unchanged_targets', APPEND = 'append') added.append('APPEND') for x in self.reserved_variables: assert x not in env, env[x] for x in added: assert x in env, bad_msg % x env.AppendUnique(TARGETS = 'targets', SOURCES = 'sources', SOURCE = 'source', TARGET = 'target', CHANGED_SOURCES = 'changed_sources', CHANGED_TARGETS = 'changed_targets', UNCHANGED_SOURCES = 'unchanged_sources', UNCHANGED_TARGETS = 'unchanged_targets', APPENDUNIQUE = 'appendunique') added.append('APPENDUNIQUE') for x in self.reserved_variables: assert x not in env, env[x] for x in added: assert x in env, bad_msg % x env.Prepend(TARGETS = 'targets', SOURCES = 'sources', SOURCE = 'source', TARGET = 'target', CHANGED_SOURCES = 'changed_sources', CHANGED_TARGETS = 'changed_targets', UNCHANGED_SOURCES = 'unchanged_sources', UNCHANGED_TARGETS = 'unchanged_targets', PREPEND = 'prepend') added.append('PREPEND') for x in self.reserved_variables: assert x not in env, env[x] for x in added: assert x in env, bad_msg % x env.Prepend(TARGETS = 'targets', SOURCES = 'sources', SOURCE = 'source', TARGET = 'target', CHANGED_SOURCES = 'changed_sources', CHANGED_TARGETS = 'changed_targets', UNCHANGED_SOURCES = 'unchanged_sources', UNCHANGED_TARGETS = 'unchanged_targets', PREPENDUNIQUE = 'prependunique') added.append('PREPENDUNIQUE') for x in self.reserved_variables: assert x not in env, env[x] for x in added: assert x in env, bad_msg % x env.Replace(TARGETS = 'targets', SOURCES = 'sources', SOURCE = 'source', TARGET = 'target', CHANGED_SOURCES = 'changed_sources', CHANGED_TARGETS = 'changed_targets', UNCHANGED_SOURCES = 'unchanged_sources', UNCHANGED_TARGETS = 'unchanged_targets', REPLACE = 'replace') added.append('REPLACE') for x in self.reserved_variables: assert x not in env, env[x] for x in added: assert x in env, bad_msg % x copy = env.Clone(TARGETS = 'targets', SOURCES = 'sources', SOURCE = 'source', TARGET = 'target', CHANGED_SOURCES = 'changed_sources', CHANGED_TARGETS = 'changed_targets', UNCHANGED_SOURCES = 'unchanged_sources', UNCHANGED_TARGETS = 'unchanged_targets', COPY = 'copy') for x in self.reserved_variables: assert x not in copy, env[x] for x in added + ['COPY']: assert x in copy, bad_msg % x over = env.Override({'TARGETS' : 'targets', 'SOURCES' : 'sources', 'SOURCE' : 'source', 'TARGET' : 'target', 'CHANGED_SOURCES' : 'changed_sources', 'CHANGED_TARGETS' : 'changed_targets', 'UNCHANGED_SOURCES' : 'unchanged_sources', 'UNCHANGED_TARGETS' : 'unchanged_targets', 'OVERRIDE' : 'override'}) for x in self.reserved_variables: assert x not in over, over[x] for x in added + ['OVERRIDE']: assert x in over, bad_msg % x def test_parse_flags(self): '''Test the Base class parse_flags argument''' # all we have to show is that it gets to MergeFlags internally env = Environment(tools=[], parse_flags = '-X') assert env['CCFLAGS'] == ['-X'], env['CCFLAGS'] env = Environment(tools=[], CCFLAGS=None, parse_flags = '-Y') assert env['CCFLAGS'] == ['-Y'], env['CCFLAGS'] env = Environment(tools=[], CPPDEFINES = 'FOO', parse_flags = '-std=c99 -X -DBAR') assert env['CFLAGS'] == ['-std=c99'], env['CFLAGS'] assert env['CCFLAGS'] == ['-X'], env['CCFLAGS'] assert env['CPPDEFINES'] == ['FOO', 'BAR'], env['CPPDEFINES'] def test_clone_parse_flags(self): '''Test the env.Clone() parse_flags argument''' # all we have to show is that it gets to MergeFlags internally env = Environment(tools = []) env2 = env.Clone(parse_flags = '-X') assert 'CCFLAGS' not in env assert env2['CCFLAGS'] == ['-X'], env2['CCFLAGS'] env = Environment(tools = [], CCFLAGS=None) env2 = env.Clone(parse_flags = '-Y') assert env['CCFLAGS'] is None, env['CCFLAGS'] assert env2['CCFLAGS'] == ['-Y'], env2['CCFLAGS'] env = Environment(tools = [], CPPDEFINES = 'FOO') env2 = env.Clone(parse_flags = '-std=c99 -X -DBAR') assert 'CFLAGS' not in env assert env2['CFLAGS'] == ['-std=c99'], env2['CFLAGS'] assert 'CCFLAGS' not in env assert env2['CCFLAGS'] == ['-X'], env2['CCFLAGS'] assert env['CPPDEFINES'] == 'FOO', env['CPPDEFINES'] assert env2['CPPDEFINES'] == ['FOO','BAR'], env2['CPPDEFINES'] class OverrideEnvironmentTestCase(unittest.TestCase,TestEnvironmentFixture): def setUp(self): env = Environment() env._dict = {'XXX' : 'x', 'YYY' : 'y'} env2 = OverrideEnvironment(env, {'XXX' : 'x2'}) env3 = OverrideEnvironment(env2, {'XXX' : 'x3', 'YYY' : 'y3', 'ZZZ' : 'z3'}) self.envs = [ env, env2, env3 ] def checkpath(self, node, expect): return str(node) == os.path.normpath(expect) def test___init__(self): """Test OverrideEnvironment initialization""" env, env2, env3 = self.envs assert env['XXX'] == 'x', env['XXX'] assert env2['XXX'] == 'x2', env2['XXX'] assert env3['XXX'] == 'x3', env3['XXX'] assert env['YYY'] == 'y', env['YYY'] assert env2['YYY'] == 'y', env2['YYY'] assert env3['YYY'] == 'y3', env3['YYY'] def test___delitem__(self): """Test deleting variables from an OverrideEnvironment""" env, env2, env3 = self.envs del env3['XXX'] assert 'XXX' not in env, "env has XXX?" assert 'XXX' not in env2, "env2 has XXX?" assert 'XXX' not in env3, "env3 has XXX?" del env3['YYY'] assert 'YYY' not in env, "env has YYY?" assert 'YYY' not in env2, "env2 has YYY?" assert 'YYY' not in env3, "env3 has YYY?" del env3['ZZZ'] assert 'ZZZ' not in env, "env has ZZZ?" assert 'ZZZ' not in env2, "env2 has ZZZ?" assert 'ZZZ' not in env3, "env3 has ZZZ?" def test_get(self): """Test the OverrideEnvironment get() method""" env, env2, env3 = self.envs assert env.get('XXX') == 'x', env.get('XXX') assert env2.get('XXX') == 'x2', env2.get('XXX') assert env3.get('XXX') == 'x3', env3.get('XXX') assert env.get('YYY') == 'y', env.get('YYY') assert env2.get('YYY') == 'y', env2.get('YYY') assert env3.get('YYY') == 'y3', env3.get('YYY') assert env.get('ZZZ') is None, env.get('ZZZ') assert env2.get('ZZZ') is None, env2.get('ZZZ') assert env3.get('ZZZ') == 'z3', env3.get('ZZZ') def test_has_key(self): """Test the OverrideEnvironment has_key() method""" env, env2, env3 = self.envs assert 'XXX' in env, 'XXX' in env assert 'XXX' in env2, 'XXX' in env2 assert 'XXX' in env3, 'XXX' in env3 assert 'YYY' in env, 'YYY' in env assert 'YYY' in env2, 'YYY' in env2 assert 'YYY' in env3, 'YYY' in env3 assert 'ZZZ' not in env, 'ZZZ' in env assert 'ZZZ' not in env2, 'ZZZ' in env2 assert 'ZZZ' in env3, 'ZZZ' in env3 def test_contains(self): """Test the OverrideEnvironment __contains__() method""" env, env2, env3 = self.envs assert 'XXX' in env assert 'XXX' in env2 assert 'XXX' in env3 assert 'YYY' in env assert 'YYY' in env2 assert 'YYY' in env3 assert not 'ZZZ' in env assert not 'ZZZ' in env2 assert 'ZZZ' in env3 def test_items(self): """Test the OverrideEnvironment Dictionary() method""" env, env2, env3 = self.envs items = env.Dictionary() assert items == {'XXX' : 'x', 'YYY' : 'y'}, items items = env2.Dictionary() assert items == {'XXX' : 'x2', 'YYY' : 'y'}, items items = env3.Dictionary() assert items == {'XXX' : 'x3', 'YYY' : 'y3', 'ZZZ' : 'z3'}, items def test_items(self): """Test the OverrideEnvironment items() method""" env, env2, env3 = self.envs items = sorted(env.items()) assert items == [('XXX', 'x'), ('YYY', 'y')], items items = sorted(env2.items()) assert items == [('XXX', 'x2'), ('YYY', 'y')], items items = sorted(env3.items()) assert items == [('XXX', 'x3'), ('YYY', 'y3'), ('ZZZ', 'z3')], items def test_gvars(self): """Test the OverrideEnvironment gvars() method""" env, env2, env3 = self.envs gvars = env.gvars() assert gvars == {'XXX' : 'x', 'YYY' : 'y'}, gvars gvars = env2.gvars() assert gvars == {'XXX' : 'x', 'YYY' : 'y'}, gvars gvars = env3.gvars() assert gvars == {'XXX' : 'x', 'YYY' : 'y'}, gvars def test_lvars(self): """Test the OverrideEnvironment lvars() method""" env, env2, env3 = self.envs lvars = env.lvars() assert lvars == {}, lvars lvars = env2.lvars() assert lvars == {'XXX' : 'x2'}, lvars lvars = env3.lvars() assert lvars == {'XXX' : 'x3', 'YYY' : 'y3', 'ZZZ' : 'z3'}, lvars def test_Replace(self): """Test the OverrideEnvironment Replace() method""" env, env2, env3 = self.envs assert env['XXX'] == 'x', env['XXX'] assert env2['XXX'] == 'x2', env2['XXX'] assert env3['XXX'] == 'x3', env3['XXX'] assert env['YYY'] == 'y', env['YYY'] assert env2['YYY'] == 'y', env2['YYY'] assert env3['YYY'] == 'y3', env3['YYY'] env.Replace(YYY = 'y4') assert env['XXX'] == 'x', env['XXX'] assert env2['XXX'] == 'x2', env2['XXX'] assert env3['XXX'] == 'x3', env3['XXX'] assert env['YYY'] == 'y4', env['YYY'] assert env2['YYY'] == 'y4', env2['YYY'] assert env3['YYY'] == 'y3', env3['YYY'] # Tests a number of Base methods through an OverrideEnvironment to # make sure they handle overridden constructionv variables properly. # # The following Base methods also call self.subst(), and so could # theoretically be subject to problems with evaluating overridden # variables, but they're never really called that way in the rest # of our code, so we won't worry about them (at least for now): # # ParseConfig() # ParseDepends() # Platform() # Tool() # # Action() # Alias() # Builder() # CacheDir() # Configure() # Environment() # FindFile() # Scanner() # SourceSignatures() # TargetSignatures() # It's unlikely Clone() will ever be called this way, so let the # other methods test that handling overridden values works. #def test_Clone(self): # """Test the OverrideEnvironment Clone() method""" # pass def test_FindIxes(self): """Test the OverrideEnvironment FindIxes() method""" env, env2, env3 = self.envs x = env.FindIxes(['xaaay'], 'XXX', 'YYY') assert x == 'xaaay', x x = env2.FindIxes(['x2aaay'], 'XXX', 'YYY') assert x == 'x2aaay', x x = env3.FindIxes(['x3aaay3'], 'XXX', 'YYY') assert x == 'x3aaay3', x def test_ReplaceIxes(self): """Test the OverrideEnvironment ReplaceIxes() method""" env, env2, env3 = self.envs x = env.ReplaceIxes('xaaay', 'XXX', 'YYY', 'YYY', 'XXX') assert x == 'yaaax', x x = env2.ReplaceIxes('x2aaay', 'XXX', 'YYY', 'YYY', 'XXX') assert x == 'yaaax2', x x = env3.ReplaceIxes('x3aaay3', 'XXX', 'YYY', 'YYY', 'XXX') assert x == 'y3aaax3', x # It's unlikely WhereIs() will ever be called this way, so let the # other methods test that handling overridden values works. #def test_WhereIs(self): # """Test the OverrideEnvironment WhereIs() method""" # pass def test_Dir(self): """Test the OverrideEnvironment Dir() method""" env, env2, env3 = self.envs x = env.Dir('ddir/$XXX') assert self.checkpath(x, 'ddir/x'), str(x) x = env2.Dir('ddir/$XXX') assert self.checkpath(x, 'ddir/x2'), str(x) x = env3.Dir('ddir/$XXX') assert self.checkpath(x, 'ddir/x3'), str(x) def test_Entry(self): """Test the OverrideEnvironment Entry() method""" env, env2, env3 = self.envs x = env.Entry('edir/$XXX') assert self.checkpath(x, 'edir/x'), str(x) x = env2.Entry('edir/$XXX') assert self.checkpath(x, 'edir/x2'), str(x) x = env3.Entry('edir/$XXX') assert self.checkpath(x, 'edir/x3'), str(x) def test_File(self): """Test the OverrideEnvironment File() method""" env, env2, env3 = self.envs x = env.File('fdir/$XXX') assert self.checkpath(x, 'fdir/x'), str(x) x = env2.File('fdir/$XXX') assert self.checkpath(x, 'fdir/x2'), str(x) x = env3.File('fdir/$XXX') assert self.checkpath(x, 'fdir/x3'), str(x) def test_Split(self): """Test the OverrideEnvironment Split() method""" env, env2, env3 = self.envs env['AAA'] = '$XXX $YYY $ZZZ' x = env.Split('$AAA') assert x == ['x', 'y'], x x = env2.Split('$AAA') assert x == ['x2', 'y'], x x = env3.Split('$AAA') assert x == ['x3', 'y3', 'z3'], x def test_parse_flags(self): '''Test the OverrideEnvironment parse_flags argument''' # all we have to show is that it gets to MergeFlags internally env = SubstitutionEnvironment() env2 = env.Override({'parse_flags' : '-X'}) assert 'CCFLAGS' not in env assert env2['CCFLAGS'] == ['-X'], env2['CCFLAGS'] env = SubstitutionEnvironment(CCFLAGS=None) env2 = env.Override({'parse_flags' : '-Y'}) assert env['CCFLAGS'] is None, env['CCFLAGS'] assert env2['CCFLAGS'] == ['-Y'], env2['CCFLAGS'] env = SubstitutionEnvironment(CPPDEFINES = 'FOO') env2 = env.Override({'parse_flags' : '-std=c99 -X -DBAR'}) assert 'CFLAGS' not in env assert env2['CFLAGS'] == ['-std=c99'], env2['CFLAGS'] assert 'CCFLAGS' not in env assert env2['CCFLAGS'] == ['-X'], env2['CCFLAGS'] assert env['CPPDEFINES'] == 'FOO', env['CPPDEFINES'] assert env2['CPPDEFINES'] == ['FOO','BAR'], env2['CPPDEFINES'] class NoSubstitutionProxyTestCase(unittest.TestCase,TestEnvironmentFixture): def test___init__(self): """Test NoSubstitutionProxy initialization""" env = self.TestEnvironment(XXX = 'x', YYY = 'y') assert env['XXX'] == 'x', env['XXX'] assert env['YYY'] == 'y', env['YYY'] proxy = NoSubstitutionProxy(env) assert proxy['XXX'] == 'x', proxy['XXX'] assert proxy['YYY'] == 'y', proxy['YYY'] def test_attributes(self): """Test getting and setting NoSubstitutionProxy attributes""" env = Environment() setattr(env, 'env_attr', 'value1') proxy = NoSubstitutionProxy(env) setattr(proxy, 'proxy_attr', 'value2') x = getattr(env, 'env_attr') assert x == 'value1', x x = getattr(proxy, 'env_attr') assert x == 'value1', x x = getattr(env, 'proxy_attr') assert x == 'value2', x x = getattr(proxy, 'proxy_attr') assert x == 'value2', x def test_subst(self): """Test the NoSubstitutionProxy.subst() method""" env = self.TestEnvironment(XXX = 'x', YYY = 'y') assert env['XXX'] == 'x', env['XXX'] assert env['YYY'] == 'y', env['YYY'] proxy = NoSubstitutionProxy(env) assert proxy['XXX'] == 'x', proxy['XXX'] assert proxy['YYY'] == 'y', proxy['YYY'] x = env.subst('$XXX') assert x == 'x', x x = proxy.subst('$XXX') assert x == '$XXX', x x = proxy.subst('$YYY', raw=7, target=None, source=None, conv=None, extra_meaningless_keyword_argument=None) assert x == '$YYY', x def test_subst_kw(self): """Test the NoSubstitutionProxy.subst_kw() method""" env = self.TestEnvironment(XXX = 'x', YYY = 'y') assert env['XXX'] == 'x', env['XXX'] assert env['YYY'] == 'y', env['YYY'] proxy = NoSubstitutionProxy(env) assert proxy['XXX'] == 'x', proxy['XXX'] assert proxy['YYY'] == 'y', proxy['YYY'] x = env.subst_kw({'$XXX':'$YYY'}) assert x == {'x':'y'}, x x = proxy.subst_kw({'$XXX':'$YYY'}) assert x == {'$XXX':'$YYY'}, x def test_subst_list(self): """Test the NoSubstitutionProxy.subst_list() method""" env = self.TestEnvironment(XXX = 'x', YYY = 'y') assert env['XXX'] == 'x', env['XXX'] assert env['YYY'] == 'y', env['YYY'] proxy = NoSubstitutionProxy(env) assert proxy['XXX'] == 'x', proxy['XXX'] assert proxy['YYY'] == 'y', proxy['YYY'] x = env.subst_list('$XXX') assert x == [['x']], x x = proxy.subst_list('$XXX') assert x == [[]], x x = proxy.subst_list('$YYY', raw=0, target=None, source=None, conv=None) assert x == [[]], x def test_subst_target_source(self): """Test the NoSubstitutionProxy.subst_target_source() method""" env = self.TestEnvironment(XXX = 'x', YYY = 'y') assert env['XXX'] == 'x', env['XXX'] assert env['YYY'] == 'y', env['YYY'] proxy = NoSubstitutionProxy(env) assert proxy['XXX'] == 'x', proxy['XXX'] assert proxy['YYY'] == 'y', proxy['YYY'] args = ('$XXX $TARGET $SOURCE $YYY',) kw = {'target' : DummyNode('ttt'), 'source' : DummyNode('sss')} x = env.subst_target_source(*args, **kw) assert x == 'x ttt sss y', x x = proxy.subst_target_source(*args, **kw) assert x == ' ttt sss ', x class EnvironmentVariableTestCase(unittest.TestCase): def test_is_valid_construction_var(self): """Testing is_valid_construction_var()""" r = is_valid_construction_var("_a") assert r is not None, r r = is_valid_construction_var("z_") assert r is not None, r r = is_valid_construction_var("X_") assert r is not None, r r = is_valid_construction_var("2a") assert r is None, r r = is_valid_construction_var("a2_") assert r is not None, r r = is_valid_construction_var("/") assert r is None, r r = is_valid_construction_var("_/") assert r is None, r r = is_valid_construction_var("a/") assert r is None, r r = is_valid_construction_var(".b") assert r is None, r r = is_valid_construction_var("_.b") assert r is None, r r = is_valid_construction_var("b1._") assert r is None, r r = is_valid_construction_var("-b") assert r is None, r r = is_valid_construction_var("_-b") assert r is None, r r = is_valid_construction_var("b1-_") assert r is None, r if __name__ == "__main__": suite = unittest.TestSuite() tclasses = [ SubstitutionTestCase, BaseTestCase, OverrideEnvironmentTestCase, NoSubstitutionProxyTestCase, EnvironmentVariableTestCase ] for tclass in tclasses: names = unittest.getTestCaseNames(tclass, 'test_') suite.addTests(list(map(tclass, names))) if not unittest.TextTestRunner().run(suite).wasSuccessful(): sys.exit(1) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/__init__.py0000644000175000017500000000313712114661560021412 0ustar dktrkranzdktrkranz"""SCons The main package for the SCons software construction utility. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/__init__.py 2013/03/03 09:48:35 garyo" __version__ = "2.3.0" __build__ = "" __buildsys__ = "reepicheep" __date__ = "2013/03/03 09:48:35" __developer__ = "garyo" # make sure compatibility is always in place import SCons.compat # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Executor.py0000644000175000017500000005171412114661557021463 0ustar dktrkranzdktrkranz"""SCons.Executor A module for executing actions with specific lists of target and source Nodes. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Executor.py 2013/03/03 09:48:35 garyo" import collections from SCons.Debug import logInstanceCreation import SCons.Errors import SCons.Memoize class Batch(object): """Remembers exact association between targets and sources of executor.""" def __init__(self, targets=[], sources=[]): self.targets = targets self.sources = sources class TSList(collections.UserList): """A class that implements $TARGETS or $SOURCES expansions by wrapping an executor Method. This class is used in the Executor.lvars() to delay creation of NodeList objects until they're needed. Note that we subclass collections.UserList purely so that the is_Sequence() function will identify an object of this class as a list during variable expansion. We're not really using any collections.UserList methods in practice. """ def __init__(self, func): self.func = func def __getattr__(self, attr): nl = self.func() return getattr(nl, attr) def __getitem__(self, i): nl = self.func() return nl[i] def __getslice__(self, i, j): nl = self.func() i = max(i, 0); j = max(j, 0) return nl[i:j] def __str__(self): nl = self.func() return str(nl) def __repr__(self): nl = self.func() return repr(nl) class TSObject(object): """A class that implements $TARGET or $SOURCE expansions by wrapping an Executor method. """ def __init__(self, func): self.func = func def __getattr__(self, attr): n = self.func() return getattr(n, attr) def __str__(self): n = self.func() if n: return str(n) return '' def __repr__(self): n = self.func() if n: return repr(n) return '' def rfile(node): """ A function to return the results of a Node's rfile() method, if it exists, and the Node itself otherwise (if it's a Value Node, e.g.). """ try: rfile = node.rfile except AttributeError: return node else: return rfile() class Executor(object): """A class for controlling instances of executing an action. This largely exists to hold a single association of an action, environment, list of environment override dictionaries, targets and sources for later processing as needed. """ if SCons.Memoize.use_memoizer: __metaclass__ = SCons.Memoize.Memoized_Metaclass memoizer_counters = [] def __init__(self, action, env=None, overridelist=[{}], targets=[], sources=[], builder_kw={}): if __debug__: logInstanceCreation(self, 'Executor.Executor') self.set_action_list(action) self.pre_actions = [] self.post_actions = [] self.env = env self.overridelist = overridelist if targets or sources: self.batches = [Batch(targets[:], sources[:])] else: self.batches = [] self.builder_kw = builder_kw self._memo = {} def get_lvars(self): try: return self.lvars except AttributeError: self.lvars = { 'CHANGED_SOURCES' : TSList(self._get_changed_sources), 'CHANGED_TARGETS' : TSList(self._get_changed_targets), 'SOURCE' : TSObject(self._get_source), 'SOURCES' : TSList(self._get_sources), 'TARGET' : TSObject(self._get_target), 'TARGETS' : TSList(self._get_targets), 'UNCHANGED_SOURCES' : TSList(self._get_unchanged_sources), 'UNCHANGED_TARGETS' : TSList(self._get_unchanged_targets), } return self.lvars def _get_changes(self): cs = [] ct = [] us = [] ut = [] for b in self.batches: if b.targets[0].is_up_to_date(): us.extend(list(map(rfile, b.sources))) ut.extend(b.targets) else: cs.extend(list(map(rfile, b.sources))) ct.extend(b.targets) self._changed_sources_list = SCons.Util.NodeList(cs) self._changed_targets_list = SCons.Util.NodeList(ct) self._unchanged_sources_list = SCons.Util.NodeList(us) self._unchanged_targets_list = SCons.Util.NodeList(ut) def _get_changed_sources(self, *args, **kw): try: return self._changed_sources_list except AttributeError: self._get_changes() return self._changed_sources_list def _get_changed_targets(self, *args, **kw): try: return self._changed_targets_list except AttributeError: self._get_changes() return self._changed_targets_list def _get_source(self, *args, **kw): #return SCons.Util.NodeList([rfile(self.batches[0].sources[0]).get_subst_proxy()]) return rfile(self.batches[0].sources[0]).get_subst_proxy() def _get_sources(self, *args, **kw): return SCons.Util.NodeList([rfile(n).get_subst_proxy() for n in self.get_all_sources()]) def _get_target(self, *args, **kw): #return SCons.Util.NodeList([self.batches[0].targets[0].get_subst_proxy()]) return self.batches[0].targets[0].get_subst_proxy() def _get_targets(self, *args, **kw): return SCons.Util.NodeList([n.get_subst_proxy() for n in self.get_all_targets()]) def _get_unchanged_sources(self, *args, **kw): try: return self._unchanged_sources_list except AttributeError: self._get_changes() return self._unchanged_sources_list def _get_unchanged_targets(self, *args, **kw): try: return self._unchanged_targets_list except AttributeError: self._get_changes() return self._unchanged_targets_list def get_action_targets(self): if not self.action_list: return [] targets_string = self.action_list[0].get_targets(self.env, self) if targets_string[0] == '$': targets_string = targets_string[1:] return self.get_lvars()[targets_string] def set_action_list(self, action): import SCons.Util if not SCons.Util.is_List(action): if not action: import SCons.Errors raise SCons.Errors.UserError("Executor must have an action.") action = [action] self.action_list = action def get_action_list(self): return self.pre_actions + self.action_list + self.post_actions def get_all_targets(self): """Returns all targets for all batches of this Executor.""" result = [] for batch in self.batches: result.extend(batch.targets) return result def get_all_sources(self): """Returns all sources for all batches of this Executor.""" result = [] for batch in self.batches: result.extend(batch.sources) return result def get_all_children(self): """Returns all unique children (dependencies) for all batches of this Executor. The Taskmaster can recognize when it's already evaluated a Node, so we don't have to make this list unique for its intended canonical use case, but we expect there to be a lot of redundancy (long lists of batched .cc files #including the same .h files over and over), so removing the duplicates once up front should save the Taskmaster a lot of work. """ result = SCons.Util.UniqueList([]) for target in self.get_all_targets(): result.extend(target.children()) return result def get_all_prerequisites(self): """Returns all unique (order-only) prerequisites for all batches of this Executor. """ result = SCons.Util.UniqueList([]) for target in self.get_all_targets(): result.extend(target.prerequisites) return result def get_action_side_effects(self): """Returns all side effects for all batches of this Executor used by the underlying Action. """ result = SCons.Util.UniqueList([]) for target in self.get_action_targets(): result.extend(target.side_effects) return result memoizer_counters.append(SCons.Memoize.CountValue('get_build_env')) def get_build_env(self): """Fetch or create the appropriate build Environment for this Executor. """ try: return self._memo['get_build_env'] except KeyError: pass # Create the build environment instance with appropriate # overrides. These get evaluated against the current # environment's construction variables so that users can # add to existing values by referencing the variable in # the expansion. overrides = {} for odict in self.overridelist: overrides.update(odict) import SCons.Defaults env = self.env or SCons.Defaults.DefaultEnvironment() build_env = env.Override(overrides) self._memo['get_build_env'] = build_env return build_env def get_build_scanner_path(self, scanner): """Fetch the scanner path for this executor's targets and sources. """ env = self.get_build_env() try: cwd = self.batches[0].targets[0].cwd except (IndexError, AttributeError): cwd = None return scanner.path(env, cwd, self.get_all_targets(), self.get_all_sources()) def get_kw(self, kw={}): result = self.builder_kw.copy() result.update(kw) result['executor'] = self return result def do_nothing(self, target, kw): return 0 def do_execute(self, target, kw): """Actually execute the action list.""" env = self.get_build_env() kw = self.get_kw(kw) status = 0 for act in self.get_action_list(): #args = (self.get_all_targets(), self.get_all_sources(), env) args = ([], [], env) status = act(*args, **kw) if isinstance(status, SCons.Errors.BuildError): status.executor = self raise status elif status: msg = "Error %s" % status raise SCons.Errors.BuildError( errstr=msg, node=self.batches[0].targets, executor=self, action=act) return status # use extra indirection because with new-style objects (Python 2.2 # and above) we can't override special methods, and nullify() needs # to be able to do this. def __call__(self, target, **kw): return self.do_execute(target, kw) def cleanup(self): self._memo = {} def add_sources(self, sources): """Add source files to this Executor's list. This is necessary for "multi" Builders that can be called repeatedly to build up a source file list for a given target.""" # TODO(batch): extend to multiple batches assert (len(self.batches) == 1) # TODO(batch): remove duplicates? sources = [x for x in sources if x not in self.batches[0].sources] self.batches[0].sources.extend(sources) def get_sources(self): return self.batches[0].sources def add_batch(self, targets, sources): """Add pair of associated target and source to this Executor's list. This is necessary for "batch" Builders that can be called repeatedly to build up a list of matching target and source files that will be used in order to update multiple target files at once from multiple corresponding source files, for tools like MSVC that support it.""" self.batches.append(Batch(targets, sources)) def prepare(self): """ Preparatory checks for whether this Executor can go ahead and (try to) build its targets. """ for s in self.get_all_sources(): if s.missing(): msg = "Source `%s' not found, needed by target `%s'." raise SCons.Errors.StopError(msg % (s, self.batches[0].targets[0])) def add_pre_action(self, action): self.pre_actions.append(action) def add_post_action(self, action): self.post_actions.append(action) # another extra indirection for new-style objects and nullify... def my_str(self): env = self.get_build_env() return "\n".join([action.genstring(self.get_all_targets(), self.get_all_sources(), env) for action in self.get_action_list()]) def __str__(self): return self.my_str() def nullify(self): self.cleanup() self.do_execute = self.do_nothing self.my_str = lambda: '' memoizer_counters.append(SCons.Memoize.CountValue('get_contents')) def get_contents(self): """Fetch the signature contents. This is the main reason this class exists, so we can compute this once and cache it regardless of how many target or source Nodes there are. """ try: return self._memo['get_contents'] except KeyError: pass env = self.get_build_env() result = "".join([action.get_contents(self.get_all_targets(), self.get_all_sources(), env) for action in self.get_action_list()]) self._memo['get_contents'] = result return result def get_timestamp(self): """Fetch a time stamp for this Executor. We don't have one, of course (only files do), but this is the interface used by the timestamp module. """ return 0 def scan_targets(self, scanner): # TODO(batch): scan by batches self.scan(scanner, self.get_all_targets()) def scan_sources(self, scanner): # TODO(batch): scan by batches if self.batches[0].sources: self.scan(scanner, self.get_all_sources()) def scan(self, scanner, node_list): """Scan a list of this Executor's files (targets or sources) for implicit dependencies and update all of the targets with them. This essentially short-circuits an N*M scan of the sources for each individual target, which is a hell of a lot more efficient. """ env = self.get_build_env() # TODO(batch): scan by batches) deps = [] if scanner: for node in node_list: node.disambiguate() s = scanner.select(node) if not s: continue path = self.get_build_scanner_path(s) deps.extend(node.get_implicit_deps(env, s, path)) else: kw = self.get_kw() for node in node_list: node.disambiguate() scanner = node.get_env_scanner(env, kw) if not scanner: continue scanner = scanner.select(node) if not scanner: continue path = self.get_build_scanner_path(scanner) deps.extend(node.get_implicit_deps(env, scanner, path)) deps.extend(self.get_implicit_deps()) for tgt in self.get_all_targets(): tgt.add_to_implicit(deps) def _get_unignored_sources_key(self, node, ignore=()): return (node,) + tuple(ignore) memoizer_counters.append(SCons.Memoize.CountDict('get_unignored_sources', _get_unignored_sources_key)) def get_unignored_sources(self, node, ignore=()): key = (node,) + tuple(ignore) try: memo_dict = self._memo['get_unignored_sources'] except KeyError: memo_dict = {} self._memo['get_unignored_sources'] = memo_dict else: try: return memo_dict[key] except KeyError: pass if node: # TODO: better way to do this (it's a linear search, # but it may not be critical path)? sourcelist = [] for b in self.batches: if node in b.targets: sourcelist = b.sources break else: sourcelist = self.get_all_sources() if ignore: idict = {} for i in ignore: idict[i] = 1 sourcelist = [s for s in sourcelist if s not in idict] memo_dict[key] = sourcelist return sourcelist def get_implicit_deps(self): """Return the executor's implicit dependencies, i.e. the nodes of the commands to be executed.""" result = [] build_env = self.get_build_env() for act in self.get_action_list(): deps = act.get_implicit_deps(self.get_all_targets(), self.get_all_sources(), build_env) result.extend(deps) return result _batch_executors = {} def GetBatchExecutor(key): return _batch_executors[key] def AddBatchExecutor(key, executor): assert key not in _batch_executors _batch_executors[key] = executor nullenv = None def get_NullEnvironment(): """Use singleton pattern for Null Environments.""" global nullenv import SCons.Util class NullEnvironment(SCons.Util.Null): import SCons.CacheDir _CacheDir_path = None _CacheDir = SCons.CacheDir.CacheDir(None) def get_CacheDir(self): return self._CacheDir if not nullenv: nullenv = NullEnvironment() return nullenv class Null(object): """A null Executor, with a null build Environment, that does nothing when the rest of the methods call it. This might be able to disapper when we refactor things to disassociate Builders from Nodes entirely, so we're not going to worry about unit tests for this--at least for now. """ def __init__(self, *args, **kw): if __debug__: logInstanceCreation(self, 'Executor.Null') self.batches = [Batch(kw['targets'][:], [])] def get_build_env(self): return get_NullEnvironment() def get_build_scanner_path(self): return None def cleanup(self): pass def prepare(self): pass def get_unignored_sources(self, *args, **kw): return tuple(()) def get_action_targets(self): return [] def get_action_list(self): return [] def get_all_targets(self): return self.batches[0].targets def get_all_sources(self): return self.batches[0].targets[0].sources def get_all_children(self): return self.batches[0].targets[0].children() def get_all_prerequisites(self): return [] def get_action_side_effects(self): return [] def __call__(self, *args, **kw): return 0 def get_contents(self): return '' def _morph(self): """Morph this Null executor to a real Executor object.""" batches = self.batches self.__class__ = Executor self.__init__([]) self.batches = batches # The following methods require morphing this Null Executor to a # real Executor object. def add_pre_action(self, action): self._morph() self.add_pre_action(action) def add_post_action(self, action): self._morph() self.add_post_action(action) def set_action_list(self, action): self._morph() self.set_action_list(action) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/SConsignTests.py0000644000175000017500000002723712114661557022436 0ustar dktrkranzdktrkranz# # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/SConsignTests.py 2013/03/03 09:48:35 garyo" import os import sys import TestCmd import unittest import SCons.dblite import SCons.SConsign class BuildInfo(object): def merge(self, object): pass class DummySConsignEntry(object): def __init__(self, name): self.name = name self.binfo = BuildInfo() def convert_to_sconsign(self): self.c_to_s = 1 def convert_from_sconsign(self, dir, name): self.c_from_s = 1 class FS(object): def __init__(self, top): self.Top = top self.Top.repositories = [] class DummyNode(object): def __init__(self, path='not_a_valid_path', binfo=None): self.path = path self.tpath = path self.fs = FS(self) self.binfo = binfo def get_stored_info(self): return self.binfo def get_binfo(self): return self.binfo class SConsignTestCase(unittest.TestCase): def setUp(self): self.save_cwd = os.getcwd() self.test = TestCmd.TestCmd(workdir = '') os.chdir(self.test.workpath('')) def tearDown(self): self.test.cleanup() SCons.SConsign.Reset() os.chdir(self.save_cwd) class BaseTestCase(SConsignTestCase): def test_Base(self): aaa = DummySConsignEntry('aaa') bbb = DummySConsignEntry('bbb') bbb.arg1 = 'bbb arg1' ccc = DummySConsignEntry('ccc') ccc.arg2 = 'ccc arg2' f = SCons.SConsign.Base() f.set_entry('aaa', aaa) f.set_entry('bbb', bbb) #f.merge() e = f.get_entry('aaa') assert e == aaa, e assert e.name == 'aaa', e.name e = f.get_entry('bbb') assert e == bbb, e assert e.name == 'bbb', e.name assert e.arg1 == 'bbb arg1', e.arg1 assert not hasattr(e, 'arg2'), e f.set_entry('bbb', ccc) e = f.get_entry('bbb') assert e.name == 'ccc', e.name assert not hasattr(e, 'arg1'), e assert e.arg2 == 'ccc arg2', e.arg1 ddd = DummySConsignEntry('ddd') eee = DummySConsignEntry('eee') fff = DummySConsignEntry('fff') fff.arg = 'fff arg' f = SCons.SConsign.Base() f.set_entry('ddd', ddd) f.set_entry('eee', eee) e = f.get_entry('ddd') assert e == ddd, e assert e.name == 'ddd', e.name e = f.get_entry('eee') assert e == eee, e assert e.name == 'eee', e.name assert not hasattr(e, 'arg'), e f.set_entry('eee', fff) e = f.get_entry('eee') assert e.name == 'fff', e.name assert e.arg == 'fff arg', e.arg def test_store_info(self): aaa = DummySConsignEntry('aaa') bbb = DummySConsignEntry('bbb') bbb.arg1 = 'bbb arg1' ccc = DummySConsignEntry('ccc') ccc.arg2 = 'ccc arg2' f = SCons.SConsign.Base() f.store_info('aaa', DummyNode('aaa', aaa)) f.store_info('bbb', DummyNode('bbb', bbb)) try: e = f.get_entry('aaa') except KeyError: pass else: raise Exception("unexpected entry %s" % e) try: e = f.get_entry('bbb') except KeyError: pass else: raise Exception("unexpected entry %s" % e) f.merge() e = f.get_entry('aaa') assert e == aaa, "aaa = %s, e = %s" % (aaa, e) assert e.name == 'aaa', e.name e = f.get_entry('bbb') assert e == bbb, "bbb = %s, e = %s" % (bbb, e) assert e.name == 'bbb', e.name assert e.arg1 == 'bbb arg1', e.arg1 assert not hasattr(e, 'arg2'), e f.store_info('bbb', DummyNode('bbb', ccc)) e = f.get_entry('bbb') assert e == bbb, e assert e.name == 'bbb', e.name assert e.arg1 == 'bbb arg1', e.arg1 assert not hasattr(e, 'arg2'), e f.merge() e = f.get_entry('bbb') assert e.name == 'ccc', e.name assert not hasattr(e, 'arg1'), e assert e.arg2 == 'ccc arg2', e.arg1 ddd = DummySConsignEntry('ddd') eee = DummySConsignEntry('eee') fff = DummySConsignEntry('fff') fff.arg = 'fff arg' f = SCons.SConsign.Base() f.store_info('ddd', DummyNode('ddd', ddd)) f.store_info('eee', DummyNode('eee', eee)) f.merge() e = f.get_entry('ddd') assert e == ddd, e assert e.name == 'ddd', e.name e = f.get_entry('eee') assert e == eee, e assert e.name == 'eee', e.name assert not hasattr(e, 'arg'), e f.store_info('eee', DummyNode('eee', fff)) e = f.get_entry('eee') assert e == eee, e assert e.name == 'eee', e.name assert not hasattr(e, 'arg'), e f.merge() e = f.get_entry('eee') assert e.name == 'fff', e.name assert e.arg == 'fff arg', e.arg class SConsignDBTestCase(SConsignTestCase): def test_SConsignDB(self): save_DataBase = SCons.SConsign.DataBase SCons.SConsign.DataBase = {} try: d1 = SCons.SConsign.DB(DummyNode('dir1')) d1.set_entry('aaa', DummySConsignEntry('aaa name')) d1.set_entry('bbb', DummySConsignEntry('bbb name')) aaa = d1.get_entry('aaa') assert aaa.name == 'aaa name' bbb = d1.get_entry('bbb') assert bbb.name == 'bbb name' d2 = SCons.SConsign.DB(DummyNode('dir2')) d2.set_entry('ccc', DummySConsignEntry('ccc name')) d2.set_entry('ddd', DummySConsignEntry('ddd name')) ccc = d2.get_entry('ccc') assert ccc.name == 'ccc name' ddd = d2.get_entry('ddd') assert ddd.name == 'ddd name' d31 = SCons.SConsign.DB(DummyNode('dir3/sub1')) d31.set_entry('eee', DummySConsignEntry('eee name')) d31.set_entry('fff', DummySConsignEntry('fff name')) eee = d31.get_entry('eee') assert eee.name == 'eee name' fff = d31.get_entry('fff') assert fff.name == 'fff name' d32 = SCons.SConsign.DB(DummyNode('dir3%ssub2' % os.sep)) d32.set_entry('ggg', DummySConsignEntry('ggg name')) d32.set_entry('hhh', DummySConsignEntry('hhh name')) ggg = d32.get_entry('ggg') assert ggg.name == 'ggg name' hhh = d32.get_entry('hhh') assert hhh.name == 'hhh name' finally: SCons.SConsign.DataBase = save_DataBase class SConsignDirFileTestCase(SConsignTestCase): def test_SConsignDirFile(self): bi_foo = DummySConsignEntry('foo') bi_bar = DummySConsignEntry('bar') f = SCons.SConsign.DirFile(DummyNode()) f.set_entry('foo', bi_foo) f.set_entry('bar', bi_bar) e = f.get_entry('foo') assert e == bi_foo, e assert e.name == 'foo', e.name e = f.get_entry('bar') assert e == bi_bar, e assert e.name == 'bar', e.name assert not hasattr(e, 'arg'), e bbb = DummySConsignEntry('bbb') bbb.arg = 'bbb arg' f.set_entry('bar', bbb) e = f.get_entry('bar') assert e.name == 'bbb', e.name assert e.arg == 'bbb arg', e.arg class SConsignFileTestCase(SConsignTestCase): def test_SConsignFile(self): test = self.test file = test.workpath('sconsign_file') assert SCons.SConsign.DataBase == {}, SCons.SConsign.DataBase assert SCons.SConsign.DB_Name == ".sconsign", SCons.SConsign.DB_Name assert SCons.SConsign.DB_Module is SCons.dblite, SCons.SConsign.DB_Module SCons.SConsign.File(file) assert SCons.SConsign.DataBase == {}, SCons.SConsign.DataBase assert SCons.SConsign.DB_Name is file, SCons.SConsign.DB_Name assert SCons.SConsign.DB_Module is SCons.dblite, SCons.SConsign.DB_Module SCons.SConsign.File(None) assert SCons.SConsign.DataBase == {}, SCons.SConsign.DataBase assert SCons.SConsign.DB_Name is file, SCons.SConsign.DB_Name assert SCons.SConsign.DB_Module is None, SCons.SConsign.DB_Module class Fake_DBM(object): def open(self, name, mode): self.name = name self.mode = mode return self def __getitem__(self, key): pass def __setitem__(self, key, value): pass fake_dbm = Fake_DBM() SCons.SConsign.File(file, fake_dbm) assert SCons.SConsign.DataBase == {}, SCons.SConsign.DataBase assert SCons.SConsign.DB_Name is file, SCons.SConsign.DB_Name assert SCons.SConsign.DB_Module is fake_dbm, SCons.SConsign.DB_Module assert not hasattr(fake_dbm, 'name'), fake_dbm assert not hasattr(fake_dbm, 'mode'), fake_dbm SCons.SConsign.ForDirectory(DummyNode(test.workpath('dir'))) assert not SCons.SConsign.DataBase is None, SCons.SConsign.DataBase assert fake_dbm.name == file, fake_dbm.name assert fake_dbm.mode == "c", fake_dbm.mode class writeTestCase(SConsignTestCase): def test_write(self): test = self.test file = test.workpath('sconsign_file') class Fake_DBM(object): def __getitem__(self, key): return None def __setitem__(self, key, value): pass def open(self, name, mode): self.sync_count = 0 return self def sync(self): self.sync_count = self.sync_count + 1 fake_dbm = Fake_DBM() SCons.SConsign.DataBase = {} SCons.SConsign.File(file, fake_dbm) f = SCons.SConsign.DB(DummyNode()) bi_foo = DummySConsignEntry('foo') bi_bar = DummySConsignEntry('bar') f.set_entry('foo', bi_foo) f.set_entry('bar', bi_bar) SCons.SConsign.write() assert bi_foo.c_to_s, bi_foo.c_to_s assert bi_bar.c_to_s, bi_bar.c_to_s assert fake_dbm.sync_count == 1, fake_dbm.sync_count if __name__ == "__main__": suite = unittest.TestSuite() tclasses = [ BaseTestCase, SConsignDBTestCase, SConsignDirFileTestCase, SConsignFileTestCase, writeTestCase, ] for tclass in tclasses: names = unittest.getTestCaseNames(tclass, 'test_') suite.addTests(list(map(tclass, names))) if not unittest.TextTestRunner().run(suite).wasSuccessful(): sys.exit(1) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Platform/0000755000175000017500000000000012114661557021067 5ustar dktrkranzdktrkranzscons-doc-2.3.0/src/engine/SCons/Platform/sunos.xml0000644000175000017500000000134512114661557022763 0ustar dktrkranzdktrkranz On Solaris systems, the package-checking program that will be used (along with &cv-PKGINFO;) to look for installed versions of the Sun PRO C++ compiler. The default is /usr/sbin/pgkchk. On Solaris systems, the package information program that will be used (along with &cv-PKGCHK;) to look for installed versions of the Sun PRO C++ compiler. The default is pkginfo. scons-doc-2.3.0/src/engine/SCons/Platform/aix.py0000644000175000017500000000474212114661557022231 0ustar dktrkranzdktrkranz"""engine.SCons.Platform.aix Platform-specific initialization for IBM AIX systems. There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Platform.Platform() selection method. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Platform/aix.py 2013/03/03 09:48:35 garyo" import os import posix def get_xlc(env, xlc=None, xlc_r=None, packages=[]): # Use the AIX package installer tool lslpp to figure out where a # given xl* compiler is installed and what version it is. xlcPath = None xlcVersion = None if xlc is None: xlc = env.get('CC', 'xlc') if xlc_r is None: xlc_r = xlc + '_r' for package in packages: cmd = "lslpp -fc " + package + " 2>/dev/null | egrep '" + xlc + "([^-_a-zA-Z0-9].*)?$'" line = os.popen(cmd).readline() if line: v, p = line.split(':')[1:3] xlcVersion = v.split()[1] xlcPath = p.split()[0] xlcPath = xlcPath[:xlcPath.rindex('/')] break return (xlcPath, xlc, xlc_r, xlcVersion) def generate(env): posix.generate(env) #Based on AIX 5.2: ARG_MAX=24576 - 3000 for environment expansion env['MAXLINELENGTH'] = 21576 # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Platform/posix.xml0000644000175000017500000000305312114661557022754 0ustar dktrkranzdktrkranz A list of paths to search for shared libraries when running programs. Currently only used in the GNU (gnulink), IRIX (sgilink) and Sun (sunlink) linkers. Ignored on platforms and toolchains that don't support it. Note that the paths added to RPATH are not transformed by &scons; in any way: if you want an absolute path, you must make it absolute yourself. An automatically-generated construction variable containing the rpath flags to be used when linking a program with shared libraries. The value of &cv-_RPATH; is created by appending &cv-RPATHPREFIX; and &cv-RPATHSUFFIX; to the beginning and end of each directory in &cv-RPATH;. The prefix used to specify a directory to be searched for shared libraries when running programs. This will be appended to the beginning of each directory in the &cv-RPATH; construction variable when the &cv-_RPATH; variable is automatically generated. The suffix used to specify a directory to be searched for shared libraries when running programs. This will be appended to the end of each directory in the &cv-RPATH; construction variable when the &cv-_RPATH; variable is automatically generated. scons-doc-2.3.0/src/engine/SCons/Platform/hpux.py0000644000175000017500000000336712114661557022436 0ustar dktrkranzdktrkranz"""engine.SCons.Platform.hpux Platform-specific initialization for HP-UX systems. There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Platform.Platform() selection method. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Platform/hpux.py 2013/03/03 09:48:35 garyo" import posix def generate(env): posix.generate(env) #Based on HP-UX11i: ARG_MAX=2048000 - 3000 for environment expansion env['MAXLINELENGTH'] = 2045000 # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Platform/PlatformTests.py0000644000175000017500000001007512114661557024253 0ustar dktrkranzdktrkranz# # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Platform/PlatformTests.py 2013/03/03 09:48:35 garyo" import SCons.compat import collections import sys import unittest import SCons.Errors import SCons.Platform class Environment(collections.UserDict): def Detect(self, cmd): return cmd def AppendENVPath(self, key, value): pass class PlatformTestCase(unittest.TestCase): def test_Platform(self): """Test the Platform() function""" p = SCons.Platform.Platform('cygwin') assert str(p) == 'cygwin', p env = Environment() p(env) assert env['PROGSUFFIX'] == '.exe', env assert env['LIBSUFFIX'] == '.a', env assert env['SHELL'] == 'sh', env p = SCons.Platform.Platform('os2') assert str(p) == 'os2', p env = Environment() p(env) assert env['PROGSUFFIX'] == '.exe', env assert env['LIBSUFFIX'] == '.lib', env p = SCons.Platform.Platform('posix') assert str(p) == 'posix', p env = Environment() p(env) assert env['PROGSUFFIX'] == '', env assert env['LIBSUFFIX'] == '.a', env assert env['SHELL'] == 'sh', env p = SCons.Platform.Platform('irix') assert str(p) == 'irix', p env = Environment() p(env) assert env['PROGSUFFIX'] == '', env assert env['LIBSUFFIX'] == '.a', env assert env['SHELL'] == 'sh', env p = SCons.Platform.Platform('aix') assert str(p) == 'aix', p env = Environment() p(env) assert env['PROGSUFFIX'] == '', env assert env['LIBSUFFIX'] == '.a', env assert env['SHELL'] == 'sh', env p = SCons.Platform.Platform('sunos') assert str(p) == 'sunos', p env = Environment() p(env) assert env['PROGSUFFIX'] == '', env assert env['LIBSUFFIX'] == '.a', env assert env['SHELL'] == 'sh', env p = SCons.Platform.Platform('hpux') assert str(p) == 'hpux', p env = Environment() p(env) assert env['PROGSUFFIX'] == '', env assert env['LIBSUFFIX'] == '.a', env assert env['SHELL'] == 'sh', env p = SCons.Platform.Platform('win32') assert str(p) == 'win32', p env = Environment() p(env) assert env['PROGSUFFIX'] == '.exe', env assert env['LIBSUFFIX'] == '.lib', env assert str try: p = SCons.Platform.Platform('_does_not_exist_') except SCons.Errors.UserError: pass else: raise env = Environment() SCons.Platform.Platform()(env) assert env != {}, env if __name__ == "__main__": suite = unittest.makeSuite(PlatformTestCase, 'test_') if not unittest.TextTestRunner().run(suite).wasSuccessful(): sys.exit(1) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Platform/cygwin.py0000644000175000017500000000400612114661557022741 0ustar dktrkranzdktrkranz"""SCons.Platform.cygwin Platform-specific initialization for Cygwin systems. There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Platform.Platform() selection method. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Platform/cygwin.py 2013/03/03 09:48:35 garyo" import posix from SCons.Platform import TempFileMunge def generate(env): posix.generate(env) env['PROGPREFIX'] = '' env['PROGSUFFIX'] = '.exe' env['SHLIBPREFIX'] = '' env['SHLIBSUFFIX'] = '.dll' env['LIBPREFIXES'] = [ '$LIBPREFIX', '$SHLIBPREFIX' ] env['LIBSUFFIXES'] = [ '$LIBSUFFIX', '$SHLIBSUFFIX' ] env['TEMPFILE'] = TempFileMunge env['TEMPFILEPREFIX'] = '@' env['MAXLINELENGTH'] = 2048 # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Platform/os2.py0000644000175000017500000000427312114661557022152 0ustar dktrkranzdktrkranz"""SCons.Platform.os2 Platform-specific initialization for OS/2 systems. There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Platform.Platform() selection method. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Platform/os2.py 2013/03/03 09:48:35 garyo" import win32 def generate(env): if 'ENV' not in env: env['ENV'] = {} env['OBJPREFIX'] = '' env['OBJSUFFIX'] = '.obj' env['SHOBJPREFIX'] = '$OBJPREFIX' env['SHOBJSUFFIX'] = '$OBJSUFFIX' env['PROGPREFIX'] = '' env['PROGSUFFIX'] = '.exe' env['LIBPREFIX'] = '' env['LIBSUFFIX'] = '.lib' env['SHLIBPREFIX'] = '' env['SHLIBSUFFIX'] = '.dll' env['LIBPREFIXES'] = '$LIBPREFIX' env['LIBSUFFIXES'] = [ '$LIBSUFFIX', '$SHLIBSUFFIX' ] env['HOST_OS'] = 'os2' env['HOST_ARCH'] = win32.get_architecture().arch # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Platform/sunos.py0000644000175000017500000000363312114661557022615 0ustar dktrkranzdktrkranz"""engine.SCons.Platform.sunos Platform-specific initialization for Sun systems. There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Platform.Platform() selection method. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Platform/sunos.py 2013/03/03 09:48:35 garyo" import posix def generate(env): posix.generate(env) # Based on sunSparc 8:32bit # ARG_MAX=1048320 - 3000 for environment expansion env['MAXLINELENGTH'] = 1045320 env['PKGINFO'] = 'pkginfo' env['PKGCHK'] = '/usr/sbin/pkgchk' env['ENV']['PATH'] = env['ENV']['PATH'] + ':/opt/SUNWspro/bin:/usr/ccs/bin' # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Platform/win32.py0000644000175000017500000003530212114661557022406 0ustar dktrkranzdktrkranz"""SCons.Platform.win32 Platform-specific initialization for Win32 systems. There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Platform.Platform() selection method. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Platform/win32.py 2013/03/03 09:48:35 garyo" import os import os.path import sys import tempfile from SCons.Platform.posix import exitvalmap from SCons.Platform import TempFileMunge import SCons.Util try: import msvcrt import win32api import win32con msvcrt.get_osfhandle win32api.SetHandleInformation win32con.HANDLE_FLAG_INHERIT except ImportError: parallel_msg = \ "you do not seem to have the pywin32 extensions installed;\n" + \ "\tparallel (-j) builds may not work reliably with open Python files." except AttributeError: parallel_msg = \ "your pywin32 extensions do not support file handle operations;\n" + \ "\tparallel (-j) builds may not work reliably with open Python files." else: parallel_msg = None import builtins _builtin_file = builtins.file _builtin_open = builtins.open class _scons_file(_builtin_file): def __init__(self, *args, **kw): _builtin_file.__init__(self, *args, **kw) win32api.SetHandleInformation(msvcrt.get_osfhandle(self.fileno()), win32con.HANDLE_FLAG_INHERIT, 0) def _scons_open(*args, **kw): fp = _builtin_open(*args, **kw) win32api.SetHandleInformation(msvcrt.get_osfhandle(fp.fileno()), win32con.HANDLE_FLAG_INHERIT, 0) return fp builtins.file = _scons_file builtins.open = _scons_open try: import threading spawn_lock = threading.Lock() # This locked version of spawnve works around a Windows # MSVCRT bug, because its spawnve is not thread-safe. # Without this, python can randomly crash while using -jN. # See the python bug at http://bugs.python.org/issue6476 # and SCons issue at # http://scons.tigris.org/issues/show_bug.cgi?id=2449 def spawnve(mode, file, args, env): spawn_lock.acquire() try: if mode == os.P_WAIT: ret = os.spawnve(os.P_NOWAIT, file, args, env) else: ret = os.spawnve(mode, file, args, env) finally: spawn_lock.release() if mode == os.P_WAIT: pid, status = os.waitpid(ret, 0) ret = status >> 8 return ret except ImportError: # Use the unsafe method of spawnve. # Please, don't try to optimize this try-except block # away by assuming that the threading module is always present. # In the test test/option-j.py we intentionally call SCons with # a fake threading.py that raises an import exception right away, # simulating a non-existent package. def spawnve(mode, file, args, env): return os.spawnve(mode, file, args, env) # The upshot of all this is that, if you are using Python 1.5.2, # you had better have cmd or command.com in your PATH when you run # scons. def piped_spawn(sh, escape, cmd, args, env, stdout, stderr): # There is no direct way to do that in python. What we do # here should work for most cases: # In case stdout (stderr) is not redirected to a file, # we redirect it into a temporary file tmpFileStdout # (tmpFileStderr) and copy the contents of this file # to stdout (stderr) given in the argument if not sh: sys.stderr.write("scons: Could not find command interpreter, is it in your PATH?\n") return 127 else: # one temporary file for stdout and stderr tmpFileStdout = os.path.normpath(tempfile.mktemp()) tmpFileStderr = os.path.normpath(tempfile.mktemp()) # check if output is redirected stdoutRedirected = 0 stderrRedirected = 0 for arg in args: # are there more possibilities to redirect stdout ? if (arg.find( ">", 0, 1 ) != -1 or arg.find( "1>", 0, 2 ) != -1): stdoutRedirected = 1 # are there more possibilities to redirect stderr ? if arg.find( "2>", 0, 2 ) != -1: stderrRedirected = 1 # redirect output of non-redirected streams to our tempfiles if stdoutRedirected == 0: args.append(">" + str(tmpFileStdout)) if stderrRedirected == 0: args.append("2>" + str(tmpFileStderr)) # actually do the spawn try: args = [sh, '/C', escape(' '.join(args)) ] ret = spawnve(os.P_WAIT, sh, args, env) except OSError, e: # catch any error try: ret = exitvalmap[e[0]] except KeyError: sys.stderr.write("scons: unknown OSError exception code %d - %s: %s\n" % (e[0], cmd, e[1])) if stderr is not None: stderr.write("scons: %s: %s\n" % (cmd, e[1])) # copy child output from tempfiles to our streams # and do clean up stuff if stdout is not None and stdoutRedirected == 0: try: stdout.write(open( tmpFileStdout, "r" ).read()) os.remove( tmpFileStdout ) except (IOError, OSError): pass if stderr is not None and stderrRedirected == 0: try: stderr.write(open( tmpFileStderr, "r" ).read()) os.remove( tmpFileStderr ) except (IOError, OSError): pass return ret def exec_spawn(l, env): try: result = spawnve(os.P_WAIT, l[0], l, env) except OSError, e: try: result = exitvalmap[e[0]] sys.stderr.write("scons: %s: %s\n" % (l[0], e[1])) except KeyError: result = 127 if len(l) > 2: if len(l[2]) < 1000: command = ' '.join(l[0:3]) else: command = l[0] else: command = l[0] sys.stderr.write("scons: unknown OSError exception code %d - '%s': %s\n" % (e[0], command, e[1])) return result def spawn(sh, escape, cmd, args, env): if not sh: sys.stderr.write("scons: Could not find command interpreter, is it in your PATH?\n") return 127 return exec_spawn([sh, '/C', escape(' '.join(args))], env) # Windows does not allow special characters in file names anyway, so no # need for a complex escape function, we will just quote the arg, except # that "cmd /c" requires that if an argument ends with a backslash it # needs to be escaped so as not to interfere with closing double quote # that we add. def escape(x): if x[-1] == '\\': x = x + '\\' return '"' + x + '"' # Get the windows system directory name _system_root = None def get_system_root(): global _system_root if _system_root is not None: return _system_root # A resonable default if we can't read the registry val = os.environ.get('SystemRoot', "C:\\WINDOWS") if SCons.Util.can_read_reg: try: # Look for Windows NT system root k=SCons.Util.RegOpenKeyEx(SCons.Util.hkey_mod.HKEY_LOCAL_MACHINE, 'Software\\Microsoft\\Windows NT\\CurrentVersion') val, tok = SCons.Util.RegQueryValueEx(k, 'SystemRoot') except SCons.Util.RegError: try: # Okay, try the Windows 9x system root k=SCons.Util.RegOpenKeyEx(SCons.Util.hkey_mod.HKEY_LOCAL_MACHINE, 'Software\\Microsoft\\Windows\\CurrentVersion') val, tok = SCons.Util.RegQueryValueEx(k, 'SystemRoot') except KeyboardInterrupt: raise except: pass _system_root = val return val # Get the location of the program files directory def get_program_files_dir(): # Now see if we can look in the registry... val = '' if SCons.Util.can_read_reg: try: # Look for Windows Program Files directory k=SCons.Util.RegOpenKeyEx(SCons.Util.hkey_mod.HKEY_LOCAL_MACHINE, 'Software\\Microsoft\\Windows\\CurrentVersion') val, tok = SCons.Util.RegQueryValueEx(k, 'ProgramFilesDir') except SCons.Util.RegError: val = '' pass if val == '': # A reasonable default if we can't read the registry # (Actually, it's pretty reasonable even if we can :-) val = os.path.join(os.path.dirname(get_system_root()),"Program Files") return val # Determine which windows CPU were running on. class ArchDefinition(object): """ A class for defining architecture-specific settings and logic. """ def __init__(self, arch, synonyms=[]): self.arch = arch self.synonyms = synonyms SupportedArchitectureList = [ ArchDefinition( 'x86', ['i386', 'i486', 'i586', 'i686'], ), ArchDefinition( 'x86_64', ['AMD64', 'amd64', 'em64t', 'EM64T', 'x86_64'], ), ArchDefinition( 'ia64', ['IA64'], ), ] SupportedArchitectureMap = {} for a in SupportedArchitectureList: SupportedArchitectureMap[a.arch] = a for s in a.synonyms: SupportedArchitectureMap[s] = a def get_architecture(arch=None): """Returns the definition for the specified architecture string. If no string is specified, the system default is returned (as defined by the PROCESSOR_ARCHITEW6432 or PROCESSOR_ARCHITECTURE environment variables). """ if arch is None: arch = os.environ.get('PROCESSOR_ARCHITEW6432') if not arch: arch = os.environ.get('PROCESSOR_ARCHITECTURE') return SupportedArchitectureMap.get(arch, ArchDefinition('', [''])) def generate(env): # Attempt to find cmd.exe (for WinNT/2k/XP) or # command.com for Win9x cmd_interp = '' # First see if we can look in the registry... if SCons.Util.can_read_reg: try: # Look for Windows NT system root k=SCons.Util.RegOpenKeyEx(SCons.Util.hkey_mod.HKEY_LOCAL_MACHINE, 'Software\\Microsoft\\Windows NT\\CurrentVersion') val, tok = SCons.Util.RegQueryValueEx(k, 'SystemRoot') cmd_interp = os.path.join(val, 'System32\\cmd.exe') except SCons.Util.RegError: try: # Okay, try the Windows 9x system root k=SCons.Util.RegOpenKeyEx(SCons.Util.hkey_mod.HKEY_LOCAL_MACHINE, 'Software\\Microsoft\\Windows\\CurrentVersion') val, tok = SCons.Util.RegQueryValueEx(k, 'SystemRoot') cmd_interp = os.path.join(val, 'command.com') except KeyboardInterrupt: raise except: pass # For the special case of not having access to the registry, we # use a temporary path and pathext to attempt to find the command # interpreter. If we fail, we try to find the interpreter through # the env's PATH. The problem with that is that it might not # contain an ENV and a PATH. if not cmd_interp: systemroot = get_system_root() tmp_path = systemroot + os.pathsep + \ os.path.join(systemroot,'System32') tmp_pathext = '.com;.exe;.bat;.cmd' if 'PATHEXT' in os.environ: tmp_pathext = os.environ['PATHEXT'] cmd_interp = SCons.Util.WhereIs('cmd', tmp_path, tmp_pathext) if not cmd_interp: cmd_interp = SCons.Util.WhereIs('command', tmp_path, tmp_pathext) if not cmd_interp: cmd_interp = env.Detect('cmd') if not cmd_interp: cmd_interp = env.Detect('command') if 'ENV' not in env: env['ENV'] = {} # Import things from the external environment to the construction # environment's ENV. This is a potential slippery slope, because we # *don't* want to make builds dependent on the user's environment by # default. We're doing this for SystemRoot, though, because it's # needed for anything that uses sockets, and seldom changes, and # for SystemDrive because it's related. # # Weigh the impact carefully before adding other variables to this list. import_env = [ 'SystemDrive', 'SystemRoot', 'TEMP', 'TMP' ] for var in import_env: v = os.environ.get(var) if v: env['ENV'][var] = v if 'COMSPEC' not in env['ENV']: v = os.environ.get("COMSPEC") if v: env['ENV']['COMSPEC'] = v env.AppendENVPath('PATH', get_system_root() + '\System32') env['ENV']['PATHEXT'] = '.COM;.EXE;.BAT;.CMD' env['OBJPREFIX'] = '' env['OBJSUFFIX'] = '.obj' env['SHOBJPREFIX'] = '$OBJPREFIX' env['SHOBJSUFFIX'] = '$OBJSUFFIX' env['PROGPREFIX'] = '' env['PROGSUFFIX'] = '.exe' env['LIBPREFIX'] = '' env['LIBSUFFIX'] = '.lib' env['SHLIBPREFIX'] = '' env['SHLIBSUFFIX'] = '.dll' env['LIBPREFIXES'] = [ '$LIBPREFIX' ] env['LIBSUFFIXES'] = [ '$LIBSUFFIX' ] env['PSPAWN'] = piped_spawn env['SPAWN'] = spawn env['SHELL'] = cmd_interp env['TEMPFILE'] = TempFileMunge env['TEMPFILEPREFIX'] = '@' env['MAXLINELENGTH'] = 2048 env['ESCAPE'] = escape env['HOST_OS'] = 'win32' env['HOST_ARCH'] = get_architecture().arch # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Platform/darwin.py0000644000175000017500000000477412114661557022741 0ustar dktrkranzdktrkranz"""engine.SCons.Platform.darwin Platform-specific initialization for Mac OS X systems. There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Platform.Platform() selection method. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Platform/darwin.py 2013/03/03 09:48:35 garyo" import posix import os def generate(env): posix.generate(env) env['SHLIBSUFFIX'] = '.dylib' # put macports paths at front to override Apple's versions, fink path is after # For now let people who want Macports or Fink tools specify it! # env['ENV']['PATH'] = '/opt/local/bin:/opt/local/sbin:' + env['ENV']['PATH'] + ':/sw/bin' # Store extra system paths in env['ENV']['PATHOSX'] filelist = ['/etc/paths',] # make sure this works on Macs with Tiger or earlier try: dirlist = os.listdir('/etc/paths.d') except: dirlist = [] for file in dirlist: filelist.append('/etc/paths.d/'+file) for file in filelist: if os.path.isfile(file): f = open(file, 'r') lines = f.readlines() for line in lines: if line: env.AppendENVPath('PATHOSX', line.strip('\n')) f.close() # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Platform/__init__.xml0000644000175000017500000001112612114661557023351 0ustar dktrkranzdktrkranz A function that will be called to escape shell special characters in command lines. The function should take one argument: the command line string to escape; and should return the escaped command line. The prefix used for (static) library file names. A default value is set for each platform (posix, win32, os2, etc.), but the value is overridden by individual tools (ar, mslib, sgiar, sunar, tlib, etc.) to reflect the names of the libraries they create. A list of all legal prefixes for library file names. When searching for library dependencies, SCons will look for files with these prefixes, the base library name, and suffixes in the &cv-LIBSUFFIXES; list. The suffix used for (static) library file names. A default value is set for each platform (posix, win32, os2, etc.), but the value is overridden by individual tools (ar, mslib, sgiar, sunar, tlib, etc.) to reflect the names of the libraries they create. A list of all legal suffixes for library file names. When searching for library dependencies, SCons will look for files with prefixes, in the &cv-LIBPREFIXES; list, the base library name, and these suffixes. The prefix used for (static) object file names. The suffix used for (static) object file names. The name of the platform used to create the Environment. If no platform is specified when the Environment is created, &scons; autodetects the platform. env = Environment(tools = []) if env['PLATFORM'] == 'cygwin': Tool('mingw')(env) else: Tool('msvc')(env) The name of the host operating system used to create the Environment. If a platform is specified when creating the Environment, then that Platform's logic will handle setting this value. This value is immutable, and should not be changed by the user after the Environment is initialized. Currently only set for Win32. The name of the host hardware architecture used to create the Environment. If a platform is specified when creating the Environment, then that Platform's logic will handle setting this value. This value is immutable, and should not be changed by the user after the Environment is initialized. Currently only set for Win32. The name of the target operating system for the compiled objects created by this Environment. This defaults to the value of HOST_OS, and the user can override it. Currently only set for Win32. The name of the target hardware architecture for the compiled objects created by this Environment. This defaults to the value of HOST_ARCH, and the user can override it. Currently only set for Win32. The prefix used for executable file names. The suffix used for executable file names. A string naming the shell program that will be passed to the &cv-SPAWN; function. See the &cv-SPAWN; construction variable for more information. The prefix used for shared library file names. The suffix used for shared library file names. The prefix used for shared object file names. The suffix used for shared object file names. The prefix for a temporary file used to execute lines longer than $MAXLINELENGTH. The default is '@'. This may be set for toolchains that use other values, such as '-@' for the diab compiler or '-via' for ARM toolchain. scons-doc-2.3.0/src/engine/SCons/Platform/posix.py0000644000175000017500000002100512114661557022601 0ustar dktrkranzdktrkranz"""SCons.Platform.posix Platform-specific initialization for POSIX (Linux, UNIX, etc.) systems. There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Platform.Platform() selection method. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Platform/posix.py 2013/03/03 09:48:35 garyo" import errno import os import os.path import subprocess import sys import select import SCons.Util from SCons.Platform import TempFileMunge exitvalmap = { 2 : 127, 13 : 126, } def escape(arg): "escape shell special characters" slash = '\\' special = '"$()' arg = arg.replace(slash, slash+slash) for c in special: arg = arg.replace(c, slash+c) return '"' + arg + '"' def exec_system(l, env): stat = os.system(' '.join(l)) if stat & 0xff: return stat | 0x80 return stat >> 8 def exec_spawnvpe(l, env): stat = os.spawnvpe(os.P_WAIT, l[0], l, env) # os.spawnvpe() returns the actual exit code, not the encoding # returned by os.waitpid() or os.system(). return stat def exec_fork(l, env): pid = os.fork() if not pid: # Child process. exitval = 127 try: os.execvpe(l[0], l, env) except OSError, e: exitval = exitvalmap.get(e[0], e[0]) sys.stderr.write("scons: %s: %s\n" % (l[0], e[1])) os._exit(exitval) else: # Parent process. pid, stat = os.waitpid(pid, 0) if stat & 0xff: return stat | 0x80 return stat >> 8 def _get_env_command(sh, escape, cmd, args, env): s = ' '.join(args) if env: l = ['env', '-'] + \ [escape(t[0])+'='+escape(t[1]) for t in env.items()] + \ [sh, '-c', escape(s)] s = ' '.join(l) return s def env_spawn(sh, escape, cmd, args, env): return exec_system([_get_env_command( sh, escape, cmd, args, env)], env) def spawnvpe_spawn(sh, escape, cmd, args, env): return exec_spawnvpe([sh, '-c', ' '.join(args)], env) def fork_spawn(sh, escape, cmd, args, env): return exec_fork([sh, '-c', ' '.join(args)], env) def process_cmd_output(cmd_stdout, cmd_stderr, stdout, stderr): stdout_eof = stderr_eof = 0 while not (stdout_eof and stderr_eof): try: (i,o,e) = select.select([cmd_stdout, cmd_stderr], [], []) if cmd_stdout in i: str = cmd_stdout.read() if len(str) == 0: stdout_eof = 1 elif stdout is not None: stdout.write(str) if cmd_stderr in i: str = cmd_stderr.read() if len(str) == 0: #sys.__stderr__.write( "stderr_eof=1\n" ) stderr_eof = 1 else: #sys.__stderr__.write( "str(stderr) = %s\n" % str ) stderr.write(str) except select.error, (_errno, _strerror): if _errno != errno.EINTR: raise def exec_popen3(l, env, stdout, stderr): proc = subprocess.Popen(' '.join(l), stdout=stdout, stderr=stderr, shell=True) stat = proc.wait() if stat & 0xff: return stat | 0x80 return stat >> 8 def exec_piped_fork(l, env, stdout, stderr): # spawn using fork / exec and providing a pipe for the command's # stdout / stderr stream if stdout != stderr: (rFdOut, wFdOut) = os.pipe() (rFdErr, wFdErr) = os.pipe() else: (rFdOut, wFdOut) = os.pipe() rFdErr = rFdOut wFdErr = wFdOut # do the fork pid = os.fork() if not pid: # Child process os.close( rFdOut ) if rFdOut != rFdErr: os.close( rFdErr ) os.dup2( wFdOut, 1 ) # is there some symbolic way to do that ? os.dup2( wFdErr, 2 ) os.close( wFdOut ) if stdout != stderr: os.close( wFdErr ) exitval = 127 try: os.execvpe(l[0], l, env) except OSError, e: exitval = exitvalmap.get(e[0], e[0]) stderr.write("scons: %s: %s\n" % (l[0], e[1])) os._exit(exitval) else: # Parent process pid, stat = os.waitpid(pid, 0) os.close( wFdOut ) if stdout != stderr: os.close( wFdErr ) childOut = os.fdopen( rFdOut ) if stdout != stderr: childErr = os.fdopen( rFdErr ) else: childErr = childOut process_cmd_output(childOut, childErr, stdout, stderr) os.close( rFdOut ) if stdout != stderr: os.close( rFdErr ) if stat & 0xff: return stat | 0x80 return stat >> 8 def piped_env_spawn(sh, escape, cmd, args, env, stdout, stderr): # spawn using Popen3 combined with the env command # the command name and the command's stdout is written to stdout # the command's stderr is written to stderr return exec_popen3([_get_env_command(sh, escape, cmd, args, env)], env, stdout, stderr) def piped_fork_spawn(sh, escape, cmd, args, env, stdout, stderr): # spawn using fork / exec and providing a pipe for the command's # stdout / stderr stream return exec_piped_fork([sh, '-c', ' '.join(args)], env, stdout, stderr) def generate(env): # If os.spawnvpe() exists, we use it to spawn commands. Otherwise # if the env utility exists, we use os.system() to spawn commands, # finally we fall back on os.fork()/os.exec(). # # os.spawnvpe() is prefered because it is the most efficient. But # for Python versions without it, os.system() is prefered because it # is claimed that it works better with threads (i.e. -j) and is more # efficient than forking Python. # # NB: Other people on the scons-users mailing list have claimed that # os.fork()/os.exec() works better than os.system(). There may just # not be a default that works best for all users. if 'spawnvpe' in os.__dict__: spawn = spawnvpe_spawn elif env.Detect('env'): spawn = env_spawn else: spawn = fork_spawn if env.Detect('env'): pspawn = piped_env_spawn else: pspawn = piped_fork_spawn if 'ENV' not in env: env['ENV'] = {} env['ENV']['PATH'] = '/usr/local/bin:/opt/bin:/bin:/usr/bin' env['OBJPREFIX'] = '' env['OBJSUFFIX'] = '.o' env['SHOBJPREFIX'] = '$OBJPREFIX' env['SHOBJSUFFIX'] = '$OBJSUFFIX' env['PROGPREFIX'] = '' env['PROGSUFFIX'] = '' env['LIBPREFIX'] = 'lib' env['LIBSUFFIX'] = '.a' env['SHLIBPREFIX'] = '$LIBPREFIX' env['SHLIBSUFFIX'] = '.so' env['LIBPREFIXES'] = [ '$LIBPREFIX' ] env['LIBSUFFIXES'] = [ '$LIBSUFFIX', '$SHLIBSUFFIX' ] env['PSPAWN'] = pspawn env['SPAWN'] = spawn env['SHELL'] = 'sh' env['ESCAPE'] = escape env['TEMPFILE'] = TempFileMunge env['TEMPFILEPREFIX'] = '@' #Based on LINUX: ARG_MAX=ARG_MAX=131072 - 3000 for environment expansion #Note: specific platforms might rise or lower this value env['MAXLINELENGTH'] = 128072 # This platform supports RPATH specifications. env['__RPATH'] = '$_RPATH' # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Platform/irix.py0000644000175000017500000000320612114661557022415 0ustar dktrkranzdktrkranz"""SCons.Platform.irix Platform-specific initialization for SGI IRIX systems. There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Platform.Platform() selection method. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Platform/irix.py 2013/03/03 09:48:35 garyo" import posix def generate(env): posix.generate(env) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Platform/win32.xml0000644000175000017500000000071212114661557022553 0ustar dktrkranzdktrkranz The maximum number of characters allowed on an external command line. On Win32 systems, link lines longer than this many characters are linked via a temporary file name. scons-doc-2.3.0/src/engine/SCons/Platform/__init__.py0000644000175000017500000002225712114661557023210 0ustar dktrkranzdktrkranz"""SCons.Platform SCons platform selection. This looks for modules that define a callable object that can modify a construction environment as appropriate for a given platform. Note that we take a more simplistic view of "platform" than Python does. We're looking for a single string that determines a set of tool-independent variables with which to initialize a construction environment. Consequently, we'll examine both sys.platform and os.name (and anything else that might come in to play) in order to return some specification which is unique enough for our purposes. Note that because this subsysem just *selects* a callable that can modify a construction environment, it's possible for people to define their own "platform specification" in an arbitrary callable function. No one needs to use or tie in to this subsystem in order to roll their own platform definition. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Platform/__init__.py 2013/03/03 09:48:35 garyo" import SCons.compat import imp import os import sys import tempfile import SCons.Errors import SCons.Subst import SCons.Tool def platform_default(): """Return the platform string for our execution environment. The returned value should map to one of the SCons/Platform/*.py files. Since we're architecture independent, though, we don't care about the machine architecture. """ osname = os.name if osname == 'java': osname = os._osType if osname == 'posix': if sys.platform == 'cygwin': return 'cygwin' elif sys.platform.find('irix') != -1: return 'irix' elif sys.platform.find('sunos') != -1: return 'sunos' elif sys.platform.find('hp-ux') != -1: return 'hpux' elif sys.platform.find('aix') != -1: return 'aix' elif sys.platform.find('darwin') != -1: return 'darwin' else: return 'posix' elif os.name == 'os2': return 'os2' else: return sys.platform def platform_module(name = platform_default()): """Return the imported module for the platform. This looks for a module name that matches the specified argument. If the name is unspecified, we fetch the appropriate default for our execution environment. """ full_name = 'SCons.Platform.' + name if full_name not in sys.modules: if os.name == 'java': eval(full_name) else: try: file, path, desc = imp.find_module(name, sys.modules['SCons.Platform'].__path__) try: mod = imp.load_module(full_name, file, path, desc) finally: if file: file.close() except ImportError: try: import zipimport importer = zipimport.zipimporter( sys.modules['SCons.Platform'].__path__[0] ) mod = importer.load_module(full_name) except ImportError: raise SCons.Errors.UserError("No platform named '%s'" % name) setattr(SCons.Platform, name, mod) return sys.modules[full_name] def DefaultToolList(platform, env): """Select a default tool list for the specified platform. """ return SCons.Tool.tool_list(platform, env) class PlatformSpec(object): def __init__(self, name, generate): self.name = name self.generate = generate def __call__(self, *args, **kw): return self.generate(*args, **kw) def __str__(self): return self.name class TempFileMunge(object): """A callable class. You can set an Environment variable to this, then call it with a string argument, then it will perform temporary file substitution on it. This is used to circumvent the long command line limitation. Example usage: env["TEMPFILE"] = TempFileMunge env["LINKCOM"] = "${TEMPFILE('$LINK $TARGET $SOURCES')}" By default, the name of the temporary file used begins with a prefix of '@'. This may be configred for other tool chains by setting '$TEMPFILEPREFIX'. env["TEMPFILEPREFIX"] = '-@' # diab compiler env["TEMPFILEPREFIX"] = '-via' # arm tool chain """ def __init__(self, cmd): self.cmd = cmd def __call__(self, target, source, env, for_signature): if for_signature: # If we're being called for signature calculation, it's # because we're being called by the string expansion in # Subst.py, which has the logic to strip any $( $) that # may be in the command line we squirreled away. So we # just return the raw command line and let the upper # string substitution layers do their thing. return self.cmd # Now we're actually being called because someone is actually # going to try to execute the command, so we have to do our # own expansion. cmd = env.subst_list(self.cmd, SCons.Subst.SUBST_CMD, target, source)[0] try: maxline = int(env.subst('$MAXLINELENGTH')) except ValueError: maxline = 2048 length = 0 for c in cmd: length += len(c) if length <= maxline: return self.cmd # We do a normpath because mktemp() has what appears to be # a bug in Windows that will use a forward slash as a path # delimiter. Windows's link mistakes that for a command line # switch and barfs. # # We use the .lnk suffix for the benefit of the Phar Lap # linkloc linker, which likes to append an .lnk suffix if # none is given. (fd, tmp) = tempfile.mkstemp('.lnk', text=True) native_tmp = SCons.Util.get_native_path(os.path.normpath(tmp)) if env['SHELL'] and env['SHELL'] == 'sh': # The sh shell will try to escape the backslashes in the # path, so unescape them. native_tmp = native_tmp.replace('\\', r'\\\\') # In Cygwin, we want to use rm to delete the temporary # file, because del does not exist in the sh shell. rm = env.Detect('rm') or 'del' else: # Don't use 'rm' if the shell is not sh, because rm won't # work with the Windows shells (cmd.exe or command.com) or # Windows path names. rm = 'del' prefix = env.subst('$TEMPFILEPREFIX') if not prefix: prefix = '@' args = list(map(SCons.Subst.quote_spaces, cmd[1:])) os.write(fd, " ".join(args) + "\n") os.close(fd) # XXX Using the SCons.Action.print_actions value directly # like this is bogus, but expedient. This class should # really be rewritten as an Action that defines the # __call__() and strfunction() methods and lets the # normal action-execution logic handle whether or not to # print/execute the action. The problem, though, is all # of that is decided before we execute this method as # part of expanding the $TEMPFILE construction variable. # Consequently, refactoring this will have to wait until # we get more flexible with allowing Actions to exist # independently and get strung together arbitrarily like # Ant tasks. In the meantime, it's going to be more # user-friendly to not let obsession with architectural # purity get in the way of just being helpful, so we'll # reach into SCons.Action directly. if SCons.Action.print_actions: print("Using tempfile "+native_tmp+" for command line:\n"+ str(cmd[0]) + " " + " ".join(args)) return [ cmd[0], prefix + native_tmp + '\n' + rm, native_tmp ] def Platform(name = platform_default()): """Select a canned Platform specification. """ module = platform_module(name) spec = PlatformSpec(name, module.generate) return spec # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/PathListTests.py0000644000175000017500000001413312114661557022432 0ustar dktrkranzdktrkranz# # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/PathListTests.py 2013/03/03 09:48:35 garyo" import sys import unittest import SCons.PathList class subst_pathTestCase(unittest.TestCase): def setUp(self): class FakeEnvironment(object): def __init__(self, **kw): self.kw = kw def subst(self, s, target=None, source=None, conv=lambda x: x): if s[0] == '$': s = s[1:] if s == 'target': s = target elif s == 'source': s = source else: s = self.kw[s] return s self.env = FakeEnvironment(AAA = 'aaa', NULL = '') from SCons.Environment import Environment self.env = Environment(AAA = 'aaa', NULL = '') def test_node(self): """Test the subst_path() method on a Node """ import SCons.Node class A(object): pass n = SCons.Node.Node() pl = SCons.PathList.PathList((n,)) result = pl.subst_path(self.env, 'y', 'z') assert result == (n,), result def test_object(self): """Test the subst_path() method on a non-Node object """ class A(object): def __str__(self): return '' a = A() pl = SCons.PathList.PathList((a,)) result = pl.subst_path(self.env, 'y', 'z') assert result == ('',), result def test_object_get(self): """Test the subst_path() method on an object with a get() method """ class B(object): def get(self): return 'b' b = B() pl = SCons.PathList.PathList((b,)) result = pl.subst_path(self.env, 'y', 'z') assert result == ('b',), result def test_string(self): """Test the subst_path() method on a non-substitution string """ self.env.subst = lambda s, target, source, conv: 'NOT THIS STRING' pl = SCons.PathList.PathList(('x')) result = pl.subst_path(self.env, 'y', 'z') assert result == ('x',), result def test_subst(self): """Test the subst_path() method on substitution strings """ pl = SCons.PathList.PathList(('$AAA', '$NULL')) result = pl.subst_path(self.env, 'y', 'z') assert result == ('aaa',), result def test_list_of_lists(self): """Test the subst_path() method on substitution of nested lists. """ pl = SCons.PathList.PathList((['$AAA', '$AAA'], '$NULL')) result = pl.subst_path(self.env, 'y', 'z') assert result == ('aaa', 'aaa'), result def test_subst_nested(self): """Test the subst_path() method on nested substitution of strings. """ self.env.Append(L1 = ['a', 'b'], L2 = ['c', 'd'], L3 = ['$L2']) pl = SCons.PathList.PathList(['$L1']) result = pl.subst_path(self.env, 'y', 'z') assert result == ('a', 'b'), result self.env.Append(L1 = ['$L2']) pl = SCons.PathList.PathList(['$L1']) result = pl.subst_path(self.env, 'y', 'z') assert result == ('a', 'b', 'c', 'd'), result self.env.Append(L1 = ['$L3']) pl = SCons.PathList.PathList(['$L1']) result = pl.subst_path(self.env, 'y', 'z') assert result == ('a', 'b', 'c', 'd', 'c', 'd'), result def test_another_env(self): """Test the subst_path does lazy evaluation. """ pl = SCons.PathList.PathList(('$AAA', '$NULL')) result = pl.subst_path(self.env, 'y', 'z') assert result == ('aaa',), result e = self.env.Clone(AAA = 'bbb') result = pl.subst_path(e, 'y', 'z') assert result == ('bbb',), result class PathListCacheTestCase(unittest.TestCase): def test_no_PathListCache(self): """Make sure the PathListCache class is not visible """ try: SCons.PathList.PathListCache except AttributeError: pass else: self.fail("Found PathListCache unexpectedly\n") class PathListTestCase(unittest.TestCase): def test_PathList(self): """Test the PathList() entry point """ x1 = SCons.PathList.PathList(('x',)) x2 = SCons.PathList.PathList(['x',]) assert x1 is x2, (x1, x2) x3 = SCons.PathList.PathList('x') assert not x1 is x3, (x1, x3) if __name__ == "__main__": suite = unittest.TestSuite() tclasses = [ subst_pathTestCase, PathListCacheTestCase, PathListTestCase, ] for tclass in tclasses: names = unittest.getTestCaseNames(tclass, 'test_') suite.addTests(list(map(tclass, names))) if not unittest.TextTestRunner().run(suite).wasSuccessful(): sys.exit(1) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Memoize.py0000644000175000017500000002266612114661557021276 0ustar dktrkranzdktrkranz# # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/Memoize.py 2013/03/03 09:48:35 garyo" __doc__ = """Memoizer A metaclass implementation to count hits and misses of the computed values that various methods cache in memory. Use of this modules assumes that wrapped methods be coded to cache their values in a consistent way. Here is an example of wrapping a method that returns a computed value, with no input parameters: memoizer_counters = [] # Memoization memoizer_counters.append(SCons.Memoize.CountValue('foo')) # Memoization def foo(self): try: # Memoization return self._memo['foo'] # Memoization except KeyError: # Memoization pass # Memoization result = self.compute_foo_value() self._memo['foo'] = result # Memoization return result Here is an example of wrapping a method that will return different values based on one or more input arguments: def _bar_key(self, argument): # Memoization return argument # Memoization memoizer_counters.append(SCons.Memoize.CountDict('bar', _bar_key)) # Memoization def bar(self, argument): memo_key = argument # Memoization try: # Memoization memo_dict = self._memo['bar'] # Memoization except KeyError: # Memoization memo_dict = {} # Memoization self._memo['dict'] = memo_dict # Memoization else: # Memoization try: # Memoization return memo_dict[memo_key] # Memoization except KeyError: # Memoization pass # Memoization result = self.compute_bar_value(argument) memo_dict[memo_key] = result # Memoization return result At one point we avoided replicating this sort of logic in all the methods by putting it right into this module, but we've moved away from that at present (see the "Historical Note," below.). Deciding what to cache is tricky, because different configurations can have radically different performance tradeoffs, and because the tradeoffs involved are often so non-obvious. Consequently, deciding whether or not to cache a given method will likely be more of an art than a science, but should still be based on available data from this module. Here are some VERY GENERAL guidelines about deciding whether or not to cache return values from a method that's being called a lot: -- The first question to ask is, "Can we change the calling code so this method isn't called so often?" Sometimes this can be done by changing the algorithm. Sometimes the *caller* should be memoized, not the method you're looking at. -- The memoized function should be timed with multiple configurations to make sure it doesn't inadvertently slow down some other configuration. -- When memoizing values based on a dictionary key composed of input arguments, you don't need to use all of the arguments if some of them don't affect the return values. Historical Note: The initial Memoizer implementation actually handled the caching of values for the wrapped methods, based on a set of generic algorithms for computing hashable values based on the method's arguments. This collected caching logic nicely, but had two drawbacks: Running arguments through a generic key-conversion mechanism is slower (and less flexible) than just coding these things directly. Since the methods that need memoized values are generally performance-critical, slowing them down in order to collect the logic isn't the right tradeoff. Use of the memoizer really obscured what was being called, because all the memoized methods were wrapped with re-used generic methods. This made it more difficult, for example, to use the Python profiler to figure out how to optimize the underlying methods. """ import types # A flag controlling whether or not we actually use memoization. use_memoizer = None CounterList = [] class Counter(object): """ Base class for counting memoization hits and misses. We expect that the metaclass initialization will have filled in the .name attribute that represents the name of the function being counted. """ def __init__(self, method_name): """ """ self.method_name = method_name self.hit = 0 self.miss = 0 CounterList.append(self) def display(self): fmt = " %7d hits %7d misses %s()" print fmt % (self.hit, self.miss, self.name) def __cmp__(self, other): try: return cmp(self.name, other.name) except AttributeError: return 0 class CountValue(Counter): """ A counter class for simple, atomic memoized values. A CountValue object should be instantiated in a class for each of the class's methods that memoizes its return value by simply storing the return value in its _memo dictionary. We expect that the metaclass initialization will fill in the .underlying_method attribute with the method that we're wrapping. We then call the underlying_method method after counting whether its memoized value has already been set (a hit) or not (a miss). """ def __call__(self, *args, **kw): obj = args[0] if self.method_name in obj._memo: self.hit = self.hit + 1 else: self.miss = self.miss + 1 return self.underlying_method(*args, **kw) class CountDict(Counter): """ A counter class for memoized values stored in a dictionary, with keys based on the method's input arguments. A CountDict object is instantiated in a class for each of the class's methods that memoizes its return value in a dictionary, indexed by some key that can be computed from one or more of its input arguments. We expect that the metaclass initialization will fill in the .underlying_method attribute with the method that we're wrapping. We then call the underlying_method method after counting whether the computed key value is already present in the memoization dictionary (a hit) or not (a miss). """ def __init__(self, method_name, keymaker): """ """ Counter.__init__(self, method_name) self.keymaker = keymaker def __call__(self, *args, **kw): obj = args[0] try: memo_dict = obj._memo[self.method_name] except KeyError: self.miss = self.miss + 1 else: key = self.keymaker(*args, **kw) if key in memo_dict: self.hit = self.hit + 1 else: self.miss = self.miss + 1 return self.underlying_method(*args, **kw) class Memoizer(object): """Object which performs caching of method calls for its 'primary' instance.""" def __init__(self): pass def Dump(title=None): if title: print title CounterList.sort() for counter in CounterList: counter.display() class Memoized_Metaclass(type): def __init__(cls, name, bases, cls_dict): super(Memoized_Metaclass, cls).__init__(name, bases, cls_dict) for counter in cls_dict.get('memoizer_counters', []): method_name = counter.method_name counter.name = cls.__name__ + '.' + method_name counter.underlying_method = cls_dict[method_name] replacement_method = types.MethodType(counter, None, cls) setattr(cls, method_name, replacement_method) def EnableMemoization(): global use_memoizer use_memoizer = 1 # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/Warnings.py0000644000175000017500000001524212114661560021443 0ustar dktrkranzdktrkranz# # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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. # """SCons.Warnings This file implements the warnings framework for SCons. """ __revision__ = "src/engine/SCons/Warnings.py 2013/03/03 09:48:35 garyo" import sys import SCons.Errors class Warning(SCons.Errors.UserError): pass class WarningOnByDefault(Warning): pass # NOTE: If you add a new warning class, add it to the man page, too! class CacheWriteErrorWarning(Warning): pass class CorruptSConsignWarning(WarningOnByDefault): pass class DependencyWarning(Warning): pass class DuplicateEnvironmentWarning(WarningOnByDefault): pass class FutureReservedVariableWarning(WarningOnByDefault): pass class LinkWarning(WarningOnByDefault): pass class MisleadingKeywordsWarning(WarningOnByDefault): pass class MissingSConscriptWarning(WarningOnByDefault): pass class NoMD5ModuleWarning(WarningOnByDefault): pass class NoMetaclassSupportWarning(WarningOnByDefault): pass class NoObjectCountWarning(WarningOnByDefault): pass class NoParallelSupportWarning(WarningOnByDefault): pass class ReservedVariableWarning(WarningOnByDefault): pass class StackSizeWarning(WarningOnByDefault): pass class VisualCMissingWarning(WarningOnByDefault): pass # Used when MSVC_VERSION and MSVS_VERSION do not point to the # same version (MSVS_VERSION is deprecated) class VisualVersionMismatch(WarningOnByDefault): pass class VisualStudioMissingWarning(Warning): pass class FortranCxxMixWarning(LinkWarning): pass # Deprecation warnings class FutureDeprecatedWarning(Warning): pass class DeprecatedWarning(Warning): pass class MandatoryDeprecatedWarning(DeprecatedWarning): pass # Special case; base always stays DeprecatedWarning class PythonVersionWarning(DeprecatedWarning): pass class DeprecatedSourceCodeWarning(FutureDeprecatedWarning): pass class DeprecatedBuildDirWarning(DeprecatedWarning): pass class TaskmasterNeedsExecuteWarning(DeprecatedWarning): pass class DeprecatedCopyWarning(MandatoryDeprecatedWarning): pass class DeprecatedOptionsWarning(MandatoryDeprecatedWarning): pass class DeprecatedSourceSignaturesWarning(MandatoryDeprecatedWarning): pass class DeprecatedTargetSignaturesWarning(MandatoryDeprecatedWarning): pass class DeprecatedDebugOptionsWarning(MandatoryDeprecatedWarning): pass class DeprecatedSigModuleWarning(MandatoryDeprecatedWarning): pass class DeprecatedBuilderKeywordsWarning(MandatoryDeprecatedWarning): pass # The below is a list of 2-tuples. The first element is a class object. # The second element is true if that class is enabled, false if it is disabled. _enabled = [] # If set, raise the warning as an exception _warningAsException = 0 # If not None, a function to call with the warning _warningOut = None def suppressWarningClass(clazz): """Suppresses all warnings that are of type clazz or derived from clazz.""" _enabled.insert(0, (clazz, 0)) def enableWarningClass(clazz): """Enables all warnings that are of type clazz or derived from clazz.""" _enabled.insert(0, (clazz, 1)) def warningAsException(flag=1): """Turn warnings into exceptions. Returns the old value of the flag.""" global _warningAsException old = _warningAsException _warningAsException = flag return old def warn(clazz, *args): global _enabled, _warningAsException, _warningOut warning = clazz(args) for clazz, flag in _enabled: if isinstance(warning, clazz): if flag: if _warningAsException: raise warning if _warningOut: _warningOut(warning) break def process_warn_strings(arguments): """Process string specifications of enabling/disabling warnings, as passed to the --warn option or the SetOption('warn') function. An argument to this option should be of the form or no-. The warning class is munged in order to get an actual class name from the classes above, which we need to pass to the {enable,disable}WarningClass() functions. The supplied is split on hyphens, each element is capitalized, then smushed back together. Then the string "Warning" is appended to get the class name. For example, 'deprecated' will enable the DeprecatedWarning class. 'no-dependency' will disable the DependencyWarning class. As a special case, --warn=all and --warn=no-all will enable or disable (respectively) the base Warning class of all warnings. """ def _capitalize(s): if s[:5] == "scons": return "SCons" + s[5:] else: return s.capitalize() for arg in arguments: elems = arg.lower().split('-') enable = 1 if elems[0] == 'no': enable = 0 del elems[0] if len(elems) == 1 and elems[0] == 'all': class_name = "Warning" else: class_name = ''.join(map(_capitalize, elems)) + "Warning" try: clazz = globals()[class_name] except KeyError: sys.stderr.write("No warning type: '%s'\n" % arg) else: if enable: enableWarningClass(clazz) elif issubclass(clazz, MandatoryDeprecatedWarning): fmt = "Can not disable mandataory warning: '%s'\n" sys.stderr.write(fmt % arg) else: suppressWarningClass(clazz) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/SCons/ExecutorTests.py0000644000175000017500000003770312114661557022510 0ustar dktrkranzdktrkranz# # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/engine/SCons/ExecutorTests.py 2013/03/03 09:48:35 garyo" import sys import unittest import SCons.Executor class MyEnvironment(object): def __init__(self, **kw): self._dict = {} self._dict.update(kw) def __getitem__(self, key): return self._dict[key] def Override(self, overrides): d = self._dict.copy() d.update(overrides) return MyEnvironment(**d) def _update(self, dict): self._dict.update(dict) class MyAction(object): def __init__(self, actions=['action1', 'action2']): self.actions = actions def __call__(self, target, source, env, **kw): for action in self.actions: action(target, source, env, **kw) def genstring(self, target, source, env): return ' '.join(['GENSTRING'] + list(map(str, self.actions)) + target + source) def get_contents(self, target, source, env): return ' '.join(self.actions + target + source) def get_implicit_deps(self, target, source, env): return [] class MyBuilder(object): def __init__(self, env, overrides): self.env = env self.overrides = overrides self.action = MyAction() class MyNode(object): def __init__(self, name=None, pre=[], post=[]): self.name = name self.implicit = [] self.pre_actions = pre self.post_actions = post self.missing_val = None def __str__(self): return self.name def build(self): executor = SCons.Executor.Executor(MyAction(self.pre_actions + [self.builder.action] + self.post_actions), self.builder.env, [], [self], ['s1', 's2']) executor(self) def get_env_scanner(self, env, kw): return MyScanner('dep-') def get_implicit_deps(self, env, scanner, path): return [scanner.prefix + str(self)] def add_to_implicit(self, deps): self.implicit.extend(deps) def missing(self): return self.missing_val def calc_signature(self, calc): return 'cs-'+calc+'-'+self.name def disambiguate(self): return self class MyScanner(object): def __init__(self, prefix): self.prefix = prefix def path(self, env, cwd, target, source): return () def select(self, node): return self class ExecutorTestCase(unittest.TestCase): def test__init__(self): """Test creating an Executor""" source_list = ['s1', 's2'] x = SCons.Executor.Executor('a', 'e', ['o'], 't', source_list) assert x.action_list == ['a'], x.action_list assert x.env == 'e', x.env assert x.overridelist == ['o'], x.overridelist targets = x.get_all_targets() assert targets == ['t'], targets source_list.append('s3') sources = x.get_all_sources() assert sources == ['s1', 's2'], sources try: x = SCons.Executor.Executor(None, 'e', ['o'], 't', source_list) except SCons.Errors.UserError: pass else: raise Exception("Did not catch expected UserError") def test__action_list(self): """Test the {get,set}_action_list() methods""" x = SCons.Executor.Executor('a', 'e', 'o', 't', ['s1', 's2']) l = x.get_action_list() assert l == ['a'], l x.add_pre_action('pre') x.add_post_action('post') l = x.get_action_list() assert l == ['pre', 'a', 'post'], l x.set_action_list('b') l = x.get_action_list() assert l == ['pre', 'b', 'post'], l x.set_action_list(['c']) l = x.get_action_list() assert l == ['pre', 'c', 'post'], l def test_get_build_env(self): """Test fetching and generating a build environment""" x = SCons.Executor.Executor(MyAction(), MyEnvironment(e=1), [], 't', ['s1', 's2']) x.env = MyEnvironment(eee=1) be = x.get_build_env() assert be['eee'] == 1, be env = MyEnvironment(X='xxx') x = SCons.Executor.Executor(MyAction(), env, [{'O':'o2'}], 't', ['s1', 's2']) be = x.get_build_env() assert be['O'] == 'o2', be['O'] assert be['X'] == 'xxx', be['X'] env = MyEnvironment(Y='yyy') overrides = [{'O':'ob3'}, {'O':'oo3'}] x = SCons.Executor.Executor(MyAction(), env, overrides, ['t'], ['s']) be = x.get_build_env() assert be['O'] == 'oo3', be['O'] assert be['Y'] == 'yyy', be['Y'] overrides = [{'O':'ob3'}] x = SCons.Executor.Executor(MyAction(), env, overrides, ['t'], ['s']) be = x.get_build_env() assert be['O'] == 'ob3', be['O'] assert be['Y'] == 'yyy', be['Y'] def test_get_build_scanner_path(self): """Test fetching the path for the specified scanner.""" t = MyNode('t') t.cwd = 'here' x = SCons.Executor.Executor(MyAction(), MyEnvironment(SCANNERVAL='sss'), [], [t], ['s1', 's2']) class LocalScanner(object): def path(self, env, dir, target, source): target = list(map(str, target)) source = list(map(str, source)) return "scanner: %s, %s, %s, %s" % (env['SCANNERVAL'], dir, target, source) s = LocalScanner() p = x.get_build_scanner_path(s) assert p == "scanner: sss, here, ['t'], ['s1', 's2']", p def test_get_kw(self): """Test the get_kw() method""" t = MyNode('t') x = SCons.Executor.Executor(MyAction(), MyEnvironment(), [], [t], ['s1', 's2'], builder_kw={'X':1, 'Y':2}) kw = x.get_kw() assert kw == {'X':1, 'Y':2, 'executor':x}, kw kw = x.get_kw({'Z':3}) assert kw == {'X':1, 'Y':2, 'Z':3, 'executor':x}, kw kw = x.get_kw({'X':4}) assert kw == {'X':4, 'Y':2, 'executor':x}, kw def test__call__(self): """Test calling an Executor""" result = [] def pre(target, source, env, result=result, **kw): result.append('pre') def action1(target, source, env, result=result, **kw): result.append('action1') def action2(target, source, env, result=result, **kw): result.append('action2') def post(target, source, env, result=result, **kw): result.append('post') env = MyEnvironment() a = MyAction([action1, action2]) t = MyNode('t') x = SCons.Executor.Executor(a, env, [], [t], ['s1', 's2']) x.add_pre_action(pre) x.add_post_action(post) x(t) assert result == ['pre', 'action1', 'action2', 'post'], result del result[:] def pre_err(target, source, env, result=result, **kw): result.append('pre_err') return 1 x = SCons.Executor.Executor(a, env, [], [t], ['s1', 's2']) x.add_pre_action(pre_err) x.add_post_action(post) try: x(t) except SCons.Errors.BuildError: pass else: raise Exception("Did not catch expected BuildError") assert result == ['pre_err'], result del result[:] def test_cleanup(self): """Test cleaning up an Executor""" orig_env = MyEnvironment(e=1) x = SCons.Executor.Executor('b', orig_env, [{'o':1}], 't', ['s1', 's2']) be = x.get_build_env() assert be['e'] == 1, be['e'] x.cleanup() x.env = MyEnvironment(eee=1) be = x.get_build_env() assert be['eee'] == 1, be['eee'] x.cleanup() be = x.get_build_env() assert be['eee'] == 1, be['eee'] def test_add_sources(self): """Test adding sources to an Executor""" x = SCons.Executor.Executor('b', 'e', 'o', 't', ['s1', 's2']) sources = x.get_all_sources() assert sources == ['s1', 's2'], sources x.add_sources(['s1', 's2']) sources = x.get_all_sources() assert sources == ['s1', 's2'], sources x.add_sources(['s3', 's1', 's4']) sources = x.get_all_sources() assert sources == ['s1', 's2', 's3', 's4'], sources def test_get_sources(self): """Test getting sources from an Executor""" x = SCons.Executor.Executor('b', 'e', 'o', 't', ['s1', 's2']) sources = x.get_sources() assert sources == ['s1', 's2'], sources x.add_sources(['s1', 's2']) sources = x.get_sources() assert sources == ['s1', 's2'], sources x.add_sources(['s3', 's1', 's4']) sources = x.get_sources() assert sources == ['s1', 's2', 's3', 's4'], sources def test_prepare(self): """Test the Executor's prepare() method""" env = MyEnvironment() t1 = MyNode('t1') s1 = MyNode('s1') s2 = MyNode('s2') s3 = MyNode('s3') x = SCons.Executor.Executor('b', env, [{}], [t1], [s1, s2, s3]) s2.missing_val = True try: r = x.prepare() except SCons.Errors.StopError, e: assert str(e) == "Source `s2' not found, needed by target `t1'.", e else: raise AssertionError("did not catch expected StopError: %s" % r) def test_add_pre_action(self): """Test adding pre-actions to an Executor""" x = SCons.Executor.Executor('b', 'e', 'o', 't', ['s1', 's2']) x.add_pre_action('a1') assert x.pre_actions == ['a1'] x.add_pre_action('a2') assert x.pre_actions == ['a1', 'a2'] def test_add_post_action(self): """Test adding post-actions to an Executor""" x = SCons.Executor.Executor('b', 'e', 'o', 't', ['s1', 's2']) x.add_post_action('a1') assert x.post_actions == ['a1'] x.add_post_action('a2') assert x.post_actions == ['a1', 'a2'] def test___str__(self): """Test the __str__() method""" env = MyEnvironment(S='string') x = SCons.Executor.Executor(MyAction(), env, [], ['t'], ['s']) c = str(x) assert c == 'GENSTRING action1 action2 t s', c x = SCons.Executor.Executor(MyAction(), env, [], ['t'], ['s']) x.add_pre_action(MyAction(['pre'])) x.add_post_action(MyAction(['post'])) c = str(x) expect = 'GENSTRING pre t s\n' + \ 'GENSTRING action1 action2 t s\n' + \ 'GENSTRING post t s' assert c == expect, c def test_nullify(self): """Test the nullify() method""" env = MyEnvironment(S='string') result = [] def action1(target, source, env, result=result, **kw): result.append('action1') env = MyEnvironment() a = MyAction([action1]) x = SCons.Executor.Executor(a, env, [], ['t1', 't2'], ['s1', 's2']) x(MyNode('', [], [])) assert result == ['action1'], result s = str(x) assert s[:10] == 'GENSTRING ', s del result[:] x.nullify() assert result == [], result x(MyNode('', [], [])) assert result == [], result s = str(x) assert s == '', s def test_get_contents(self): """Test fetching the signatures contents""" env = MyEnvironment(C='contents') x = SCons.Executor.Executor(MyAction(), env, [], ['t'], ['s']) c = x.get_contents() assert c == 'action1 action2 t s', c x = SCons.Executor.Executor(MyAction(actions=['grow']), env, [], ['t'], ['s']) x.add_pre_action(MyAction(['pre'])) x.add_post_action(MyAction(['post'])) c = x.get_contents() assert c == 'pre t sgrow t spost t s', c def test_get_timestamp(self): """Test fetching the "timestamp" """ x = SCons.Executor.Executor('b', 'e', 'o', 't', ['s1', 's2']) ts = x.get_timestamp() assert ts == 0, ts def test_scan_targets(self): """Test scanning the targets for implicit dependencies""" env = MyEnvironment(S='string') t1 = MyNode('t1') t2 = MyNode('t2') sources = [MyNode('s1'), MyNode('s2')] x = SCons.Executor.Executor(MyAction(), env, [{}], [t1, t2], sources) deps = x.scan_targets(None) assert t1.implicit == ['dep-t1', 'dep-t2'], t1.implicit assert t2.implicit == ['dep-t1', 'dep-t2'], t2.implicit t1.implicit = [] t2.implicit = [] deps = x.scan_targets(MyScanner('scanner-')) assert t1.implicit == ['scanner-t1', 'scanner-t2'], t1.implicit assert t2.implicit == ['scanner-t1', 'scanner-t2'], t2.implicit def test_scan_sources(self): """Test scanning the sources for implicit dependencies""" env = MyEnvironment(S='string') t1 = MyNode('t1') t2 = MyNode('t2') sources = [MyNode('s1'), MyNode('s2')] x = SCons.Executor.Executor(MyAction(), env, [{}], [t1, t2], sources) deps = x.scan_sources(None) assert t1.implicit == ['dep-s1', 'dep-s2'], t1.implicit assert t2.implicit == ['dep-s1', 'dep-s2'], t2.implicit t1.implicit = [] t2.implicit = [] deps = x.scan_sources(MyScanner('scanner-')) assert t1.implicit == ['scanner-s1', 'scanner-s2'], t1.implicit assert t2.implicit == ['scanner-s1', 'scanner-s2'], t2.implicit def test_get_unignored_sources(self): """Test fetching the unignored source list""" env = MyEnvironment() s1 = MyNode('s1') s2 = MyNode('s2') s3 = MyNode('s3') x = SCons.Executor.Executor('b', env, [{}], [], [s1, s2, s3]) r = x.get_unignored_sources(None, []) assert r == [s1, s2, s3], list(map(str, r)) r = x.get_unignored_sources(None, [s2]) assert r == [s1, s3], list(map(str, r)) r = x.get_unignored_sources(None, [s1, s3]) assert r == [s2], list(map(str, r)) if __name__ == "__main__": suite = unittest.TestSuite() tclasses = [ ExecutorTestCase ] for tclass in tclasses: names = unittest.getTestCaseNames(tclass, 'test_') suite.addTests(list(map(tclass, names))) if not unittest.TextTestRunner().run(suite).wasSuccessful(): sys.exit(1) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/engine/MANIFEST-xml.in0000644000175000017500000000435212114661557020616 0ustar dktrkranzdktrkranzSCons/Action.xml SCons/Defaults.xml SCons/Environment.xml SCons/Platform/__init__.xml SCons/Platform/posix.xml SCons/Platform/sunos.xml SCons/Platform/win32.xml SCons/Scanner/__init__.xml SCons/Script/Main.xml SCons/Script/SConscript.xml SCons/Subst.xml SCons/Tool/386asm.xml SCons/Tool/BitKeeper.xml SCons/Tool/CVS.xml SCons/Tool/Perforce.xml SCons/Tool/RCS.xml SCons/Tool/SCCS.xml SCons/Tool/Subversion.xml SCons/Tool/__init__.xml SCons/Tool/aixc++.xml SCons/Tool/aixcc.xml SCons/Tool/aixf77.xml SCons/Tool/aixlink.xml SCons/Tool/applelink.xml SCons/Tool/ar.xml SCons/Tool/as.xml SCons/Tool/bcc32.xml SCons/Tool/c++.xml SCons/Tool/cc.xml SCons/Tool/cvf.xml SCons/Tool/default.xml SCons/Tool/dmd.xml SCons/Tool/dvi.xml SCons/Tool/dvipdf.xml SCons/Tool/dvips.xml SCons/Tool/f77.xml SCons/Tool/f90.xml SCons/Tool/f95.xml SCons/Tool/fortran.xml SCons/Tool/g++.xml SCons/Tool/g77.xml SCons/Tool/gas.xml SCons/Tool/gcc.xml SCons/Tool/gfortran.xml SCons/Tool/gnulink.xml SCons/Tool/gs.xml SCons/Tool/hpc++.xml SCons/Tool/hpcc.xml SCons/Tool/hplink.xml SCons/Tool/icc.xml SCons/Tool/icl.xml SCons/Tool/ifl.xml SCons/Tool/ifort.xml SCons/Tool/ilink.xml SCons/Tool/ilink32.xml SCons/Tool/install.xml SCons/Tool/intelc.xml SCons/Tool/jar.xml SCons/Tool/javac.xml SCons/Tool/javah.xml SCons/Tool/latex.xml SCons/Tool/lex.xml SCons/Tool/link.xml SCons/Tool/linkloc.xml SCons/Tool/m4.xml SCons/Tool/masm.xml SCons/Tool/midl.xml SCons/Tool/mingw.xml SCons/Tool/mslib.xml SCons/Tool/mslink.xml SCons/Tool/mssdk.xml SCons/Tool/msvc.xml SCons/Tool/msvs.xml SCons/Tool/mwcc.xml SCons/Tool/mwld.xml SCons/Tool/nasm.xml SCons/Tool/packaging.xml SCons/Tool/packaging/__init__.xml SCons/Tool/pdf.xml SCons/Tool/pdflatex.xml SCons/Tool/pdftex.xml SCons/Tool/qt.xml SCons/Tool/rmic.xml SCons/Tool/rpcgen.xml SCons/Tool/sgiar.xml SCons/Tool/sgic++.xml SCons/Tool/sgicc.xml SCons/Tool/sgilink.xml SCons/Tool/sunar.xml SCons/Tool/sunc++.xml SCons/Tool/suncc.xml SCons/Tool/sunf77.xml SCons/Tool/sunf90.xml SCons/Tool/sunf95.xml SCons/Tool/sunlink.xml SCons/Tool/swig.xml SCons/Tool/tar.xml SCons/Tool/tex.xml SCons/Tool/textfile.xml SCons/Tool/tlib.xml SCons/Tool/yacc.xml SCons/Tool/zip.xml SCons/Tool/gettext.xml SCons/Tool/msgfmt.xml SCons/Tool/msginit.xml SCons/Tool/msgmerge.xml SCons/Tool/xgettext.xml scons-doc-2.3.0/src/RELEASE.txt0000644000175000017500000000546612114661557016645 0ustar dktrkranzdktrkranz A new SCons release, 2.3.0, is now available on the SCons download page: http://www.scons.org/download.php This release adds several new features and fixes many issues. Here is a summary of the changes since 2.2: NEW FUNCTIONALITY - Versioned shared library support for Linux and Mac: Add SHLIBVERSION as an option that tells SharedLibrary to build a versioned shared library and create the required symlinks. Add builder InstallVersionedLib to create the required symlinks installing a versioned shared library. DEPRECATED FUNCTIONALITY - Removed a lot of Python 2.3 and older support code - Hide deprecated --debug={dtree,stree,tree} from --help output CHANGED/ENHANCED EXISTING FUNCTIONALITY - No changes FIXES - Fix subprocess spawning on Windows. Work around a Windows bug that can crash python occasionally when using -jN. (#2449) - Fix nested LIBPATH expansion by flattening sequences in subst_path. - Fix WiX Tool to use .wixobj rather than .wxiobj for compiler output - Add MSVC10 and MSVC11 support to get_output low-level bat script runner. - Fix MSVS solution generation for VS11, and fixed tests. IMPROVEMENTS - Error messages from option parser now include hints about valid choices - Support building with WiX releases after 2.0 - Print target name with command execution time with --debug=time - Updated the TeX builder to support the \newglossary command in LaTeX's glossaries package and the files it creates. - Improve support for new versions of biblatex in the TeX builder so biber is called automatically if biblatex requires it. PACKAGING - No changes DEVELOPMENT - Updated test framework to support dir and file fixtures and added ability to test external (out-of-tree) tools. See doc in QMTest/test-framework.rst. - Added ability to run scripts/scons.py directly from source checkout - Several fixes for runtest.py - Fixed several errors in the test suite. - Add -jN support to runtest.py to run tests in parallel Thanks to: Dirk Baechle, Vincent Beffar, Thomas Berg, Jean-François Colson, Bauke Conijn, Bill Deegan, Ken Deeter, dubcanada on Bitbucket, Luca Falavigna, Alexander Goomenyuk, Justin Gullingsrud, Joshua Hughes, Alexey Klimkin, Steven Knight, Arve Knudsen, Jean-Baptiste Lab, Juan Lang, Rob Managan, Mortoray, Gary Oberbrunner, Alexey Petruchik, Evgeny Podjachev, smallbub on Bitbucket, Sohail Somani, Anatoly Techtonik, Paweł Tomulik, Greg Ward, Allen Weeks, Russel Winder, Joe Zuntz for their contributions to this release. Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation src/RELEASE.txt 2013/03/03 09:48:35 garyo scons-doc-2.3.0/src/MANIFEST.in0000644000175000017500000000000012114661557016535 0ustar dktrkranzdktrkranzscons-doc-2.3.0/src/setup.cfg0000644000175000017500000000014412114661560016623 0ustar dktrkranzdktrkranz[bdist_rpm] group = Development/Tools [bdist_wininst] title = SCons - a software construction tool scons-doc-2.3.0/src/README.txt0000644000175000017500000001774212114661557016522 0ustar dktrkranzdktrkranz SCons - a software construction tool Version 2.3.0 This is SCons, a tool for building software (and other files). SCons is implemented in Python, and its "configuration files" are actually Python scripts, allowing you to use the full power of a real scripting language to solve build problems. You do not, however, need to know Python to use SCons effectively. See the RELEASE.txt file for notes about this specific release, including known problems. See the CHANGES.txt file for a list of changes since the previous release. LATEST VERSION ============== Before going further, you can check that this package you have is the latest version by checking the SCons download page at: http://www.scons.org/download.html EXECUTION REQUIREMENTS ====================== Running SCons requires Python version 2.4 or later. There should be no other dependencies or requirements to run SCons. (There is, however, an additional requirement to *install* SCons from this particular package; see the next section.) By default, SCons knows how to search for available programming tools on various systems--see the SCons man page for details. You may, of course, override the default SCons choices made by appropriate configuration of Environment construction variables. INSTALLATION REQUIREMENTS ========================= Nothing special. INSTALLATION ============ Assuming your system satisfies the installation requirements in the previous section, install SCons from this package simply by running the provided Python-standard setup script as follows: # python setup.py install By default, the above command will do the following: -- Install the version-numbered "scons-2.3.0" and "sconsign-2.3.0" scripts in the default system script directory (/usr/bin or C:\Python*\Scripts, for example). This can be disabled by specifying the "--no-version-script" option on the command line. -- Install scripts named "scons" and "sconsign" scripts in the default system script directory (/usr/bin or C:\Python*\Scripts, for example). This can be disabled by specifying the "--no-scons-script" option on the command line, which is useful if you want to install and experiment with a new version before making it the default on your system. On UNIX or Linux systems, you can have the "scons" and "sconsign" scripts be hard links or symbolic links to the "scons-2.3.0" and "sconsign-2.3.0" scripts by specifying the "--hardlink-scons" or "--symlink-scons" options on the command line. -- Install "scons-2.3.0.bat" and "scons.bat" wrapper scripts in the Python prefix directory on Windows (C:\Python*, for example). This can be disabled by specifying the "--no-install-bat" option on the command line. On UNIX or Linux systems, the "--install-bat" option may be specified to have "scons-2.3.0.bat" and "scons.bat" files installed in the default system script directory, which is useful if you want to install SCons in a shared file system directory that can be used to execute SCons from both UNIX/Linux and Windows systems. -- Install the SCons build engine (a Python module) in an appropriate version-numbered SCons library directory (/usr/lib/scons-2.3.0 or C:\Python*\scons-2.3.0, for example). See below for more options related to installing the build engine library. -- Install the troff-format man pages in an appropriate directory on UNIX or Linux systems (/usr/share/man/man1 or /usr/man/man1, for example). This can be disabled by specifying the "--no-install-man" option on the command line. The man pages can be installed on Windows systems by specifying the "--install-man" option on the command line. Note that, by default, SCons does not install its build engine library in the standard Python library directories. If you want to be able to use the SCons library modules (the build engine) in other Python scripts, specify the "--standard-lib" option on the command line, as follows: # python setup.py install --standard-lib This will install the build engine in the standard Python library directory (/usr/lib/python*/site-packages or C:\Python*\Lib\site-packages). Alternatively, you can have SCons install its build engine library in a hard-coded standalone library directory, instead of the default version-numbered directory, by specifying the "--standalone-lib" option on the command line, as follows: # python setup.py install --standalone-lib This is usually not recommended, however. Note that, to install SCons in any of the above system directories, you should have system installation privileges (that is, "root" or "Administrator") when running the setup.py script. If you don't have system installation privileges, you can use the --prefix option to specify an alternate installation location, such as your home directory: $ python setup.py install --prefix=$HOME This will install SCons in the appropriate locations relative to $HOME--that is, the scons script itself $HOME/bin and the associated library in $HOME/lib/scons, for example. DOCUMENTATION ============= See the RELEASE.txt file for notes about this specific release, including known problems. See the CHANGES.txt file for a list of changes since the previous release. The scons.1 man page is included in this package, and contains a section of small examples for getting started using SCons. Additional documentation for SCons is available at: http://www.scons.org/doc.html LICENSING ========= SCons is distributed under the MIT license, a full copy of which is available in the LICENSE.txt file. The MIT license is an approved Open Source license, which means: This software is OSI Certified Open Source Software. OSI Certified is a certification mark of the Open Source Initiative. More information about OSI certifications and Open Source software is available at: http://www.opensource.org/ REPORTING BUGS ============== Please report bugs by following the detailed instructions on our Bug Submission page: http://scons.tigris.org/bug-submission.html You can also send mail to the SCons developers' mailing list: dev@scons.tigris.org But even if you send email to the mailing list please make sure that you ALSO submit a bug report to the project page bug tracker, because bug reports in email often get overlooked in the general flood of messages. MAILING LISTS ============= An active mailing list for users of SCons is available. You may send questions or comments to the list at: users@scons.tigris.org You may subscribe to the mailing list by sending email to: users-subscribe@scons.tigris.org There is also a low-volume mailing list available for announcements about SCons. Subscribe by sending email to: announce-subscribe@scons.tigris.org There are other mailing lists available for SCons developers, for notification of SCons code changes, and for notification of updated bug reports and project documents. Please see our mailing lists page for details. DONATIONS ========= If you find SCons helpful, please consider making a donation (of cash, software, or hardware) to support continued work on the project. Information is available at: http://www.scons.org/donate.html FOR MORE INFORMATION ==================== Check the SCons web site at: http://www.scons.org/ AUTHOR INFO =========== Steven Knight knight at baldmt dot com http://www.baldmt.com/~knight/ With plenty of help from the SCons Development team: Chad Austin Charles Crain Steve Leblanc Greg Noel Gary Oberbrunner Anthony Roach Greg Spencer Christoph Wiedemann Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation src/README.txt 2013/03/03 09:48:35 garyo scons-doc-2.3.0/src/setup.py0000644000175000017500000004006112114661560016516 0ustar dktrkranzdktrkranz# # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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. """ NOTE: Installed SCons is not importable like usual Python packages. It is executed explicitly with command line scripts. This allows multiple SCons versions to coexist within single Python installation, which is critical for enterprise build cases. Explicit invokation is necessary to avoid confusion over which version of SCons is active. By default SCons is installed into versioned directory, e.g. site-packages/scons-2.1.0.alpha.20101125 and much of the stuff below is dedicated to make it happen on various platforms. """ __revision__ = "src/setup.py 2013/03/03 09:48:35 garyo" import os import stat import sys Version = "2.3.0" man_pages = [ 'scons.1', 'sconsign.1', 'scons-time.1', ] # Exit with error if trying to install with Python >= 3.0 if sys.version_info >= (3,0,0): msg = "scons: *** SCons does not run under Python version %s.\n\ Python 3 and above are not yet supported.\n" sys.stderr.write(msg % (sys.version.split()[0])) sys.exit(1) # change to setup.py directory if it was executed from other dir (head, tail) = os.path.split(sys.argv[0]) if head: os.chdir(head) sys.argv[0] = tail # flag if setup.py is run on win32 or _for_ win32 platform, # (when building windows installer on linux, for example) is_win32 = 0 if not sys.platform == 'win32': try: if sys.argv[1] == 'bdist_wininst': is_win32 = 1 except IndexError: pass else: is_win32 = 1 import distutils import distutils.core import distutils.command.install import distutils.command.install_data import distutils.command.install_lib import distutils.command.install_scripts import distutils.command.build_scripts _install = distutils.command.install.install _install_data = distutils.command.install_data.install_data _install_lib = distutils.command.install_lib.install_lib _install_scripts = distutils.command.install_scripts.install_scripts _build_scripts = distutils.command.build_scripts.build_scripts class _options(object): pass Options = _options() Installed = [] def set_explicitly(name, args): """ Return if the installation directory was set explicitly by the user on the command line. This is complicated by the fact that "install --install-lib=/foo" gets turned into "install_lib --install-dir=/foo" internally. """ if args[0] == "install_" + name: s = "--install-dir=" else: # The command is something else (usually "install") s = "--install-%s=" % name set = 0 length = len(s) for a in args[1:]: if a[:length] == s: set = 1 break return set class install(_install): user_options = _install.user_options + [ ('no-scons-script', None, "don't install 'scons', only install 'scons-%s'" % Version), ('no-version-script', None, "don't install 'scons-%s', only install 'scons'" % Version), ('install-bat', None, "install 'scons.bat' script"), ('no-install-bat', None, "do not install 'scons.bat' script"), ('install-man', None, "install SCons man pages"), ('no-install-man', None, "do not install SCons man pages"), ('standard-lib', None, "install SCons library in standard Python location"), ('standalone-lib', None, "install SCons library in separate standalone directory"), ('version-lib', None, "install SCons library in version-numbered directory"), ] boolean_options = _install.boolean_options + [ 'no-scons-script', 'no-version-script', 'install-bat', 'no-install-bat', 'install-man', 'no-install-man', 'standard-lib', 'standalone-lib', 'version-lib' ] if hasattr(os, 'link'): user_options.append( ('hardlink-scons', None, "hard link 'scons' to the version-numbered script, don't make a separate 'scons' copy"), ) boolean_options.append('hardlink-script') if hasattr(os, 'symlink'): user_options.append( ('symlink-scons', None, "make 'scons' a symbolic link to the version-numbered script, don't make a separate 'scons' copy"), ) boolean_options.append('symlink-script') def initialize_options(self): _install.initialize_options(self) self.no_scons_script = 0 self.no_version_script = 0 self.install_bat = 0 self.no_install_bat = not is_win32 self.install_man = 0 self.no_install_man = is_win32 self.standard_lib = 0 self.standalone_lib = 0 self.version_lib = 0 self.hardlink_scons = 0 self.symlink_scons = 0 # Don't warn about having to put the library directory in the # search path. self.warn_dir = 0 def finalize_options(self): _install.finalize_options(self) if self.install_bat: Options.install_bat = 1 else: Options.install_bat = not self.no_install_bat if self.install_man: Options.install_man = 1 else: Options.install_man = not self.no_install_man Options.standard_lib = self.standard_lib Options.standalone_lib = self.standalone_lib Options.version_lib = self.version_lib Options.install_scons_script = not self.no_scons_script Options.install_version_script = not self.no_version_script Options.hardlink_scons = self.hardlink_scons Options.symlink_scons = self.symlink_scons def get_scons_prefix(libdir, is_win32): """ Return the right prefix for SCons library installation. Find this by starting with the library installation directory (.../site-packages, most likely) and crawling back up until we reach a directory name beginning with "python" (or "Python"). """ drive, head = os.path.splitdrive(libdir) while head: if head == os.sep: break head, tail = os.path.split(head) if tail.lower()[:6] == "python": # Found the Python library directory... if is_win32: # ...on Win32 systems, "scons" goes in the directory: # C:\PythonXX => C:\PythonXX\scons return os.path.join(drive + head, tail) else: # ...on other systems, "scons" goes above the directory: # /usr/lib/pythonX.X => /usr/lib/scons return os.path.join(drive + head) return libdir def force_to_usr_local(self): """ A hack to decide if we need to "force" the installation directories to be under /usr/local. This is because Mac Os X Tiger and Leopard, by default, put the libraries and scripts in their own directories under /Library or /System/Library. """ return (sys.platform[:6] == 'darwin' and (self.install_dir[:9] == '/Library/' or self.install_dir[:16] == '/System/Library/')) class install_lib(_install_lib): def finalize_options(self): _install_lib.finalize_options(self) if force_to_usr_local(self): self.install_dir = '/usr/local/lib' args = self.distribution.script_args if not set_explicitly("lib", args): # They didn't explicitly specify the installation # directory for libraries... is_win32 = sys.platform == "win32" or args[0] == 'bdist_wininst' prefix = get_scons_prefix(self.install_dir, is_win32) if Options.standalone_lib: # ...but they asked for a standalone directory. self.install_dir = os.path.join(prefix, "scons") elif Options.version_lib or not Options.standard_lib: # ...they asked for a version-specific directory, # or they get it by default. self.install_dir = os.path.join(prefix, "scons-%s" % Version) msg = "Installed SCons library modules into %s" % self.install_dir Installed.append(msg) class install_scripts(_install_scripts): def finalize_options(self): _install_scripts.finalize_options(self) if force_to_usr_local(self): self.install_dir = '/usr/local/bin' self.build_dir = os.path.join('build', 'scripts') msg = "Installed SCons scripts into %s" % self.install_dir Installed.append(msg) def do_nothing(self, *args, **kw): pass def hardlink_scons(self, src, dst, ver): try: os.unlink(dst) except OSError: pass os.link(ver, dst) def symlink_scons(self, src, dst, ver): try: os.unlink(dst) except OSError: pass os.symlink(os.path.split(ver)[1], dst) def copy_scons(self, src, dst, *args): try: os.unlink(dst) except OSError: pass self.copy_file(src, dst) self.outfiles.append(dst) def run(self): # --- distutils copy/paste --- if not self.skip_build: self.run_command('build_scripts') # --- /distutils copy/paste --- # Custom SCons installation stuff. if Options.hardlink_scons: create_basename_script = self.hardlink_scons elif Options.symlink_scons: create_basename_script = self.symlink_scons elif Options.install_scons_script: create_basename_script = self.copy_scons else: create_basename_script = self.do_nothing if Options.install_version_script: create_version_script = self.copy_scons else: create_version_script = self.do_nothing inputs = self.get_inputs() bat_scripts = [x for x in inputs if x[-4:] == '.bat'] non_bat_scripts = [x for x in inputs if x[-4:] != '.bat'] self.outfiles = [] self.mkpath(self.install_dir) for src in non_bat_scripts: base = os.path.basename(src) scons = os.path.join(self.install_dir, base) scons_ver = scons + '-' + Version if is_win32: scons += '.py' scons_ver += '.py' create_version_script(src, scons_ver) create_basename_script(src, scons, scons_ver) if Options.install_bat: if is_win32: bat_install_dir = get_scons_prefix(self.install_dir, is_win32) else: bat_install_dir = self.install_dir for src in bat_scripts: scons_bat = os.path.join(bat_install_dir, 'scons.bat') scons_version_bat = os.path.join(bat_install_dir, 'scons-' + Version + '.bat') self.copy_scons(src, scons_bat) self.copy_scons(src, scons_version_bat) # --- distutils copy/paste --- if os.name == 'posix': # Set the executable bits (owner, group, and world) on # all the scripts we just installed. for file in self.get_outputs(): if self.dry_run: # log.info("changing mode of %s", file) pass else: # Use symbolic versions of permissions so this script doesn't fail to parse under python3.x exec_and_read_permission = stat.S_IXOTH | stat.S_IXUSR | stat.S_IXGRP | stat.S_IROTH | stat.S_IRUSR | stat.S_IRGRP mode_mask = 4095 # Octal 07777 used because python3 has different octal syntax than python 2 mode = ((os.stat(file)[stat.ST_MODE]) | exec_and_read_permission) & mode_mask # log.info("changing mode of %s to %o", file, mode) os.chmod(file, mode) # --- /distutils copy/paste --- class build_scripts(_build_scripts): def finalize_options(self): _build_scripts.finalize_options(self) self.build_dir = os.path.join('build', 'scripts') class install_data(_install_data): def initialize_options(self): _install_data.initialize_options(self) def finalize_options(self): _install_data.finalize_options(self) if force_to_usr_local(self): self.install_dir = '/usr/local' if Options.install_man: if is_win32: dir = 'Doc' else: dir = os.path.join('man', 'man1') self.data_files = [(dir, man_pages)] man_dir = os.path.join(self.install_dir, dir) msg = "Installed SCons man pages into %s" % man_dir Installed.append(msg) else: self.data_files = [] description = "Open Source next-generation build tool." long_description = """Open Source next-generation build tool. Improved, cross-platform substitute for the classic Make utility. In short, SCons is an easier, more reliable and faster way to build software.""" scripts = [ 'script/scons', 'script/sconsign', 'script/scons-time', # We include scons.bat in the list of scripts, even on UNIX systems, # because we provide an option to allow it be installed explicitly, # for example if you're installing from UNIX on a share that's # accessible to Windows and you want the scons.bat. 'script/scons.bat', ] arguments = { 'name' : "scons", 'version' : Version, 'description' : description, 'long_description' : long_description, 'author' : 'Steven Knight', 'author_email' : 'knight@baldmt.com', 'url' : "http://www.scons.org/", 'packages' : ["SCons", "SCons.compat", "SCons.Node", "SCons.Options", "SCons.Platform", "SCons.Scanner", "SCons.Script", "SCons.Tool", "SCons.Tool.MSCommon", "SCons.Tool.packaging", "SCons.Variables", ], 'package_dir' : {'' : 'engine'}, 'data_files' : [('man/man1', man_pages)], 'scripts' : scripts, 'cmdclass' : {'install' : install, 'install_lib' : install_lib, 'install_data' : install_data, 'install_scripts' : install_scripts, 'build_scripts' : build_scripts} } distutils.core.setup(**arguments) if Installed: for i in Installed: print(i) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: scons-doc-2.3.0/src/Announce.txt0000644000175000017500000013543612114661557017334 0ustar dktrkranzdktrkranz SCons - a software construction tool Release Notes This is SCons, a tool for building software (and other files). SCons is implemented in Python, and its "configuration files" are actually Python scripts, allowing you to use the full power of a real scripting language to solve build problems. You do not, however, need to know Python to use SCons effectively. Please go to http://www.scons.org/download.php to get the latest production release of SCons. So that everyone using SCons can help each other learn how to use it more effectively, please go to http://scons.org/lists.php#users to sign up for the scons-users mailing list. RELEASE 2.3.0 - Mon, 02 Mar 2013 13:22:29 -0400 Please consult the RELEASE.txt file for a summary of changes since the last release and consult the CHANGES.txt file for complete a list of changes since last release. This announcement highlights only the important changes. Please note the following important changes since release 2.2.0: -- SUPPORT FOR PYTHON VERSIONS BEFORE 2.7 IS NOW DEPRECATED ***IMPORTANT***: This release is the last version of SCons to support Python versions older than 2.7. This release will warn if you are running on Python 2.6 or older; future releases will probably not work at all, as we are moving toward supporting Python 3. Use --warn=no-python-version to suppress the warning if needed. -- A lot of python pre-2.4 compatibility code was removed in this release. 2.4 is the official floor for SCons, but this release will likely enforce it more rigidly. -- Spawning subprocesses on Windows should now be more reliable with -jN -- MSVC10 and MSVC11 support improved, and fixed MSVS11 solution generation. -- Various TeX/LaTeX builder improvements -- Support for versioned shared libs on Linux and Mac, via SHLIBVERSION and InstallVersionedLib. -- WiX builder updates Please note the following important changes since release 2.1.0: -- New gettext toolset for internationalization -- Support for Visual Studio 11 -- Support for Intel C/C++ compiler v12 on Linux and Mac -- LaTeX support for multibib, biblatex and biber Please note the following important changes since release 2.0.0: -- Support for Windows manifest generation -- SCons now searches sitewide dirs for site_scons -- Support for Latex bibunits package has been added along with support for tex files generated by other builders Please note the following important changes since release 1.3.0: -- SUPPORT FOR PYTHON VERSIONS PRIOR TO 2.4 HAS BEEN REMOVED Although SCons is still tested with Python 2.3, use of Python versions prior to 2.4 is deprecated. -- DEPRECATED FEATURES WILL GENERATE MANDATORY WARNINGS IN 2.0.0 In keeping with our deprecation cycle, the following deprecated features will still be supported in 2.0.0 but will generate mandatory, non-disableable warnings: -- The overrides= keyword argument to the Builder() call. -- The scanner= keyword argument to the Builder() call. -- The BuildDir() function and env.BuildDir() method. -- The env.Copy() method. -- The SourceSignatures() function and env.SourceSignatures() method. -- The TargetSignatures() function and env.TargetSignatures() method. -- The Sig module (now an unnused stub). -- The --debug=dtree, --debug=stree and --debug=tree options. -- The --debug=nomemoizer option. -- The Options object and the related BoolOption(), EnumOption(), ListOption(), PackageOption() and PathOption() functions. The mandatory warnings will be issued in order to make sure users of 1.3.0 notice *prior* to the release of SCons 2.0.0, that these features will be removed. In SCons 2.0.0 these features will no longer work at all, and will instead generate specific fatal errors when anyone tries to use them. Please note the following important changes since release 1.2.0: -- MICROSOFT VISUAL STUDIO VERSION/ARCH DETECTION HAS CHANGED The way SCons detects Visual Studio on Windows has changed in 1.3. By default, it should now use the latest installed Visual Studio version on your machine, and compile for 32 or 64 bits according to whether your OS is 32 or 64 bits (32/64 bit Python makes no difference). Two new variables control Visual Studio: MSVC_VERSION and TARGET_ARCH. These variables ONLY take effect when passed to the Environment() constructor; setting them later has no effect. To use a non-default Visual Studio version, set MSVC_VERSION to e.g. "8.0" or "7.1". Setting it to "xxx" (or any nonexistent value) will make it print out the valid versions on your system. To use a non-default architecture, set TARGET_ARCH to "x86" or "x86_64" (various synonyms are accepted). Note that if you use MSVS_VERSION to build Visual Studio projects from your SConstructs, MSVS_VERSION must be set to the same version as MSVC_VERSION. Support for HOST_OS,HOST_ARCH,TARGET_OS, TARGET_ARCH has been added to allow specifying different target arch than the host system. This is only supported for Visual Studio/Visual C++ at this time. -- Support for Latex glossaries and acronyms has been added -- VISUAL C/C++ PRECOMPILED HEADERS WILL BE REBUILT Precompiled header files built with Visual C/C++ will be rebuilt after upgrading from 1.2.0 to a later release. This rebuild is normal and will occur because the command line defined by the $PCHCOM construction variable has had the $CCFLAGS variable added, and has been rearranged to put the "/Fo" output flag towards the beginning of the line, consistent with the related command lines for $CCCOM, $CXXCOM, etc. -- CHANGES TO SOME LINKER COMMAND LINES WILL CAUSE RELINKING Changes to the command line definitions for the Microsoft link.exe linker, the OS/2 ilink linker and the Phar Lap linkloc linker will cause targets built with those tools be to be rebuilt after upgrading from 1.2.0 to a later release. This relink is normal and will occur because the command lines for these tools have been redefined to remove unnecessary nested $( and $) character strings. -- MSVS_USE_MFC_DIRS and MSVS_IGNORE_IDE_PATHS are obsoleted and have no effect. Please note the following important changes since release 1.1.0: -- THE $CHANGED_SOURCES, $CHANGED_TARGETS, $UNCHANGED_SOURCES AND $UNCHANGED_TARGETS VARIABLES WILL BECOME RESERVED A future release (probably 1.3.0) will make the construction variable names $CHANGED_SOURCES, $CHANGED_TARGETS, $UNCHANGED_SOURCES and $UNCHANGED_TARGETS into reserved construction variable names controlled by SCons itself (like the current $SOURCE, $TARGETS, etc.). Setting these variable names in the current release will generate a warning but still set the variables. When they become reserved variable names, they will generate a different warning message and attempts to set these variables will be ignored. SCons configurations that happen to use these variable names should be changed to use different variable names, in order to ensure that the configuration continues to work with future versions of SCons. -- THE Options OBJECT AND RELATED FUNCTIONS NOW GENERATE WARNINGS Use of the Options object, and related functions BoolOption(), EnumOption(), ListOption(), PackageOption() and PathOption() were announced as deprecated in release 0.98.1. Since then, however, no warning messages were ever implemented for the use of these deprecated functions. By default, release 1.2.0 prints warning messages when these deprecated features are used. Warnings about all deprecated features may be suppressed by using the --warn=no-deprecated command-line option: $ scons --warn=no-deprecated Or by using the appropriate SetOption() call in any SConscript file: SetOption('warn', 'no-deprecated') You may optionally disable just warnings about the deprecation of the Options object and its related functions as follows: SetOption('warn', 'no-deprecated-options') The current plan is for these warnings to become mandatory (non-suppressible) in release 1.3.0, and for the use of Options and its related functions to generate errors in release 2.0. Please note the following important changes since release 0.98.4: -- scons.bat NOW RETURNS THE REAL SCONS EXIT STATUS The scons.bat script shipped with SCons used to exit with a status of 1 when it detected any failed (non-zero) exit status from the underlying Python execution of SCons itself. The scons.bat script now exits with the actual status returned by SCons. -- SCONS NOW WARNS WHEN TRYING TO LINK C++ AND FORTRAN OBJECT FILES Some C++ toolchains do not understand Fortran runtimes and create unpredictable executables when linking C++ and Fortran object files together. SCons now issues a warning if you try to link C++ and Fortran object files into the same executable: scons: warning: Using $CXX to link Fortran and C++ code together. This may generate a buggy executable if the '/usr/bin/gcc' compiler does not know how to deal with Fortran runtimes. The warning may be suppressed with either the --warning=no-link or --warning=no-fortran-cxx-mix command line options, or by adding either of the following lines to a SConscript file: SetOption('warn', 'no-link') SetOption('warn', 'no-fortran-cxx-mix') Please note the following important changes since release 0.98: -- SCONS NO LONGER SETS THE GNU TOOLCHAIN -fPIC FLAG IN $SHCXXFLAGS The GNU toolchain support in previous versions of SCons would add the -fPIC flag to the $SHCXXFLAGS construction variable. The -fPIC flag has now been removed from the default $SHCXXFLAGS setting. Instead, the $SHCXXCOM construction variable (the default SCons command line for compiling shared objects from C++ source files) has been changed to add the $SHCCFLAGS variable, which contains the -fPIC flag. This change was made in order to make the behavior of the default C++ compilation line including $SHCCFLAGS consistent with the default C compilation line including $CCFLAGS. This change should have no impact on configurations that use the default $SHCXXCOM command line. It may have an impact on configurations that were using the default $SHCXXFLAGS value *without* the $SHCCFLAGS variable to get the -fPIC flag into a custom command line. You can fix these by adding the $SHCCFLAGS to the custom command line. Adding $SHCCFLAGS is backwards compatible with older SCons releases, although it might cause the -fPIC flag to be repeated on the command line if you execute it on an older version of SCons that sets -fPIC in both the $SHCCLAFGS and $SHCXXFLAGS variables. Duplicating the -fPIC flag on the g++ command line will not cause any compilation problems, but the change to the command line may cause SCons to rebuild object files. -- FORTRAN NOW COMPILES .f FILES WITH gfortran BY DEFAULT The Fortran Tool modules have had a major overhaul with the intent of making them work as-is for most configurations. In general, most configurations that use default settings should not see any noticeable difference. One configuration that has changed is if you have both a gfortran and g77 compiler installed. In this case, previous versions of SCons would, by default, use g77 by default to compile files with a .f suffix, while SCons 0.98.1 will use the gfortran compiler by default. The old behavior may be preserved by explicitly initializing construction environments with the 'g77' Tool module: env = Environment(tools = ['g77', 'default']) The above code is backwards compatible to older versions of SCons. If you notice any other changes in the behavior of default Fortran support, please let us know so we can document them in these release notes for other users. Please note the following important changes since release 0.97.0d20071212: -- SUPPORT FOR PYTHON VERSIONS BEFORE 2.2 IS NOW DEPRECATED SCons now prints the following warning when it is run by any Python 1.5, 2.0 or 2.1 release or sub-release: scons: warning: Support for pre-2.2 Python (VERSION) is deprecated. If this will cause hardship, contact dev@scons.tigris.org. You may disable all warnings about deprecated features by adding the option "--warn=no-deprecated" to the command line or to the $SCONSFLAGS environment variable: $ scons --warn=no-deprecated Using '--warn=no-deprecated' is compatible with earlier versions of SCons. You may also, as of this version of SCons, disable all warnings about deprecated features by adding the following to any SConscript file: SetOption('warn', 'no-deprecated') You may disable only the specific warning about running under a deprecated Python version by adding the following to any SConscript file: SetOption('warn', 'no-python-version') The warning may also be suppressed on the command line: $ scons --warn=no-python-version Or by specifying the --warn=no-python-version option in the $SCONSFLAGS environment variable. Using SetOption('warn', ...), and the 'no-python-version' command-line option for suppressing this specific warning, are *not* backwards-compatible to earlier versions of SCons. -- THE env.Copy() METHOD IS NOW OFFICIALLY DEPRECATED The env.Copy() method is now officially deprecated and will be removed in a future release. Using the env.Copy() method now generates the following message: scons: warning: The env.Copy() method is deprecated; use the env.Clone() method instead. You may disable all warnings about deprecated features by adding the option "--warn=no-deprecated" to the command line or to the $SCONSFLAGS environment variable: $ scons --warn=no-deprecated Using '--warn=no-deprecated' is compatible with earlier versions of SCons. You may also, as of this version of SCons, disable all warnings about deprecated features by adding the following to any SConscript file: SetOption('warn', 'no-deprecated') You may disable only the specific warning about the deprecated env.Copy() method by adding the following to any SConscript file: SetOption('warn', 'no-deprecated-copy') The warning may also be suppressed on the command line: $ scons --warn=no-deprecated-copy Or by specifying the --warn=no-deprecated-copy option in the $SCONSFLAGS environment variable. Using SetOption('warn', ...), and the 'no-deprecated-copy' command-line option for suppressing this specific warning, are *not* backwards-compatible to earlier versions of SCons. -- THE --debug=dtree, --debug=stree AND --debug=tree OPTIONS ARE DEPRECATED The --debug=dtree, --debug=stree and --debug=tree methods are now officially deprecated and will be removed in a future release. Using these options now generate a warning message recommending use of the --tree=derived, --tree=all,status and --tree=all options, respectively. You may disable these warnings, and all warnings about deprecated features, by adding the option "--warn=no-deprecated" to the command line or to the $SCONSFLAGS environment variable: $ scons --warn=no-deprecated Using '--warn=no-deprecated' is compatible with earlier versions of SCons. -- THE TargetSignatures() AND SourceSignatures() FUNCTIONS ARE DEPRECATED The TargetSignatures() and SourceSignatures() functions, and their corresponding env.TargetSignatures() and env.SourceSignatures() methods, are now officially deprecated and will be be removed in a future release. Using ahy of these functions or methods now generates a message similar to the following: scons: warning: The env.TargetSignatures() method is deprecated; convert your build to use the env.Decider() method instead. You may disable all warnings about deprecated features by adding the option "--warn=no-deprecated" to the command line or to the $SCONSFLAGS environment variable: $ scons --warn=no-deprecated Using '--warn=no-deprecated' is compatible with earlier versions of SCons. You may also, as of this version of SCons, disable all warnings about deprecated features by adding the following to any SConscript file: SetOption('warn', 'no-deprecated') You may disable only the specific warning about the use of TargetSignatures() or SourceSignatures() by adding the following to any SConscript file: SetOption('warn', 'no-deprecated-target-signatures') SetOption('warn', 'no-deprecated-source-signatures') The warnings may also be suppressed on the command line: $ scons --warn=no-deprecated-target-signatures --warn=no-deprecated-source-signatures Or by specifying these options in the $SCONSFLAGS environment variable. Using SetOption('warn', ...), or the command-line options for suppressing these warnings, is *not* backwards-compatible to earlier versions of SCons. -- File(), Dir() and Entry() NOW RETURN A LIST WHEN THE INPUT IS A SEQUENCE Previously, if these methods were passed a list, the list was substituted and stringified, then passed as a single string to create a File/Dir/Entry Node. This rarely if ever worked with more than one element in the list. They now return a list of Nodes when passed a list. One case that works differently now is a passing in a single-element sequence; that formerly was stringified (returning its only element) and then a single Node would be returned. Now a single-element list containing the Node will be returned, for consistency. -- THE env.subst() METHOD NOW RETURNS A LIST WHEN THE INPUT IS A SEQUENCE The env.subst() method now returns a list with the elements expanded when given a list as input. Previously, the env.subst() method would always turn its result into a string. This behavior was changed because it interfered with being able to include things like lists within the expansion of variables like $CPPPATH and then have SCons understand that the elements of the "internal" lists still needed to be treated separately. This would cause a $CPPPATH list like ['subdir1', 'subdir'] to show up in a command line as "-Isubdir1 subdir". -- THE Jar() BUILDER NOW USES THE Java() BUILDER CLASSDIR BY DEFAULT By default, the Jar() Builder will now use the class directory specified when the Java() builder is called. So the following input: classes = env.Java('classes', 'src') env.Jar('out.jar', classes) Will cause "-C classes" to be passed the "jar" command invocation, and the Java classes in the "out.jar" file will not be prefixed "classes/". Explicitly setting the $JARCHDIR variable overrides this default behavior. The old behavior of not passing any -C option to the "jar" command can be preserved by explicitly setting $JARCHDIR to None: env = Environment(JARCHDIR = None) The above setting is compatible with older versions of SCons. Please note the following important changes since release 0.97.0d20070918: -- SCons REDEFINES PYTHON open() AND file() ON Windows TO NOT PASS ON OPEN FILE HANDLES TO CREATED PROCESSES On Windows systems, SCons now redefines the Python open() and file() functions so that, if the Python Win32 extensions are available, the file handles for any opened files will *not* be inherited by subprocesses, such as the spawned compilers and other tools invoked to build the software. This prevents certain race conditions where a file handle for a file opened by Python (either in a Python function action, or directly in a SConscript file) could be inherited and help open by a subprocess, interfering with the ability of other processes to create or modify the file. In general, this should not cause problems for the vast majority of configurations. The only time this would be a problem would be in the unlikely event that a process spawned by SCons specifically *expected* to use an inherited file handle opened by SCons. If the Python Win32 extensions are not installed or are an earlier version that does not have the ability to disable file handle inheritance, SCons will print a warning message when the -j option is used. The warning message may be suppressed by specifying --warn=no-parallel-support. Please note the following important changes since release 0.97.0d20070809: -- "content" SIGNATURES ARE NOW THE DEFAULT BEHAVIOR The default behavior of SCons is now to use the MD5 checksum of all file contents to decide if any files have changed and should cause rebuilds of their source files. This means that SCons may decide not to rebuild "downstream" targets if a a given input file is rebuilt to the exact same contents as the last time. The old behavior may preserved by explicity specifying: TargetSignatures("build") In any of your SConscript files. -- TARGETS NOW IMPLICITLY DEPEND ON THE COMMAND THAT BUILDS THEM For all targets built by calling external commands (such as a compiler or other utility), SCons now adds an implicit dependency on the command(s) used to build the target. This will cause rebuilds of all targets built by external commands when running SCons in a tree built by previous version of SCons, in order to update the recorded signatures. The old behavior of not having targets depend on the external commands that build them can be preserved by setting a new $IMPLICIT_COMMAND_DEPENDENCIES construction variable to a non-True value: env = Environment(IMPLICIT_COMMAND_DEPENDENCIES = 0) or by adding Ignore() calls for any targets where the behavior is desired: Ignore('/usr/bin/gcc', 'foo.o') Both of these settings are compatible with older versions of SCons. -- CHANGING SourceSignature() MAY CAUSE "UNECESSARY" REBUILDS If you change the SourceSignature() value from 'timestamp' to 'MD5', SCons will now rebuild targets that were already up-to-date with respect to their source files. This will happen because SCons did not record the content signatures of the input source files when the target was last built--it only recorded the timestamps--and it must record them to make sure the signature information is correct. However, the content of source files may have changed since the last timestamp build was performed, and SCons would not have any way to verify that. (It would have had to open up the file and record a content signature, which is one of the things you're trying to avoid by specifying use of timestamps....) So in order to make sure the built targets reflect the contents of the source files, the targets must be rebuilt. Change the SourceSignature() value from 'MD5' to 'timestamp' should correctly not rebuild target files, because the timestamp of the files is always recorded. In previous versions of SCons, changing the SourceSignature() value would lead to unpredictable behavior, usually including rebuilding targets. -- THE Return() FUNCTION NOW ACTUALLY RETURNS IMMEDIATELY The Return() function now immediately stops processing the SConscript file in which it appears and returns the values of the variables named in its arguments. It used to continue processing the rest of the SConscript file, and then return the values of the specified variables at the point the Return() function was called. The old behavior may be requested by adding a "stop=False" keyword argument to the Return() call: Return('value', stop=False) The "stop=" keyword argument is *not* compatible with SCons versions 0.97.0d20070809 or earlier. Please note the following important changes since release 0.97: -- env.CacheDir() NOW ONLY AFFECTS CONSTRUCTION ENVIRONMENT TARGETS The env.CacheDir() method now only causes derived files to be retrieved from the specified cache directory for targets built with the specified specified construction environment ("env"). Previously, any call to env.CacheDir() or CacheDir() would modify a global setting and cause all built targets to be retrieved from the specified cache directory. This behavior was changed so that env.CacheDir() would be consistent with other construction environment methods, which only affect targets built with the specified construction environment. The old behavior of changing the global behavior may be preserved by changing any env.CacheDir() calls to: CacheDir('/path/to/cache/directory') The above change is backwards-compatible and works in all earlier versions of SCons that support CacheDir(). -- INTERPRETATION OF SUFFIX-LESS SOURCE ARGUMENTS HAS CHANGED The interpretation of source arguments (files) without suffixes has changed in one specific configuration. Previously, if a Builder had a src_suffix specified (indicating that source files without suffixes should have that suffix appended), the suffix would only be applied to suffix-less source arguments if the Builder did *not* have one or more attached source Builders (that is, the Builder was not a "multi-stage" Builder). So in the following configuration: build_foo = Builder(src_suffix = '.foo') build_bar = Builder(src_suffix = '.bar', src_builder = build_bar) env = Environment(BUILDERS = { 'Foo' : build_foo, 'Boo' : build_bar, }) env.Foo('tgt1', 'src1') env.Bar('tgt2', 'src2') SCons would have expected to find a source file 'src1.foo' for the env.Foo() call, but a source file 'src2' for the env.Bar() call. This behavior has now been made consistent, so that the two above calls would expect source files named 'src1.foo' and 'src2.bar', respectively. Note that, if genuinely desired, the old behavior of building from a source file without a suffix at all (when the Builder has a src_suffix *and* a src_builder) can be specified explicity by turning the string into a File Node directly: env.Bar('tgt2', File('src2')) The above use of File() is backwards-compatible and will work on earlier versions of SCons. -- 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 looks for an exact match of the previous text. Please note the following planned, future changes: -- THE Options OBJECT AND RELATED FUNCTIONS WILL BE DEPRECATED The Options object is being replaced by a new Variables object, which uses a new Variables.AddVariable() method where the previous interface used Options.AddOptions(). Similarly, the following utility functions are being replaced by the following similarly-named functions: BoolOption() BoolVariable() EnumOption() EnumVariable() ListOption() ListVariable() PackageOption() PackageVariable() PathOption() PathVariable() And also related, the options= keyword argument when creating construction environments with the Environment() functions is being replaced with a variables= keyword argument. In some future release a deprecation warning will be added to existing uses of the Options object, its methods, the above utility functions, and the options= keyword argument of the Environment() function. At some point after the deprecation warning is added, the Options object, related functions and options= keyword argument will be removed entirely. You can prepare for this by changing all your uses of the Options object and related functions to the Variables object and the new function names, and changing any uses of the options= keyword argument to variables=. NOTE: CONVERTING TO USING THE NEW Variables OBJECT OR THE RELATED *Variable() FUNCTIONS, OR USING THE NEW variable= KEYWORD ARGUMENT, IS NOT BACKWARDS COMPATIBLE TO VERSIONS OF SCons BEFORE 0.98. YOUR SConscript FILES WILL NOT WORK ON EARLIER VERSIONS OF SCons AFTER MAKING THIS CHANGE. If you change SConscript files in software that you make available for download or otherwise distribute, other users may try to build your software with an earlier version of SCons that does not have the Variables object or related *Variable() functions. We recommend preparing for this in one of two ways: -- Make your SConscript files backwards-compatible by modifying your calls with Python try:-except: blocks as follows: try: vars = Variables('custom.py', ARGUMENTS) vars.AddVariables( BoolVariable('WARNINGS', 'cmopile with -Wall', 1), EnumVariable('DEBUG', 'debug version', 'no' allowed_values=('yes', 'no', 'full'), map={}, ignorecase=0), ListVariable('SHAREDLIBS', 'libraries to build shared', 'all', names = list_of_libs), PackageVariable('X11', 'use X11 from here', '/usr/bin/X11'), PathVariable('QTDIR', 'root of Qt', qtdir), ) except NameError: vars = Options('custom.py', ARGUMENTS) vars.AddOptions( BoolOption('WARNINGS', 'cmopile with -Wall', 1), EnumOption('DEBUG', 'debug version', 'no' allowed_values=('yes', 'no', 'full'), map={}, ignorecase=0), ListOption('SHAREDLIBS', 'libraries to build shared', 'all', names = list_of_libs), PackageOption('X11', 'use X11 from here', '/usr/bin/X11'), PathOption('QTDIR', 'root of Qt', qtdir), ) Additionally, you can check for availability of the new variables= keyword argument as follows: try: env = Environment(variables=vars) except TypeError: env = Environment(options=vars) (Note that we plan to maintain the existing Options object name for some time, to ensure backwards compatibility, so in practice it may be easier to just continue to use the old name until you're reasonably sure you won't have people trying to build your software with versions of SCons earlier than 0.98.1.) -- Use the EnsureSConsVersion() function to provide a descriptive error message if your SConscript files are executed by an earlier version of SCons: EnsureSConsVersion(0, 98, 1) -- THE BuildDir() METHOD AND FUNCTION WILL BE DEPRECATED The env.BuildDir() method and BuildDir() function are being replaced by the new env.VariantDir() method and VariantDir() function. In some future release a deprecation warning will be added to existing uses of the env.BuildDir() method and BuildDir() function. At some point after the deprecation warning, the env.Builder() method and BuildDir() function will either be removed entirely or have their behavior changed. You can prepare for this by changing all your uses of the env.BuildDir() method to env.VariantDir() and uses of the global BuildDir() function to VariantDir(). If you use a named keyword argument of "build_dir" when calling env.BuildDir() or BuildDir(): env.BuildDir(build_dir='opt', src_dir='src') The keyword must be changed to "variant_dir": env.VariantDir(variant_dir='opt', src_dir='src') NOTE: CHANGING USES OF env.BuildDir() AND BuildDir() to env.VariantDir() AND VariantDir() IS NOT BACKWARDS COMPATIBLE TO VERSIONS OF SCons BEFORE 0.98. YOUR SConscript FILES WILL NOT WORK ON EARLIER VERSIONS OF SCons AFTER MAKING THIS CHANGE. If you change SConscript files in software that you make available for download or otherwise distribute, other users may try to build your software with an earlier version of SCons that does not have the env.VariantDir() method or VariantDir() fnction. We recommend preparing for this in one of two ways: -- Make your SConscript files backwards-compatible by including the following code near the beginning of your top-level SConstruct file: import SCons.Environment try: SCons.Environment.Environment.VariantDir except AttributeError: SCons.Environment.Environment.VariantDir = \ SCons.Environment.Environment.BuildDir -- Use the EnsureSConsVersion() function to provide a descriptive error message if your SConscript files are executed by an earlier version of SCons: EnsureSConsVersion(0, 98) -- THE SConscript() "build_dir" KEYWORD ARGUMENT WILL BE DEPRECATED The "build_dir" keyword argument of the SConscript function and env.SConscript() method are being replaced by a new "variant_dir" keyword argument. In some future release a deprecation warning will be added to existing uses of the SConscript()/env.SConscript() "build_dir" keyword argument. At some point after the deprecation warning, support for this keyword argument will be removed entirely. You can prepare for this by changing all your uses of the SConscript()/env.SConscript() 'build_dir" keyword argument: SConscript('src/SConscript', build_dir='opt') To use the new "variant_dir" keyword argument: SConscript('src/SConscript', variant_dir='opt') NOTE: USING THE NEW "variant_dir" KEYWORD IS NOT BACKWARDS COMPATIBLE TO VERSIONS OF SCons BEFORE 0.98. YOUR SConscript FILES WILL NOT WORK ON EARLIER VERSIONS OF SCons AFTER MAKING THIS CHANGE. If you change SConscript files in software that you make available for download or otherwise distribute, other users may try to build your software with an earlier version of SCons that does not support the "variant_dir" keyword. If you can insist that users use a recent version of SCons that supports "variant_dir", we recommend using the EnsureSConsVersion() function to provide a descriptive error message if your SConscript files are executed by an earlier version of SCons: EnsureSConsVersion(0, 98) If you want to make sure that your SConscript files will still work with earlier versions of SCons, then your best bet is to continue to use the "build_dir" keyword until the support is removed (which, in all likelihood, won't happen for quite some time). -- SCANNER NAMES HAVE BEEN DEPRECATED AND WILL BE REMOVED Several internal variable names in SCons.Defaults for various pre-made default Scanner objects have been deprecated and will be removed in a future revision. In their place are several new global variable names that are now part of the publicly-supported interface: NEW NAME DEPRECATED NAME -------- ---------------------------- CScanner SCons.Defaults.CScan DSCanner SCons.Defaults.DScan SourceFileScanner SCons.Defaults.ObjSourceScan ProgramScanner SCons.Defaults.ProgScan Of these, only ObjSourceScan was probably used at all, to add new mappings of file suffixes to other scanners for use by the Object() Builder. This should now be done as follows: SourceFileScanner.add_scanner('.x', XScanner) -- THE env.Copy() METHOD WILL CHANGE OR GO AWAY ENTIRELY The env.Copy() method (to make a copy of a construction environment) is being replaced by the env.Clone() method. As of SCons 0.98, a deprecation warning has been added to current uses of the env.Copy() method. At some point in the future, the env.Copy() method will either be removed entirely or have its behavior changed. You can prepare for this by changing all your uses of env.Copy() to env.Clone(), which has the exact same calling arguments. NOTE: CHANGING USES OF env.Copy() TO env.Clone() WILL MAKE YOUR SConscript FILES NOT WORK ON VERSIONS OF SCons BEFORE 0.96.93. If you change SConscript files in software that you make available for download or otherwise distribute, other users may try to build your software with an earlier version of SCons that does not have the env.Clone() method. We recommend preparing for this in one of two ways: -- Make your SConscript files backwards-compatible by including the following code near the beginning of your top-level SConstruct file: import SCons.Environment try: SCons.Environment.Environment.Clone except AttributeError: SCons.Environment.Environment.Clone = \ SCons.Environment.Environment.Copy -- Use the EnsureSConsVersion() function to provide a descriptive error message if your SConscript files are executed by an earlier version of SCons: EnsureSConsVersion(0, 96, 93) -- THE CheckLib Configure TEST WILL CHANGE BEHAVIOR The CheckLib() Configure test appends the lib(s) to the Environment's LIBS list in 1.3 and earlier. In 1.3 there is a new CheckLib argument, append, which defaults to True to preserve the old behavior. In a future release, append will be changed to default to False, to conform with autoconf and user expectations, since it is usually used to build up library lists in a right-to-left way. SCons is developed with an extensive regression test suite, and a rigorous development methodology for continually improving that suite. Because of this, SCons is of sufficient quality that you can use it for real work. The interfaces in release 1.0 will *not* be knowingly changed in any new, future 1.x release. If an interface change should ever become necessary due to extraordinary circumstances, the change and an appropriate transition strategy will be documented in these RELEASE notes. As you use SCons, please heed the following: - Please report any bugs or other problems that you find to our bug tracker at our SourceForge project page: http://sourceforge.net/tracker/?func=add&group_id=30337&atid=398971 We have a reliable bug-fixing methodology already in place and strive to respond to problems relatively quickly. - Documentation is spottier than we'd like. You may need to dive into the source code to figure out how to do something. Asking questions on the scons-users mailing list is also welcome. We will be addressing the documentation in upcoming releases, but would be more than glad to have your assistance in correcting this problem... :-) - The "SCons Design" documentation on the SCons web site is very out of date, as we made significant changes to portions of the interface as we figured out what worked and what didn't during the extensive beta implementation. The "SCons Design" document should be used only for historical purposes, or for just an extremely general understanding of SCons' architectural goals. - There may be performance issues. Improving SCons performance is an ongoing priority. If you still find the performance unacceptable, we would very much like to hear from you and learn more about your configuration so we can optimize the right things. - Error messages don't always exist where they'd be helpful. Please let us know about any errors you ran into that would have benefitted from a (more) descriptive message. KNOWN PROBLEMS IN THIS RELEASE: For a complete list of known problems, consult the SCons Issue Tracker at tigris.org: http://scons.tigris.org/project_issues.html - Support for parallel builds (-j) does not work on WIN32 systems prior to *official* Python release 2.2 (not 2.2 pre-releases). Prior to Python 2.2, there is a bug in Python's Win32 implementation such that when a thread spawns an external command, it blocks all threads from running. This breaks the SCons multithreading architecture used to support -j builds. We have included a patch file, os_spawnv_fix.diff, that you can use if you you want to fix your version of Python to support parallel builds in SCons. - Again, the "SCons Design" documentation on the SCons web site is out of date. Take what you read there with a grain of salt. - On Win32 systems, you must put a space between the redirection characters < and >, and the specified files (or construction variable expansions): command < $SOURCE > $TARGET If you don't supply a space (for example, "<$SOURCE"), SCons will not recognize the redirection. - MSVC .res files are not rebuilt when icons change. - The -c option does not clean up .sconsign files or directories created as part of the build, and also does not clean up SideEffect files (for example, Visual Studio .pdb files). - When using multiple Repositories, changing the name of an include file can cause an old version of the file to be used. - There is currently no way to force use of a relative path (../*) for directories outside the top-level SConstruct file. - The Jar() Builder will, on its second or subsequent invocation, package up the .sconsign files that SCons uses to track signatures. You can work around this by using the SConsignFile() function to collect all of the .sconsign information into a single file outside of the directory being packaged by Jar(). - SCons does not currently have a way to detect that an intermediate file has been corrupted from outside and should be rebuilt. - Unicode characters in path names do not work in all circumstances. - SCons does not currently automatically check out SConstruct or SConscript files from SCCS, RCS or BitKeeper. - No support yet for the following planned command-line options: -d -e -l --list-actions --list-derived --list-where -o --override -p -r -R -w --write-filenames -W --warn-undefined-variables Thank you for your interest, and please let us know how we can help improve SCons for your needs. Steven Knight knight at baldmt dot com http://www.baldmt.com/~knight/ With plenty of help from the SCons Development team: Chad Austin Charles Crain Bill Deegan Steve Leblanc Greg Noel Gary Oberbrunner Anthony Roach Greg Spencer Christoph Wiedemann Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation src/Announce.txt 2013/03/03 09:48:35 garyo scons-doc-2.3.0/src/CHANGES.txt0000644000175000017500000057045512114661557016642 0ustar dktrkranzdktrkranz SCons - a software construction tool Change Log RELEASE 2.3.0 - Mon, 02 Mar 2013 13:22:29 -0400 From Anatoly Techtonik: - Added ability to run scripts/scons.py directly from source checkout - Hide deprecated --debug={dtree,stree,tree} from --help output - Error messages from option parser now include hints about valid choices - Cleaned up some Python 1.5 and pre-2.3 code, so don't expect SCons to run on anything less than Python 2.4 anymore - Several fixes for runtest.py: * exit with an error if no tests were found * removed --noqmtest option - this behavior is by default * replaced `-o FILE --xml` combination with `--xml FILE` * changed `-o, --output FILE` option to capture stdout/stderr output from runtest.py - Remove os_spawnv_fix.diff patch required to enable parallel builds support prior to Python 2.2 From Juan Lang: - Fix WiX Tool to use .wixobj rather than .wxiobj for compiler output - Support building with WiX releases after 2.0 From Alexey Klimkin: - Fix nested LIBPATH expansion by flattening sequences in subst_path. From eyan on Bitbucket: - Print target name with command execution time with --debug=time From Thomas Berg and Evgeny Podjachev: - Fix subprocess spawning on Windows. Work around a Windows bug that can crash python occasionally when using -jN. (#2449) From Dirk Baechle: - Updated test framework to support dir and file fixtures and added ability to test external (out-of-tree) tools (#2862). See doc in QMTest/test-framework.rst. - Fixed several errors in the test suite (Java paths, MSVS version detection, Tool import), additionally * provided MinGW command-line support for the CXX, AS and Fortran tests, * refactored the detection of the gcc version and the according Fortran startup library, * provided a new module rpmutils.py, wrapping the RPM naming rules for target files and further hardware-dependent info (compatibility, compiler flags, ...), * added new test methods must_exist_one_of() and must_not_exist_any_of() and * removed Aegis support from runtest.py. (#2872) From Gary Oberbrunner: - Add -jN support to runtest.py to run tests in parallel - Add MSVC10 and MSVC11 support to get_output low-level bat script runner. - Fix MSVS solution generation for VS11, and fixed tests. From Rob Managan: - Updated the TeX builder to support the \newglossary command in LaTeX's glossaries package and the files it creates. - Improve support for new versions of biblatex in the TeX builder so biber is called automatically if biblatex requires it. - Add SHLIBVERSION as an option that tells SharedLibrary to build a versioned shared library and create the required symlinks. Add builder InstallVersionedLib to create the required symlinks installing a versioned shared library. RELEASE 2.2.0 - Mon, 05 Aug 2012 15:37:48 +0000 From dubcanada on Bitbucket: - Fix 32-bit Visual Express C++ on 64-bit Windows (generate 32-bit code) From Paweł Tomulik: - Added gettext toolset - Fixed FindSourceFiles to find final sources (leaf nodes). From Greg Ward: - Allow Node objects in Java path (#2825) From Joshua Hughes: - Make Windows not redefine builtin file as un-inheritable (#2857) - Fix WINDOWS_INSERT_DEF on MinGW (Windows) (#2856) From smallbub on Bitbucket: - Fix LINKCOMSTR, SHLINKCOMSTR, and LDMODULECOMSTR on Windows (#2833). From Mortoray: - Make -s (silent mode) be silent about entering subdirs (#2976). - Fix cloning of builders when cloning environment (#2821). From Gary Oberbrunner: - Show valid Visual Studio architectures in error message when user passes invalid arch. From Alexey Petruchik: - Support for Microsoft Visual Studio 11 (both using it and generating MSVS11 solution files). From Alexey Klimkin: - Fixed the Taskmaster, curing spurious build failures in multi-threaded runs (#2720). From Dirk Baechle: - Improved documentation of command-line variables (#2809). - Fixed scons-doc.py to properly convert main XML files (#2812). From Rob Managan: - Updated the TeX builder to support LaTeX's multibib package. - Updated the TeX builder to support LaTeX's biblatex package. - Added support for using biber instead of bibtex by setting env['BIBTEX'] = 'biber' From Arve Knudsen: - Test for FORTRANPPFILESUFFIXES (#2129). RELEASE 2.1.0 - Mon, 09 Sep 2011 20:54:57 -0700 From Anton Lazarev: - Fix Windows resource compiler scanner to accept DOS line endings. From Matthias: - Update MSVS documents to remove note indicating that only one project is currently supported per solution file. From Grzegorz Bizoń: - Fix long compile lines in batch mode by using TEMPFILE - Fix MSVC_BATCH=False (was treating it as true) From Justin Gullingsrud: - support -std=c++0x and related CXXFLAGS in pkgconfig (ParseFlags) From Vincent Beffara: - Support -dylib_file in pkgconfig (ParseFlags) From Gary Oberbrunner and Sohail Somani: - new construction variable WINDOWS_EMBED_MANIFEST to automatically embed manifests in Windows EXEs and DLLs. From Gary Oberbrunner: - Fix Visual Studio project generation when CPPPATH contains Dir nodes - Ensure Visual Studio project is regenerated when CPPPATH or CPPDEFINES change - Fix unicode error when using non-ASCII filenames with Copy or Install - Put RPATH in LINKCOM rather than LINKFLAGS so resetting LINKFLAGS doesn't kill RPATH - Fix precompiled headers on Windows when variant dir name has spaces. - Adding None to an Action no longer fails (just returns original action) - New --debug=prepare option to show each target as it's being prepared, whether or not anything needs to be done for it. - New debug option --debug=duplicate to print a line for each unlink/relink (or copy) of a variant file from its source file. - Improve error message for EnumVariables to show legal values. - Fix Intel compiler to sort versions >9 correctly (esp. on Linux) - Fix Install() when the source and target are directories and the target directory exists. From David Garcia Garzon: - Fix Delete to be able to delete broken symlinks and dir symlinks. From Robert Lehr: - Handle .output file generated by bison/yacc properly. Cleaning it when necessary. From Antoine Dechaume: - Handle SWIG file where there is whitespace after the module name properly. Previously the generated files would include the whitespace. From Dmitry R.: - Handle Environment in case __semi_deepcopy is None From Benoit Belley: - Much improved support for Windows UNC paths (\\SERVERNAME). From Jean-Baptiste Lab: - Fix problems with appending CPPDEFINES that contain dictionaries, and related issues with Parse/MergeFlags and CPPDEFINES. From Allen Weeks: - Fix for an issue with implicit-cache with multiple targets when dependencies are removed on disk. From Evgeny Podjachev and Alexey Petruchick: - Support generation of Microsoft Visual Studio 2008 (9.0) and 2010 (10.0) project and solution files. From Ken Deeter: - Fix a problem when FS Entries which are actually Dirs have builders. From Luca Falavigna: - Support Fortran 03 From Gary Oberbrunner: - Print the path to the SCons package in scons --version From Jean-Fran�ois Colson: - Improve Microsoft Visual Studio Solution generation, and fix various errors in the generated solutions especially when using MSVS_SCC_PROVIDER, and when generating multiple projects. The construction variable MSVS_SCC_PROJECT_BASE_PATH, which never worked properly, is removed. Users can use the new variable MSVS_SCC_CONNECTION_ROOT instead if desired. From Anatoly Techtonik: - Use subprocess in bootstrap.py instead of os.execve to avoid losing output control on Windows (http://bugs.python.org/issue9148) - Revert patch for adding SCons to App Paths, because standard cmd shell doesn't search there. This is confusing, because `scons` can be executed from explorer, but fail to start from console. - Fix broken installation with easy_install on Windows (issue #2051) SCons traditionally installed in a way that allowed to run multiple versions side by side. This custom logic was incompatible with easy_install way of doing things. - Use epydoc module for generating API docs in HTML if command line utility is not found in PATH. Actual for Windows. From Alexander Goomenyuk: - Add .sx to assembly source scanner list so .sx files get their header file dependencies detected. From Arve Knudsen: - Set module metadata when loading site_scons/site_init.py so it is treated as a proper module; __doc__, __file__ and __name__ now refer to the site_init.py file. From Russel Winder: - Users Guide updates explaining that Tools can be packages as well as python modules. From Gary Oberbrunner: - New systemwide and per-user site_scons dirs. From Dirk Baechle: - XML fixes in User's Guide. - Fixed the detection of 'jar' and 'rmic' during the initialization of the respective Tools (#2730). - Improved docs for custom Decider functions and custom Scanner objects (#2711, #2713). - Corrected SWIG module names for generated *.i files (#2707). From Joe Zuntz: - Fixed a case-sensitivity problem with Fortran modules. From Bauke Conijn: - Added Users Guide example for auto-generated source code From Steven Knight: - Fix explicit dependencies (Depends()) on Nodes that don't have attached Builders. - Fix use of the global Alias() function with command actions. From Matt Hughes: - Fix the ability to append to default $*FLAGS values (which are implemented as CLVar instances) in a copied construction environment without affecting the original construction environment's value. From Rob Managan: - Updated the TeX command strings to include a /D on Windows in case the new directory is on a different drive letter. - Fixed the LaTeX scanner so dependencies are found in commands that are broken across lines with a comment or have embedded spaces. - The TeX builders should now work with tex files that are generated by another program. Thanks to Hans-Martin von Gaudecker for isolating the cause of this bug. - Added support for INDEXSTYLE environment variable so makeindex can find style files. - Added support for the bibunits package so we call bibtex on all the bu*.aux files. - Add support of finding path information on OSX for TeX applications MacPorts and Fink paths need to be added by the user From Russel Winder: - Add support for DMD version 2 (the phobos2 library). From William Deegan: - Add initial support for VS/VC 2010 (express and non-express versions) - Remove warning for not finding MS VC/VS install. "scons: warning: No version of Visual Studio compiler found - C/C++ compilers most likely not set correctly" - Add support for Linux 3.0 RELEASE 2.0.1 - Mon, 15 Aug 2010 15:46:32 -0700 From Dirk Baechle: - Fix XML in documentation. From Joe Zuntz: - Fixed a case-sensitivity problem with Fortran modules. From Bauke Conijn: - Added Users Guide example for auto-generated source code From Steven Knight: - Fix explicit dependencies (Depends()) on Nodes that don't have attached Builders. From Matt Hughes: - Fix the ability to append to default $*FLAGS values (which are implemented as CLVar instances) in a copied construction environment without affecting the original construction environment's value. From Rob Managan: - Updated the TeX command strings to include a /D on Windows in case the new directory is on a different drive letter. - Fixed the LaTeX scanner so dependencies are found in commands that are broken across lines with a comment or have embedded spaces. RELEASE 2.0.0.final.0 - Mon, 14 Jun 2010 22:01:37 -0700 From Dirk Baechle: - Fix XML in documentation. From Steven Knight: - Provide forward compatibility for the 'profile' module. - Provide forward compatibility for the 'pickle' module. - Provide forward compatibility for the 'io' module. - Provide forward compatibility for the 'queue' module. - Provide forward compatibility for the 'collections' module. - Provide forward compatibility for the 'builtins' module. - Provide forward compatibility for 'sys.intern()'. - Convert to os.walk() from of os.path.walk(). - Remove compatibility logic no longer needed. - Add a '-3' option to runtest to print 3.x incompatibility warnings. - Convert old-style classes into new-style classes. - Fix "Ignoring corrupt sconsign entry" warnings when building in a tree with a pre-2.0 .sconsign file. - Fix propagation from environment of VS*COMNTOOLS to resolve issues initializing MSVC/MSVS/SDK issues. - Handle detecting Visual C++ on Python verions with upper-case platform architectures like 'AMD64'. From W. Trevor King: - Revisions to README. From Greg Noel: - Apply numerous Python fixers to update code to more modern idioms. Find where fixers should be applied to code in test strings and apply the fixers there, too. - Write a fixer to convert string functions to string methods. - Modify the 'dict' fixer to be less conservative. - Modify the 'apply' fixer to handle more cases. - Create a modified 'types' fixer that converts types to 2.x equivalents rather than 3.x equivalents. - Write a 'division' fixer to highlight uses of the old-style division operator. Correct usage where needed. - Add forward compatibility for the new 'memoryview' function (which replaces the 'buffer' function). - Add forward compatibility for the 'winreg' module. - Remove no-longer-needed 'platform' module. - Run tests with the '-3' option to Python 2.6 and clear up various reported incompatibilities. - Comb out code paths specialized to Pythons older than 2.4. - Update deprecation warnings; most now become mandatory. - Start deprecation cycle for BuildDir() and build_dir. - Start deprecation cycle for SourceCode() and related factories - Fixed a problem with is_Dict() not identifying some objects derived from UserDict. From Jim Randall: - Document the AllowSubstExceptions() function in the User's Guide. From William Deegan: - Migrate MSVC/MSVS/SDK improvements from 1.3 branch. RELEASE 1.3.0 - Tue, 23 Mar 2010 21:44:19 -0400 From Steven Knight: - Update man page and documentation. From William Deegan (plus minor patch from Gary Oberbrunner): - Support Visual Studio 8.0 Express RELEASE 1.2.0.d20100306 - Sat, 06 Mar 2010 16:18:33 -0800 From Luca Falavigna: - Fix typos in the man page. From Gottfried Ganssauge: - Support execution when SCons is installed via easy_install. From Steven Knight: - Make the messages for Configure checks of compilers consistent. - Issue an error message if a BUILDERS entry is not a Builder object or a callable wrapper. From Rob Managan: - Update tex builder to handle the case where a \input{foo} command tries to work with a directory named foo instead of the file foo.tex. The builder now ignores a directory and continues searching to find the correct file. Thanks to Lennart Sauerbeck for the test case and initial patch Also allow the \include of files in subdirectories when variantDir is used with duplicate=0. Previously latex would crash since the directory in which the .aux file is written was not created. Thanks to Stefan Hepp for finding this and part of the solution. From James Teh: - Patches to fix some issues using MS SDK V7.0 From William Deegan: - Lots of testing and minor patches to handle mixed MS VC and SDK installations, as well as having only the SDK installed. RELEASE 1.2.0.d20100117 - Sun, 17 Jan 2010 14:26:59 -0800 From Jim Randall: - Fixed temp filename race condition on Windows with long cmd lines. From David Cournapeau: - Fixed tryRun when sconf directory is in a variant dir. - Do not add -fPIC for ifort tool on non-posix platforms (darwin and windows). - Fix bug 2294 (spurious CheckCC failures). - Fix scons bootstrap process on windows 64 (wrong wininst name) From William Deegan: - Final merge from vs_revamp branch to main - Added definition and usage of HOST_OS, HOST_ARCH, TARGET_OS, TARGET_ARCH, currently only defined/used by Visual Studio Compilers. This will be rolled out to other platforms/tools in the future. - Add check for python >= 3.0.0 and exit gracefully. For 1.3 python >= 1.5.2 and < 3.0.0 are supported - Fix bug 1944 - Handle non-existent .i file in swig emitter, previously it would crash with an IOError exception. Now it will try to make an educated guess on the module name based on the filename. From Lukas Erlinghagen: - Have AddOption() remove variables from the list of seen-but-unknown variables (which are reported later). - An option name and aliases can now be specified as a tuple. From Hartmut Goebel: - Textfile builder. From Jared Grubb: - use "is/is not" in comparisons with None instead of "==" or "!=". From Jim Hunziker: - Avoid adding -gphobos to a command line multiple times when initializing use of the DMD compiler. From Jason Kenney: - Sugguested HOST/TARGET OS/ARCH separation. From Steven Knight: - Fix the -n option when used with VariantDir(duplicate=1) and the variant directory doesn't already exist. - Fix scanning of Unicode files for both UTF-16 endian flavors. - Fix a TypeError on #include of file names with Unicode characters. - Fix an exception if a null command-line argument is passed in. - Evaluate Requires() prerequisites before a Node's direct children (sources and dependencies). From Greg Noel: - Remove redundant __metaclass__ initializations in Environment.py. - Correct the documentation of text returned by sconf.Result(). - Document that filenames with '.' as the first character are ignored by Glob() by default (matching UNIX glob semantics). - Fix SWIG testing infrastructure to work on Mac OS X. - Restructure a test that occasionally hung so that the test would detect when it was stuck and fail instead. - Substfile builder. From Gary Oberbrunner: - When reporting a target that SCons doesn't know how to make, specify whether it's a File, Dir, etc. From Ben Webb: - Fix use of $SWIGOUTDIR when generating Python wrappers. - Add $SWIGDIRECTORSUFFIX and $SWIGVERSION construction variables. From Rob Managan: - Add -recorder flag to Latex commands and updated internals to use the output to find files TeX creates. This allows the MiKTeX installations to find the created files - Notify user of Latex errors that would get buried in the Latex output - Remove LATEXSUFFIXES from environments that don't initialize Tex. - Add support for the glossaries package for glossaries and acronyms - Fix problem that pdftex, latex, and pdflatex tools by themselves did not create the actions for bibtex, makeindex,... by creating them and other environment settings in one routine called by all four tex tools. - Fix problem with filenames of sideeffects when the user changes the name of the output file from the latex default - Add scanning of files included in Latex by means of \lstinputlisting{} Patch from Stefan Hepp. - Change command line for epstopdf to use --outfile= instead of -o since this works on all platforms. Patch from Stefan Hepp. - Change scanner to properly search for included file from the directory of the main file instead of the file it is included from. Also update the emitter to add the .aux file associated with \include{filename} commands. This makes sure the required directories if any are created for variantdir cases. Half of the patch from Stefan Hepp. RELEASE 1.2.0.d20090223 - Mon, 23 Feb 2009 08:41:06 -0800 From Stanislav Baranov: - Make suffix-matching for scanners case-insensitive on Windows. From David Cournapeau: - Change the way SCons finds versions of Visual C/C++ and Visual Studio to find and use the Microsoft v*vars.bat files. From Robert P. J. Day: - User's Guide updates. From Dan Eaton: - Fix generation of Visual Studio 8 project files on x64 platforms. From Allan Erskine: - Set IncludeSearchPath and PreprocessorDefinitions in generated Visual Studio 8 project files, to help IntelliSense work. From Mateusz Gruca: - Fix deletion of broken symlinks by the --clean option. From Steven Knight: - Fix the error message when use of a non-existent drive on Windows is detected. - Add sources for files whose targets don't exist in $CHANGED_SOURCES. - Detect implicit dependencies on commands even when the command is quoted. - Fix interaction of $CHANGED_SOURCES with the --config=force option. - Fix finding #include files when the string contains escaped backslashes like "C:\\some\\include.h". - Pass $CCFLAGS to Visual C/C++ precompiled header compilation. - Remove unnecessary nested $( $) around $_LIBDIRFLAGS on link lines for the Microsoft linker, the OS/2 ilink linker and the Phar Lap linkloc linker. - Spell the Windows environment variables consistently "SystemDrive" and "SystemRoot" instead of "SYSTEMDRIVE" and "SYSTEMROOT". RELEASE 1.2.0.d20090113 - Tue, 13 Jan 2009 02:50:30 -0800 From Stanislav Baranov, Ted Johnson and Steven Knight: - Add support for batch compilation of Visual Studio C/C++ source files, controlled by a new $MSVC_BATCH construction variable. From Steven Knight: - Print the message, "scons: Build interrupted." on error output, not standard output. - Add a --warn=future-deprecated option for advance warnings about deprecated features that still have warnings hidden by default. - Fix use of $SOURCE and $SOURCES attributes when there are no sources specified in the Builder call. - Add support for new $CHANGED_SOURCES, $CHANGED_TARGETS, $UNCHANGED_SOURCES and $UNCHANGED_TARGETS variables. - Add general support for batch builds through new batch_key= and targets= keywords to Action object creation. From Arve Knudsen: - Make linker tools differentiate properly between SharedLibrary and LoadableModule. - Document TestCommon.shobj_prefix variable. - Support $SWIGOUTDIR values with spaces. From Rob Managan: - Don't automatically try to build .pdf graphics files for .eps files in \includegraphics{} calls in TeX/LaTeX files when building with the PDF builder (and thus using pdflatex). From Gary Oberbrunner: - Allow AppendENVPath() and PrependENVPath() to interpret '#' for paths relative to the top-level SConstruct directory. - Use the Borland ilink -e option to specify the output file name. - Document that the msvc Tool module uses $PCH, $PCHSTOP and $PDB. - Allow WINDOWS_INSERT_DEF=0 to disable --output-def when linking under MinGW. From Zia Sobhani: - Fix typos in the User's Guide. From Greg Spencer: - Support implicit dependency scanning of files encoded in utf-8 and utf-16. From Roberto de Vecchi: - Remove $CCFLAGS from the the default definitions of $CXXFLAGS for Visual C/C++ and MIPSpro C++ on SGI so, they match other tools and avoid flag duplication on C++ command lines. From Ben Webb: - Handle quoted module names in SWIG source files. - Emit *_wrap.h when SWIG generates header file for directors From Matthew Wesley: - Copy file attributes so we identify, and can link a shared library from, shared object files in a Repository. RELEASE 1.2.0 - Sat, 20 Dec 2008 22:47:29 -0800 From Steven Knight: - Don't fail if can't import a _subprocess module on Windows. - Add warnings for use of the deprecated Options object. RELEASE 1.1.0.d20081207 - Sun, 07 Dec 2008 19:17:23 -0800 From Benoit Belley: - Improve the robustness of GetBuildFailures() by refactoring SCons exception handling (especially BuildError exceptions). - Have the --taskmastertrace= option print information about individual Task methods, not just the Taskmaster control flow. - Eliminate some spurious dependency cycles by being more aggressive about pruning pending children from the Taskmaster walk. - Suppress mistaken reports of a dependency cycle when a child left on the pending list is a single Node in EXECUTED state. From David Cournapeau: - Fix $FORTRANMODDIRPREFIX for the ifort (Intel Fortran) tool. From Brad Fitzpatrick: - Don't pre-generate an exception message (which will likely be ignored anyway) when an EntryProxy re-raises an AttributeError. From Jared Grubb: - Clean up coding style and white space in Node/FS.py. - Fix a typo in the documentation for $_CPPDEFFLAGS. - Issue 2401: Fix usage of comparisons with None. From Ludwig H�hne: - Handle Java inner classes declared within a method. From Steven Knight: - Fix label placement by the "scons-time.py func" subcommand when a profile value was close to (or equal to) 0.0. - Fix env.Append() and env.Prepend()'s ability to add a string to list-like variables like $CCFLAGS under Python 2.6. - Other Python2.6 portability: don't use "as" (a Python 2.6 keyword). Don't use the deprecated Exception.message attribute. - Support using the -f option to search for a different top-level file name when walking up with the -D, -U or -u options. - Fix use of VariantDir when the -n option is used and doesn't, therefore, actually create the variant directory. - Fix a stack trace from the --debug=includes option when passed a static or shared library as an argument. - Speed up the internal find_file() function (used for searching CPPPATH, LIBPATH, etc.). - Add support for using the Python "in" keyword on construction environments (for example, if "CPPPATH" in env: ...). - Fix use of Glob() when a repository or source directory contains an in-memory Node without a corresponding on-disk file or directory. - Add a warning about future reservation of $CHANGED_SOURCES, $CHANGED_TARGETS, $UNCHANGED_SOURCES and $UNCHANGED_TARGETS. - Enable by default the existing warnings about setting the resource $SOURCE, $SOURCES, $TARGET and $TARGETS variable. From Rob Managan: - Scan for TeX files in the paths specified in the $TEXINPUTS construction variable and the $TEXINPUTS environment variable. - Configure the PDF() and PostScript() Builders as single_source so they know each source file generates a separate target file. - Add $EPSTOPDF, $EPSTOPDFFLAGS and $EPSTOPDFCOM - Add .tex as a valid extension for the PDF() builder. - Add regular expressions to find \input, \include and \includegraphics. - Support generating a .pdf file from a .eps source. - Recursive scan included input TeX files. - Handle requiring searched-for TeX input graphics files to have extensions (to avoid trying to build a .eps from itself, e.g.). From Greg Noel: - Make the Action() function handle positional parameters consistently. - Clarify use of Configure.CheckType(). - Make the File.{Dir,Entry,File}() methods create their entries relative to the calling File's directory, not the SConscript directory. - Use the Python os.devnull variable to discard error output when looking for the $CC or $CXX version. - Mention LoadableModule() in the SharedLibrary() documentation. From Gary Oberbrunner: - Update the User's Guide to clarify use of the site_scons/ directory and the site_init.py module. - Make env.AppendUnique() and env.PrependUnique remove duplicates within a passed-in list being added, too. From Randall Spangler: - Fix Glob() so an on-disk file or directory beginning with '#' doesn't throw an exception. RELEASE 1.1.0 - Thu, 09 Oct 2008 08:33:47 -0700 From Chris AtLee - Use the specified environment when checking for the GCC compiler version. From Ian P. Cardenas: - Fix Glob() polluting LIBPATH by returning copy of list From David Cournapeau: - Add CheckCC, CheckCXX, CheckSHCC and CheckSHCXX tests to configuration contexts. - Have the --profile= argument use the much faster cProfile module (if it's available in the running Python version). - Reorder MSVC compilation arguments so the /Fo is first. From Bill Deegan: - Add scanning Windows resource (.rc) files for implicit dependencies. From John Gozde: - When scanning for a #include file, don't use a directory that has the same name as the file. From Ralf W. Grosse-Kunstleve - Suppress error output when checking for the GCC compiler version. From Jared Grubb: - Fix VariantDir duplication of #included files in subdirectories. From Ludwig H�hne: - Reduce memory usage when a directory is used as a dependency of another Node (such as an Alias) by returning a concatenation of the children's signatures + names, not the children's contents, as the directory contents. - Raise AttributeError, not KeyError, when a Builder can't be found. - Invalidate cached Node information (such as the contenst returned by the get_contents() method) when calling actions with Execute(). - Avoid object reference cycles from frame objects. - Reduce memory usage from Null Executor objects. - Compute MD5 checksums of large files without reading the entire file contents into memory. Add a new --md5-chunksize option to control the size of each chunk read into memory. From Steven Knight: - Fix the ability of the add_src_builder() method to add a new source builder to any other builder. - Avoid an infinite loop on non-Windows systems trying to find the SCons library directory if the Python library directory does not begin with the string "python". - Search for the SCons library directory in "scons-local" (with no version number) after "scons-local-{VERSION}". From Rob Managan: - Fix the user's ability to interrupt the TeX build chain. - Fix the TeX builder's allowing the user to specify the target name, instead of always using its default output name based on the source. - Iterate building TeX output files until all warning are gone and the auxiliary files stop changing, or until we reach the (configurable) maximum number of retries. - Add TeX scanner support for: glossaries, nomenclatures, lists of figures, lists of tables, hyperref and beamer. - Use the $BIBINPUTS, $BSTINPUTS, $TEXINPUTS and $TEXPICTS construction variables as search paths for the relevant types of input file. - Fix building TeX with VariantDir(duplicate=0) in effect. - Fix the LaTeX scanner to search for graphics on the TEXINPUTS path. - Have the PDFLaTeX scanner search for .gif files as well. From Greg Noel: - Fix typos and format bugs in the man page. - Add a first draft of a wrapper module for Python's subprocess module. - Refactor use of the SCons.compat module so other modules don't have to import it individually. - Add .sx as a suffix for assembly language files that use the C preprocessor. From Gary Oberbrunner: - Make Glob() sort the returned list of Files or Nodes to prevent spurious rebuilds. - Add a delete_existing keyword argument to the AppendENVPath() and PrependENVPath() Environment methods. - Add ability to use "$SOURCE" when specifying a target to a builder From Damyan Pepper: - Add a test case to verify that SConsignFile() files can be created in previously non-existent subdirectories. From Jim Randall: - Make the subdirectory in which the SConsignFile() file will live, if the subdirectory doesn't already exist. From Ali Tofigh: - Add a test to verify duplication of files in VariantDir subdirectories. RELEASE 1.0.1 - Sat, 06 Sep 2008 07:29:34 -0700 From Greg Noel: - Add a FindFile() section to the User's Guide. - Fix the FindFile() documentation in the man page. - Fix formatting errors in the Package() description in the man page. - Escape parentheses that appear within variable names when spawning command lines using os.system(). RELEASE 1.0.0 - XXX From Jared Grubb: - Clear the Node state when turning a generic Entry into a Dir. From Ludwig H�hne: - Fix sporadic output-order failures in test/GetBuildFailures/parallel.py. - Document the ParseDepends() function in the User's Guide. From khomenko: - Create a separate description and long_description for RPM packages. From Steven Knight: - Document the GetLaunchDir() function in the User's Guide. - Have the env.Execute() method print an error message if the executed command fails. - Add a script for creating a standard SCons development system on Ubuntu Hardy. Rewrite subsidiary scripts for install Python and SCons versions in Python (from shell). From Greg Noel: - Handle yacc/bison on newer Mac OS X versions creating file.hpp, not file.cpp.h. - In RPCGEN tests, ignore stderr messages from older versions of rpcgen on some versions of Mac OS X. - Fix typos in man page descriptions of Tag() and Package(), and in the scons-time man page. - Fix documentation of SConf.CheckLibWithHeader and other SConf methods. - Update documentation of SConscript(variant_dir) usage. - Fix SWIG tests for (some versions of) Mac OS X. From Jonas Olsson: - Print the warning about -j on Windows being potentially unreliable if the pywin32 extensions are unavailable or lack file handle operations. From Jim Randall: - Fix the env.WhereIs() method to expand construction variables. From Rogier Schouten: - Enable building of shared libraries with the Bordand ilink32 linker. RELEASE 1.0.0 - Sat, 09 Aug 2008 12:19:44 -0700 From Luca Falavigna: - Fix SCons man page indentation under Debian's man page macros. From Steven Knight: - Clarify the man page description of the SConscript(src_dir) argument. - User's Guide updates: - Document the BUILD_TARGETS, COMMAND_LINE_TARGETS and DEFAULT_TARGETS variables. - Document the AddOption(), GetOption() and SetOption() functions. - Document the Requires() function; convert to the Variables object, its UnknownOptions() method, and its associated BoolVariable(), EnumVariable(), ListVariable(), PackageVariable() and PathVariable() functions. - Document the Progress() function. - Reorganize the chapter and sections describing the different types of environments and how they interact. Document the SetDefault() method. Document the PrependENVPath() and AppendENVPath() functions. - Reorganize the command-line arguments chapter. Document the ARGLIST variable. - Collect some miscellaneous sections into a chapter about configuring build output. - Man page updates: - Document suggested use of the Visual C/C++ /FC option to fix the ability to double-click on file names in compilation error messages. - Document the need to use Clean() for any SideEffect() files that must be explicitly removed when their targets are removed. - Explicitly document use of Node lists as input to Dependency(). From Greg Noel: - Document MergeFlags(), ParseConfig(), ParseFlags() and SideEffect() in the User's Guide. From Gary Oberbrunner: - Document use of the GetBuildFailures() function in the User's Guide. From Adam Simpkins: - Add man page text clarifying the behavior of AddPreAction() and AddPostAction() when called with multiple targets. From Alexey Zezukin: - Fix incorrectly swapped man page descriptions of the --warn= options for duplicate-environment and missing-sconscript. RELEASE 0.98.5 - Sat, 07 Jun 2008 08:20:35 -0700 From Benoit Belley: - Fix the Intel C++ compiler ABI specification for EMT64 processors. From David Cournapeau: - Issue a (suppressable) warning, not an error, when trying to link C++ and Fortran object files into the same executable. From Steven Knight: - Update the scons.bat file so that it returns the real exit status from SCons, even though it uses setlocal + endlocal. - Fix the --interactive post-build messages so it doesn't get stuck mistakenly reporting failures after any individual build fails. - Fix calling File() as a File object method in some circumstances. - Fix setup.py installation on Mac OS X so SCons gets installed under /usr/lcoal by default, not in the Mac OS X Python framework. RELEASE 0.98.4 - Sat, 17 May 2008 22:14:46 -0700 From Benoit Belley: - Fix calculation of signatures for Python function actions with closures in Python versions before 2.5. From David Cournapeau: - Fix the initialization of $SHF77FLAGS so it includes $F77FLAGS. From Jonas Olsson: - Fix a syntax error in the Intel C compiler support on Windows. From Steven Knight: - Change how we represent Python Value Nodes when printing and when stored in .sconsign files (to avoid blowing out memory by storing huge strings in .sconsign files after multiple runs using Configure contexts cause the Value strings to be re-escaped each time). - Fix a regression in not executing configuration checks after failure of any configuration check that used the same compiler or other tool. - Handle multiple destinations in Visual Studio 8 settings for the analogues to the INCLUDE, LIBRARY and PATH variables. From Greg Noel: - Update man page text for VariantDir(). RELEASE 0.98.3 - Tue, 29 Apr 2008 22:40:12 -0700 From Greg Noel: - Fix use of $CXXFLAGS when building C++ shared object files. From Steven Knight: - Fix a regression when a Builder's source_scanner doesn't select a more specific scanner for the suffix of a specified source file. - Fix the Options object backwards compatibility so people can still "import SCons.Options.{Bool,Enum,List,Package,Path}Option" submodules. - Fix searching for implicit dependencies when an Entry Node shows up in the search path list. From Stefano: - Fix expansion of $FORTRANMODDIR in the default Fortran command line(s) when it's set to something like ${TARGET.dir}. RELEASE 0.98.2 - Sun, 20 Apr 2008 23:38:56 -0700 From Steven Knight: - Fix a bug in Fortran suffix computation that would cause SCons to run out of memory on Windows systems. - Fix being able to specify --interactive mode command lines with \ (backslash) path name separators on Windows. From Gary Oberbrunner: - Document Glob() in the User's Guide. RELEASE 0.98.1 - Fri, 18 Apr 2008 19:11:58 -0700 From Benoit Belley: - Speed up the SCons.Util.to_string*() functions. - Optimize various Node intialization and calculations. - Optimize Executor scanning code. - Optimize Taskmaster execution, including dependency-cycle checking. - Fix the --debug=stree option so it prints its tree once, not twice. From Johan Boul�: - Fix the ability to use LoadableModule() under MinGW. From David Cournapeau: - Various missing Fortran-related construction variables have been added. - SCons now uses the program specified in the $FORTRAN construction variable to link Fortran object files. - Fortran compilers on Linux (Intel, g77 and gfortran) now add the -fPIC option by default when compilling shared objects. - New 'sunf77', 'sunf90' and 'sunf95' Tool modules have been added to support Sun Fortran compilers. On Solaris, the Sun Fortran compilers are used in preference to other compilers by default. - Fortran support now uses gfortran in preference to g77. - Fortran file suffixes are now configurable through the $F77FILESUFFIXES, $F90FILESUFFIXES, $F95FILESUFFIXES and $FORTRANFILESUFFIXES variables. From Steven Knight: - Make the -d, -e, -w and --no-print-directory options "Ignored for compatibility." (We're not going to implement them.) - Fix a serious inefficiency in how SCons checks for whether any source files are missing when a Builder call creates many targets from many input source files. - In Java projects, make the target .class files depend only on the specific source .java files where the individual classes are defined. - Don't store duplicate source file entries in the .sconsign file so we don't endlessly rebuild the target(s) for no reason. - Add a Variables object as the first step towards deprecating the Options object name. Similarly, add BoolVariable(), EnumVariable(), ListVariable(), PackageVariable() and PathVariable() functions as first steps towards replacing BoolOption(), EnumOption(), ListOption(), PackageOption() and PathOption(). - Change the options= keyword argument to the Environment() function to variables=, to avoid confusion with SCons command-line options. Continue supporting the options= keyword for backwards compatibility. - When $SWIGFLAGS contains the -python flag, expect the generated .py file to be in the same (sub)directory as the target. - When compiling C++ files, allow $CCFLAGS settings to show up on the command line even when $CXXFLAGS has been redefined. - Fix --interactive with -u/-U/-D when a VariantDir() is used. From Anatoly Techtonik: - Have the scons.bat file add the script execution directory to its local %PATH% on Windows, so the Python executable can be found. From Mike Wake: - Fix passing variable names as a list to the Return() function. From Matthew Wesley: - Add support for the GDC 'D' language compiler. RELEASE 0.98 - Sun, 30 Mar 2008 23:33:05 -0700 From Benoit Belley: - Fix the --keep-going flag so it builds all possible targets even when a later top-level target depends on a child that failed its build. - Fix being able to use $PDB and $WINDWOWS_INSERT_MANIFEST together. - Don't crash if un-installing the Intel C compiler leaves left-over, dangling entries in the Windows registry. - Improve support for non-standard library prefixes and suffixes by stripping all prefixes/suffixes from file name string as appropriate. - Reduce the default stack size for -j worker threads to 256 Kbytes. Provide user control over this value by adding --stack-size and --warn=stack-size options, and a SetOption('stack_size') function. - Fix a crash on Linux systems when trying to use the Intel C compiler and no /opt/intel_cc_* directories are found. - Improve using Python functions as actions by incorporating into a FunctionAction's signature: - literal values referenced by the byte code. - values of default arguments - code of nested functions - values of variables captured by closures - names of referenced global variables and functions - Fix the closing message when --clean and --keep-going are both used and no errors occur. - Add support for the Intel C compiler on Mac OS X. - Speed up reading SConscript files by about 20% (for some configurations) by: 1) optimizing the SCons.Util.is_*() and SCons.Util.flatten() functions; 2) avoiding unnecessary os.stat() calls by using a File's .suffix attribute directly instead of stringifying it. From Jérôme Berger: - Have the D language scanner search for .di files as well as .d files. - Add a find_include_names() method to the Scanner.Classic class to abstract out how included names can be generated by subclasses. - Allow the D language scanner to detect multiple modules imported by a single statement. From Konstantin Bozhikov: - Support expansion of construction variables that contain or refer to lists of other variables or Nodes within expansions like $CPPPATH. - Change variable substitution (the env.subst() method) so that an input sequence (list or tuple) is preserved as a list in the output. From David Cournapeau: - Add a CheckDeclaration() call to configure contexts. - Improve the CheckTypeSize() code. - Add a Define() call to configure contexts, to add arbitrary #define lines to a generated configure header file. - Add a "gfortran" Tool module for the GNU F95/F2003 compiler. - Avoid use of -rpath with the Mac OS X linker. - Add comment lines to the generated config.h file to describe what the various #define/#undef lines are doing. From Steven Knight: - Support the ability to subclass the new-style "str" class as input to Builders. - Improve the performance of our type-checking by using isinstance() with new-style classes. - Fix #include (and other $*PATH variables searches) of files with absolute path names. Don't die if they don't exist (due to being #ifdef'ed out or the like). - Fix --interactive mode when Default(None) is used. - Fix --debug=memoizer to work around a bug in base Python 2.2 metaclass initialization (by just not allowing Memoization in Python versions that have the bug). - Have the "scons-time time" subcommand handle empty log files, and log files that contain no results specified by the --which option. - Fix the max Y of vertical bars drawn by "scons-time --fmt=gnuplot". - On Mac OS X, account for the fact that the header file generated from a C++ file will be named (e.g.) file.cpp.h, not file.hpp. - Fix floating-point numbers confusing the Java parser about generated .class file names in some configurations. - Document (nearly) all the values you can now fetch with GetOption(). - Fix use of file names containing strings of multiple spaces when using ActionFactory instances like the Copy() or Move() function. - Fix a 0.97 regression when using a variable expansion (like $OBJSUFFIX) in a source file name to a builder with attached source builders that match suffix (like Program()+Object()). - Have the Java parser recognize generics (surrounded by angle brackets) so they don't interfere with identifying anonymous inner classes. - Avoid an infinite loop when trying to use saved copies of the env.Install() or env.InstallAs() after replacing the method attributes. - Improve the performance of setting construction variables. - When cloning a construction environment, avoid over-writing an attribute for an added method if the user explicitly replaced it. - Add a warning about deprecated support for Python 1.5, 2.0 and 2.1. - Fix being able to SetOption('warn', ...) in SConscript files. - Add a warning about env.Copy() being deprecated. - Add warnings about the --debug={dtree,stree,tree} options being deprecated. - Add VariantDir() as the first step towards deprecating BuildDir(). Add the keyword argument "variant_dir" as the replacement for "build_dir". - Add warnings about the {Target,Source}Signatures() methods and functions being deprecated. From Rob Managan: - Enhance TeX and LaTeX support to work with BuildDir(duplicate=0). - Re-run LaTeX when it issues a package warning that it must be re-run. From Leanid Nazdrynau: - Have the Copy() action factory preserve file modes and times when copying individual files. From Jan Nijtmans: - If $JARCHDIR isn't set explicitly, use the .java_classdir attribute that was set when the Java() Builder built the .class files. From Greg Noel: - Document the Dir(), File() and Entry() methods of Dir and File Nodes. - Add the parse_flags option when creating Environments From Gary Oberbrunner: - Make File(), Dir() and Entry() return a list of Nodes when passed a list of names, instead of trying to make a string from the name list and making a Node from that string. - Fix the ability to build an Alias in --interactive mode. - Fix the ability to hash the contents of actions for nested Python functions on Python versions where the inability to pickle them returns a TypeError (instead of the documented PicklingError). From Jonas Olsson: - Fix use of the Intel C compiler when the top compiler directory, but not the compiler version, is specified. - Handle Intel C compiler network license files (port@system). From Jim Randall: - Fix how Python Value Nodes are printed in --debug=explain output. From Adam Simpkins: - Add a --interactive option that starts a session for building (or cleaning) targets without re-reading the SConscript files every time. - Fix use of readline command-line editing in --interactive mode. - Have the --interactive mode "build" command with no arguments build the specified Default() targets. - Fix the Chmod(), Delete(), Mkdir() and Touch() Action factories to take a list (of Nodes or strings) as arguments. From Vaclav Smilauer: - Fix saving and restoring an Options value of 'all' on Python versions where all() is a builtin function. From Daniel Svensson: - Code correction in SCons.Util.is_List(). From Ben Webb: - Support the SWIG %module statement with following modifiers in parenthese (e.g., '%module(directors="1")'). RELEASE 0.97.0d20071212 - Wed, 12 Dec 2007 09:29:32 -0600 From Benoit Belley: - Fix occasional spurious rebuilds and inefficiency when using --implicit-cache and Builders that produce multiple targets. - Allow SCons to not have to know about the builders of generated files when BuildDir(duplicate=0) is used, potentially allowing some SConscript files to be ignored for smaller builds. From David Cournapeau: - Add a CheckTypeSize() call to configure contexts. From Ken Deeter: - Make the "contents" of Alias Nodes a concatenation of the children's content signatures (MD5 checksums), not a concatenation of the children's contents, to avoid using large amounts of memory during signature calculation. From Malte Helmert: - Fix a lot of typos in the man page and User's Guide. From Geoffrey Irving: - Speed up conversion of paths in .sconsign files to File or Dir Nodes. From Steven Knight: - Add an Options.UnknownOptions() method that returns any settings (from the command line, or whatever dictionary was passed in) that aren't known to the Options object. - Add a Glob() function. - When removing targets with the -c option, use the absolute path (to avoid problems interpreting BuildDir() when the top-level directory is the source directory). - Fix problems with Install() and InstallAs() when called through a clone (of a clone, ...) of a cloned construction environment. - When executing a file containing Options() settings, add the file's directory to sys.path (so modules can be imported from there) and explicity set __name__ to the name of the file so the statement's in the file can deduce the location if they need to. - Fix an O(n^2) performance problem when adding sources to a target through calls to a multi Builder (including Aliases). - Redefine the $WINDOWSPROGMANIFESTSUFFIX and $WINDOWSSHLIBMANIFESTSUFFIX variables so they pick up changes to the underlying $SHLIBSUFFIX and $PROGSUFFIX variables. - Add a GetBuildFailures() function that can be called from functions registered with the Python atexit module to print summary information about any failures encountered while building. - Return a NodeList object, not a Python list, when a single_source Builder like Object() is called with more than one file. - When searching for implicit dependency files in the directories in a $*PATH list, don't create Dir Nodes for directories that don't actually exist on-disk. - Add a Requires() function to allow the specification of order-only prerequisites, which will be updated before specified "downstream" targets but which don't actually cause the target to be rebuilt. - Restore the FS.{Dir,File,Entry}.rel_path() method. - Make the default behavior of {Source,Target}Signatures('timestamp') be equivalent to 'timestamp-match', not 'timestamp-newer'. - Fix use of CacheDir with Decider('timestamp-newer') by updating the modification time when copying files from the cache. - Fix random issues with parallel (-j) builds on Windows when Python holds open file handles (especially for SCons temporary files, or targets built by Python function actions) across process creation. From Maxim Kartashev: - Fix test scripts when run on Solaris. From Gary Oberbrunner: - Fix Glob() when a pattern is in an explicitly-named subdirectory. From Philipp Scholl: - Fix setting up targets if multiple Package builders are specified at once. RELEASE 0.97.0d20070918 - Tue, 18 Sep 2007 10:51:27 -0500 From Steven Knight: - Fix the wix Tool module to handle null entries in $PATH variables. - Move the documentation of Install() and InstallAs() from the list of functions to the list of Builders (now that they're implemented as such). - Allow env.CacheDir() to be set per construction environment. The global CacheDir() function now sets an overridable global default. - Add an env.Decider() method and a Node.Decider() method that allow flexible specification of an arbitrary function to decide if a given dependency has changed since the last time a target was built. - Don't execute Configure actions (while reading SConscript files) when cleaning (-c) or getting help (-h or -H). - Add to each target an implicit dependency on the external command(s) used to build the target, as found by searching env['ENV']['PATH'] for the first argument on each executed command line. - Add support for a $IMPLICIT_COMMAND_DEPENDENCIES construction variabe that can be used to disable the automatic implicit dependency on executed commands. - Add an "ensure_suffix" keyword to Builder() definitions that, when true, will add the configured suffix to the targets even if it looks like they already have a different suffix. - Add a Progress() function that allows for calling a function or string (or list of strings) to display progress while walking the DAG. - Allow ParseConfig(), MergeFlags() and ParseFlags() to handle output from a *config command with quoted path names that contain spaces. - Make the Return() function stop processing the SConscript file and return immediately. Add a "stop=" keyword argument that can be set to False to preserve the old behavior. - Fix use of exitstatfunc on an Action. - Introduce all man page function examples with "Example:" or "Examples:". - When a file gets added to a directory, make sure the directory gets re-scanned for the new implicit dependency. - Fix handling a file that's specified multiple times in a target list so that it doesn't cause dependent Nodes to "disappear" from the dependency graph walk. From Carsten Koch: - Avoid race conditions with same-named files and directory creation when pushing copies of files to CacheDir(). From Tzvetan Mikov: - Handle $ in Java class names. From Gary Oberbrunner: - Add support for the Intel C compiler on Windows64. - On SGI IRIX, have $SHCXX use $CXX by default (like other platforms). From Sohail Somani: - When Cloning a construction environment, set any variables before applying tools (so the tool module can access the configured settings) and re-set them after (so they end up matching what the user set). From Matthias Troffaes: - Make sure extra auxiliary files generated by some LaTeX packages and not ending in .aux also get deleted by scons -c. From Greg Ward: - Add a $JAVABOOTCLASSPATH variable for directories to be passed to the javac -bootclasspath option. From Christoph Wiedemann: - Add implicit dependencies on the commands used to build a target. RELEASE 0.97.0d20070809 - Fri, 10 Aug 2007 10:51:27 -0500 From Lars Albertsson: - Don't error if a #include line happens to match a directory somewhere on a path (like $CPPPATH, $FORTRANPATH, etc.). 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. - Add a new AddOption() function to support user-defined command- line flags (like --prefix=, --force, etc.). - Replace modified Optik version with new optparse compatibility module for command line processing in Scripts/SConsOptions.py - 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). - Fix the --debug=time option when the -j option is specified and all files are up to date. - Add a $SWIGOUTDIR variable to allow setting the swig -outdir option, and use it to identify files created by the swig -java option. - Add a $SWIGPATH variable that specifies the path to be searched for included SWIG files, Also add related $SWIGINCPREFIX and $SWIGINCSUFFIX variables that specify the prefix and suffix to be be added to each $SWIGPATH directory when expanded on the SWIG command line. - More efficient copying of construction environments (mostly borrowed from copy.deepcopy() in the standard Python library). - When printing --tree=prune output, don't print [brackets] around source files, only do so for built targets with children. - Fix interpretation of Builder source arguments when the Builder has a src_suffix *and* a source_builder and the argument has no suffix. - Fix use of expansions like ${TARGET.dir} or ${SOURCE.dir} in the following construction variables: $FORTRANMODDIR, $JARCHDIR, $JARFLAGS, $LEXFLAGS, $SWIGFLAGS, $SWIGOUTDIR and $YACCFLAGS. - Fix dependencies on Java files generated by SWIG so they can be detected and built in one pass. - Fix SWIG when used with a BuildDir(). From Leanid Nazdrynau: - When applying Tool modules after a construction environment has already been created, don't overwrite existing $CFILESUFFIX and $CXXFILESUFFIX value. - Support passing the Java() builder a list of explicit .java files (not only a list of directories to be scanned for .java files). - Support passing .java files to the Jar() and JavaH() builders, which then use the builder underlying the Java() builder to turn them into .class files. (That is, the Jar()-Java() chain of builders become multi-step, like the Program()-Object()-CFile() builders.) - Support passing SWIG .i files to the Java builders (Java(), Jar(), JavaH()), to cause intermediate .java files to be created automatically. - Add $JAVACLASSPATH and $JAVASOURCEPATH variables, that get added to the javac "-classpath" and "-sourcepath" options. (Note that SCons does *not* currently search these paths for implicit dependencies.) - Commonize initialization of Java-related builders. From Jan Nijtmans: - Find Java anonymous classes when the next token after the name is an open parenthesis. From Gary Oberbrunner: - Fix a code example in the man page. From Tilo Prutz: - Add support for the file names that Java 1.5 (and 1.6) generates for nested anonymous inner classes, which are different from Java 1.4. From Adam Simpkins: - Allow worker threads to terminate gracefully when all jobs are finished. 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: - Fix a bug that would make parallel builds stop in their tracks if Nodes that depended on lists that contained some Nodes built together caused the reference count to drop below 0 if the Nodes were visited and commands finished in the wrong order. - Make sure the DirEntryScanner doesn't choke if it's handed something that's not a directory (Node.FS.Dir) Node. RELEASE 0.96.96 - Thu, 12 Apr 2007 12:36:25 -0500 NOTE: This is (Yet) a(nother) pre-release of 0.97 for testing purposes. From Joe Bloggs: - Man page fix: remove cut-and-paste sentence in NoCache() description. From Dmitry Grigorenko and Gary Oberbrunner: - Use the Intel C++ compiler, not $CC, to link C++ source. From Helmut Grohne: - Fix the man page example of propagating a user's external environment. From Steven Knight: - Back out (most of) the Windows registry installer patch, which seems to not work on some versions of Windows. - Don't treat Java ".class" attributes as defining an inner class. - Fix detecting an erroneous Java anonymous class when the first non-skipped token after a "new" keyword is a closing brace. - Fix a regression when a CPPDEFINES list contains a tuple, the second item of which (the option value) is a construction variable expansion (e.g. $VALUE) and the value of the variable isn't a string. - Improve the error message if an IOError (like trying to read a directory as a file) occurs while deciding if a node is up-to-date. - Fix "maximum recursion" / "unhashable type" errors in $CPPPATH PathList expansion if a subsidiary expansion yields a stringable, non-Node object. - Generate API documentation from the docstrings (using epydoc). - Fix use of --debug=presub with Actions for out-of-the-box Builders. - Fix handling nested lists within $CPPPATH, $LIBPATH, etc. - Fix a "builders_used" AttributeError that real-world Qt initialization triggered in the refactored suffix handling for Builders. - Make the reported --debug=time timings meaningful when used with -j. Better documentation of what the times mean. - User Guide updates: --random, AlwaysBuild(), --tree=, --debug=findlibs, --debug=presub, --debug=stacktrace, --taskmastertrace. - Document (in both man page and User's Guide) that --implicit-cache ignores changes in $CPPPATH, $LIBPATH, etc. From Jean-Baptiste Lab: - Remove hard-coded dependency on Python 2.2 from Debian packaging files. From Jeff Mahovsky: - Handle spaces in the build target name in Visual Studio project files. From Rob Managan: - Re-run LaTeX after BibTeX has been re-run in response to a changed .bib file. From Joel B. Mohler: - Make additional TeX auxiliary files (.toc, .idx and .bbl files) Precious so their removal doesn't affect whether the necessary sections are included in output PDF or PostScript files. From Gary Oberbrunner: - Fix the ability to import modules in the site_scons directory from a subdirectory. From Adam Simpkins: - Make sure parallel (-j) builds all targets even if they show up multiple times in the child list (as a source and a dependency). From Matthias Troffaes: - Don't re-run TeX if the triggering strings (\makeindex, \bibliography \tableofcontents) are commented out. From Richard Viney: - Fix use of custom include and lib paths with Visual Studio 8. - Select the default .NET Framework SDK Dir based on the version of Visual Studio being used. RELEASE 0.96.95 - Mon, 12 Feb 2007 20:25:16 -0600 From Anatoly Techtonik: - Add the scons.org URL and a package description to the setup.py arguments. - Have the Windows installer add a registry entry for scons.bat in the "App Paths" key, so scons.bat can be executed without adding the directory to the %PATH%. (Python itself works this way.) From Anonymous: - Fix looking for default paths in Visual Studio 8.0 (and later). - Add -lm to the list of default D libraries for linking. From Matt Doar: - Provide a more complete write-your-own-Scanner example in the man page. From Ralf W. Grosse-Kunstleve: - Contributed upstream Python change to our copied subprocess.py module for more efficient standard input processing. From Steven Knight: - Fix the Node.FS.Base.rel_path() method when the two nodes are on different drive letters. (This caused an infinite loop when trying to write .sconsign files.) - Fully support Scanners that use a dictionary to map file suffixes to other scanners. - Support delayed evaluation of the $SPAWN variable to allow selection of a function via ${} string expansions. - Add --srcdir as a synonym for -Y/--repository. - Document limitations of #include "file.h" with Repository(). - Fix use of a toolpath under the source directory of a BuildDir(). - Fix env.Install() with a file name portion that begins with '#'. - Fix ParseConfig()'s handling of multiple options in a string that's replaced a *FLAGS construction variable. - Have the C++ tools initialize common C compilation variables ($CCFLAGS, $SHCCFLAGS and $_CCCOMCOM) even if the 'cc' Tool isn't loaded. From Leanid Nazdrynau: - Fix detection of Java anonymous classes if a newline precedes the opening brace. From Gary Oberbrunner: - Document use of ${} to execute arbitrary Python code. - Add support for: 1) automatically adding a site_scons subdirectory (in the top-level SConstruct directory) to sys.path (PYTHONPATH); 2) automatically importing site_scons/site_init.py; 3) automatically adding site_scons/site_tools to the toolpath. From John Pye: - Change ParseConfig() to preserve white space in arguments passed in as a list. From a smith: - Fix adding explicitly-named Java inner class files (and any other file names that may contain a '$') to Jar files. From David Vitek: - Add a NoCache() function to mark targets as unsuitable for propagating to (or retrieving from) a CacheDir(). From Ben Webb: - If the swig -noproxy option is used, it won't generate a .py file, so don't emit it as a target that we expect to be built. RELEASE 0.96.94 - Sun, 07 Jan 2007 18:36:20 -0600 NOTE: This is a pre-release of 0.97 for testing purposes. From Anonymous: - Allow arbitrary white space after a SWIG %module declaration. From Paul: - When compiling resources under MinGW, make sure there's a space between the --include-dir option and its argument. From Jay Kint: - Alleviate long command line issues on Windows by executing command lines directly via os.spawnv() if the command line doesn't need shell interpretation (has no pipes, redirection, etc.). From Walter Franzini: - Exclude additional Debian packaging files from the copyright check. From Fawad Halim: - Handle the conflict between the impending Python 2.6 'as' keyword and our Tool/as.py module name. From Steven Knight: - Speed up the Node.FS.Dir.rel_path() method used to generate path names that get put into the .sconsign* file(s). - Optimize Node.FS.Base.get_suffix() by computing the suffix once, up front, when we set the Node's name. (Duh...) - Reduce the Memoizer's responsibilities to simply counting hits and misses when the --debug=memoizer option is used, not to actually handling the key calculation and memoization itself. This speeds up some configurations significantly, and should cause no functional differences. - Add a new scons-time script with subcommands for generating consistent timing output from SCons configurations, extracting various information from those timings, and displaying them in different formats. - Reduce some unnecessary stat() calls from on-disk entry type checks. - Fix SideEffect() when used with -j, which was badly broken in 0.96.93. - Propagate TypeError exceptions when evaluating construction variable expansions up the stack, so users can see what's going on. - When disambiguating a Node.FS.Entry into a Dir or File, don't look in the on-disk source directory until we've confirmed there's no on-disk entry locally and there *is* one in the srcdir. This avoids creating a phantom Node that can interfere with dependencies on directory contents. - Add an AllowSubstExceptions() function that gives the SConscript files control over what exceptions cause a string to expand to '' vs. terminating processing with an error. - Allow the f90.py and f95.py Tool modules to compile earlier source source files of earlier Fortran version. - Fix storing signatures of files retrieved from CacheDir() so they're correctly identified as up-to-date next invocation. - Make sure lists of computed source suffixes cached by Builder objects don't persist across changes to the list of source Builders (so the addition of suffixes like .ui by the qt.py Tool module take effect). - Enhance the bootstrap.py script to allow it to be used to execute SCons more easily from a checked-out source tree. From Ben Leslie: - Fix post-Memoizer value caching misspellings in Node.FS._doLookup(). From Rob Managan, Dmitry Mikhin and Joel B. Mohler: - Handle TeX/LaTeX files in subdirectories by changing directory before invoking TeX/LaTeX. - Scan LaTeX files for \bibliography lines. - Support multiple file names in a "\bibliography{file1,file2}" string. - Handle TeX warnings about undefined citations. - Support re-running LaTeX if necessary due to a Table of Contents. From Dmitry Mikhin: - Return LaTeX if "Rerun to get citations correct" shows up on the next line after the "Warning:" string. From Gary Oberbrunner: - Add #include lines to fix portability issues in two tests. - Eliminate some unnecessary os.path.normpath() calls. - Add a $CFLAGS variable for C-specific options, leaving $CCFLAGS for options common to C and C++. From Tom Parker: - Have the error message print the missing file that Qt can't find. From John Pye: - Fix env.MergeFlags() appending to construction variable value of None. From Steve Robbins: - Fix the "sconsign" script when the .sconsign.dblite file is explicitly specified on the command line (and not intuited from the old way of calling it with just ".sconsign"). From Jose Pablo Ezequiel "Pupeno" Fernandez Silva: - Give the 'lex' tool knowledge of the additional target files produced by the flex "--header-file=" and "--tables-file=" options. - Give the 'yacc' tool knowledge of the additional target files produced by the bison "-g", "--defines=" and "--graph=" options. - Generate intermediate files with Objective C file suffixes (.m) when the lex and yacc source files have appropriate suffixes (.lm and .ym). From Sohail Somain: - Have the mslink.py Tool only look for a 'link' executable on Windows systems. From Vaclav Smilauer: - Add support for a "srcdir" keyword argument when calling a Builder, which will add a srcdir prefix to all non-relative string sources. From Jonathan Ultis: - Allow Options converters to take the construction environment as an optional argument. RELEASE 0.96.93 - Mon, 06 Nov 2006 00:44:11 -0600 NOTE: This is a pre-release of 0.97 for testing purposes. From Anonymous: - Allow Python Value Nodes to be Builder targets. From Matthias: - Only filter Visual Studio common filename prefixes on complete directory names. From Chad Austin: - Fix the build of the SCons documentation on systems that don't have "python" in the $PATH. From Ken Boortz: - Enhance ParseConfig() to recognize options that begin with '+'. From John Calcote, Elliot Murphy: - Document ways to override the CCPDBFLAGS variable to use the Microsoft linker's /Zi option instead of the default /Z7. From Christopher Drexler: - Make SCons aware bibtex must be called if any \include files cause creation of a bibliography. - Make SCons aware that "\bilbiography" in TeX source files means that related .bbl and .blg bibliography files will be created. (NOTE: This still needs to search for the string in \include files.) From David Gruener: - Fix inconsistent handling of Action strfunction arguments. - Preserve white space in display Action strfunction strings. From James Y. Knight and Gerard Patel: - Support creation of shared object files from assembly language. From Steven Knight: - Speed up the Taskmaster significantly by avoiding unnecessary re-scans of Nodes to find out if there's work to be done, having it track the currently-executed top-level target directly and not through its presence on the target list, and eliminating some other minor list(s), method(s) and manipulation. - Fix the expansion of $TARGET and $SOURCE in the expansion of $INSTALLSTR displayed for non-environment calls to InstallAs(). - Fix the ability to have an Alias() call refer to a directory name that's not identified as a directory until later. - Enhance runtest.py with an option to use QMTest as the harness. This will become the default behavior as we add more functionality to the QMTest side. - Let linking on mingw use the default function that chooses $CC (gcc) or $CXX (g++) depending on whether there are any C++ source files. - Work around a bug in early versions of the Python 2.4 profile module that caused the --profile= option to fail. - Only call Options validators and converters once when initializing a construction environment. - Fix the ability of env.Append() and env.Prepend(), in all known Python versions, to handle different input value types when the construction variable being updated is a dictionary. - Add a --cache-debug option for information about what files it's looking for in a CacheDir(). - Document the difference in construction variable expansion between {Action,Builder}() and env.{Action,Builder}(). - Change the name of env.Copy() to env.Clone(), keeping the old name around for backwards compatibility (with the intention of eventually phasing it out to avoid confusion with the Copy() Action factory). From Arve Knudsen: - Support cleaning and scanning SWIG-generated files. From Carsten Koch: - Allow selection of Visual Studio version by setting $MSVS_VERSION after construction environment initialization. From Jean-Baptiste Lab: - Try using zipimport if we can't import Tool or Platform modules using the normal "imp" module. This allows SCons to be packaged using py2exe's all-in-one-zip-file approach. From Ben Liblit: - Do not re-scan files if the scanner returns no implicit dependencies. From Sanjoy Mahajan: - Change use of $SOURCES to $SOURCE in all TeX-related Tool modules. From Joel B. Mohler: - Make SCons aware that "\makeindex" in TeX source files means that related .ilg, .ind and .idx index files will be created. (NOTE: This still needs to search for the string in \include files.) - Prevent scanning the TeX .aux file for additional files from trying to remove it twice when the -c option is used. From Leanid Nazdrynau: - Give the MSVC RES (resource) Builder a src_builder list and a .rc src_suffix so other builders can generate .rc files. From Matthew A. Nicholson: - Enhance Install() and InstallAs() to handle directory trees as sources. From Jan Nijtmans: - Don't use the -fPIC flag when using gcc on Windows (e.g. MinGW). From Greg Noel: - Add an env.ParseFlags() method that provides separate logic for parsing GNU tool chain flags into a dictionary. - Add an env.MergeFlags() method to apply an arbitrary dictionary of flags to a construction environment's variables. From Gary Oberbrunner: - Fix parsing tripartite Intel C compiler version numbers on Linux. - Extend the ParseConfig() function to recognize -arch and -isysroot options. - Have the error message list the known suffixes when a Builder call can't build a source file with an unknown suffix. From Karol Pietrzak: - Avoid recursive calls to main() in the program snippet used by the SConf subsystem to test linking against libraries. This changes the default behavior of CheckLib() and CheckLibWithHeader() to print "Checking for C library foo..." instead of "Checking for main() in C library foo...". From John Pye: - Throw an exception if a command called by ParseConfig() or ParseFlags() returns an error. From Stefan Seefeld: - Initial infrastructure for running SCons tests under QMTest. From Sohail Somani: - Fix tests that fail due to gcc warnings. From Dobes Vandermeer: - In stack traces, print the full paths of SConscript files. From Atul Varma: - Fix detection of Visual C++ Express Edition. From Dobes Vandermeer: - Let the src_dir option to the SConscript() function affect all the the source file paths, instead of treating all source files paths as relative to the SConscript directory itself. From Nicolas Vigier: - Fix finding Fortran modules in build directories. - Fix use of BuildDir() when the source file in the source directory is a symlink with a relative path. From Edward Wang: - Fix the Memoizer when the SCons Python modules are executed from .pyo files at different locations from where they were compiled. From Johan Zander: - Fix missing os.path.join() when constructing the $FRAMEWORKSDKDIR/bin. RELEASE 0.96.92 - Mon, 10 Apr 2006 21:08:22 -0400 NOTE: This was a pre-release of 0.97 for testing purposes. From Anonymous: - Fix the intelc.py Tool module to not throw an exception if the only installed version is something other than ia32. - Set $CCVERSION when using gcc. From Matthias: - Support generating project and solution files for Microsoft Visual Studio version 8. - Support generating more than one project file for a Microsoft Visual Studio solution file. - Add support for a support "runfile" parameter to Microsoft Visual Studio project file creation. - Put the project GUID, not the solution GUID, in the right spot in the solution file. From Erling Andersen: - Fix interpretation of Node.FS objects wrapped in Proxy instances, allowing expansion of things like ${File(TARGET)} in command lines. From Stanislav Baranov: - Add a separate MSVSSolution() Builder, with support for the following new construction variables: $MSVSBUILDCOM, $MSVSCLEANCOM, $MSVSENCODING, $MSVSREBUILDCOM, $MSVSSCONS, $MSVSSCONSCOM, $MSVSSCONSFLAGS, $MSVSSCONSCRIPT and $MSVSSOLUTIONCOM. From Ralph W. Grosse-Kunstleve and Patrick Mezard: - Remove unneceesary (and incorrect) SCons.Util strings on some function calls in SCons.Util. From Bob Halley: - Fix C/C++ compiler selection on AIX to not always use the external $CC environment variable. From August Hörandl: - Add a scanner for \include and \import files, with support for searching a directory list in $TEXINPUTS (imported from the external environment). - Support $MAKEINDEX, $MAKEINDEXCOM, $MAKEINDEXCOMSTR and $MAKEINDEXFLAGS for generating indices from .idx files. From Steven Johnson: - Add a NoClean() Environment method and function to override removal of targets during a -c clean, including documentation and tests. From Steven Knight: - Check for whether files exist on disk by listing the directory contents, not calling os.path.exists() file by file. This is somewhat more efficient in general, and may be significantly more efficient on Windows. - Minor speedups in the internal is_Dict(), is_List() and is_String() functions. - Fix a signature refactoring bug that caused Qt header files to get re-generated every time. - Don't fail when writing signatures if the .sconsign.dblite file is owned by a different user (e.g. root) from a previous run. - When deleting variables from stacked OverrideEnvironments, don't throw a KeyError if we were able to delte the variable from any Environment in the stack. - Get rid of the last indentation tabs in the SCons source files and add -tt to the Python invocations in the packaging build and the tests so they don't creep back in. - In Visual Studio project files, put quotes around the -C directory so everything works even if the path has spaces in it. - The Intel Fortran compiler uses -object:$TARGET, not "-o $TARGET", when building object files on Windows. Have the the ifort Tool modify the default command lines appropriately. - Document the --debug=explain option in the man page. (How did we miss this?) - Add a $LATEXRETRIES variable to allow configuration of the number of times LaTex can be re-called to try to resolve undefined references. - Change the order of the arguments to Configure.Checklib() to match the documentation. - Handle signature calculation properly when the Python function used for a FunctionAction is an object method. - On Windows, assume that absolute path names without a drive letter refer to the drive on which the SConstruct file lives. - Add /usr/ccs/bin to the end of the the default external execution PATH on Solaris. - Add $PKGCHK and $PKGINFO variables for use on Solaris when searching for the SunPRO C++ compiler. Make the default value for $PKGCHK be /usr/sbin/pgkchk (since /usr/sbin isn't usually on the external execution $PATH). - Fix a man page example of overriding variables when calling SharedLibrary() to also set the $LIBSUFFIXES variable. - Add a --taskmastertrace=FILE option to give some insight on how the taskmaster decides what Node to build next. - Changed the names of the old $WIN32DEFPREFIX, $WIN32DEFSUFFIX, $WIN32DLLPREFIX and $WIN32IMPLIBPREFIX construction variables to new $WINDOWSDEFPREFIX, $WINDOWSDEFSUFFIX, $WINDOWSDLLPREFIX and $WINDOWSIMPLIBPREFIX construction variables. The old names are now deprecated, but preserved for backwards compatibility. - Fix (?) a runtest.py hang on Windows when the --xml option is used. - Change the message when an error occurs trying to interact with the file system to report the target(s) in square brackets (as before) and the actual file or directory that encountered the error afterwards. From Chen Lee: - Add x64 support for Microsoft Visual Studio 8. From Baptiste Lepilleur: - Support the --debug=memory option on Windows when the Python version has the win32process and win32api modules. - Add support for Visual Studio 2005 Pro. - Fix portability issues in various tests: test/Case.py, Test/Java/{JAR,JARCHDIR,JARFLAGS,JAVAC,JAVACFLAGS,JAVAH,RMIC}.py, test/MSVS/vs-{6.0,7.0,7.1,8.0}-exec.py, test/Repository/{Java,JavaH,RMIC}.py, test/QT/{generated-ui,installed,up-to-date,warnings}.py, test/ZIP/ZIP.py. - Ignore pkgchk errors on Solaris when searching for the C++ compiler. - Speed up the SCons/EnvironmentTests.py unit tests. - Add a --verbose= option to runtest.py to print executed commands and their output at various levels. From Christian Maaser: - Add support for Visual Studio Express Editions. - Add support for Visual Studio 8 *.manifest files, includng new $WINDOWS_INSERT_MANIFEST, $WINDOWSPROGMANIFESTSUFFIX, $WINDOWSPROGMANIFESTPREFIX, $WINDOWSPROGMANIFESTSUFFIX, $WINDOWSSHLIBMANIFESTPREFIX and $WINDOWSSHLIBMANIFESTSUFFIX construction variables. From Adam MacBeth: - Fix detection of additional Java inner classes following use of a "new" keyword inside an inner class. From Sanjoy Mahajan: - Correct TeX-related command lines to just $SOURCE, not $SOURCES From Patrick Mezard: - Execute build commands for a command-line target if any of the files built along with the target is out of date or non-existent, not just if the command-line target itself is out of date. - Fix the -n option when used with -c to print all of the targets that will be removed for a multi-target Builder call. - If there's no file in the source directory, make sure there isn't one in the build directory, too, to avoid dangling files left over from previous runs when a source file is removed. - Allow AppendUnique() and PrependUnique() to append strings (and other atomic objects) to lists. From Joel B. Mohler: - Extend latex.py, pdflatex.py, pdftex.py and tex.py so that building from both TeX and LaTeX files uses the same logic to call $BIBTEX when it's necessary, to call $MAKEINDEX when it's necessary, and to call $TEX or $LATEX multiple times to handle undefined references. - Add an emitter to the various TeX builders so that the generated .aux and .log files also get deleted by the -c option. From Leanid Nazdrynau: - Fix the Qt UIC scanner to work with generated .ui files (by using the FindFile() function instead of checking by-hand for the file). From Jan Nieuwenhuizen: - Fix a problem with interpreting quoted argument lists on command lines. From Greg Noel: - Add /sw/bin to the default execution PATH on Mac OS X. From Kian Win Ong: - When building a .jar file and there is a $JARCHDIR, put the -C in front of each .class file on the command line. - Recognize the Java 1.5 enum keyword. From Asfand Yar Qazi: - Add /opt/bin to the default execution PATH on all POSIX platforms (between /usr/local/bin and /bin). From Jon Rafkind: - Fix the use of Configure() contexts from nested subsidiary SConscript files. From Christoph Schulz: - Add support for $CONFIGUREDIR and $CONFIGURELOG variables to control the directory and logs for configuration tests. - Add support for a $INSTALLSTR variable. - Add support for $RANLIBCOM and $RANLIBCOMSTR variables (which fixes a bug when setting $ARCOMSTR). From Amir Szekely: - Add use of $CPPDEFINES to $RCCOM (resource file compilation) on MinGW. From Erick Tryzelaar: - Fix the error message when trying to report that a given option is not gettable/settable from an SConscript file. From Dobes Vandermeer: - Add support for SCC and other settings in Microsoft Visual Studio project and solution files: $MSVS_PROJECT_BASE_PATH, $MSVS_PROJECT_GUID, $MSVS_SCC_AUX_PATH, $MSVS_SCC_LOCAL_PATH, $MSVS_SCC_PROJECT_NAME, $MSVS_SCC_PROVIDER, - Add support for using a $SCONS_HOME variable (imported from the external environment, or settable internally) to put a shortened SCons execution line in the Visual Studio project file. From David J. Van Maren: - Only filter common prefixes from source files names in Visual Studio project files if the prefix is a complete (sub)directory name. From Thad Ward: - If $MSVSVERSIONS is already set, don't overwrite it with information from the registry. RELEASE 0.96.91 - Thu, 08 Sep 2005 07:18:23 -0400 NOTE: This was a pre-release of 0.97 for testing purposes. From Chad Austin: - Have the environment store the toolpath and re-use it to find Tools modules during later Copy() or Tool() calls (unless overridden). - Normalize the directory path names in SConsignFile() database files so the same signature file can interoperate on Windows and non-Windows systems. - Make --debug=stacktrace print a stacktrace when a UserError is thrown. - Remove an old, erroneous cut-and-paste comment in Scanner/Dir.py. From Stanislav Baranov: - Make it possible to support with custom Alias (sub-)classes. - Allow Builders to take empty source lists when called. - Allow access to both TARGET and SOURCE in $*PATH expansions. - Allow SConscript files to modify BUILD_TARGETS. From Timothee Besset: - Add support for Objective C/C++ .m and .mm file suffixes (for Mac OS X). From Charles Crain - Fix the PharLap linkloc.py module to use target+source arguments when calling env.subst(). From Bjorn Eriksson: - Fix an incorrect Command() keyword argument in the man page. - Add a $TEMPFILEPREFIX variable to control the prefix or flag used to pass a long-command-line-execution tempfile to a command. From Steven Knight: - Enhanced the SCons setup.py script to install man pages on UNIX/Linux systems. - Add support for an Options.FormatOptionHelpText() method that can be overridden to customize the format of Options help text. - Add a global name for the Entry class (which had already been documented). - Fix re-scanning of generated source files for implicit dependencies when the -j option is used. - Fix a dependency problem that caused $LIBS scans to not be added to all of the targets in a multiple-target builder call, which could cause out-of-order builds when the -j option is used. - Store the paths of source files and dependencies in the .sconsign* file(s) relative to the target's directory, not relative to the top-level SConstruct directory. This starts to make it possible to subdivide the dependency tree arbitrarily by putting an SConstruct file in every directory and using content signatures. - Add support for $YACCHFILESUFFIX and $YACCHXXFILESUFFIX variables that accomodate parser generators that write header files to a different suffix than the hard-coded .hpp when the -d option is used. - The default behavior is now to store signature information in a single .sconsign.dblite file in the top-level SConstruct directory. The old behavior of a separate .sconsign file in each directory can be specified by calling SConsignFile(None). - Remove line number byte codes within the signature calculation of Python function actions, so that changing the location of an otherwise unmodified Python function doesn't cause rebuilds. - Fix AddPreAction() and AddPostAction() when an action has more than one target file: attach the actions to the Executor, not the Node. - Allow the source directory of a BuildDir / build_dir to be outside of the top-level SConstruct directory tree. - Add a --debug=nomemoizer option that disables the Memoizer for clearer looks at the counts and profiles of the underlying function calls, not the Memoizer wrappers. - Print various --debug= stats even if we exit early (e.g. using -h). - Really only use the cached content signature value if the file is older than --max-drift, not just if --max-drift is set. - Remove support for conversion from old (pre 0.96) .sconsign formats. - Add support for a --diskcheck option to enable or disable various on-disk checks: that File and Dir nodes match on-disk entries; whether an RCS file exists for a missing source file; whether an SCCS file exists for a missing source file. - Add a --raw argument to the sconsign script, so it can print a raw representation of each entry's NodeInfo dictionary. - Add the 'f90' and 'f95' tools to the list of Fortran compilers searched for by default. - Add the +Z option by default when compiling shared objects on HP-UX. From Chen Lee: - Handle Visual Studio project and solution files in Unicode. From Sanjoy Mahajan: - Fix a bad use of Copy() in an example in the man page, and a bad regular expression example in the man page and User's Guide. From Shannon Mann: - Have the Visual Studio project file(s) echo "Starting SCons" before executing SCons, mainly to work around a quote-stripping bug in (some versions of?) the Windows cmd command executor. From Georg Mischler: - Remove the space after the -o option when invoking the Borland BCC compiler; some versions apparently require that the file name argument be concatenated with the option. From Leanid Nazdrynau: - Fix the Java parser's handling of backslashes in strings. From Greg Noel: - Add construction variables to support frameworks on Mac OS X: $FRAMEWORKS, $FRAMEWORKPREFIX, $FRAMEWORKPATH, $FRAMEWORKPATHPREFIX. - Re-order link lines so the -o option always comes right after the command name. From Gary Oberbrunner: - Add support for Intel C++ beta 9.0 (both 32 and 64 bit versions). - Document the new $FRAMEWORK* variables for Mac OS X. From Karol Pietrzak: - Add $RPATH (-R) support to the Sun linker Tool (sunlink). - Add a description of env.subst() to the man page. From Chris Prince: - Look in the right directory, not always the local directory, for a same-named file or directory conflict on disk. - On Windows, preserve the external environment's %SYSTEMDRIVE% variable, too. From Craig Scott: - Have the Fortran module emitter look for Fortan modules to be created relative to $FORTRANMODDIR, not the top-level directory. - When saving Options to a file, run default values through the converter before comparing them with the set values. This correctly suppresses Boolean Option values from getting written to the saved file when they're one of the many synonyms for a default True or False value. - Fix the Fortran Scanner's ability to handle a module being used in the same file in which it is defined. From Steve-o: - Add the -KPIC option by default when compiling shared objects on Solaris. - Change the default suffix for Solaris objects to .o, to conform to Sun WorkShop's expectations. Change the profix to so_ so they can still be differentiated from static objects in the same directory. From Amir Szekely: - When calling the resource compiler on MinGW, add --include-dir and the source directory so it finds the source file. - Update EnsureSConsVersion() to support revision numbers. From Greg Ward: - Fix a misplaced line in the man page. RELEASE 0.96.90 - Tue, 15 Feb 2005 21:21:12 +0000 NOTE: This was a pre-release of 0.97 for testing purposes. From Anonymous: - Fix Java parsing to avoid erroneously identifying a new array of class instances as an anonymous inner class. - Fix a typo in the man page description of PathIsDirCreate. From Chad Austin: - Allow Help() to be called multiple times, appending to the help text each call. - Allow Tools found on a toolpath to import Python modules from their local directory. From Steve Christensen: - Handle exceptions from Python functions as build actions. - Add a set of canned PathOption validators: PathExists (the default), PathIsFile, PathIsDir and PathIsDirCreate. From Matthew Doar: - Add support for .lex and .yacc file suffixes for Lex and Yacc files. From Eric Frias: - Huge performance improvement: wrap the tuples representing an include path in an object, so that the time it takes to hash the path doesn't grow porportionally to the length of the path. From Gottfried Ganssauge: - Fix SCons on SuSE/AMD-64 Linux by having the wrapper script also check for the build engine in the parent directory of the Python library directory (/usr/lib64 instead of /usr/lib). From Stephen Kennedy: - Speed up writing the .sconsign file at the end of a run by only calling sync() once at the end, not after every entry. From Steven Knight: - When compiling with Microsoft Visual Studio, don't include the ATL and MFC directories in the default INCLUDE and LIB environment variables. - Remove the following deprecated features: the ParseConfig() global function (deprecated in 0.93); the misspelled "validater" keyword to the Options.Add() method (deprecated in 0.91); the SetBuildSignatureType(), SetContentSignatureType(), SetJobs() and GetJobs() global functions (deprecated in 0.14). - Fix problems with corrupting the .sconsign.dblite file when interrupting builds by writing to a temporary file and renaming, not writing the file directly. - Fix a 0.96 regression where when running with -k, targets built from walking dependencies later on the command line would not realize that a dependency had failed an earlier build attempt, and would try to rebuild the dependent targets. - Change the final messages when using -k and errors occur from "{building,cleaning} terminated because of errors" to "done {building,cleaning} targets (errors occurred during {build,clean})." - Allow Configure.CheckFunc() to take an optional header argument (already supported by Conftest.py) to specify text at the top of the compiled test file. - Fix the --debug=explain output when a Python function action changed so it prints a meaningful string, not the binary representation of the function contents. - Allow a ListOption's default value(s) to be a Python list of specified values, not just a string containing a comma-separated list of names. - Add a ParseDepends() function that will parse up a list of explicit dependencies from a "make depend" style file. - Support the ability to change directory when executing an Action through "chdir" keyword arguments to Action and Builder creation and calls. - Fix handling of Action ojects (and other callables that don't match our calling arguments) in construction variable expansions. - On Win32, install scons.bat in the Python directory when installing from setup.py. (The bdist_wininst installer was already doing this.) - Fix env.SConscript() when called with a list of SConscipt files. (The SConscript() global function already worked properly.) - Add a missing newline to the end of the --debug=explain "unknown reasons" message. - Enhance ParseConfig() to work properly for spaces in between the -I, -L and -l options and their arguments. - Packaging build fix: Rebuild the files that are use to report the --version of SCons whenever the development version number changes. - Fix the ability to specify a target_factory of Dir() to a Builder, which the default create-a-directory Builder was interfering with. - Mark a directory as built if it's created as part of the preparation for another target, to avoid trying to build it again when it comes up in the target list. - Allow a function with the right calling signature to be put directly in an Environment's BUILDERS dictionary, making for easier creation and use of wrappers (pseudo-Builders) that call other Builders. - On Python 2.x, wrap lists of Nodes returned by Builders in a UserList object that adds a method that makes str() object return a string with all of the Nodes expanded to their path names. (Builders under Python 1.5.2 still return lists to avoid TypeErrors when trying to extend() list, so Python 1.5.2 doesn't get pretty-printing of Node lists, but everything should still function.) - Allow Aliases to have actions that will be executed whenever any of the expanded Alias targets are out of date. - Fix expansion of env.Command() overrides within target and source file names. - Support easier customization of what's displayed by various default actions by adding lots of new construction variables: $ARCOMSTR, $ASCOMSTR, $ASPPCOMSTR, $BIBTEXCOMSTR, $BITKEEPERCOMSTR, $CCCOMSTR, $CVSCOMSTR, $CXXCOMSTR, $DCOMSTR, $DVIPDFCOMSTR, $F77COMSTR, $F90COMSTR, $F95COMSTR, $FORTRANCOMSTR, $GSCOMSTR, $JARCOMSTR, $JAVACCOMSTR, $JAVAHCOMSTR, $LATEXCOMSTR, $LEXCOMSTR, $LINKCOMSTR, $M4COMSTR, $MIDLCOMSTR, $P4COMSTR, $PCHCOMSTR, $PDFLATEXCOMSTR, $PDFTEXCOMSTR, $PSCOMSTR, $QT_MOCFROMCXXCOMSTR, $QT_MOCFROMHCOMSTR, $QT_UICCOMSTR, $RCCOMSTR, $REGSVRCOMSTR, $RCS_COCOMSTR, $RMICCOMSTR, $SCCSCOMSTR, $SHCCCOMSTR, $SHCXXCOMSTR, $SHF77COMSTR, $SHF90COMSTR, $SHF95COMSTR, $SHFORTRANCOMSTR, $SHLINKCOMSTR, $SWIGCOMSTR, $TARCOMSTR, $TEXCOMSTR, $YACCCOMSTR and $ZIPCOMSTR. - Add an optional "map" keyword argument to ListOption() that takes a dictionary to map user-specified values to legal values from the list (like EnumOption() already doee). - Add specific exceptions to try:-except: blocks without any listed, so that they won't catch and mask keyboard interrupts. - Make --debug={tree,dtree,stree} print something even when there's a build failure. - Fix how Scanners sort the found dependencies so that it doesn't matter whether the dependency file is in a Repository or not. This may cause recompilations upon upgrade to this version. - Make AlwaysBuild() work with Alias and Python value Nodes (making it much simpler to support aliases like "clean" that just invoke an arbitrary action). - Have env.ParseConfig() use AppendUnique() by default to suppress duplicate entries from multiple calls. Add a "unique" keyword argument to allow the old behavior to be specified. - Allow the library modules imported by an SConscript file to get at all of the normally-available global functions and variables by saying "from SCons.Script import *". - Add a --debug=memoizer option to print Memoizer hit/mass statistics. - Allow more than one --debug= option to be set at a time. - Change --debug=count to report object counts before and after reading SConscript files and before and after building targets. - Change --debug=memory output to line up the numbers and to better match (more or less) the headers on the --debug=count columns. - Speed things up when there are lists of targets and/or sources by getting rid of some N^2 walks of the lists involved. - Cache evaluation of LazyActions so we don't create a new object for each invocation. - When scanning, don't create Nodes for include files that don't actually exist on disk. - Make supported global variables CScanner, DScanner, ProgramScanner and SourceFileScanner. Make SourceFileScanner.add_scanner() a supported part of the public interface. Keep the old SCons.Defaults.*Scan names around for a while longer since some people were already using them. - By default, don't scan directories for on-disk files. Add a DirScanner global scanner that can be used in Builders or Command() calls that want source directory trees scanned for on-disk changes. Have the Tar() and Zip() Builders use the new DirScanner to preserve the behavior of rebuilding a .tar or .zip file if any file or directory under a source tree changes. Add Command() support for a source_scanner keyword argument to Command() that can be set to DirScanner to get this behavior. - Documentation changes: Explain that $CXXFLAGS contains $CCFLAGS by default. Fix a bad target_factory example in the man page. Add appendices to the User's Guide to cover the available Tools, Builders and construction variables. Comment out the build of the old Python 10 paper, which doesn't build on all systems and is old enough at this point that it probably isn't worth the effort to make it do so. From Wayne Lee: - Avoid "maximum recursion limit" errors when removing $(-$) pairs from long command lines. From Clive Levinson: - Make ParseConfig() recognize and add -mno-cygwin to $LINKFLAGS and $CCFLAGS, and -mwindows to $LINKFLAGS. From Michael McCracken: - Add a new "applelink" tool to handle the things like Frameworks and bundles that Apple has added to gcc for linking. - Use more appropriate default search lists of linkers, compilers and and other tools for the 'darwin' platform. - Add a LoadableModule Builder that builds a bundle on Mac OS X (Darwin) and a shared library on other systems. - Improve SWIG tests for use on Mac OS X (Darwin). From Elliot Murphy: - Enhance the tests to guarantee persistence of ListOption values in saved options files. - Supply the help text when -h is used with the -u, -U or -D options. From Christian Neeb: - Fix the Java parser's handling of string definitions to avoid ignoring subsequent code. From Han-Wen Nienhuys: - Optimize variable expansion by: using the re.sub() method (when possible); not using "eval" for variables for which we can fetch the value directory; avoiding slowing substitution logic when there's no '$' in the string. From Gary Oberbrunner: - Add an Environment.Dump() method to print the contents of a construction environment. - Allow $LIBS (and similar variables) to contain explicit File Nodes. - Change ParseConfig to add the found library names directly to the $LIBS variable, instead of returning them. - Add ParseConfig() support for the -framework GNU linker option. - Add a PRINT_CMD_LINE_FUNC construction variable to allow people to filter (or log) command-line output. - Print an internal Python stack trace in response to an otherwise unexplained error when --debug=stacktrace is specified. - Add a --debug=findlibs option to print what's happening when the scanner is searching for libraries. - Allow Tool specifications to be passed a dictionary of keyword arguments. - Support an Options default value of None, in which case the variable will not be added to the construction environment unless it's set explicitly by the user or from an Options file. - Avoid copying __builtin__ values into a construction environment's dictionary when evaluating construction variables. - Add a new cross-platform intelc.py Tool that can detect and configure the Intel C++ v8 compiler on both Windows, where it's named icl, and Linux, where it's named icc. It also checks that the directory specified in the Windows registry exists, and sets a new $INTEL_C_COMPILER_VERSION construction variable to identify the version being used. (Niall Douglas contributed an early prototype of parts of this module.) - Fix the private Conftest._Have() function so it doesn't change non-alphanumeric characters to underscores. - Supply a better error message when a construction variable expansion has an unknown attribute. - Documentation changes: Update the man page to describe use of filenames or Nodes in $LIBS. From Chris Pawling: - Have the linkloc tool use $MSVS_VERSION to select the Microsoft Visual Studio version to use. From Kevin Quick: - Fix the Builder name returned from ListBuilders and other instances of subclasses of the BuilderBase class. - Add Builders and construction variables to support rpcgen: RPCGenClient(), RPCGenHeader(), RPCGenService(), RPCGenXDR(), $RPCGEN, $RPCGENFLAGS, $RPCGENCLIENTFLAGS, $RPCGENHEADERFLAGS, $RPCGENSERVICEFLAGS, $RPCGENXDRFLAGS. - Update the man page to document that prefix and suffix Builder keyword arguments can be strings, callables or dictionaries. - Provide more info in the error message when a user tries to build a target multiple ways. - Fix Delete() when a file doesn't exist and must_exist=1. (We were unintentionally dependent on a bug in versions of the Python shutil.py module prior to Python 2.3, which would generate an exception for a nonexistent file even when ignore_errors was set.) - Only replace a Node's builder with a non-null source builder. - Fix a stack trace when a suffix selection dictionary is passed an empty source file list. - Allow optional names to be attached to Builders, for default Builders that don't get attached to construction environments. - Fix problems with Parallel Task Exception handling. - Build targets in an associated BuildDir even if there are targets or subdirectories locally in the source directory. - If a FunctionAction has a callable class as its underlying Python function, use its strfunction() method (if any) to display the action. - Fix handling when BuildDir() exists but is unwriteable. Add "Stop." to those error messages for consistency. - Catch incidents of bad builder creation (without an action) and supply meaningful error messages. - Fix handling of src_suffix values that aren't extensions (don't begin with a '.'). - Don't retrieve files from a CacheDir, but report what would happen, when the -n option is used. - Use the source_scanner from the target Node, not the source node itself. - Internal Scanners fixes: Make sure Scanners are only passed Nodes. Fix how a Scanner.Selector called its base class initialization. Make comparisons of Scanner objects more robust. Add a name to an internal default ObjSourceScanner. - Add a deprecated warning for use of the old "scanner" keyword argument to Builder creation. - Improve the --debug=explain message when the build action changes. - Test enhancements in SourceCode.py, option-n.py, midl.py. Better Command() and Scanner test coverage. Improved test infrastructure for -c output. - Refactor the interface between Action and Executor objects to treat Actions atomically. - The --debug=presub option will now report the pre-substitution each action seprately, instead of reporting the entire list before executing the actions one by one. - The --debug=explain option explaining a changed action will now (more correctly) show pre-substitution action strings, instead of the commands with substituted file names. - A Node (file) will now be rebuilt if its PreAction or PostAction actions change. - Python Function actions now have their calling signature (target, source, env) reported correctly when displayed. - Fix BuildDir()/build_dir handling when the build_dir is underneath the source directory and trying to use entries from the build_dir as sources for other targets in the build-dir. - Fix hard-coding of JDK path names in various Java tests. - Handle Python stack traces consistently (stop at the SConscript stack frame, by default) even if the Python source code isn't available. - Improve the performance of the --debug={tree,dtree} options. - Add --debug=objects logging of creation of OverrideWarner, EnvironmentCopy and EnvironmentOverride objects. - Fix command-line expansion of Python Value Nodes. - Internal cleanups: Remove an unnecessary scan argument. Associate Scanners only with Builders, not nodes. Apply overrides once when a Builder is called, not in multiple places. Cache results from the Node.FS.get_suffix() and Node.get_build_env() methods. Use the Python md5 modules' hexdigest() method, if there is one. Have Taskmaster call get_stat() once for each Node and re-use the value instead of calling it each time it needs the value. Have Node.depends_on() re-use the list from the children() method instead of calling it multiple times. - Use the correct scanner if the same source file is used for targets in two different environments with the same path but different scanners. - Collect logic for caching values in memory in a Memoizer class, which cleans up a lot of special-case code in various methods and caches additional values to speed up most configurations. - Add a PathAccept validator to the list of new canned PathOption validators. From Jeff Squyres: - Documentation changes: Use $CPPDEFINES instead of $CCFLAGS in man page examples. From Levi Stephen: - Allow $JARCHDIR to be expanded to other construction variables. From Christoph Wiedemann: - Add an Environment.SetDefault() method that only sets values if they aren't already set. - Have the qt.py Tool not override variables already set by the user. - Add separate $QT_BINPATH, $QT_CPPPATH and $QT_LIBPATH variables so these can be set individually, instead of being hard-wired relative to $QTDIR. - The %TEMP% and %TMP% external environment variables are now propagated automatically to the command execution environment on Windows systems. - A new --config= command-line option allows explicit control of of when the Configure() tests are run: --config=force forces all checks to be run, --config=cache uses all previously cached values, --config=auto (the default) runs tests only when dependency analysis determines it's necessary. - The Configure() subsystem can now write a config.h file with values like HAVE_STDIO_H, HAVE_LIBM, etc. - The Configure() subsystem now executes its checks silently when the -Q option is specified. - The Configure() subsystem now reports if a test result is being taken from cache, and prints the standard output and error output of tests even when cached. - Configure() test results are now reported as "yes" or "no" instead of "ok" or "failed." - Fixed traceback printing when calling the env.Configure() method instead of the Configure() global function. - The Configure() subsystem now caches build failures in a .sconsign file in the subdirectory, not a .cache file. This may cause tests to be re-executed the first time after you install 0.97. - Additional significant internal cleanups in the Configure() subsystem and its tests. - Have the Qt Builder make uic-generated files dependent on the .ui.h file, if one exists. - Add a test to make sure that SCons source code does not contain try:-except: blocks that catch all errors, which potentially catch and mask keyboard interrupts. - Fix us of TargetSignatures('content') with the SConf subsystem. From Russell Yanofsky: - Add support for the Metrowerks Codewarrior compiler and linker (mwcc and mwld). RELEASE 0.96.1 - Mon, 23 Aug 2004 12:55:50 +0000 From Craig Bachelor: - Handle white space in the executable Python path name within in MSVS project files by quoting the path. - Correct the format of a GUID string in a solution (.dsw) file so MSVS can correctly "build enable" a project. From Steven Knight: - Add a must_exist flag to Delete() to let the user control whether it's an error if the specified entry doesn't exist. The default behavior is now to silently do nothing if it doesn't exist. - Package up the new Platform/darwin.py, mistakenly left out of 0.96. - Make the scons.bat REM statements into @REM so they aren't printed. - Make the SCons packaging SConscript files platform independent. From Anthony Roach: - Fix scanning of pre-compiled header (.pch) files for #includes, broken in 0.96. RELEASE 0.96 - Wed, 18 Aug 2004 13:36:40 +0000 From Chad Austin: - Make the CacheDir() directory if it doesn't already exist. - Allow construction variable substitutions in $LIBS specifications. - Allow the emitter argument to a Builder() to be or expand to a list of emitter functions, which will be called in sequence. - Suppress null values in construction variables like $LIBS that use the internal _concat() function. - Remove .dll files from the construction variables searched for libraries that can be fed to Win32 compilers. From Chad Austin and Christoph Wiedemann: - Add support for a $RPATH variable to supply a list of directories to search for shared libraries when linking a program. Used by the GNU and IRIX linkers (gnulink and sgilink). From Charles Crain: - Restore the ability to do construction variable substitutions in all kinds of *PATH variables, even when the substitution returns a Node or other object. From Tom Epperly: - Allow the Java() Builder to take more than one source directory. From Ralf W. Grosse-Kunstleve: - Have SConsignFile() use, by default, a custom "dblite.py" that we can control and guarantee to work on all Python versions (or nearly so). From Jonathan Gurley: - Add support for the newer "ifort" versions of the Intel Fortran Compiler for Linux. From Bob Halley: - Make the new *FLAGS variable type work with copied Environments. From Chris Hoeppler: - Initialize the name of a Scanner.Classic scanner correctly. From James Juhasz: - Add support for the .dylib shared library suffix and the -dynamiclib linker option on Mac OS X. From Steven Knight: - Add an Execute() method for executing actions directly. - Support passing environment override keyword arguments to Command(). - Fix use of $MSVS_IGNORE_IDE_PATHS, which was broken when we added support for $MSVS_USE_MFC_DIRS last release. - Make env.Append() and env.Prepend() act like the underlying Python behavior when the variable being appended to is a UserList object. - Fix a regression that prevented the Command() global function in 0.95 from working with command-line strings as actions. - Fix checking out a file from a source code management system when the env.SourceCode() method was called with an individual file name or node, not a directory name or node. - Enhance the Task.make_ready() method to create a list of the out-of-date Nodes for the task for use by the wrapping interface. - Allow Scanners to pull the list of suffixes from the construction environment when the "skeys" keyword argument is a string containing a construction variable to be expanded. - Support new $CPPSUFFIXES, $DSUFFIXES $FORTRANSUFFIXES, and $IDLSUFFIXES. construction variables that contain the default list of suffixes to be scanned by a given type of scanner, allowing these suffix lists to be easily added to or overridden. - Speed up Node creation when calling a Builder by comparing whether two Environments are the same object, not if their underlying dictionaries are equivalent. - Add a --debug=explain option that reports the reason(s) why SCons thinks it must rebuild something. - Add support for functions that return platform-independent Actions to Chmod(), Copy(), Delete(), Mkdir(), Move() and Touch() files and/or directories. Like any other Actions, the returned Action object may be executed directly using the Execute() global function or env.Execute() environment method, or may be used as a Builder action or in an env.Command() action list. - Add support for the strfunction argument to all types of Actions: CommandAction, ListAction, and CommandGeneratorAction. - Speed up turning file system Nodes into strings by caching the values after we're finished reading the SConscript files. - Have ParseConfig() recognize and supporting adding the -Wa, -Wl, and -Wp, flags to ASFLAGS, LINKFLAGS and CPPFLAGS, respectively. - Change the .sconsign format and the checks for whether a Node is up-to-date to make dependency checks more efficient and correct. - Add wrapper Actions to SCons.Defaults for $ASCOM, $ASPPCOM, $LINKCOM, $SHLINKCOM, $ARCOM, $LEXCOM and $YACCCOM. This makes it possible to replace the default print behavior with a custom strfunction() for each of these. - When a Node has been built, don't walk the whole tree back to delete the parents's implicit dependencies, let returning up the normal Taskmaster descent take care of it for us. - Add documented support for separate target_scanner and source_scanner arguments to Builder creation, which allows different scanners to be applied to source files - Don't re-install or (re-generate) .h files when a subsidiary #included .h file changes. This eliminates incorrect circular dependencies with .h files generated from other source files. - Slim down the internal Sig.Calculator class by eliminating methods whose functionality is now covered by Node methods. - Document use of the target_factory and source_factory keyword arguments when creating Builder objects. Enhance Dir Nodes so that they can be created with user-specified Builder objects. - Don't blow up with stack trace when the external $PATH environment variable isn't set. - Make Builder calls return lists all the time, even if there's only one target. This keeps things consistent and easier to program to across platforms. - Add a Flatten() function to make it easier to deal with the Builders all returning lists of targets, not individual targets. - Performance optimizations in Node.FS.__doLookup(). - Man page fixes: formatting typos, misspellings, bad example. - User's Guide fixes: Fix the signatures of the various example *Options() calls. Triple-quote properly a multi-line Split example. - User's Guide additions: Chapter describing File and Directory Nodes. Section describing declarative nature of SCons functions in SConscript files. Better organization and clarification of points raised by Robert P. J. Day. Chapter describing SConf (Autoconf-like) functionality. Chapter describing how to install Python and SCons. Chapter describing Java builds. From Chris Murray: - Add a .win32 attribute to force file names to expand with Windows backslash path separators. - Fix escaping file names on command lines when the expansion is concatenated with another string. - Add support for Fortran 90 and Fortran 95. This adds $FORTRAN* variables that specify a default compiler, command-line, flags, etc. for all Fortran versions, plus separate $F90* and $F95* variables for when different compilers/flags/etc. must be specified for different Fortran versions. - Have individual tools that create libraries override the default $LIBPREFIX and $LIBSUFFIX values set by the platform. This makes it easier to use Microsoft Visual Studio tools on a CygWin platform. From Gary Oberbrunner: - Add a --debug=presub option to print actions prior to substitution. - Add a warning upon use of the override keywords "targets" and "sources" when calling Builders. These are usually mistakes which are otherwise silently (and confusingly) turned into construction variable overrides. - Try to find the ICL license file path name in the external environment and the registry before resorting to the hard-coded path name. - Add support for fetching command-line keyword=value arguments in order from an ARGLIST list. - Avoid stack traces when trying to read dangling symlinks. - Treat file "extensions" that only contain digits as part of the file basename. This supports version numbers as part of shared library names, for example. - Avoid problems when there are null entries (None or '') in tool lists or CPPPATH. - Add an example and explanation of how to use "tools = ['default', ..." when creating a construction environment. - Add a section describing File and Directory Nodes and some of their attributes and methods. - Have ParseConfig() add a returned -pthread flag to both $CCFLAGS and $LINKFLAGS. - Fix some test portability issues on Mac OS X (darwin). From Simon Perkins: - Fix a bug introduced in building shared libraries under MinGW. From Kevin Quick: - Handling SCons exceptions according to Pythonic standards. - Fix test/chained-build.py on systems that execute within one second. - Fix tests on systems where 'ar' warns about archive creation. From Anthony Roach: - Fix use of the --implicit-cache option with timestamp signatures. - If Visual Studio is installed, assume the C/C++ compiler, the linker and the MIDL compiler that comes with it are available, too. - Better error messages when evaluating a construction variable expansion yields a Python syntax error. - Change the generation of PDB files when using Visual Studio from compile time to link time. From sam th: - Allow SConf.CheckLib() to search a list of libraries, like the Autoconf AC_SEARCH_LIBS macro. - Allow the env.WhereIs() method to take a "reject" argument to let it weed out specific path names. From Christoph Wiedemann: - Add new Moc() and Uic() Builders for more explicit control over Qt builds, plus new construction variables to control them: $QT_AUTOSCAN, $QT_DEBUG, $QT_MOCCXXPREFIX, $QT_MOCCXXSUFFIX, $QT_MOCHPREFIX, $QT_MOCHSUFFIX, $QT_UICDECLPREFIX, $QT_UICDECLSUFFIX, $QT_UICIMPLPREFIX, $QT_UICIMPLSUFFIX and $QT_UISUFFIX. - Add a new single_source keyword argument for Builders that enforces a single source file on calls to the Builder. RELEASE 0.95 - Mon, 08 Mar 2004 06:43:20 -0600 From Chad Austin: - Replace print statements with calls to sys.stdout.write() so output lines stay together when -j is used. - Add portability fixes for a number of tests. - Accomodate the fact that Cygwin's os.path.normcase() lies about the underlying system being case-sensitive. - Fix an incorrect _concat() call in the $RCINCFLAGS definition for the mingw Tool. - Fix a problem with the msvc tool with Python versions prior to 2.3. - Add support for a "toolpath" Tool() and Environment keyword that allows Tool modules to be found in specified local directories. - Work around Cygwin Python's silly fiction that it's using a case-sensitive file system. - More robust handling of data in VCComponents.dat. - If the "env" command is available, spawn commands with the more general "env -" instead of "env -i". From Kerim Borchaev: - Fix a typo in a msvc.py's registry lookup: "VCComponents.dat", not "VSComponents.dat". From Chris Burghart: - Fix the ability to save/restore a PackageOption to a file. From Steve Christensen: - Update the MSVS .NET and MSVC 6.0/7.0 path detection. From David M. Cooke: - Make the Fortran scanner case-insensitive for the INCLUDE string. From Charles Crain: - If no version of MSVC is detected but the tool is specified, use the MSVC 6.0 paths by default. - Ignore any "6.1" version of MSVC found in the registry; this is a phony version number (created by later service packs?) and would throw off the logic if the user had any non-default paths configure. - Correctly detect if the user has independently configured the MSVC "include," "lib" or "path" in the registry and use the appropriate values. Previously, SCons would only use the values if all three were set in the registry. - Make sure side-effect nodes are prepare()d before building their corresponding target. - Preserve the ability to call BuildDir() multiple times with the same target and source directory arguments. From Andy Friesen: - Add support for the Digital Mars "D" programming language. From Scott Lystig Fritchie: - Fix the ability to use a custom _concat() function in the construction environment when calling _stripixes(). - Make the message about ignoring a missing SConscript file into a suppressable Warning, not a hard-coded sys.stderr.write(). - If a builder can be called multiple times for a target (because the sources and overrides are identical, or it's a builder with the "multi" flag set), allow the builder to be called through multiple environments so long as the builders have the same signature for the environments in questions (that is, they're the same action). From Bob Halley: - When multiple targets are built by a single action, retrieve all of them from cache, not just the first target, and exec the build command if any of the targets isn't present in the cache. From Zephaniah Hull: - Fix command-line ARGUMENTS with multiple = in them. From Steven Knight: - Fix EnsureSConsVersion() so it checks against the SCons version, not the Python version, on Pythons with sys.version_info. - Don't swallow the AttributeError when someone uses an expansion like $TARGET.bak, so we can supply a more informative error message. - Fix an odd double-quote escape sequence in the man page. - Fix looking up a naked drive letter as a directory (Dir('C:')). - Support using File nodes in the LIBS construction variable. - Allow the LIBS construction variable to be a single string or File node, not a list, when only one library is needed. - Fix typos in the man page: JAVACHDIR => JARCHDIR; add "for_signature" to the __call__() example in the "Variable Substitution" section. - Correct error message spellings of "non-existant" to "non-existent." - When scanning for libraries to link with, don't append $LIBPREFIXES or $LIBSUFFIXES values to the $LIBS values if they're already present. - Add a ZIPCOMPRESSION construction variable to control whether the internal Python action for the Zip Builder compresses the file or not. The default value is zipfile.ZIP_DEFLATED, which generates a compressed file. - Refactor construction variable expansion to support recursive expansion of variables (e.g. CCFLAGS = "$CCFLAGS -g") without going into an infinite loop. Support this in all construction variable overrides, as well as when copying Environments. - Fix calling Configure() from more than one subsidiary SConscript file. - Fix the env.Action() method so it returns the correct type of Action for its argument(s). - Fix specifying .class files as input to JavaH with the .class suffix when they weren't generated using the Java Builder. - Make the check for whether all of the objects going into a SharedLibrary() are shared work even if the object was built in a previous run. - Supply meaningful error messages, not stack traces, if we try to add a non-Node as a source, dependency, or ignored dependency of a Node. - Generate MSVS Project files that re-invoke SCons properly regardless of whether the file was built via scons.bat or scons.py. (Thanks to Niall Douglas for contributing code and testing.) - Fix TestCmd.py, runtest.py and specific tests to accomodate being run from directories whose paths include white space. - Provide a more useful error message if a construction variable expansion contains a syntax error during evaluation. - Fix transparent checkout of implicit dependency files from SCCS and RCS. - Added new --debug=count, --debug=memory and --debug=objects options. --debug=count and --debug=objects only print anything when run under Python 2.1 or later. - Deprecate the "overrides" keyword argument to Builder() creation in favor of using keyword argument values directly (like we do for builder execution and the like). - Always use the Builder overrides in substitutions, not just if there isn't a target-specific environment. - Add new "rsrcpath" and "rsrcdir" and attributes to $TARGET/$SOURCE, so Builder command lines can find things in Repository source directories when using BuildDir. - Fix the M4 Builder so that it chdirs to the Repository directory when the input file is in the source directory of a BuildDir. - Save memory at build time by allowing Nodes to delete their build environments after they've been built. - Add AppendUnique() and PrependUnique() Environment methods, which add values to construction variables like Append() and Prepend() do, but suppress any duplicate elements in the list. - Allow the 'qt' tool to still be used successfully from a copied Environment. The include and library directories previously ended up having the same string re-appended to the end, yielding an incorrect path name. - Supply a more descriptive error message when the source for a target can't be found. - Initialize all *FLAGS variables with objects do the right thing with appending flags as strings or lists. - Make things like ${TARGET.dir} work in *PATH construction variables. - Allow a $MSVS_USE_MFC_DIRS construction variable to control whether ATL and MFC directories are included in the default INCLUDE and LIB paths. - Document the dbm_module argument to the SConsignFile() function. From Vincent Risi: - Add support for the bcc32, ilink32 and tlib Borland tools. From Anthony Roach: - Supply an error message if the user tries to configure a BuildDir for a directory that already has one. - Remove documentation of the still-unimplemented -e option. - Add -H help text listing the legal --debug values. - Don't choke if a construction variable is a non-string value. - Build Type Libraries in the target directory, not the source directory. - Add an appendix to the User's Guide showing how to accomplish various common tasks in Python. From Greg Spencer: - Add support for Microsoft Visual Studio 2003 (version 7.1). - Evaluate $MSVSPROJECTSUFFIX and $MSVSSOLUTIONSUFFIX when the Builder is invoked, not when the tool is initialized. From Christoph Wiedemann: - When compiling Qt, make sure the moc_*.cc files are compiled using the flags from the environment used to specify the target, not the environment that first has the Qt Builders attached. RELEASE 0.94 - Fri, 07 Nov 2003 05:29:48 -0600 From Hartmut Goebel: - Add several new types of canned functions to help create options: BoolOption(), EnumOption(), ListOption(), PackageOption(), PathOption(). From Steven Knight: - Fix use of CPPDEFINES with C++ source files. - Fix env.Append() when the operand is an object with a __cmp__() method (like a Scanner instance). - Fix subclassing the Environment and Scanner classes. - Add BUILD_TARGETS, COMMAND_LINE_TARGETS and DEFAULT_TARGETS variables. From Steve Leblanc: - SGI fixes: Fix C++ compilation, add a separate Tool/sgic++.py module. From Gary Oberbrunner: - Fix how the man page un-indents after examples in some browsers. From Vincent Risi: - Fix the C and C++ tool specifications for AIX. RELEASE 0.93 - Thu, 23 Oct 2003 07:26:55 -0500 From J.T. Conklin: - On POSIX, execute commands with the more modern os.spawnvpe() function, if it's available. - Scan .S, .spp and .SPP files for C preprocessor dependencies. - Refactor the Job.Parallel() class to use a thread pool without a condition variable. This improves parallel build performance and handles keyboard interrupts properly when -j is used. From Charles Crain: - Add support for a JARCHDIR variable to control changing to a directory using the jar -C option. - Add support for detecting Java manifest files when using jar, and specifying them using the jar m flag. - Fix some Python 2.2 specific things in various tool modules. - Support directories as build sources, so that a rebuild of a target can be triggered if anything underneath the directory changes. - Have the scons.bat and scons.py files look for the SCons modules in site-packages as well. From Christian Engel: - Support more flexible inclusion of separate C and C++ compilers. - Use package management tools on AIX and Solaris to find where the comilers are installed, and what version they are. - Add support for CCVERSION and CXXVERSION variables for a number of C and C++ compilers. From Sergey Fogel: - Add test cases for the new capabilities to run bibtex and to rerun latex as needed. From Ralf W. Grosse-Kunstleve: - Accomodate anydbm modules that don't have a sync() method. - Allow SConsignFile() to take an argument specifying the DBM module to be used. From Stephen Kennedy: - Add support for a configurable global .sconsign.dbm file which can be used to avoid cluttering each directory with an individual .sconsign file. From John Johnson: - Fix (re-)scanning of dependencies in generated or installed header files. From Steven Knight: - The -Q option suppressed too many messages; fix it so that it only suppresses the Reading/Building messages. - Support #include when there's no space before the opening quote or angle bracket. - Accomodate alphanumeric version strings in EnsurePythonVersion(). - Support arbitrary expansion of construction variables within file and directory arguments to Builder calls and Environment methods. - Add Environment-method versions of the following global functions: Action(), AddPostAction(), AddPreAction(), Alias(), Builder(), BuildDir(), CacheDir(), Clean(), Configure(), Default(), EnsurePythonVersion(), EnsureSConsVersion(), Environment(), Exit(), Export(), FindFile(), GetBuildPath(), GetOption(), Help(), Import(), Literal(), Local(), Platform(), Repository(), Scanner(), SConscriptChdir(), SConsignFile(), SetOption(), SourceSignatures(), Split(), TargetSignatures(), Tool(), Value(). - Add the following global functions that correspond to the same-named Environment methods: AlwaysBuild(), Command(), Depends(), Ignore(), Install(), InstallAs(), Precious(), SideEffect() and SourceCode(). - Add the following global functions that correspond to the default Builder methods supported by SCons: CFile(), CXXFile(), DVI(), Jar(), Java(), JavaH(), Library(), M4(), MSVSProject(), Object(), PCH(), PDF(), PostScript(), Program(), RES(), RMIC(), SharedLibrary(), SharedObject(), StaticLibrary(), StaticObject(), Tar(), TypeLibrary() and Zip(). - Rearrange the man page to show construction environment methods and global functions in the same list, and to explain the difference. - Alphabetize the explanations of the builder methods in the man page. - Rename the Environment.Environment class to Enviroment.Base. Allow the wrapping interface to extend an Environment by using its own subclass of Environment.Base and setting a new Environment.Environment variable as the calling entry point. - Deprecate the ParseConfig() global function in favor of a same-named construction environment method. - Allow the Environment.WhereIs() method to take explicit path and pathext arguments (like the underlying SCons.Util.WhereIs() function). - Remove the long-obsolete {Get,Set}CommandHandler() functions. - Enhance env.Append() to suppress null values when appropriate. - Fix ParseConfig() so it works regardless of initial construction variable values. Extend CheckHeader(), CheckCHeader(), CheckCXXHeader() and CheckLibWithHeader() to accept a list of header files that will be #included in the test. The last one in the list is assumed to be the one being checked for. (Prototype code contributed by Gerard Patel and Niall Douglas). - Supply a warning when -j is used and threading isn't built in to the current version of Python. - First release of the User's Guide (finally, and despite a lot of things still missing from it...). From Clark McGrew: - Generalize the action for .tex files so that it will decide whether a file is TeX or LaTeX, check the .aux output to decide if it should run bibtex, and check the .log output to re-run LaTeX if needed. From Bram Moolenaar: - Split the non-SCons-specific functionality from SConf.py to a new, re-usable Conftest.py module. From Gary Oberbrunner: - Allow a directory to be the target or source or dependency of a Depends(), Ignore(), Precious() or SideEffect() call. From Gerard Patel: - Use the %{_mandir} macro when building our RPM package. From Marko Rauhamaa: - Have the closing message say "...terminated because of errors" if there were any. From Anthony Roach: - On Win32 systems, only use "rm" to delete files if Cygwin is being used. ("rm" doesn't understand Win32-format path names.) From Christoph Wiedemann: - Fix test/SWIG.py to find the Python include directory in all cases. - Fix a bug in detection of Qt installed on the local system. - Support returning Python 2.3 BooleanType values from Configure checks. - Provide an error message if someone mistakenly tries to call a Configure check from within a Builder function. - Support calling a Builder when a Configure context is still open. - Handle interrupts better by eliminating all try:-except: blocks which caught any and all exceptions, including KeyboardInterrupt. - Add a --duplicate= option to control how files are duplicated. RELEASE 0.92 - Wed, 20 Aug 2003 03:45:28 -0500 From Charles Crain and Gary Oberbrunner: - Fix Tool import problems with the Intel and PharLap linkers. From Steven Knight - Refactor the DictCmdGenerator class to be a Selector subclass. - Allow the DefaultEnvironment() function to take arguments and pass them to instantiation of the default construction environment. - Update the Debian package so it uses Python 2.2 and more closely resembles the currently official Debian packaging info. From Gerard Patel - When the yacc -d flag is used, take the .h file base name from the target .c file, not the source (matching what yacc does). RELEASE 0.91 - Thu, 14 Aug 2003 13:00:44 -0500 From Chad Austin: - Support specifying a list of tools when calling Environment.Copy(). - Give a Value Nodes a timestamp of the system time when they're created, so they'll work when using timestamp-based signatures. - Add a DefaultEnvironment() function that only creates a default environment on-demand (for fetching source files, e.g.). - Portability fix for test/M4.py. From Steven Knight: - Tighten up the scons -H help output. - When the input yacc file ends in .yy and the -d flag is specified, recognize that a .hpp file (not a .h file) will be created. - Make builder prefixes work correctly when deducing a target from a source file name in another directory. - Documentation fixes: typo in the man page; explain up-front about not propagating the external environment. - Use "cvs co -d" instead of "cvs co -p >" when checking out something from CVS with a specified module name. This avoids zero-length files when there is a checkout error. - Add an "sconsign" script to print the contents of .sconsign files. - Speed up maintaining the various lists of Node children by using dictionaries to avoid "x in list" searches. - Cache the computed list of Node children minus those being Ignored so it's only calculated once. - Fix use of the --cache-show option when building a Program() (or using any other arbitrary action) by making sure all Action instances have strfunction() methods. - Allow the source of Command() to be a directory. - Better error handling of things like raw TypeErrors in SConscripts. - When installing using "setup.py install --prefix=", suppress the distutils warning message about adding the (incorrect) library directory to your search path. - Correct the spelling of the "validater" option to "validator." Add a DeprecatedWarning when the old spelling is used. - Allow a Builder's emitter to be a dictionary that maps source file suffixes to emitter functions, using the suffix of the first file in the source list to pick the right one. - Refactor the creation of the Program, *Object and *Library Builders so that they're moved out of SCons.Defaults and created on demand. - Don't split SConscript file names on white space. - Document the SConscript function's "dirs" and "name" keywords. - Remove the internal (and superfluous) SCons.Util.argmunge() function. - Add /TP to the default CXXFLAGS for msvc, so it can compile all of the suffixes we use as C++ files. - Allow the "prefix" and "suffix" attributes of a Builder to be callable objects that return generated strings, or dictionaries that map a source file suffix to the right prefix/suffix. - Support a MAXLINELINELENGTH construction variable on Win32 systems to control when a temporary file is used for long command lines. - Make how we build .rpm packages not depend on the installation locations from the distutils being used. - When deducing a target Node, create it directly from the first source Node, not by trying to create the right string to pass to arg2nodes(). - Add support for SWIG. From Bram Moolenaar: - Test portability fixes for FreeBSD. From Gary Oberbrunner: - Report the target being built in error messages when building multiple sources from different extensions, or when the target file extension can't be deduced, or when we don't have an action for a file suffix. - Provide helpful error messages when the arguments to env.Install() are incorrect. - Fix the value returned by the Node.prevsiginfo() method to conform to a previous change when checking whether a node is current. - Supply a stack trace if the Taskmaster catches an exception. - When using a temporary file for a long link line on Win32 systems, (also) print the command line that is being executed through the temporary file. - Initialize the LIB environment variable when using the Intel compiler (icl). - Documentation fixes: better explain the AlwaysBuild() function. From Laurent Pelecq: - When the -debug=pdb option is specified, use pdb.Pdb().runcall() to call pdb directly, don't call Python recursively. From Ben Scott: - Add support for a platform-independent CPPDEFINES variable. From Christoph Wiedemann: - Have the g++ Tool actually use g++ in preference to c++. - Have the gcc Tool actually use gcc in preference to cc. - Add a gnutools.py test of the GNU tool chain. - Be smarter about linking: use $CC by default and $CXX only if we're linking with any C++ objects. - Avoid SCons hanging when a piped command has a lot of output to read. - Add QT support for preprocessing .ui files into .c files. RELEASE 0.90 - Wed, 25 Jun 2003 14:24:52 -0500 From Chad Austin: - Fix the _concat() documentation, and add a test for it. - Portability fixes for non-GNU versions of lex and yacc. From Matt Balvin: - Fix handling of library prefixes when the subdirectory matches the prefix. From Timothee Bessett: - Add an M4 Builder. From Charles Crain: - Use '.lnk' as the suffix on the temporary file for linking long command lines (necessary for the Phar Lap linkloc linker). - Save non-string Options values as their actual type. - Save Options string values that contain a single quote correctly. - Save any Options values that are changed from the default Environment values, not just ones changed on the command line or in an Options file. - Make closing the Options file descriptor exception-safe. From Steven Knight: - SCons now enforces (with an error) that construction variables must have the same form as valid Python identifiers. - Fix man page bugs: remove duplicate AddPostAction() description; document no_import_lib; mention that CPPFLAGS does not contain $_CPPINCFLAGS; mention that F77FLAGS does not contain $_F77INCFLAGS; mention that LINKFLAGS and SHLINKFLAGS contains neither $_LIBFLAGS nor $_LIBDIRFLAGS. - Eliminate a dependency on the distutils.fancy_getopt module by copying and pasting its wrap_text() function directly. - Make the Script.Options() subclass match the underlying base class implementation. - When reporting a target is up to date, quote the target like make (backquote-quote) instead of with double quotes. - Fix handling of ../* targets when using -U, -D or -u. From Steve Leblanc: - Don't update the .sconsign files when run with -n. From Gary Oberbrunner: - Add support for the Intel C Compiler (icl.exe). From Anthony Roach - Fix Import('*'). From David Snopek - Fix use of SConf in paths with white space in them. - Add CheckFunc and CheckType functionality to SConf. - Fix use of SConf with Builders that return a list of nodes. From David Snopek and Christoph Wiedemann - Fix use of the SConf subsystem with SConscriptChdir(). From Greg Spencer - Check for the existence of MS Visual Studio on disk before using it, to avoid getting fooled by leftover junk in the registry. - Add support for MSVC++ .NET. - Add support for MS Visual Studio project files (DSP, DSW, SLN and VCPROJ files). From Christoph Wiedemann - SConf now works correctly when the -n and -q options are used. RELEASE 0.14 - Wed, 21 May 2003 05:16:32 -0500 From Chad Austin: - Use .dll (not .so) for shared libraries on Cygwin; use -fPIC when compiling them. - Use 'rm' to remove files under Cygwin. - Add a PLATFORM variable to construction environments. - Remove the "platform" argument from tool specifications. - Propogate PYTHONPATH when running the regression tests so distutils can be found in non-standard locations. - Using MSVC long command-line linking when running Cygwin. - Portability fixes for a lot of tests. - Add a Value Node class for dependencies on in-core Python values. From Allen Bierbaum: - Pass an Environment to the Options validator method, and add an Options.Save() method. From Steve Christensen: - Add an optional sort function argument to the GenerateHelpText() Options function. - Evaluate the "varlist" variables when computing the signature of a function action. From Charles Crain: - Parse the source .java files for class names (including inner class names) to figure out the target .class files that will be created. - Make Java support work with Repositories and SConscriptChdir(0). - Pass Nodes, not strings, to Builder emitter functions. - Refactor command-line interpolation and signature calculation so we can use real Node attributes. From Steven Knight: - Add Java support (javac, javah, jar and rmic). - Propagate the external SYSTEMROOT environment variable into ENV on Win32 systems, so external commands that use sockets will work. - Add a .posix attribute to PathList expansions. - Check out CVS source files using POSIX path names (forward slashes as separators) even on Win32. - Add Node.clear() and Node.FS.Entry.clear() methods to wipe out a Node's state, allowing it to be re-evaluated by continuous integration build interfaces. - Change the name of the Set{Build,Content}SignatureType() functions to {Target,Source}Signatures(). Deprecate the old names but support them for backwards compatibility. - Add internal SCons.Node.FS.{Dir,File}.Entry() methods. - Interpolate the null string if an out-of-range subscript is used for a construction variable. - Fix the internal Link function so that it properly links or copies files in subsidiary BuildDir directories. - Refactor the internal representation of a single execution instance of an action to eliminate redundant signature calculations. - Eliminate redundant signature calculations for Nodes. - Optimize out calling hasattr() before accessing attributes. - Say "Cleaning targets" (not "Building...") when the -c option is used. From Damyan Pepper: - Quote the "Entering directory" message like Make. From Stefan Reichor: - Add support for using Ghostscript to convert Postscript to PDF files. From Anthony Roach: - Add a standalone "Alias" function (separate from an Environment). - Make Export() work for local variables. - Support passing a dictionary to Export(). - Support Import('*') to import everything that's been Export()ed. - Fix an undefined exitvalmap on Win32 systems. - Support new SetOption() and GetOption() functions for setting various command-line options from with an SConscript file. - Deprecate the old SetJobs() and GetJobs() functions in favor of using the new generic {Set,Get}Option() functions. - Fix a number of tests that searched for a Fortran compiler using the external PATH instead of what SCons would use. - Fix the interaction of SideEffect() and BuildDir() so that (for example) PDB files get put correctly in a BuildDir(). From David Snopek: - Contribute the "Autoscons" code for Autoconf-like checking for the existence of libraries, header files and the like. - Have the Tool() function add the tool name to the $TOOLS construction variable. From Greg Spencer: - Support the C preprocessor #import statement. - Allow the SharedLibrary() Builder on Win32 systems to be able to register a newly-built dll using regsvr32. - Add a Builder for Windows type library (.tlb) files from IDL files. - Add an IDL scanner. - Refactor the Fortran, C and IDL scanners to share common logic. - Add .srcpath and .srcdir attributes to $TARGET and $SOURCE. From Christoph Wiedemann: - Integrate David Snopek's "Autoscons" code as the new SConf configuration subsystem, including caching of values between runs (using normal SCons dependency mechanisms), tests, and documentation. RELEASE 0.13 - Mon, 31 Mar 2003 20:22:00 -0600 From Charles Crain: - Fix a bug when BuildDir(duplicate=0) is used and SConscript files are called from within other SConscript files. - Support (older) versions of Perforce which don't set the Windows registry. RELEASE 0.12 - Thu, 27 Mar 2003 23:52:09 -0600 From Charles Crain: - Added support for the Perforce source code management system. - Fix str(Node.FS) so that it returns a path relative to the calling SConscript file's directory, not the top-level directory. - Added support for a separate src_dir argument to SConscript() that allows explicit specification of where the source files for an SConscript file can be found. - Support more easily re-usable flavors of command generators by calling callable variables when strings are expanded. From Steven Knight: - Added an INSTALL construction variable that can be set to a function to control how the Install() and InstallAs() Builders install files. The default INSTALL function now copies, not links, files. - Remove deprecated features: the "name" argument to Builder objects, and the Environment.Update() method. - Add an Environment.SourceCode() method to support fetching files from source code systems. Add factory methods that create Builders to support BitKeeper, CVS, RCS, and SCCS. Add support for fetching files from RCS or SCCS transparently (like GNU Make). - Make the internal to_String() function more efficient. - Make the error message the same as other build errors when there's a problem unlinking a target file in preparation for it being built. - Make TARGET, TARGETS, SOURCE and SOURCES reserved variable names and warn if the user tries to set them in a construction environment. - Add support for Tar and Zip files. - Better documentation of the different ways to export variables to a subsidiary SConscript file. Fix documentation bugs in a tools example, places that still assumed SCons split strings on white space, and typos. - Support fetching arbitrary files from the TARGETS or SOURCES lists (e.g. ${SOURCES[2]}) when calculating the build signature of a command. - Don't silently swallow exceptions thrown by Scanners (or other exceptions while finding a node's dependent children). - Push files to CacheDir() before calling the superclass built() method (which may clear the build signature as part of clearing cached implicit dependencies, if the file has a source scanner). (Bug reported by Jeff Petkau.) - Raise an internal error if we attempt to push a file to CacheDir() with a build signature of None. - Add an explicit Exit() function for terminating early. - Change the documentation to correctly describe that the -f option doesn't change to the directory in which the specified file lives. - Support changing directories locally with SConscript directory path names relative to any SConstruct file specified with -f. This allows you to build in another directory by simply changing there and pointing at the SConstruct file in another directory. - Change the default SConscriptChdir() behavior to change to the SConscript directory while it's being read. - Fix an exception thrown when the -U option was used with no Default() target specified. - Fix -u so that it builds things in corresponding build directories when used in a source directory. From Lachlan O'Dea: - Add SharedObject() support to the masm tool. - Fix WhereIs() to return normalized paths. From Jeff Petkau: - Don't copy a built file to a CacheDir() if it's already there. - Avoid partial copies of built files in a CacheDir() by copying to a temporary file and renaming. From Anthony Roach: - Fix incorrect dependency-cycle errors when an Aliased source doesn't exist. RELEASE 0.11 - Tue, 11 Feb 2003 05:24:33 -0600 From Chad Austin: - Add support for IRIX and the SGI MIPSPro tool chain. - Support using the MSVC tool chain when running Cygwin Python. From Michael Cook: - Avoid losing signal bits in the exit status from a command, helping terminate builds on interrupt (CTRL+C). From Charles Crain: - Added new AddPreAction() and AddPostAction() functions that support taking additional actions before or after building specific targets. - Add support for the PharLap ETS tool chain. From Steven Knight: - Allow Python function Actions to specify a list of construction variables that should be included in the Action's signature. - Allow libraries in the LIBS variable to explicitly include the prefix and suffix, even when using the GNU linker. (Bug reported by Neal Becker.) - Use DOS-standard CR-LF line endings in the scons.bat file. (Bug reported by Gary Ruben.) - Doc changes: Eliminate description of deprecated "name" keyword argument from Builder definition (reported by Gary Ruben). - Support using env.Append() on BUILDERS (and other dictionaries). (Bug reported by Bj=F6rn Bylander.) - Setting the BUILDERS construction variable now properly clears the previous Builder attributes from the construction Environment. (Bug reported by Bj=F6rn Bylander.) - Fix adding a prefix to a file when the target isn't specified. (Bug reported by Esa Ilari Vuokko.) - Clean up error messages from problems duplicating into read-only BuildDir directories or into read-only files. - Add a CommandAction.strfunction() method, and add an "env" argument to the FunctionAction.strfunction() method, so that all Action objects have strfunction() methods, and the functions for building and returning a string both take the same arguments. - Add support for new CacheDir() functionality to share derived files between builds, with related options --cache-disable, --cache-force, and --cache-show. - Change the default behavior when no targets are specified to build everything in the current directory and below (like Make). This can be disabled by specifying Default(None) in an SConscript. - Revamp SCons installation to fix a case-sensitive installation on Win32 systems, and to add SCons-specific --standard-lib, --standalone-lib, and --version-lib options for easier user control of where the libraries get installed. - Fix the ability to directly import and use Platform and Tool modules that have been implicitly imported into an Environment(). - Add support for allowing an embedding interface to annotate a node when it's created. - Extend the SConscript() function to accept build_dir and duplicate keyword arguments that function like a BuildDir() call. From Steve Leblanc: - Fix the output of -c -n when directories are involved, so it matches -c. From Anthony Roach: - Use a different shared object suffix (.os) when using gcc so shared and static objects can exist side-by-side in the same directory. - Allow the same object files on Win32 to be linked into either shared or static libraries. - Cache implicit cache values when using --implicit-cache. RELEASE 0.10 - Thu, 16 Jan 2003 04:11:46 -0600 From Derrick 'dman' Hudson: - Support Repositories on other file systems by symlinking or copying files when hard linking won't work. From Steven Knight: - Remove Python bytecode (*.pyc) files from the scons-local packages. - Have FunctionActions print a description of what they're doing (a representation of the Python call). - Fix the Install() method so that, like other actions, it prints what would have happened when the -n option is used. - Don't create duplicate source files in a BuildDir when the -n option is used. - Refactor the Scanner interface to eliminate unnecessary Scanner calls and make it easier to write efficient scanners. - Added a "recursive" flag to Scanner creation that specifies the Scanner should be invoked recursively on dependency files returned by the scanner. - Significant performance improvement from using a more efficient check, throughout the code, for whether a Node has a Builder. - Fix specifying only the source file to MultiStepBuilders such as the Program Builder. (Bug reported by Dean Bair.) - Fix an exception when building from a file with the same basename as the subdirectory in which it lives. (Bug reported by Gerard Patel.) - Fix automatic deduction of a target file name when there are multiple source files specified; the target is now deduced from just the first source file in the list. - Documentation fixes: better initial explanation of SConscript files; fix a misformatted "table" in the StaticObject explanation. From Steven Knight and Steve Leblanc: - Fix the -c option so it will remove symlinks. From Steve Leblanc: - Add a Clean() method to support removing user-specified targets when using the -c option. - Add a development script for running SCons through PyChecker. - Clean up things found by PyChecker (mostly unnecessary imports). - Add a script to use HappyDoc to create HTML class documentation. From Lachlan O'Dea: - Make the Environment.get() method return None by default. From Anthony Roach: - Add SetJobs() and GetJobs() methods to allow configuration of the number of default jobs (still overridden by -j). - Convert the .sconsign file format from ASCII to a pickled Python data structure. - Error message cleanups: Made consistent the format of error messages (now all start with "scons: ***") and warning messages (now all start with "scons: warning:"). Caught more cases with the "Do not know how to build" error message. - Added support for the MinGW tool chain. - Added a --debug=includes option. RELEASE 0.09 - Thu, 5 Dec 2002 04:48:25 -0600 From Chad Austin: - Add a Prepend() method to Environments, to append values to the beginning of construction variables. From Matt Balvin: - Add long command-line support to the "lib" Tool (Microsoft library archiver), too. From Charles Crain: - Allow $$ in a string to be passed through as $. - Support file names with odd characters in them. - Add support for construction variable substition on scanner directories (in CPPPATH, F77PATH, LIBPATH, etc.). From Charles Crain and Steven Knight: - Add Repository() functionality, including the -Y option. From Steven Knight: - Fix auto-deduction of target names so that deduced targets end up in the same subdirectory as the source. - Don't remove source files specified on the command line! - Suport the Intel Fortran Compiler (ifl.exe). - Supply an error message if there are no command-line or Default() targets specified. - Fix the ASPPCOM values for the GNU assembler. (Bug reported by Brett Polivka.) - Fix an exception thrown when a Default() directory was specified when using the -U option. - Issue a warning when -c can't remove a target. - Eliminate unnecessary Scanner calls by checking for the existence of a file before scanning it. (This adds a generic hook to check an arbitrary condition before scanning.) - Add explicit messages to tell when we're "Reading SConscript files ...," "done reading SConscript files," "Building targets," and "done building targets." Add a -Q option to supress these. - Add separate $SHOBJPREFIX and $SHOBJSUFFIX construction variables (by default, the same as $OBJPREFIX and $OBJSUFFIX). - Add Make-like error messages when asked to build a source file, and before trying to build a file that doesn't have all its source files (including when an invalid drive letter is used on WIN32). - Add an scons-local-{version} package (in both .tar.gz and .zip flavors) to help people who want to ship SCons as a stand-alone build tool in their software packages. - Prevent SCons from unlinking files in certain situations when the -n option is used. - Change the name of Tool/lib.py to Tool/mslib.py. From Steven Knight and Anthony Roach: - Man page: document the fact that Builder calls return Node objects. From Steve LeBlanc: - Refactor option processing to use our own version of Greg Ward's Optik module, modified to run under Python 1.5.2. - Add a ParseConfig() command to modify an environment based on parsing output from a *-config command. From Jeff Petkau: - Fix interpretation of '#/../foo' on Win32 systems. From Anthony Roach: - Fixed use of command lines with spaces in their arguments, and use of Nodes with spaces in their string representation. - Make access and modification times of files in a BuildDir match the source file, even when hard linking isn't available. - Make -U be case insensitive on Win32 systems. - Issue a warning and continue when finding a corrupt .sconsign file. - Fix using an alias as a dependency of a target so that if one of the alias' dependencies gets rebuilt, the resulting target will, too. - Fix differently ordered targets causing unnecessary rebuilds on case insensitive systems. - Use os.system() to execute external commands whenever the "env" utility is available, which is much faster than fork()/exec(), and fixes the -j option on several platforms. - Fix use of -j with multiple targets. - Add an Options() object for friendlier accomodation of command- line arguments. - Add support for Microsoft VC++ precompiled header (.pch) files, debugger (.pdb) files, and resource (.rc) files. - Don't compute the $_CPPINCFLAGS, $_F77INCFLAGS, $_LIBFLAGS and $_LIBDIRFLAGS variables each time a command is executed, define them so they're computed only as needed. Add a new _concat function to the Environment that allows people to define their own similar variables. - Fix dependency scans when $LIBS is overridden. - Add EnsurePythonVersion() and EnsureSConsVersion() functions. - Fix the overly-verbose stack trace on ListBuilder build errors. - Add a SetContentSignatureType() function, allowing use of file timestamps instead of MD5 signatures. - Make -U and Default('source') fail gracefully. - Allow the File() and Dir() methods to take a path-name string as the starting directory, in addition to a Dir object. - Allow the command handler to be selected via the SPAWN, SHELL and ESCAPE construction variables. - Allow construction variables to be overridden when a Builder is called. From sam th: - Dynamically check for the existence of utilities with which to initialize Environments by default. RELEASE 0.08 - Mon, 15 Jul 2002 12:08:51 -0500 From Charles Crain: - Fixed a bug with relative CPPPATH dirs when using BuildDir(). (Bug reported by Bob Summerwill.) - Added a warnings framework and a --warn option to enable or disable warnings. - Make the C scanner warn users if files referenced by #include directives cannot be found and --warn=dependency is specified. - The BUILDERS construction variable should now be a dictionary that maps builder names to actions. Existing uses of lists, and the Builder name= keyword argument, generate warnings about use of deprecated features. - Removed the "shared" keyword argument from the Object and Library builders. - Added separated StaticObject, SharedObject, StaticLibrary and SharedLibrary builders. Made Object and Library synonyms for StaticObject and StaticLibrary, respectively. - Add LIBS and LIBPATH dependencies for shared libraries. - Removed support for the prefix, suffix and src_suffix arguments to Builder() to be callable functions. - Fix handling file names with multiple dots. - Allow a build directory to be outside of the SConstruct tree. - Add a FindFile() function that searches for a file node with a specified name. - Add $CPPFLAGS to the shared-object command lines for g++ and gcc. From Charles Crain and Steven Knight: - Add a "tools=" keyword argument to Environment instantiation, and a separate Tools() method, for more flexible specification of tool-specific environment changes. From Steven Knight: - Add a "platform=" keyword argument to Environment instantiation, and a separate Platform() method, for more flexible specification of platform-specific environment changes. - Updated README instructions and setup.py code to catch an installation failure from not having distutils installed. - Add descriptions to the -H help text for -D, -u and -U so people can tell them apart. - Remove the old feature of automatically splitting strings of file names on white space. - Add a dependency Scanner for native Fortran "include" statements, using a new "F77PATH" construction variable. - Fix C #include scanning to detect file names with characters like '-' in them. - Add more specific version / build output to the -v option. - Add support for the GNU as, Microsoft masm, and nasm assemblers. - Allow the "target" argument to a Builder call to be omitted, in which case the target(s) are deduced from the source file(s) and the Builder's specified suffix. - Add a tar archive builder. - Add preliminary support for the OS/2 Platform, including the icc and ilink Tools. From Jeff Petkau: - Fix --implicit-cache if the scanner returns an empty list. From Anthony Roach: - Add a "multi" keyword argument to Builder creation that specifies it's okay to call the builder multiple times for a target. - Set a "multi" on Aliases so multiple calls will append to an Alias. - Fix emitter functions' use of path names when using BuildDir or in subdirectories. - Fix --implicit-cache causing redundant rebuilds when the header file list changed. - Fix --implicit-cache when a file has no implicit dependencies and its source is generated. - Make the drive letters on Windows always be the same case, so that changes in the case of drive letters don't cause a rebuild. - Fall back to importing the SCons.TimeStamp module if the SCons.MD5 module can't be imported. - Fix interrupt handling to guarantee that a single interrupt will halt SCons both when using -j and not. - Fix .sconsign signature storage so that output files of one build can be safely used as input files to another build. - Added a --debug=time option to print SCons execution times. - Print an error message if a file can't be unlinked before being built, rather than just silently terminating the build. - Add a SideEffect() method that can be used to tell the build engine that a given file is created as a side effect of building a target. A file can be specified as a side effect of more than one build comand, in which case the commands will not be executed simultaneously. - Significant performance gains from not using our own version of the inefficient stock os.path.splitext() method, caching source suffix computation, code cleanup in MultiStepBuilder.__call__(), and replicating some logic in scons_subst(). - Add --implicit-deps-changed and --implicit-deps-unchanged options. - Add a GetLaunchDir() function. - Add a SetBuildSignatureType() function. From Zed Shaw: - Add an Append() method to Environments, to append values to construction variables. - Change the name of Update() to Replace(). Keep Update() as a deprecated synonym, at least for now. From Terrel Shumway: - Use a $PYTHON construction variable, initialized to sys.executable, when using Python to build parts of the SCons packages. - Use sys.prefix, not sys.exec_prefix, to find pdb.py. RELEASE 0.07 - Thu, 2 May 2002 13:37:16 -0500 From Chad Austin: - Changes to build SCons packages on IRIX (and other *NIces). - Don't create a directory Node when a file already exists there, and vice versa. - Add 'dirs' and 'names' keyword arguments to SConscript for easier specification of subsidiary SConscript files. From Charles Crain: - Internal cleanup of environment passing to function Actions. - Builders can now take arbitrary keyword arguments to create attributes to be passed to: command generator functions, FunctionAction functions, Builder emitter functions (below), and prefix/suffix generator functions (below). - Command generator functions can now return ANYTHING that can be converted into an Action (a function, a string, a CommandGenerator instance, even an ActionBase instance). - Actions now call get_contents() with the actual target and source nodes used for the build. - A new DictCmdGenerator class replaces CompositeBuilder to support more flexible Builder behavior internally. - Builders can now take an emitter= keyword argument. An emitter is a function that takes target, source, and env argument, then return a 2-tuple of (new sources, new targets). The emitter is called when the Builder is __call__'ed, allowing a user to modify source and target lists. - The prefix, suffix and src_suffix Builder arguments now take a callable as well a string. The callable is passed the Environment and any extra Builder keyword arguments and is expected to return the appropriate prefix or suffix. - CommandActions can now be a string, a list of command + argument strings, or a list of commands (strings or lists). - Added shared library support. The Object and Library Builders now take a "shared=1" keyword argument to specify that a shared object or shared library should be built. It is an error to try to build static objects into a shared library or vice versa. - Win32 support for .def files has been added. Added the Win32-specific construction variables $WIN32DEFPREFIX, $WIN32DEFSUFFIX, $WIN32DLLPREFIX and $WIN32IMPLIBPREFIX. When building a .dll, the new construction variable $WIN32_INSERT_DEF, controls whether the appropriately-named .def file is inserted into the target list (if not already present). A .lib file is always added to a Library build if not present in the list of targets. - ListBuilder now passes all targets to the action, not just the first. - Fix so that -c now deletes generated yacc .h files. - Builder actions and emitter functions can now be initialized, through construction variables, to things other than strings. - Make top-relative '#/dir' lookups work like '#dir'. - Fix for relative CPPPATH directories in subsidiary SConscript files (broken in 0.06). - Add a for_signature argument to command generators, so that generators that need to can return distinct values for the command signature and for executing the command. From Alex Jacques: - Create a better scons.bat file from a py2bat.py script on the Python mailing list two years ago (modeled after pl2bat.pl). From Steven Knight: - Fix so that -c -n does *not* remove the targets! - Man page: Add a hierarchical libraries + Program example. - Support long MSVC linker command lines through a builder action that writes to a temporary file and uses the magic MSVC "link @file" argument syntax if the line is longer than 2K characters. - Fix F77 command-line options on Win32 (use /Fo instead of -o). - Use the same action to build from .c (lower case) and .C (upper case) files on case-insensitive systems like Win32. - Support building a PDF file directly from a TeX or LaTeX file using pdftex or pdflatex. - Add a -x option to runtest.py to specify the script being tested. A -X option indicates it's an executable, not a script to feed to the Python interpreter. - Add a Split() function (identical to SCons.Util.argmunge()) for use in the next release, when Builders will no longer automatically split strings on white space. From Steve Leblanc: - Add the SConscriptChdir() method. From Anthony Roach: - Fix --debug=tree when used with directory targets. - Significant internal restructuring of Scanners and Taskmaster. - Added new --debug=dtree option. - Fixes for --profile option. - Performance improvement in construction variable substitution. - Implemented caching of content signatures, plus added --max-drift option to control caching. - Implemented caching of dependency signatures, enabled by new --implicit-cache option. - Added abspath construction variable modifier. - Added $SOURCE variable as a synonym for $SOURCES[0]. - Write out .sconsign files on error or interrupt so intermediate build results are saved. - Change the -U option to -D. Make a new -U that builds just the targets from the local SConscript file. - Fixed use of sys.path so Python modules can be imported from the SConscript directory. - Fix for using Aliases with the -u, -U and -D options. - Fix so that Nodes can be passed to SConscript files. From Moshe Zadka: - Changes for official Debian packaging. RELEASE 0.06 - Thu, 28 Mar 2002 01:24:29 -0600 From Charles Crain: - Fix command generators to expand construction variables. - Make FunctionAction arguments be Nodes, not strings. From Stephen Kennedy: - Performance: Use a dictionary, not a list, for a Node's parents. From Steven Knight: - Add .zip files to the packages we build. - Man page: document LIBS, fix a typo, document ARGUMENTS. - Added RANLIB and RANLIBFLAGS construction variables. Only use them in ARCOM if there's a "ranlib" program on the system. - Add a configurable CFILESUFFIX for the Builder of .l and .y files into C files. - Add a CXXFile Builder that turns .ll and .yy files into .cc files (configurable via a CXXFILESUFFIX construction variable). - Use the POSIX-standard lex -t flag, not the GNU-specific -o flag. (Bug reported by Russell Christensen.) - Fixed an exception when CPPPATH or LIBPATH is a null string. (Bug reported by Richard Kiss.) - Add a --profile=FILE option to make profiling SCons easier. - Modify the new DVI builder to create .dvi files from LaTeX (.ltx and .latex) files. - Add support for Aliases (phony targets). - Add a WhereIs() method for searching for path names to executables. - Add PDF and PostScript document builders. - Add support for compiling Fortran programs from a variety of suffixes (a la GNU Make): .f, .F, .for, .FOR, .fpp and .FPP - Support a CPPFLAGS variable on all default commands that use the C preprocessor. From Steve Leblanc: - Add support for the -U option. - Allow CPPPATH, LIBPATH and LIBS to be specified as white-space separated strings. - Add a document builder to create .dvi files from TeX (.tex) files. From Anthony Roach: - Fix: Construction variables with values of 0 were incorrectly interpolated as ''. - Support env['VAR'] to fetch construction variable values. - Man page: document Precious(). RELEASE 0.05 - Thu, 21 Feb 2002 16:50:03 -0600 From Chad Austin: - Set PROGSUFFIX to .exe under Cygwin. From Charles Crain: - Allow a library to specified as a command-line source file, not just in the LIBS construction variable. - Compensate for a bug in os.path.normpath() that returns '' for './' on WIN32. - More performance optimizations: cache #include lines from files, eliminate unnecessary calls. - If a prefix or suffix contains white space, treat the resulting concatenation as separate arguments. - Fix irregularities in the way we fetch DevStudio information from the Windows registry, and in our registry error handling. From Steven Knight: - Flush stdout after print so it intermixes correctly with stderr when redirected. - Allow Scanners to return a list of strings, and document how to write your own Scanners. - Look up implicit (scanned) dependencies relative to the directory of file being scanned. - Make writing .sconsign files more robust by first trying to write to a temp file that gets renamed. - Create all of the directories for a list of targets before trying to build any of the targets. - WIN32 portability fixes in tests. - Allow the list of variables exported to an SConscript file to be a UserList, too. - Document the overlooked LIBPATH construction variable. (Bug reported by Eicke Godehardt.) - Fix so that Ignore() ignores indirect, implicit dependencies (included files), not just direct dependencies. - Put the man page in the Debian distribution. - Run HTML docs through tidy to clean up the HTML (for Konqueror). - Add preliminary support for Unicode strings. - Efficiency: don't scan dependencies more than once during the walk of a tree. - Fix the -c option so it doesn't stop removing targets if one doesn't already exist. (Bug reported by Paul Connell.) - Fix the --debug=pdb option when run on Windows NT. (Bug reported by Paul Connell.) - Add support for the -q option. From Steve Leblanc: - Add support for the -u option. - Add .cc and .hh file suffixes to the C Scanner. From Anthony Roach: - Make the scons script return an error code on failures. - Add support for using code to generate a command to build a target. RELEASE 0.04 - Wed, 30 Jan 2002 11:09:42 -0600 From Charles Crain: - Significant performance improvements in the Node.FS and Scanner subsystems. - Fix signatures of binary files on Win32 systems. - Allow LIBS and LIBPATH to be strings, not just arrays. - Print a traceback if a Python-function builder throws an exception. From Steven Knight: - Fix using a directory as a Default(), and allow Default() to support white space in file names for strings in arrays. - Man page updates: corrected some mistakes, documented various missing Environment methods, alphabetized the construction variables and other functions, defined begin and end macros for the example sections, regularized white space separation, fixed the use of Export() in the Multiple Variants example. - Function action fixes: None is now a successful return value. Exceptions are now reported. Document function actions. - Add 'Action' and 'Scanner' to the global keywords so SConscript files can use them too. - Removed the Wrapper class between Nodes and Walkers. - Add examples using Library, LIBS, and LIBPATH. - The C Scanner now always returns a sorted list of dependencies so order changes don't cause unnecessary rebuilds. - Strip $(-$) bracketed text from command lines. Use this to surround $_INCDIRS and $_LIBDIRS so we don't rebuild in response to changes to -I or -L options. - Add the Ignore() method to ignore dependencies. - Provide an error message when a nonexistent target is specified on the command line. - Remove targets before building them, and add an Environment Precious() method to override that. - Eliminate redundant calls to the same builder when the target is a list of targets: Add a ListBuilder class that wraps Builders to handle lists atomically. Extend the Task class to support building and updating multiple targets in a single Task. Simplify the interface between Task and Taskmaster. - Add a --debug=pdb option to re-run SCons under the Python debugger. - Only compute a build signature once for each node. - Changes to our sys.path[] manipulation to support installation into an arbitrary --prefix value. From Steve Leblanc: - Add var=value command-line arguments. RELEASE 0.03 - Fri, 11 Jan 2002 01:09:30 -0600 From Charles Crain: - Performance improvements in the Node.FS and Sig.Calculator classes. - Add the InstallAs() method. - Execute commands through an external interpreter (sh, cmd.exe, or command.com) to handle redirection metacharacters. - Allow the user to supply a command handler. From Steven Knight: - Search both /usr/lib and /usr/local/lib for scons directories by adding them both to sys.path, with whichever is in sys.prefix first. - Fix interpreting strings of multiple white-space separated file names as separate file names, allowing prefixes and suffixes to be appended to each individually. - Refactor to move CompositeBuilder initialization logic from the factory wrapper to the __init__() method, and allow a Builder to have both an action and a src_builder (or array of them). - Refactor BuilderBase.__call__() to separate Node creation/lookup from initialization of the Node's builder information. - Add a CFile Builder object that supports turning lex (.l) and yacc (.y) files into .c files. - Document: variable interpretation attributes; how to propogate the user's environment variables to executed commands; how to build variants in multiple BuildDirs. - Collect String, Dict, and List type-checking in common utility routines so we can accept User{String,Dict,List}s all over. - Put the Action factory and classes into their own module. - Use one CPlusPlusAction in the Object Builder's action dictionary, instead of letting it create multiple identical instances. - Document the Install() and InstallAs() methods. From Steve Leblanc: - Require that a Builder be given a name argument, supplying a useful error message when it isn't. From Anthony Roach: - Add a "duplicate" keyword argument to BuildDir() that can be set to prevent linking/copying source files into build directories. - Add a "--debug=tree" option to print an ASCII dependency tree. - Fetch the location of the Microsoft Visual C++ compiler(s) from the Registry, instead of hard-coding the location. - Made Scanner objects take Nodes, not path names. - Have the C Scanner cache the #include file names instead of (re-)scanning the file each time it's called. - Created a separate class for parent "nodes" of file system roots, eliminating the need for separate is-parent-null checks everywhere. - Removed defined __hash__() and __cmp() methods from FS.Entry, in favor of Python's more efficient built-in identity comparisons. RELEASE 0.02 - Sun, 23 Dec 2001 19:05:09 -0600 From Charles Crain: - Added the Install(), BuildDir(), and Export() methods. - Fix the -C option by delaying setting the top of the FS tree. - Avoid putting the directory path on the libraries in the LIBS construction variable. - Added a GetBuildPath() method to return the full path to the Node for a specified string. - Fixed variable substitution in CPPPATH and LIBPATH. From Steven Knight: - Fixed the version comment in the scons.bat (the UNIX geek used # instead of @rem). - Fix to setup.py so it doesn't require a sys.argv[1] argument. - Provide make-like warning message for "command not found" and similar errors. - Added an EXAMPLES section to the man page. - Make Default() targets properly relative to their SConscript file's subdirectory. From Anthony Roach: - Documented CXXFLAGS, CXXCOM, and CPPPATH. - Fixed SCONS_LIB_DIR to work as documented. - Made Default() accept Nodes as arguments. - Changed Export() to make it easier to use. - Added the Import() and Return() methods. RELEASE 0.01 - Thu Dec 13 19:25:23 CST 2001 A brief overview of important functionality available in release 0.01: - C and C++ compilation on POSIX and Windows NT. - Automatic scanning of C/C++ source files for #include dependencies. - Support for building libraries; setting construction variables allows creation of shared libraries. - Library and C preprocessor search paths. - File changes detected using MD5 signatures. - User-definable Builder objects for building files. - User-definable Scanner objects for scanning for dependencies. - Parallel build (-j) support. - Dependency cycles detected. - Linux packages available in RPM and Debian format. - Windows installer available. Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation src/CHANGES.txt 2013/03/03 09:48:35 garyo scons-doc-2.3.0/src/LICENSE.txt0000644000175000017500000000216012114661557016633 0ustar dktrkranzdktrkranzCopyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation 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. scons-doc-2.3.0/src/test_strings.py0000644000175000017500000002003612114661560020106 0ustar dktrkranzdktrkranz#!/usr/bin/env python # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # 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__ = "src/test_strings.py 2013/03/03 09:48:35 garyo" """ Verify that we have proper strings like Copyright notices on all the right files in our distributions. Note that this is a source file and packaging test, not a functional test, so the name of this script doesn't end in *Tests.py. """ import fnmatch import os import os.path import re import TestCmd import TestSCons # Use TestCmd, not TestSCons, so we don't chdir to a temporary directory. test = TestCmd.TestCmd() scons_version = TestSCons.SConsVersion def build_path(*args): return os.path.join('build', *args) build_scons = build_path('scons') build_local = build_path('scons-local', 'scons-local-'+scons_version) build_src = build_path('scons-src') class Checker(object): def __init__(self, directory, search_list = [], remove_list = [], remove_patterns = []): self.directory = directory self.search_list = search_list self.remove_dict = {} for r in remove_list: self.remove_dict[os.path.join(directory, r)] = 1 self.remove_patterns = remove_patterns def directory_exists(self): return os.path.exists(self.directory) def remove_this(self, name, path): if self.remove_dict.get(path): return 1 else: for pattern in self.remove_patterns: if fnmatch.fnmatch(name, pattern): return 1 return 0 def search_this(self, path): if self.search_list: for pattern in self.search_list: if fnmatch.fnmatch(path, pattern): return 1 return None else: return os.path.isfile(path) def find_missing(self): result = [] for dirpath, dirnames, filenames in os.walk(self.directory): if '.svn' in dirnames: dirnames.remove('.svn') for dname in dirnames[:]: dpath = os.path.join(dirpath, dname) if self.remove_this(dname, dpath): dirnames.remove(dname) for fname in filenames: fpath = os.path.join(dirpath, fname) if self.search_this(fpath) and not self.remove_this(fname, fpath): body = open(fpath, 'r').read() for expr in self.expressions: if not expr.search(body): msg = '%s: missing %s' % (fpath, repr(expr.pattern)) result.append(msg) return result class CheckUnexpandedStrings(Checker): expressions = [ re.compile('Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation'), re.compile('src/test_strings.py 2013/03/03 09:48:35 garyo'), ] def must_be_built(self): return None class CheckPassTest(Checker): expressions = [ re.compile(r'\.pass_test()'), ] def must_be_built(self): return None class CheckExpandedCopyright(Checker): expressions = [ re.compile('Copyright.*The SCons Foundation'), ] def must_be_built(self): return 1 check_list = [ CheckUnexpandedStrings( 'src', search_list = [ '*.py' ], remove_list = [ 'engine/SCons/compat/_scons_sets.py', 'engine/SCons/compat/_scons_subprocess.py', 'engine/SCons/Conftest.py', 'engine/SCons/dblite.py', ], ), CheckUnexpandedStrings( 'test', search_list = [ '*.py' ], ), CheckPassTest( 'test', search_list = [ '*.py' ], remove_list = [ 'Fortran/common.py', ], ), CheckExpandedCopyright( build_scons, remove_list = [ 'build', 'build-stamp', 'configure-stamp', 'debian', 'dist', 'gentoo', 'engine/SCons/compat/_scons_sets.py', 'engine/SCons/compat/_scons_subprocess.py', 'engine/SCons/Conftest.py', 'engine/SCons/dblite.py', 'MANIFEST', 'setup.cfg', ], # We run epydoc on the *.py files, which generates *.pyc files. remove_patterns = [ '*.pyc', ] ), CheckExpandedCopyright( build_local, remove_list = [ 'SCons/compat/_scons_sets.py', 'SCons/compat/_scons_subprocess.py', 'SCons/Conftest.py', 'SCons/dblite.py', 'scons-%s.egg-info' % scons_version, ], ), CheckExpandedCopyright( build_src, remove_list = [ 'bench/timeit.py', 'bin', 'config', 'debian', 'gentoo', 'doc/design', 'doc/MANIFEST', 'doc/python10', 'doc/reference', 'doc/developer/MANIFEST', 'doc/man/MANIFEST', 'doc/user/cons.pl', 'doc/user/MANIFEST', 'doc/user/SCons-win32-install-1.jpg', 'doc/user/SCons-win32-install-2.jpg', 'doc/user/SCons-win32-install-3.jpg', 'doc/user/SCons-win32-install-4.jpg', 'examples', 'gentoo', 'QMTest/classes.qmc', 'QMTest/configuration', 'QMTest/TestCmd.py', 'QMTest/TestCmdTests.py', 'QMTest/TestCommon.py', 'QMTest/TestCommonTests.py', 'src/MANIFEST.in', 'src/setup.cfg', 'src/engine/MANIFEST.in', 'src/engine/MANIFEST-xml.in', 'src/engine/setup.cfg', 'src/engine/SCons/compat/_scons_sets.py', 'src/engine/SCons/compat/_scons_subprocess.py', 'src/engine/SCons/Conftest.py', 'src/engine/SCons/dblite.py', 'src/script/MANIFEST.in', 'src/script/setup.cfg', 'test/Fortran/.exclude_tests', 'timings/changelog.html', 'timings/ElectricCloud/genscons.pl', 'timings/graph.html', 'timings/index.html', 'review.py', ], remove_patterns = [ '*.js', ] ), ] missing_strings = [] not_built = [] for collector in check_list: if collector.directory_exists(): missing_strings.extend(collector.find_missing()) elif collector.must_be_built(): not_built.append(collector.directory) if missing_strings: print "Found the following files with missing strings:" print "\t" + "\n\t".join(missing_strings) test.fail_test(1) if not_built: print "Cannot check all strings, the following have apparently not been built:" print "\t" + "\n\t".join(not_built) test.no_result(1) test.pass_test() # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: