Files
archived-mbuild/mbuild/env.py
2016-12-19 17:09:03 +09:00

2103 lines
75 KiB
Python
Executable File

#!/usr/bin/env python
# -*- python -*-
# Mark Charney
#BEGIN_LEGAL
#
#Copyright (c) 2016 Intel Corporation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
#END_LEGAL
"""Environment support"""
import os
import sys
import re
import platform
import types
import optparse
import time
import copy
from base import *
import util
import build_env
import plan
import msvs
import mbuild
def _remove_libname(args,env):
#lib = env.expand('%(LIBNAME)s')
lib = args[0]
msgb("REMOVING", lib)
util.remove_file(lib)
return (0,['REMOVED %s\n' % ( lib )])
# 2014-04-02: Intel has 2 compilers for mac: icc and icl. Intel now
# calls their mac llvm-based comiler "icl". This confuses thing with
# the name of the windows intel compiler which is also called "icl".
# In mbuild, we call the Intel llvm-based compiler "iclang" to
# disambiguate the conflict.
class env_t(object):
"""The is the environment for compilation. The environment
includes a dictionary for holding everything custom about this
environment. The default environment includes:
- command line options. These are also in the environment dictionary.
- build_dir defaultE{:} obj
- src_dir defaultE{:} . or path to the mfile
- gen_dir defaultE{:} None (default path for generated files, if set)
- shared defaultE{:} False (default: no shared libraries)
- static defaultE{:} False (default: not to statically link)
- opt defaultE{:} 'noopt' (could be 'noopt, 0,1,2,3)
- debug defaultE{:} False
- separate_pdb_files defaultE{:} False
- targets defaultE{:} [] targets to build
- verbose defaultE{:} 1
- compiler defaultE{:} 'gnu', 'ms', 'clang', 'icc', 'icl', 'iclang'
- extra_defines defaultE{:} ''
- extra_flags defaultE{:} '' (for both CXXFLAGS & CCFLAGS)
- extra_cxxflags defaultE{:} ''
- extra_ccflags defaultE{:} ''
- extra_linkflags defaultE{:} ''
- extra_libs defaultE{:} ''
- use_yasm defaultE{:} False
- CPPPATH defaultE{:} [] The list of include paths
- SYSTEMINCLUDE defaultE{:} [] The list of system include paths (Not
supported by MSVS).
- DEFINES defaultE{:} {} The dictionary of defines
- short names for the primary compiler toolsE{:}
- CXX_COMPILER cl or g++
- CC_COMPILER cl or gcc
- ASSEMBLER ml/ml64 or gcc/gas (gcc is the default for gnu)
- LINKER link or g++/gcc (g++ is the default for gnu)
- ARCHIVER ar
- RANLIB_CMD ranlib
- toolchain path to the compiler tools (default is ''). If toolchain is
set, it should end with a trailing slash.
- vc_dir path to the compiler VC directory for MSVS (default is '')n
- icc_version 7, 8, 9, 10, ...
- gcc_version 2.96, 3.x.y, 4.x.y, ...
- msvs_version 6 (VC98), 7 (.NET 2003), 8 (Pro 2005), ...
- primary compilation toolsE{:}
- CC cl or gcc (with toolchain path)
- CXX cl or g++ (with toolchain path)
- AS ml,ml64 or gcc/gas (with toolchain path)
- LINK link or gcc/g++ (with toolchain path)
- AR lib or ar (with toolchain path)
- RANLIB ranlib (with toolchain path)
- flags for primary toolsE{:}
- CCFLAGS
- CXXFLAGS
- ASFLAGS
- ARFLAGS
- LINKFLAGS
- LIBS (libraries for the end of the link statement)
- preprocessor flags
- DOPT /D or -D
- ASDOPT /D or -D
- IOPT /I or -I
- OPTPOPT /O or -O
- DEBUGOPT /Zi or -g
- options to control compilation outputE{:}
- COPT /c or -c
- COUT /Fo or -o
- ASMOUT /Fo or -o
- LIBOUT /outE{:} or -o
- LINKOUT /OUTE{:} or -o
- DLLOPT -shared
- Override-buildersE{:} set these to a function pointer if you want
to replace the default builder function.
- ASSEMBLE_BUILDER if not set default is to use assemble_default()
- CXX_COMPILE_BUILDER if not set default is to use cxx_default()
- CC_COMPILE_BUILDER if not set default is to use cc_default()
- LINK_BUILDER if not set default is to use link_default()
- STATIC_LIBRARY_BUILDER if not set default is to use static_lib_default()
- DYNAMIC_LIBRARY_BUILDER if not set default is to use dynamic_lib_default()
- default extensionsE{:}
- OBJEXT .obj or .o
- LIBEXT .lib or .a
- DLLEXT .dll, .so, or .dylib
- EXEEXT .exe or ''
- System valuesE{:}
- uname standard python tuple of values from uname.
- system standard valuesE{:} 'Linux', 'Windows', 'Darwin', 'Microsoft', 'FreeBSD', 'NetBSD'
- hostname
- build_os standard valuesE{:} 'lin', 'win', 'mac', 'bsd'
- host_os standard valuesE{:} 'lin', 'win', 'mac', 'bsd'
- build_cpu standard valuesE{:} 'ia32', 'x86-64', 'ipf'
- host_cpu standard valuesE{:} 'ia32', 'x86-64', 'ipf'
"""
obj_pattern = re.compile(r'.obj$')
objext_pattern = re.compile(r'[%][(]OBJEXT[)]s$')
mbuild_subs_pattern = re.compile('%[(][^)]+[)]')
#FIXME: no backslashes in patterns!
assignment_pattern = re.compile(r'(?P<name>[-A-Za-z0-9_]+)[=](?P<value>.+)')
supplement_pattern = re.compile(r'(?P<name>[-A-Za-z0-9_]+)[+][=](?P<value>.+)')
def version(self):
"""Emit the version string.
@rtype: string
@return: The version string
"""
# FIXME: could put an Id in each sub-module and look at the
# doc strings for each one.
msgb("VERSION", "$Id: mbuild_env.py 44 2007-03-16 15:54:44Z mjcharne $")
def __setitem__(self,k,value):
"""Write a value to the environment dictionary"""
if isinstance(value,types.StringType):
self.env[k] = util.posix_slashes(value)
else:
self.env[k] = value
def __contains__(self,k):
if k in self.env:
return True
return False
def __getitem__(self,k):
"""Read the environment dictionary. Not doing any
substitutions."""
#return self.env[k]
try:
return self.env[k]
except:
die("env key not found: %s" % (k))
def expand(self, command_string, newenv=None):
"""Alias for expand_string()"""
return self.expand_string(command_string, newenv)
def expand_string(self, command_string, newenv=None):
"""Read the environment dictionary, doing recursive
substitutions from the environment. If no environment is
supplied, then the default environment is used.
@type command_string: string or list of strings
@param command_string: A string with %(...)s variables in it
@type newenv: L{env_t}
@param newenv: An environment within which to do the expansion. If
null, the default environment is used.
@rtype: string
"""
if newenv == None:
newenv = self.env
if isinstance(command_string, types.StringType):
return self._iterative_substitute(command_string, newenv)
if isinstance(command_string, types.ListType):
return map(lambda(x): self._iterative_substitute(x, newenv), command_string)
die("expand_string only handles substitution in strings or lists of strings")
def expand_key(self,k, newenv=None):
"""Read the the value of k from the environment dictionary,
doing recursive substitutions from the environment. If no
environment is supplied, then the default environment is used.
@type k: string or list of strings
@param k: A string (or strings) containing a single key name(s)
@type newenv: L{env_t}
@param newenv: An environment within which to do the expansion. If
null, the default environment is used.
@rtype: string
"""
if newenv == None:
newenv = self.env
if k not in newenv:
die("Could not find %s in the environment" % k)
if isinstance(newenv[k],types.ListType):
# We must process each string in the list and do
# substitutions on them. For example, CPPPATH
return map(lambda(x): self._iterative_substitute(x,newenv), newenv[k])
if isinstance(newenv[k], types.StringType):
return self._iterative_substitute("%(" + k + ")s", newenv)
# non strings (scalars)
return newenv[k]
def _mysub(self,input, keyname, newval):
"""Replace %(keyname)s in input with newval"""
# only handling %(...)s replacement. Nothing fancy.
s = '%('+keyname+')s'
# simple string replacement, not regexp replacement
output = input.replace(s,newval)
return output
def _iterative_substitute(self,s,dct1,debug=False):
"""Replace all the %(...)s with values in s from the
dictionary dct1. Note, the dictionary can contain tuples of
the form (key, dict). In this case, this code uses the lookup
result of dct1[key] to query yet the dictionary dict. That
lookup can result in a string or another such tuple."""
#error_msg("iterative_substitute", str(s))
subs_pattern = re.compile('%[(](?P<name>[^)]+)[)]s')
t = s
m = subs_pattern.search(t)
while m:
name = m.group('name')
if name not in dct1:
die("Bad substitution for " + name)
#print "SUBSTITUTING %s" % name
v = dct1[name]
# repeatedly expand any tuples that show up.
while not isinstance(v,types.StringType):
if isinstance(v,types.TupleType):
(key, dct) = v
# look up key in the main dictionary to create a
# subkey for use in the 2nd level dictionary
try:
subkey = dct1[key]
except:
die("nested dictionary lookup error during iterative string " +
" expansion. key=%s" % (str(key)))
try:
v = dct[ subkey ]
except:
try:
v = dct['otherwise']
except:
die("nested dictionary lookup error during iterative string " +
" expansion. key=%s subkey=%s" % (str(key),str(subkey)))
elif isinstance(v,types.FunctionType):
try:
v = v(dct1)
except:
die("Bad function invokation during iterative string expansion")
else:
die("Bad environment value: " + str(v) +
" when searching: " + s)
t = self._mysub(t,name,v)
m = subs_pattern.search(t)
if debug:
print t
return t
def _dosub_old(self,s,d):
"""Repeatedly substitute values from the dictionary d into the
string s while '%(...)' substrings remain in the thing we want
to return. If the input s is a list, then we recursively
expand each element of that list"""
if isinstance(s,types.ListType):
return map(lambda(x): self.dosub(x,d), s)
# The common case: Just expanding a simple string.
t = s
while env_t.mbuild_subs_pattern.search(t):
t = t % d
return t
def __str__(self):
"""Print out the environment"""
s = []
s.append("BUILD_CPU:")
s.append(self.env['build_cpu'])
s.append("HOST_CPU:")
s.append(self.env['host_cpu'])
s.append("\nBUILD_OS: ")
s.append(self.env['build_os'])
s.append("\nHOST_OS: ")
s.append(self.env['host_os'])
s.append("\nUNAME: ")
s.append(str(self.env['uname']))
s.append("\nHOSTNAME: ")
s.append(self.env['hostname'])
s.append("\nSYSTEM: ")
s.append(self.env['system'])
s.append("\nDICTIONARY:\n")
for k,v in self.env.iteritems():
s.append("\t")
s.append(k)
s.append("->")
s.append(str(v))
s.append("\n")
return ''.join(s)
def verbose_startup(self):
if self._emitted_startup_msg:
return
self._emitted_startup_msg = True
if verbose(1):
msgb("INVOKED", " ".join(sys.argv))
msgb("START TIME", self.env['start_time_str'])
msgb("CURRENT DIRECTORY", os.getcwd())
msgb('UNAME', str(self.env['uname']).replace(':','_'))
msgb('SYSTEM', self.env['system'])
msgb('HOSTNAME', self.env['hostname'])
msgb("BUILD_OS", self.env['build_os'])
msgb("BUILD_CPU", self.env['build_cpu'])
msgb("HOST_OS", self.env['host_os'])
msgb("HOST_CPU", self.env['host_cpu'])
def _check_registry_environment(self,env_var):
s = 'SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment'
try:
import _winreg
key = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, s)
(val, typ) = _winreg.QueryValueEx(key, env_var)
return val
except:
mbuild.die(("Could not read windows registry for variable %s.\n" % \
(env_var)) +
"Use win32 python and install pywin32")
def _check_processor_identifier_windows(self):
return self._check_registry_environment('PROCESSOR_IDENTIFIER')
def _check_number_of_processors_windows(self):
return self._check_registry_environment('NUMBER_OF_PROCESSORS')
def __init__(self, init_verbose=1, default_knobs=True):
"""Build up the environment for compilation.
"""
set_verbosity(int(init_verbose))
self.env = {}
self.parsed_args = False
self.added_common_knobs=False
self.added_default_knobs=False
self.env['python'] = sys.executable
self.env['CPPPATH'] = []
self.env['SYSTEMINCLUDE'] = []
self.env['DEFINES'] = {}
self.env['LINKPATH'] = []
self.env['LINKDIRS'] = ''
self.env['LINKFLAGS'] = ' %(LINKDIRS)s '
self.env['targets'] = []
# defaults for the build dir and src dir
self.env['build_dir'] = 'obj'
self.env['src_dir'] = '' # we set this accordingly
self.env['gen_dir'] = None # location of generated files that do not exist
self.env['shared'] = False
self.env['static'] = False
self.env['debug'] = False
self.env['separate_pdb_files'] = False
self.env['opt'] = 'noopt'
self.env['LIBS'] = '' # default link libraries
self.env['CXX_COMPILER'] = ''
self.env['CC_COMPILER'] = ''
self.env['ASSEMBLER'] = ''
self.env['LINKER'] = ''
# windows rc tool for dll resource files.
self.env['RC'] = ''
self.env['RC_CMD'] = ''
self.env['RCFLAGS'] = ''
# use_compiler_to_link = True if using the compiler to link.
# use_compiler_to_link = False if using the linker to link
self.env['use_compiler_to_link'] = False
self.env['ARCHIVER'] = ''
self.env['RANLIB_CMD'] = ''
self.env['CXX'] = ''
self.env['CC'] = ''
self.env['LINK'] = ''
self.env['AR'] = ''
self.env['AS'] = ''
self.env['RANLIB'] = ''
self.env['uname'] = platform.uname()
self.env['hostname'] = platform.node()
self.env['system'] = platform.system() # sort of like build_os
# distro is the empty string on mac and windows
if util.check_python_version(2,6):
(distro, distro_ver, distro_id) = platform.linux_distribution()
else:
distro = ''
distro_ver = ''
self.env['distro'] = distro.strip()
self.env['distro_version'] = distro_ver
if 'HOME' in os.environ:
self.env['home'] = os.environ['HOME']
else:
self.env['home'] = 'unknown'
# the colons in the time string are replaced by underscores.
# The colons confused xemacs compilation mode error
# parsing. (emacs was fine)
self.env['start_time_str'] = re.sub(":","_",util.get_time_str())
self.start_time = util.get_time()
#Old versions of mbuild used target_cpu erroneously instead of
#host_cpu. We do a little magic later to try to make those old
#uses continue to work.
self.env['target_cpu']=None
if self.env['system'] in [ 'Linux', 'FreeBSD', 'NetBSD']:
uname = platform.uname()
self.env['build_os'] = self._normalize_os_name(uname[0])
self.env['build_cpu'] = \
self._normalize_cpu_name(uname[4])
elif self.env['system'] in [ 'Darwin']:
uname = platform.uname()
self.env['build_os'] = self._normalize_os_name(uname[0])
x = uname[4]
if self._check_mac_64b():
x = 'x86_64'
self.env['build_cpu'] = \
self._normalize_cpu_name(x)
elif self.on_windows():
self.env['build_os'] = self._normalize_os_name(os.environ['OS'])
if 'PROCESSOR_IDENTIFIER' in os.environ:
p = os.environ['PROCESSOR_IDENTIFIER']
else:
p = self._check_processor_identifier_windows()
self.env['build_cpu'] = \
self._normalize_cpu_name(p)
else:
die("Unknown platform")
# where the compiled thing runs, not where it is built
# but that is the starting default.
self.env['host_cpu'] = self.env['build_cpu']
self.env['host_os'] = self.env['build_os']
self._add_compilation_support()
self._emitted_startup_msg = False
mbuild_env_defaults = dict(
args = [],
mbuild_version=False,
jobs='4',
build_dir='obj',
src_dir='',
gen_dir=None,
verbose= -1,
arg_host_cpu=None,
arg_host_os=None,
compiler=self.default_compiler(),
debug=False,
shared=False,
static=False,
opt='noopt',
silent=False,
extra_defines=[],
extra_flags=[],
extra_cxxflags=[],
extra_ccflags=[],
extra_linkflags=[],
extra_libs=[],
toolchain='',
ignorable_files=[], # deprecated, unused 2011-10-20
required_files=[],
vc_dir='',
msvs_version='',
setup_msvc=False,
icc_version='',
gcc_version='',
cc='',
cxx='',
linker='',
ar='',
use_yasm=False,
cygwin_limit_jobs=True
)
# as is a keyword so must set it separately
mbuild_env_defaults['as']=''
# store the default if we ever need them
self.env_defaults = mbuild_env_defaults
# put them in the initial environment
self.update_dict(mbuild_env_defaults)
self.parser = optparse.OptionParser()
# set the defaults in the command line option parser
self.parser.set_defaults(**mbuild_env_defaults)
if default_knobs:
self.add_common_knobs()
self.add_default_knobs()
def add_common_knobs(self):
if self.added_common_knobs:
return
self.added_common_knobs=True
self.parser.add_option(
"-j", "--jobs",
dest="jobs",
action="store",
help="Number of concurrent worker threads to use.")
def add_default_knobs(self):
if self.added_default_knobs:
return
self.added_default_knobs=True
self.parser.add_option(
"--mbuild-version",
dest="mbuild_version",
action="store_true",
help="Emit the version information")
self.parser.add_option(
"--build-dir",
dest="build_dir",
action="store",
help="Build directory, default is 'obj'")
self.parser.add_option(
"--src-dir",
action="store",
dest="src_dir",
help="The directory where the sources are located.")
self.parser.add_option(
"--gen-dir",
action="store",
dest="gen_dir",
help="The directory where generated sources are assumed" +
" to be located.")
self.parser.add_option(
"-v",
"--verbose",
action="store",
dest="verbose",
help="Verbosity level. Defaults to value passed to env_t()")
self.parser.add_option(
"--compiler",
dest="compiler",
action="store",
help="Compiler (ms,gnu,clang,icc,icl,iclang)." +
" Default is gnu on linux and" +
" ms on windows. Default is: %s" % (self.default_compiler()))
self.parser.add_option(
"--debug",
dest="debug",
action="store_true",
help="Debug build")
self.parser.add_option(
"--shared",
dest="shared",
action="store_true",
help="Shared DLL build")
self.parser.add_option(
"--static",
dest="static",
action="store_true",
help="Statically link executables")
self.parser.add_option(
"--opt",
dest="opt",
action="store",
help="Optimization level noopt, 0, 1, 2, 3")
self.parser.add_option(
"-s",
"--silent",
dest="silent",
action="store_true",
help="Silence all but the most important messages")
self.parser.add_option(
"--extra-defines",
dest="extra_defines",
action="append",
help="Extra preprocessor defines")
self.parser.add_option(
"--extra-flags",
dest="extra_flags",
action="append",
help="Extra values for CXXFLAGS and CCFLAGS")
self.parser.add_option(
"--extra-cxxflags",
dest="extra_cxxflags",
action="append",
help="Extra values for CXXFLAGS")
self.parser.add_option(
"--extra-ccflags",
dest="extra_ccflags",
action="append",
help="Extra values for CCFLAGS")
self.parser.add_option(
"--extra-linkflags",
dest="extra_linkflags",
action="append",
help="Extra values for LINKFLAGS")
self.parser.add_option(
"--extra-libs",
dest="extra_libs",
action="append",
help="Extra values for LIBS")
self.parser.add_option(
"--toolchain",
dest="toolchain",
action="store",
help="Compiler toolchain")
self.parser.add_option(
"--vc-dir",
dest="vc_dir",
action="store",
help="MSVS Compiler VC directory. For finding libraries " +
" and setting the toolchain")
self.parser.add_option(
'--msvs-version',
'--msvc-version',
'--msvsversion',
'--msvcversion',
dest='msvs_version',
action='store',
help="MSVS version 6=VC98, 7=VS .Net 2003, 8=VS 2005," +
" 9=VS 2008, 10=VS 2010/DEV10, 11=VS2012/DEV11" +
"This sets certain flags and idioms for quirks in some compilers.")
self.parser.add_option(
'--setup-msvc',
'--setup-msvs',
'--msvs-setup',
'--msvc-setup',
dest='setup_msvc',
action='store_true',
help="Use the value of the --msvc-version to initialize" +
" the MSVC configuration.")
self.parser.add_option(
'--icc-version',
'--iccver',
'--icc-ver',
dest='icc_version',
action='store',
help="ICC/ICL version 7, 8, 9, 10, 11")
self.parser.add_option(
'--gcc-version',
'--gccversion',
'--gcc-ver',
dest='gcc_version',
action='store',
help="GCC version, with dots as in 2.96, 3.4.3, 4.2.0, etc. ")
self.parser.add_option(
"--cc",
dest="cc",
action="store",
help="full path to C compiler")
self.parser.add_option(
"--cxx",
dest="cxx",
action="store",
help="full path to C++ compiler")
self.parser.add_option(
"--linker",
dest="linker",
action="store",
help="full path to linker")
self.parser.add_option(
"--ar",
dest="ar",
action="store",
help="full path to archiver (lib/ar)")
self.parser.add_option(
"--as",
dest="as",
action="store",
help="full path to assembler (gas/as/ml/ml64)")
self.parser.add_option(
"--yasm",
dest="use_yasm",
action="store_true",
help="Use yasm")
self.parser.add_option(
"--no-cygwin-limit",
dest="cygwin_limit_jobs",
action="store_false",
help="Do not limit cygwin to one job at a time. " +
" Default is to limit cygwin to one job.")
self.parser.add_option(
"--host-cpu",
dest="arg_host_cpu",
action="store",
help="Host CPU, typically ia32, intel64 or x86-64")
self.parser.add_option(
"--host-os",
dest="arg_host_os",
action="store",
help="Host OS (where the binary runs)")
def _implied_compiler(self,dct):
"""If one of the icc_version, gcc_version_ or msvs_version
variables are set, deduce the compiler variable setting."""
# windows default is ms so no need to set that.
if dct['icc_version'] != '':
if self.on_windows():
dct['compiler'] = 'icl'
else:
dct['compiler'] = 'icc'
if dct['gcc_version'] != '':
dct['compiler'] = 'gnu'
def _check_mac_ncpu(self):
"""How many CPUs on a mac"""
cmd = "/usr/sbin/sysctl hw.ncpu"
(retval,output, error_output) = util.run_command(cmd)
if retval == 0 and len(output)>0:
if re.match('hw.ncpu', output[0]):
n = int(re.sub('hw.ncpu: ','',output[0]))
return n
return 0
def number_of_cpus(self):
"""Return the number of CPUs or 0 if we don't know anything for sure"""
n = 0
if self.on_mac():
n = self._check_mac_ncpu()
elif self.on_windows():
ns = "NUMBER_OF_PROCESSORS"
if ns in os.environ:
nsv = os.environ[ns]
else:
nsv = self._check_number_of_processors_windows()
n = int(nsv)
elif self.on_freebsd():
getconf = "/usr/bin/getconf"
if os.path.exists(getconf):
cmd = "%s NPROCESSORS_ONLN" % (getconf) # or NPROCESSORS_CONF
(retval, output, error_output) = util.run_command(cmd)
if retval == 0 and len(output)>0:
n = int(output[0])
elif self.on_netbsd():
sysctl = "/sbin/sysctl"
if os.path.exists(sysctl):
cmd = "%s -n hw.ncpuonline" % (sysctl)
(retval, output, error_output) = util.run_command(cmd)
if retval == 0 and len(output)>0:
n = int(output[0])
else:
f = '/proc/cpuinfo'
proc_pat= re.compile(r'proces')
if os.path.exists(f):
for line in file(f).readlines():
if proc_pat.search(line):
n += 1
return n
def update_dict(self, dct):
"""Update the environment dictionary with another dictionary."""
self.env.update(dct)
def copy_settings(self, incoming_env, kwds, replace=False):
"""Update the environment dictionary with elements of kwds
from the dictionary in the incoming_env. Lists are extended with the
incoming elements and other types of elements are assigned directly.
@type incoming_env: env_t
@param incoming_env: the source environment
@type kwds: list of strings
@param kwds: elements to copy from the source enviornment
@type replace: bool
@param replace: if True, replace lists in the source environment
"""
for k in kwds:
if k in incoming_env:
t = incoming_env[k]
if isinstance(t,types.ListType) and replace==False:
self.env[k].extend(t)
else:
self.env[k] = t
else:
die("copy_settings() could not read key %s from incoming environment" % k)
def update(self, targets=None):
"""Post process the current environment, setting targets and bindings"""
# if the dct['args'] exists, supplement the targets list with
# that. This is how non-command-line invocations of mbuild
# pass the "other stuff"
if targets == None:
targets = []
if not isinstance(targets,types.ListType):
die("The 'targets' environment option must be a list")
if 'args' in self.env:
args = self.env['args']
if isinstance(args,types.ListType):
targets.extend(args)
else:
die("The 'args' environment option must be a list")
# split up the targets list so we can extract the command line
# variable bindings
just_targets = []
bindings = []
for t in targets:
ap = env_t.assignment_pattern.match(t)
if ap:
msgb("BINDING", "%s --> [%s]" %
(ap.group('name'), ap.group('value')))
bindings.append( (ap.group('name'),
ap.group('value'), 'equals' ))
continue
sp = env_t.supplement_pattern.match(t)
if ap:
msgb("BINDING", "%s --> [%s]" %
(ap.group('name'), ap.group('value')))
bindings.append( (ap.group('name'),
ap.group('value'), 'plusequals') )
continue
just_targets.append(t)
# add command line variable bindings to the environment
for (var,value, how) in bindings:
if how == 'equals':
self.env[var] = value
# early versions of mbuild used target_cpu instead of
# host_cpu. This next override compensates for that,
# compatibility with older clients.
if var == 'target_cpu':
self.env['host_cpu'] = value
elif how == 'plusequals':
self.add_to_var(var,value)
# give precidence to the knob for --host-cpu and --host-os
# over the default binding.
if self.env['arg_host_cpu']:
self.env['host_cpu'] = self.env['arg_host_cpu']
if self.env['arg_host_os']:
self.env['host_os'] = self.env['arg_host_os']
# make sure we understand what host cpu we are dealing
# with. If someone puts in an Intel64 it'll come out as
# x86-64.
self.env['host_cpu'] = self._normalize_cpu_name(self.env['host_cpu'])
self.env['host_os'] = self._normalize_os_name(self.env['host_os'])
self.add_to_var('targets',just_targets)
# old versions of mbuild used target_cpu. To allow them to
# continue to work, we copy target_cpu to host_cpu if
# target_cpu is non null and differs from the setting for the
# host_cpu and the host_cpu is the same as the build_cpu. If
# the host_cpu and build_cpu differ, someone must have set
# host_cpu so leave it alone in that case.
if self.env['target_cpu']:
if self.env['target_cpu'] != self.env['host_cpu']:
# build_cpu and host_cpu start out the same, so only
# change host_cpu if it has the original value.
if self.env['build_cpu'] == self.env['host_cpu']:
self.env['host_cpu'] = self.env['target_cpu']
def process_user_settings(self):
"""Set the initial derived environment settings"""
self.update()
if self.env['mbuild_version']:
self.version()
sys.exit(0)
self._implied_compiler(self.env)
if self.env['silent']:
set_verbosity(0)
else:
arg_verbosity = int(self.env['verbose'])
if arg_verbosity >= 0:
set_verbosity( arg_verbosity )
self.verbose_startup()
# convert several of the lists to strings
for f in ['extra_cxxflags', 'extra_ccflags', 'extra_linkflags',
'extra_libs', 'extra_flags']:
self._flatten_list_to_string(f,self.env)
# distribute the "extra" flags.
if self.env['extra_flags']:
self.env['extra_cxxflags'] += ' ' + self.env['extra_flags']
self.env['extra_ccflags'] += ' ' + self.env['extra_flags']
# This starts the compilation environment off CLEAN
self.set_compiler_env()
# if the user did not use --src-dir, then we check the path to
# the mbuild script. If it there is no path, we assume we are
# in the right directory. If there is a path, we assume that
# is where the sources are, and change the option before anyone
# can see it.
if self.env['src_dir'] == '':
(path_to_src, this_file) = os.path.split(sys.argv[0])
if path_to_src == '':
path_to_src = '.'
self.env['src_dir'] = util.posix_slashes(path_to_src)
# This works around a longstanding python-specific bug in
# cygwin with running multiple threads.
if self.on_windows():
try:
import win32api # we don't use it. We just test for it.
except:
if self.env['cygwin_limit_jobs'] and self.on_cygwin():
msgb('NOTE',
'Using just one worker thread to avoid' + \
' a cygwin threading problem.')
self.env['jobs'] = "1"
# if 0 jobs were specified, try to use 2x the number of cpus.
if self.env['jobs'] == '0':
n = self.number_of_cpus()
if n:
self.env['jobs'] = str(2*n)
msgb('NOTE',
'Setting jobs to %d, 2x the detected number of CPUs (%d)' %
(2*n,n))
else:
self.env['jobs'] = "1"
msgb('NOTE',
'Setting jobs to 1 because we could not detect' +
' the number of CPUs')
if verbose(1):
# print host_cpu here because it may be overridden for
# cross compilations
msgb("HOST_CPU", self.env['host_cpu'])
def _flatten_list_to_string(self, field, dct):
"""See if options has a field named field. If it does and its
value is a list, flatten the list, joining the substrings with
spaces."""
if field in dct:
v = dct[field]
if isinstance(v,types.ListType):
vflat = ' '.join(v)
dct[field]= vflat
def set_defaults(self, dct):
"""Take the dictionary of defaults and apply to the
environment. Any extra bindings and targets should be listed
in the 'args' list option of the dictionary"""
self.parser.set_defaults(**dct)
self.update_dict(dct)
def parse_args(self, user_default_options=None):
"""Call this to re-initialize the environment from the command
line arguments. This calls update() with the results of
command line processing.
@type user_default_options: dict
@param user_default_options: dictionary of default options
"""
# make parse_args() runnable only once per environment.
# ("append"-mode arguments get messed up if args parsed
# more than once.)
if self.parsed_args:
return
self.parsed_args=True
if user_default_options:
# pass a dictionary where keyword args are expected using
# "**" SEE:
# http://docs.python.org/tut/node6.html#SECTION006740000000000000000
self.parser.set_defaults(**user_default_options)
(options, args) = self.parser.parse_args()
dct = vars(options)
dct['args'].extend(args)
self.update_dict(dct)
self.process_user_settings()
def on_ipf(self):
"""@rtype: bool
@return: True iff on IA64"""
if self.env['build_cpu'] == 'ipf':
return True
return False
def on_ia32(self):
"""@rtype: bool
@return: True iff on IA32"""
if self.env['build_cpu'] == 'ia32':
return True
return False
def on_intel64(self):
"""@rtype: bool
@return: True iff on Intel64"""
if self.env['build_cpu'] == 'x86-64':
return True
return False
def on_mac(self):
"""@rtype: bool
@return: True iff on Mac OSX Darwin"""
if self.env['system'] == 'Darwin':
return True
return False
def mac_ver(self):
if self.on_mac():
ver = platform.mac_ver()[0]
(maj,min,rev) = ver.split('.')
return (int(maj),int(min),int(rev))
return None
def check_mac_ver(self, x,y,z):
"""@rtype: bool
@return: True iff on a mac and the version is later than x.y.z"""
if self.on_mac():
(maj,min,rev) = self.mac_ver()
if x > maj:
return False
if x == maj and y > min:
return False
if x == maj and y == min and z > rev:
return False
return True
return False
def on_tiger(self):
"""@rtype: bool
@return: True iff on Mac running OS X Tiger 10.4.x"""
if self.check_mac_ver(10,4,0):
return True
return False
def on_leopard(self):
"""@rtype: bool
@return: True iff on Mac running OS X Leopard 10.5.x"""
if self.check_mac_ver(10,5,0):
return True
return False
def on_freebsd(self):
"""@rtype: bool
@return: True iff on freebsd"""
if self.env['system'] == 'FreeBSD':
return True
return False
def on_netbsd(self):
"""@rtype: bool
@return: True iff on netbsd"""
if self.env['system'] == 'NetBSD':
return True
return False
def on_linux(self):
"""@rtype: bool
@return: True iff on linux"""
if self.env['system'] == 'Linux':
return True
return False
def on_cygwin(self):
"""@rtype: bool
@return: True iff on cygwin"""
if len(self.env['system']) >= 6 and self.env['system'][0:6] == 'CYGWIN':
return True
return False
def windows_native(self):
"""@rtype: bool
@return: True iff on windows native -- not using cygwin"""
if self.env['system'] == 'Windows' or self.env['system'] == 'Microsoft':
return True
return False
def on_windows(self):
"""@rtype: bool
@return: True iff on windows"""
if self.windows_native():
return True
return self.on_cygwin()
def supports_avx(self):
"""Return True if system supports AVX1. Does not work
on windows"""
if self.on_linux():
lines = file('/proc/cpuinfo').readlines()
for l in lines:
if 'avx' in l:
return True
elif self.on_mac():
cmd = "/usr/sbin/sysctl hw.optional.avx1_0"
(retval, output, error_output) = util.run_command(cmd)
if retval == 0 and len(output)>0:
if re.match('hw.optional.avx1_0: 1', output[0]):
return True
# FIXME: find some way of doing this on windows
return False
def _check_mac_64b(self):
"""Check to see if a mac is 64b"""
cmd = "/usr/sbin/sysctl hw.optional.x86_64"
(retval,output, error_output) = util.run_command(cmd)
if retval == 0 and len(output)>0:
if re.match('hw.optional.x86_64: 1', output[0]):
return True
return False
def _normalize_cpu_name(self, name):
"""Internal function. Standardize various CPU identifiers"""
if name in ['ia32', 'i386', 'i686','x86']:
return 'ia32'
elif name in ['ia32e', 'x86_64', 'amd64',
'x86-64', 'Intel64','intel64']:
return 'x86-64'
elif name == 'ia64':
return 'ipf'
elif name[0:5] == 'EM64T':
return 'x86-64'
elif name[0:7] == 'Intel64':
return 'x86-64'
elif name == 'intel64':
return 'x86-64'
elif name[0:3] == 'x86':
return 'ia32'
else:
die("Unknown cpu " + name)
def _normalize_os_name(self,name):
"""Internal function. Standardize various O/S identifiers"""
if name in ['android']:
return 'android'
elif name in ['lin', 'Linux']:
return 'lin'
elif name in ['mac', 'Darwin']:
return 'mac'
elif name in ['bsd', 'FreeBSD', 'NetBSD']:
return 'bsd'
elif name[0:6] == 'CYGWIN':
return 'win'
elif name in ['win', 'Windows_NT']:
return 'win'
else:
die("Unknown os " + name)
def default_compiler(self):
"""Default to ms on windows and gnu everywhere else.
@rtype: string
@returns: "ms" on windows, "clang" on mac, otherwise "gnu"
"""
if self.on_windows():
return "ms"
if self.on_mac():
return "clang"
return "gnu"
def set_compiler_env(self, compiler_family=None):
"""Initialize the build environment based on the compiler
environment variable setting.
Adds in the "extra" flags from the environment.
@type compiler_family: string
@param compiler_family: an override for the default
compiler family (gnu, ms, clang, icl, icc, iclang)
"""
# copy the command line version of the tool overrides to the
# real ones that we use.
if self.env['cxx'] != '':
self.env['CXX'] = self.env['cxx']
if self.env['cc'] != '':
self.env['CC'] = self.env['cc']
if self.env['linker'] != '':
self.env['LINK'] = self.env['linker']
if self.env['ar'] != '':
self.env['AR'] = self.env['ar']
if self.env['as'] != '':
self.env['AS'] = self.env['as']
if compiler_family == None:
if 'compiler' in self.env:
self.env['compiler'] = self.env['compiler'].lower()
compiler_family = self.env['compiler']
else:
die("Compiler family not specified in the environment or as an argument")
if compiler_family == 'gnu':
build_env.set_env_gnu(self)
elif compiler_family == 'clang':
build_env.set_env_clang(self)
elif compiler_family == 'ms':
build_env.set_env_ms(self)
elif compiler_family == 'icc':
build_env.set_env_icc(self)
elif compiler_family == 'iclang':
build_env.set_env_iclang(self)
elif compiler_family == 'icl':
build_env.set_env_icl(self)
else:
die("Compiler family not recognized. Need gnu or ms")
if self.env['use_yasm']:
if verbose(1):
msgb("USE YASM")
build_env.yasm_support(self)
self.add_to_var('CXXFLAGS', self.env['extra_cxxflags'])
self.add_to_var('CCFLAGS', self.env['extra_ccflags'] )
self.add_to_var('LINKFLAGS', self.env['extra_linkflags'] )
self.add_to_var('LIBS', self.env['extra_libs'] )
for d in self.env['extra_defines']:
self.add_define(d)
def resuffix(self, fn, newext):
"""Replace the suffix of single fn (or list of files) with
newext. newext should supply its own dot if you want one.
@type fn: string (or list of strings)
@param fn: a filename
@type newext: string
@param newext: a new extension starting with a '.'
@rtype: string
@return: fn with a new suffix specified by newext
"""
if isinstance(fn,types.ListType):
return map(lambda(x): self.resuffix(x,newext), fn)
else:
(root,ext) = os.path.splitext(fn)
return root + newext
def osenv_add_to_front(self,evar,newstring,osenv=None):
"""Add newstring to front of the environment variable osenv if given
if not given add to os.environ """
environ = os.environ
if osenv:
environ = osenv
if self.on_windows():
sep = ';'
else:
sep = ':'
if evar in environ:
# The environment variable already exists
environ[evar]= newstring + sep + environ[evar]
else:
# Support creation of a new environment variable
environ[evar]= newstring
def path_search(self,exe):
path = os.environ['PATH']
if self.on_freebsd() or self.on_linux() or self.on_cygwin() or self.on_netbsd():
sep = ':'
else:
sep = ';'
for p in path.split(sep):
t = util.prefix_files(p,exe)
if os.path.exists(t):
return t
return None
def make_obj(self,flist):
"""Take file or list of files and return a file or list of
files with the OBJEXT extension from the environment.
@type flist: string or list of strings
@param flist: a filename (or list of filenames)
@rtype: string
@return: fn with a suffix specified %(OBJEXT)s
"""
return self.resuffix(flist,"%(OBJEXT)s")
def build_dir_join(self,files):
"""Make the file (or list of files) with the build
directory name.
@type files: string or list of strings
@param files: filename(s)
@rtype: string or list of strings
@return: filenames prepended with the current build_dir
"""
# FIXME: could do this lazily... and just prepend %(build_dir)s
try:
objdir = self.env['build_dir']
except:
die("build_dir not defined in build_dir_join")
if objdir == '':
return files
return util.prefix_files(objdir, files)
def src_dir_join(self,files):
"""Prefix file (or list of files) with the src directory name.
@type files: string or list of strings
@param files: filename(s)
@rtype: string or list of strings
@return: filenames prepended with the current src_dir
"""
# FIXME: could do this lazily... and just prepend %(src_dir)s
try:
srcdir = self.env['src_dir']
except:
die("src_dir not defined in src_dir_join")
if srcdir == '':
return files
return util.prefix_files(srcdir, files)
def add_define(self,newdef):
"""Add a define or list defines to the CXXFLAGS and CCFLAGS
@type newdef: string or list of strings
@param newdef: string to add to the CXXFLAGS and CCFLAGS
environment variables.
"""
self.add_cc_define(newdef)
self.add_cxx_define(newdef)
self.add_as_define(newdef)
def _collect_defines(self, dlist):
for d in dlist:
if d not in self.env['DEFINES']:
self.env['DEFINES'][d]=True
def add_as_define(self,newdef):
"""Add a define or list defines to the ASFLAGS
@type newdef: string or list of strings
@param newdef: string to add to the ASFLAGS
environment variable.
"""
if isinstance(newdef,types.ListType):
deflist = newdef
else:
deflist = [ newdef ]
self._collect_defines(deflist)
for d in deflist:
self.add_to_var('ASFLAGS', "%(ASDOPT)s" + d )
def add_cc_define(self,newdef):
"""Add a define or list defines to the CCFLAGS
@type newdef: string or list of strings
@param newdef: string to add to the CCFLAGS
environment variable.
"""
if isinstance(newdef,types.ListType):
deflist = newdef
else:
deflist = [ newdef ]
self._collect_defines(deflist)
for d in deflist:
self.add_to_var('CCFLAGS', "%(DOPT)s" + d )
def add_cxx_define(self,newdef):
"""Add a define or list defines to the CXXFLAGS
@type newdef: string or list of strings
@param newdef: string to add to the CXXFLAGS
environment variable.
"""
if isinstance(newdef,types.ListType):
deflist = newdef
else:
deflist = [ newdef ]
self._collect_defines(deflist)
for d in deflist:
self.add_to_var('CXXFLAGS', "%(DOPT)s" + d )
def add_include_dir(self,include_dir):
"""Add a directory or list of directories to the CPPPATH. Just
a short cut for adding things to the list of files in the
env['CPPPATH']
@type include_dir: string or list of strings
@param include_dir: string to add to the CPPPATH environment variable
"""
if isinstance(include_dir,types.ListType):
lst = include_dir
else:
lst = [ include_dir ]
for d in lst:
p = util.posix_slashes(d)
if p not in self.env['CPPPATH']:
self.env['CPPPATH'].append(p)
def add_system_include_dir(self,sys_include_dir):
"""Add a directory or list of directories to the SYSTEMINCLUDE. Just
a short cut for adding things to the list of files in the
env['SYSTEMINCLUDE']
@type sys_include_dir: string or list of strings
@param sys_include_dir: string to add to the SYSTEMINCLUDE environment variable
"""
if isinstance(sys_include_dir,types.ListType):
lst = sys_include_dir
else:
lst = [ sys_include_dir ]
for d in lst:
p = util.posix_slashes(d)
if p not in self.env['SYSTEMINCLUDE']:
self.env['SYSTEMINCLUDE'].append(p)
def add_link_dir(self,link_dir):
"""Add a directory or list of directories to the LINKPATH. These
get included in the LINKFLAGS
@type link_dir: string or list of strings
@param link_dir: string to add to the LINKPATH variable
"""
if isinstance(link_dir,types.ListType):
for d in link_dir:
self.env['LINKPATH'].append(util.posix_slashes(d))
else:
self.env['LINKPATH'].append(util.posix_slashes(link_dir))
def remove_from_var(self, var, value):
"""Remove a substring (or list entry) from env[var]. Opposite
of add_to_var().
@type var: string
@param var: name of a dictionary key
@type value: string
@param value: the value to remove
"""
if var in self.env:
if isinstance(self.env[var], types.ListType):
try:
self.env[var].remove(value)
except:
pass
else:
self.env[var] = re.sub(value,'',self.env[var])
def add_to_var(self, var, value):
"""Add or append value to the environment variable var. If the
variable is not in the environment, then it is added as
is. Otherwise if the variable is in the environment and is a
list then value is appended. Otherwise, the value is appended
as a string with a leading space. This will *NOT* do variable
substitution when adding to a variable.
@type var: string
@param var: name of a dictionary key
@type value: string
@param value: the value to add or append
"""
if var not in self.env:
self.env[var] = value
elif isinstance(self.env[var],types.ListType):
if isinstance(value, types.ListType):
self.env[var].extend(value)
else:
self.env[var].append(value)
else:
self.env[var] += ' ' + value # This would do variable expansion when calling __getitem__
# These strings should be % env expanded.
# COUT should be "-o " on linux. Note the trailing space
# COPT should be "-c" on linux
# OBJNAME and SRCNAME should be fully qualified suffix-wise
# OBJNAMES is used for the link and lib statements
# EXENAME is used for link statements
# LIBNAME is used for lib statements
# SOLIBNAME is used for shared objects "soname" embedded names
# LIBOUT, LINKOUT should be set appropriately. Trailing spaces needed on linux
# DLLOPT is needed for dynamic libraries
# Example:
# a = '%(lang)s has %(c)03d quote types.' % dict(lang='Python', c=2)
def _add_c_compile(self):
s = "%(CC)s %(CPPINCLUDES)s %(SYSINCLUDES)s %(CCFLAGS)s %(COPT)s %(COUT)s%(OBJNAME)s %(SRCNAME)s"
return s
def _add_assemble(self):
s = "%(AS)s %(CPPINCLUDES)s %(SYSINCLUDES)s %(ASFLAGS)s %(ASMOUT)s%(OBJNAME)s %(SRCNAME)s"
return s
def _add_cxx_compile(self):
s = "%(CXX)s %(CPPINCLUDES)s %(SYSINCLUDES)s %(CXXFLAGS)s %(COPT)s %(COUT)s%(OBJNAME)s %(SRCNAME)s"
return s
def _add_link(self):
s = "%(LINK)s %(LINKFLAGS)s %(LINKOUT)s%(EXENAME)s %(OBJNAMES)s %(LIBS)s"
return s
def _add_static_lib(self):
s = [ _remove_libname,
"%(AR)s %(ARFLAGS)s %(LIBOUT)s%(LIBNAME)s %(OBJNAMES)s" ]
return s
def _add_dynamic_lib(self):
s = "%(LINK)s %(LINKFLAGS)s %(DLLOPT)s %(LIBOUT)s%(LIBNAME)s %(OBJNAMES)s %(LIBS)s"
return s
def _add_cxx_shared_lib(self):
s = "%(CXX)s %(LINKFLAGS)s %(DLLOPT)s %(COUT)s%(LIBNAME)s %(OBJNAMES)s %(LIBS)s"
return s
def _add_res_file_cmd(self):
s = "%(RC)s %(RCFLAGS)s /fo%(RESNAME)s %(RCNAME)s"
return s
def _add_default_builders(self):
"""Private. Part of initialization for the environment. Sets
the default builders"""
# Instead use default function if these are not set.
self.env['ASSEMBLE_BUILDER'] = None
self.env['CXX_COMPILE_BUILDER'] = None
self.env['CC_COMPILE_BUILDER'] = None
self.env['LINK_BUILDER'] = None
self.env['STATIC_LIBRARY_BUILDER'] = None
self.env['DYNAMIC_LIBRARY_BUILDER'] = None
self.env['RES_FILE_BUILDER'] = None
def _add_default_builder_templates(self):
"""Private. Part of initialization for the environment. Sets
the default templates used by the default builders"""
self.env['CC_COMPILE_COMMAND'] = self._add_c_compile()
self.env['CXX_COMPILE_COMMAND'] = self._add_cxx_compile()
self.env['ASSEMBLE_COMMAND'] = self._add_assemble()
self.env['LINK_COMMAND'] = self._add_link()
self.env['STATIC_LIB_COMMAND'] = self._add_static_lib()
self.env['DYNAMIC_LIB_COMMAND'] = self._add_dynamic_lib()
self.env['CXX_SHARED_LIB_COMMAND'] = self._add_cxx_shared_lib()
self.env['RES_FILE_COMMAND'] = self._add_res_file_cmd()
def _add_compilation_support(self):
"""Private. Part of initialization for the environment. Sets
the default builders and templates."""
self._add_default_builders()
self._add_default_builder_templates()
def escape_string(self,s):
if self.on_windows():
return util.cond_add_quotes(s)
else:
t = s.replace(' ','\ ')
return t
def _escape_list_of_strings(self,sl):
n = []
for s in sl:
n.append(self.escape_string(s))
return n
def _make_cpp_include(self):
s = []
iopt = self.env['IOPT']
for p in self.env['CPPPATH']:
s.extend([iopt, self.escape_string(p), ' '])
return ''.join(s)
def _make_system_include(self):
s = []
iopt = self.env['ISYSOPT']
for p in self.env['SYSTEMINCLUDE']:
s.extend([iopt, self.escape_string(p), ' '])
return ''.join(s)
def _make_link_dirs(self):
s = []
lopt = self.env['LOPT']
for p in self.env['LINKPATH']:
s.extend([lopt, self.escape_string(p), ' '])
return ''.join(s)
def _make_cpp_flags(self):
self.env['CPPINCLUDES'] = self._make_cpp_include()
def _make_sys_include_flags(self):
self.env['SYSINCLUDES'] = self._make_system_include()
def _make_link_flags(self):
self.env['LINKDIRS'] = self._make_link_dirs()
def make_derived_flags(self):
"""Put together any derived flags. This is required to be
called by builder functions before they do their expansion.
"""
self._make_cpp_flags()
self._make_sys_include_flags()
self._make_link_flags()
def assemble(self, source, obj=None):
"""Indirection function. Reads builder function from the
environment variable ASSEMBLER_BUILDER. Assemble a source file
to the obj file. If no obj file name is given one will be
created in the build directory.
@type source: string
@param source: filename to assemble
@type obj: string
@param obj: output filename.
@rtype: L{plan_t}
@return: an input for the DAG
"""
# FIXME abspath breaks windows compilation under cygwin python
new_source = os.path.abspath(source)
f= self.env['ASSEMBLE_BUILDER']
if f:
return f(new_source,obj)
return self._assemble_default(new_source,obj)
def cxx_compile(self, source, obj=None):
"""Indirection function. Reads builder function from the
environment variable CXX_COMPILE_BUILDER. C++-compile a source
file to a file called obj. If no obj file name is given one
will be created in the build directory.
@type source: string
@param source: filename to compile
@type obj: string
@param obj: output filename.
@rtype: L{plan_t}
@return: an input for the DAG
"""
# FIXME abspath breaks windows compilation under cygwin python
new_source = os.path.abspath(source)
f = self.env['CXX_COMPILE_BUILDER']
if f:
return f(new_source,obj)
return self._cxx_compile_default(new_source,obj)
def cc_compile(self, source, obj=None):
"""Indirection function. Reads builder function from the
environment variable CC_COMPILE_BUILDER. C-compile a source
file to a file named obj. If no obj file name is given one
will be created in the build directory.
@type source: string
@param source: filename to compile
@type obj: string
@param obj: output filename.
@rtype: L{plan_t}
@return: an input for the DAG
"""
# FIXME abspath breaks windows compilation under cygwin python
new_source = os.path.abspath(source)
f = self.env['CC_COMPILE_BUILDER']
if f:
return f(new_source,obj)
return self._cc_compile_default(new_source,obj)
def link(self, objs, exename, relocate=False):
"""Indirection function. Reads builder function from the
environment variable LINK_BUILDER. Link an executable from
objs. If relocate is True, then prefix exename with the build
directory name.
@type objs: list of strings
@param objs: filenames to link
@type exename: string
@param exename: output filename.
@type relocate: bool
@param relocate: If true, relocate the exename to the build directory.
@rtype: L{plan_t}
@return: an input for the DAG
"""
f = self.env['LINK_BUILDER']
if f:
return f(objs,exename, relocate)
return self._link_default(objs,exename,relocate)
def static_lib(self, objs, libname, relocate=False):
"""Indirection function. Reads builder function from the
environment variable STATIC_LIBRARY_BUILDER. Make a static
library libname from objs. If relocate is True, then prefix
libname with the build directory name
@type objs: list of strings
@param objs: filenames to link
@type libname: string
@param libname: output filename.
@type relocate: bool
@param relocate: If true, relocate the library to the build directory.
@rtype: L{plan_t}
@return: an input for the DAG
"""
f = self.env['STATIC_LIBRARY_BUILDER']
if f:
return f(objs,libname, relocate)
return self._static_lib_default(objs,libname,relocate)
def compile_and_static_lib(self, dag, sources, libname):
"""Build all the sources by adding them to the dag. Use the
suffixes to figure out how to handle the files. The dag can be
passed to a work queue. See the build function. """
# Compile
objs = self.compile(dag, sources)
# Link the lib
dag.add(self, self.static_lib(objs, libname, relocate=True))
def dynamic_lib_name(self, base):
return self.shared_lib_name(base)
def shared_lib_name(self, base):
if self.on_windows():
s = '{}%(DLLEXT)s'.format(base)
else:
s = 'lib{}%(DLLEXT)s'.format(base)
return s
def static_lib_name(self, base):
if self.on_windows():
s = '{}%(LIBEXT)s'.format(base)
else:
s = 'lib{}%(LIBEXT)s'.format(base)
return s
def dynamic_lib(self, objs, libname, relocate=False):
"""Indirection function. Reads builder function from the
environment variable DYNAMIC_LIBRARY_BUILDER. Make a dynamic
library libname from objs. If relocate is True, then prefix
libname with the build directory name
@type objs: list of strings
@param objs: filenames to link
@type libname: string
@param libname: output filename.
@type relocate: bool
@param relocate: If true, relocate the library to the build directory.
@rtype: L{plan_t}
@return: an input for the DAG
"""
f = self.env['DYNAMIC_LIBRARY_BUILDER']
if f:
return f(objs,libname, relocate)
return self._dynamic_lib_default(objs,libname,relocate)
def rc_file(self, rc_file, res_file=None):
"""Indirection function. For making RES files
from RC files on windows.
@type rc_file: string
@param rc_file: filename for RC file
@type res_file: string
@param res_file: filename for RES file
"""
f = self.env['RES_FILE_BUILDER']
if f:
return f(rc_file, res_file)
return self._res_file_builder_default(rc_file, res_file)
def _escape_dict(self, d):
file_name_keys = ['SRCNAME','OBJNAME', 'LIBNAME',
'SOLIBNAME', 'EXENAME',
'RCNAME', 'RESNAME' ]
for k in file_name_keys:
if k in d:
d[k] = self.escape_string(d[k])
def _assemble_default(self, source, obj=None):
"""Assemble a source file to the obj file. If no obj file name
is given one will be created in the build directory."""
cmd = self.env['ASSEMBLE_COMMAND']
d = copy.copy(self)
self.make_derived_flags()
d['SRCNAME'] = source
if obj == None:
(filepath,fullfilename) = os.path.split(source)
(filename,ext) = os.path.splitext(fullfilename)
obj = filename + self.env['OBJEXT']
obj = self.build_dir_join(obj)
d['OBJNAME'] = obj
self._escape_dict(d)
s = self.expand_string(cmd, d)
return plan.plan_t(command=s, output=obj, input=source)
def _make_pdb_file(self,obj):
"""If obj obj file ends in '.obj$' or '%(OBJEXT)s' replace it
so it looks like: '%(PDBEXT)s'"""
if env_t.obj_pattern.search(obj):
pdbfile = env_t.obj_pattern.sub('%(PDBEXT)s',obj)
elif env_t.objext_pattern.search(obj):
pdbfile = env_t.objext_pattern.sub('%(PDBEXT)s',obj)
else:
die("Could not make PDB file from OBJ file: %s" % obj)
return pdbfile
def _cxx_compile_default(self, source, obj=None):
"""C++-compile a source file to a file called obj. If no obj file
name is given one will be created in the build directory."""
cmd = self.env['CXX_COMPILE_COMMAND']
d = copy.copy(self)
self.make_derived_flags()
d['SRCNAME'] = source
if obj == None:
(filepath,fullfilename) = os.path.split(source)
(filename,ext) = os.path.splitext(fullfilename)
obj = filename + self.env['OBJEXT']
obj = self.build_dir_join(obj)
if d['separate_pdb_files'] and d['compiler'] == 'ms' and d['debug'] == 1:
pdbfile = self._make_pdb_file(obj)
d['CXXFLAGS'] += ' /Fd%s ' % pdbfile
d['OBJNAME'] = obj
self._escape_dict(d)
s = self.expand_string(cmd, d)
return plan.plan_t(command=s, output=obj, input=source)
def _cc_compile_default(self, source, obj=None):
"""C-compile a source file to a file named obj. If no obj file
name is given one will be created in the build directory."""
cmd = self.env['CC_COMPILE_COMMAND']
d = copy.copy(self)
self.make_derived_flags()
d['SRCNAME'] = source
if obj == None:
(filepath,fullfilename) = os.path.split(source)
(filename,ext) = os.path.splitext(fullfilename)
obj = filename + self.env['OBJEXT']
obj = self.build_dir_join(obj)
if d['separate_pdb_files'] and d['compiler'] == 'ms' and d['debug'] == 1:
pdbfile = self._make_pdb_file(obj)
d['CCFLAGS'] += ' /Fd%s ' % pdbfile
d['OBJNAME'] = obj
self._escape_dict(d)
s = self.expand_string(cmd, d)
return plan.plan_t(command=s, output=obj, input=source)
def _find_libs(self):
libs = []
for lib in self.expand_string('%(LIBS)s').split():
if lib:
# ignore libraries that start with "-" as in -lc -lm. I
# would not know what suffix to put on them anyway
# (LIBEXT,DLLEXT) without trying them all.
if lib[0]=='-':
continue
if os.path.exists(lib):
#msgb("ADDING DEPENDENCE ON LIBRARY", lib)
libs.append(lib)
else:
for dir in self.env['LINKPATH']:
t = mbuild.join(dir,lib)
if os.path.exists(t):
#msgb("ADDING DERIVED DEPENDENCE ON LIBRARY", t)
libs.append(t)
return libs
def _link_default(self, objs, exename, relocate=False):
"""Link an executable from objs. If relocate is True,
then prefix exename with the build directory name."""
cmd = self.env['LINK_COMMAND']
d = copy.copy(self)
self.make_derived_flags()
if relocate:
exename = self.build_dir_join(exename)
d['EXENAME'] = exename
if not isinstance(objs, types.ListType):
objs = [ objs ]
objs = self._escape_list_of_strings(objs)
obj = " ".join(objs)
d['OBJNAMES'] = obj
self._escape_dict(d)
s = self.expand_string(cmd, d)
return plan.plan_t(command=s, output=exename, input=objs + self._find_libs())
def _static_lib_default(self, objs, libname, relocate=False):
"""Make a static library libname from objs. If relocate is True,
then prefix libname with the build directory name"""
d = copy.copy(self)
self.make_derived_flags()
if relocate:
libname = self.build_dir_join(libname)
d['LIBNAME'] = libname
if not isinstance(objs,types.ListType):
objs = [ objs ]
objs = self._escape_list_of_strings(objs)
obj = " ".join(objs)
d['OBJNAMES'] = obj
self._escape_dict(d)
n = []
scmd = self.env['STATIC_LIB_COMMAND']
if not isinstance(scmd,types.ListType):
scmd = [ scmd ]
for cmd in scmd:
if isinstance(cmd,types.StringType):
n.append(self.expand_string(cmd, d))
else:
n.append(cmd)
# we pass args to the python scripts... Must expand now or
# else suffer concurrency bugs at build time.
args = [ self.expand_string('%(LIBNAME)s') ]
return plan.plan_t(command=n, output=libname,
args=args,
input=objs, env=self)
def _dynamic_lib_default(self, objs, libname, relocate=False):
"""Make a dynamic library libname from objs. If relocate is True,
then prefix libname with the build directory name"""
if self.env['compiler'] in [ 'gnu','icc','clang','iclang']:
cmd = self.env['CXX_SHARED_LIB_COMMAND']
else:
cmd = self.env['DYNAMIC_LIB_COMMAND']
d = copy.copy(self)
self.make_derived_flags()
if relocate:
libname = self.build_dir_join(libname)
d['LIBNAME'] = libname
d['SOLIBNAME'] = os.path.basename(libname)
if not isinstance(objs,types.ListType):
objs = [ objs ]
objs = self._escape_list_of_strings(objs)
obj = " ".join(objs)
d['OBJNAMES'] = obj
self._escape_dict(d)
s = self.expand_string(cmd, d)
return plan.plan_t(command=s, output=libname,
input=objs + self._find_libs())
def _res_file_builder_default(self, rc_file,res_file=None):
"""Make a res file from an rc file. Windows only."""
cmd = self.env['RES_FILE_COMMAND']
d = copy.copy(self)
if not res_file:
res_file = self.build_dir_join(self.resuffix(rc_file,'%(RESEXT)s'))
d['RESNAME'] = res_file
d['RCNAME'] = rc_file
self._escape_dict(d)
s = self.expand_string(cmd, d)
return plan.plan_t(command=s,
output=res_file,
input=rc_file)
def compile(self, dag, sources):
"""Build all the sources by adding them to the dag. Use the
suffixes to figure out how to handle the files. The dag can be
passed to a work queue. See the build function. """
objs = []
for s in sources:
b = os.path.basename(s) # filename component of path/filename
(base,ext) = os.path.splitext(b)
if ext in ['.rc' ]:
obj = self.build_dir_join(self.resuffix(b,'%(RESEXT)s'))
else:
obj = self.build_dir_join(self.make_obj(b))
if ext in ['.asm', '.s' ]:
c = self.assemble( s, obj )
elif ext in ['.c']:
c = self.cc_compile( s, obj )
elif ext in ['.cpp', '.C' ]:
c = self.cxx_compile( s, obj )
elif ext in ['.rc' ]:
c = self.rc_file( s, obj ) # obj is a res file in this case
else:
die("Unsupported file type %s" % (s))
cmd = dag.add(self,c)
objs.append(self.expand_string(obj))
return objs
def compile_and_link(self, dag, sources, exe, shared_object=False, libs=[]):
"""Build all the sources by adding them to the dag. Use the
suffixes to figure out how to handle the files. The dag can be
passed to a work queue. See the build function. """
objs = self.compile(dag, sources)
if shared_object:
cmd2 = dag.add(self,
self.dynamic_lib(objs + libs, exe, relocate=True))
else:
cmd2 = dag.add(self,
self.link(objs + libs , exe,relocate=True))
return cmd2
def build(self, work_queue, dag, phase='BUILD',terminate_on_errors=False):
"""Build everything in the work queue"""
okay = work_queue.build(dag=dag, die_on_errors=False)
if not okay:
if terminate_on_errors:
die("[%s] failed." % phase)
else:
msgb(phase,"failed.")
return False
msgb(phase, "succeeded")
return True