summaryrefslogtreecommitdiffstats
path: root/src/engine/SCons/Environment.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/engine/SCons/Environment.py')
-rw-r--r--src/engine/SCons/Environment.py307
1 files changed, 235 insertions, 72 deletions
diff --git a/src/engine/SCons/Environment.py b/src/engine/SCons/Environment.py
index 691098d..d76f71d 100644
--- a/src/engine/SCons/Environment.py
+++ b/src/engine/SCons/Environment.py
@@ -38,6 +38,7 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
import copy
import os
import os.path
+import popen2
import string
from UserDict import UserDict
@@ -83,7 +84,7 @@ def installFunc(target, source, env):
return install(target[0].path, source[0].path, env)
def installString(target, source, env):
- return env.subst(env['INSTALLSTR'], 0, target, source)
+ return env.subst_target_source(env['INSTALLSTR'], 0, target, source)
installAction = SCons.Action.Action(installFunc, installString)
@@ -435,6 +436,34 @@ class SubstitutionEnvironment:
subst_target_source = subst
+ def backtick(self, command):
+ try:
+ popen2.Popen3
+ except AttributeError:
+ (tochild, fromchild, childerr) = os.popen3(self.subst(command))
+ tochild.close()
+ err = childerr.read()
+ out = fromchild.read()
+ fromchild.close()
+ status = childerr.close()
+ else:
+ p = popen2.Popen3(command, 1)
+ p.tochild.close()
+ out = p.fromchild.read()
+ err = p.childerr.read()
+ status = p.wait()
+ if err:
+ import sys
+ sys.stderr.write(err)
+ if status:
+ try:
+ if os.WIFEXITED(status):
+ status = os.WEXITSTATUS(status)
+ except AttributeError:
+ pass
+ raise OSError("'%s' exited %s" % (command, status))
+ return out
+
def Override(self, overrides):
"""
Produce a modified environment whose variables are overriden by
@@ -458,6 +487,196 @@ class SubstitutionEnvironment:
else:
return self
+ 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' : [],
+ 'CCFLAGS' : [],
+ 'CPPDEFINES' : [],
+ 'CPPFLAGS' : [],
+ 'CPPPATH' : [],
+ 'FRAMEWORKPATH' : [],
+ 'FRAMEWORKS' : [],
+ 'LIBPATH' : [],
+ 'LIBS' : [],
+ 'LINKFLAGS' : [],
+ 'RPATH' : [],
+ }
+
+ # The use of the "me" parameter to provide our own name for
+ # recursion is an egregious hack to support Python 2.1 and before.
+ def do_parse(arg, me, self = self, dict = dict):
+ # if arg is a sequence, recurse with each element
+ if not arg:
+ return
+
+ if not SCons.Util.is_String(arg):
+ for t in arg: me(t, me)
+ 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 = string.split(name, '=')
+ if len(t) == 1:
+ dict['CPPDEFINES'].append(name)
+ else:
+ dict['CPPDEFINES'].append([t[0], string.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 = string.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[: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:
+ appencd_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 == '-mno-cygwin':
+ dict['CCFLAGS'].append(arg)
+ dict['LINKFLAGS'].append(arg)
+ elif arg == '-mwindows':
+ dict['LINKFLAGS'].append(arg)
+ elif arg == '-pthread':
+ dict['CCFLAGS'].append(arg)
+ dict['LINKFLAGS'].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, do_parse)
+ return dict
+
+ def MergeFlags(self, args, unique=1):
+ """
+ Merge the dict in args into the construction variables. 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 not SCons.Util.is_Dict(args):
+ args = self.ParseFlags(args)
+ if not unique:
+ apply(self.Append, (), args)
+ return self
+ for key, value in args.items():
+ if value == '':
+ continue
+ try:
+ orig = self[key]
+ except KeyError:
+ orig = value
+ else:
+ if len(orig) == 0: orig = []
+ elif not SCons.Util.is_List(orig): orig = [orig]
+ orig = 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
+
class Base(SubstitutionEnvironment):
"""Base class for "real" construction Environments. These are the
primary objects used to communicate dependency and construction
@@ -838,82 +1057,22 @@ class Base(SubstitutionEnvironment):
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
+ in order to modify the current environment. The 'command' can
be a string or a list of strings representing a command and
- it's arguments. 'Function' is an optional argument that takes
- the environment and the output of the command. If no function is
- specified, the output will be treated as the output of a typical
- 'X-config' command (i.e. gtk-config) and used to append to the
- ASFLAGS, CCFLAGS, CPPFLAGS, CPPPATH, LIBPATH, LIBS, LINKFLAGS
- and CCFLAGS variables.
+ 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.
"""
-
- # the default parse function
- def parse_conf(env, output, fs=self.fs, unique=unique):
- dict = {
- 'ASFLAGS' : [],
- 'CCFLAGS' : [],
- 'CPPFLAGS' : [],
- 'CPPPATH' : [],
- 'LIBPATH' : [],
- 'LIBS' : [],
- 'LINKFLAGS' : [],
- }
-
- params = string.split(output)
- append_next_arg_to='' # for multi-word args
- for arg in params:
- if append_next_arg_to:
- dict[append_next_arg_to].append(arg)
- append_next_arg_to = ''
- elif arg[0] != '-':
- dict['LIBS'].append(fs.File(arg))
- 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)
- elif arg[:4] == '-Wl,':
- dict['LINKFLAGS'].append(arg)
- elif arg[:4] == '-Wp,':
- dict['CPPFLAGS'].append(arg)
- elif arg == '-framework':
- dict['LINKFLAGS'].append(arg)
- append_next_arg_to='LINKFLAGS'
- elif arg == '-mno-cygwin':
- dict['CCFLAGS'].append(arg)
- dict['LINKFLAGS'].append(arg)
- elif arg == '-mwindows':
- dict['LINKFLAGS'].append(arg)
- elif arg == '-pthread':
- dict['CCFLAGS'].append(arg)
- dict['LINKFLAGS'].append(arg)
- else:
- dict['CCFLAGS'].append(arg)
- if unique:
- appender = env.AppendUnique
- else:
- appender = env.Append
- apply(appender, (), dict)
-
if function is None:
+ def parse_conf(env, cmd, unique=unique):
+ return env.MergeFlags(cmd, unique)
function = parse_conf
- if type(command) is type([]):
+ if SCons.Util.is_List(command):
command = string.join(command)
command = self.subst(command)
- return function(self, os.popen(command).read())
+ return function(self, self.backtick(command))
def ParseDepends(self, filename, must_exist=None, only_one=0):
"""
@@ -1137,7 +1296,11 @@ class Base(SubstitutionEnvironment):
#######################################################################
def Action(self, *args, **kw):
- nargs = self.subst(args)
+ def subst_string(a, self=self):
+ if SCons.Util.is_String(a):
+ a = self.subst(a)
+ return a
+ nargs = map(subst_string, args)
nkw = self.subst_kw(kw)
return apply(SCons.Action.Action, nargs, nkw)