LSSTApplications  1.1.2+25,10.0+13,10.0+132,10.0+133,10.0+224,10.0+41,10.0+8,10.0-1-g0f53050+14,10.0-1-g4b7b172+19,10.0-1-g61a5bae+98,10.0-1-g7408a83+3,10.0-1-gc1e0f5a+19,10.0-1-gdb4482e+14,10.0-11-g3947115+2,10.0-12-g8719d8b+2,10.0-15-ga3f480f+1,10.0-2-g4f67435,10.0-2-gcb4bc6c+26,10.0-28-gf7f57a9+1,10.0-3-g1bbe32c+14,10.0-3-g5b46d21,10.0-4-g027f45f+5,10.0-4-g86f66b5+2,10.0-4-gc4fccf3+24,10.0-40-g4349866+2,10.0-5-g766159b,10.0-5-gca2295e+25,10.0-6-g462a451+1
LSSTDataManagementBasePackage
state.py
Go to the documentation of this file.
1 ##
2 # @file state.py
3 #
4 # This module acts like a singleton, holding all global state for sconsUtils.
5 # This includes the primary Environment object (state.env), the message log (state.log),
6 # the command-line variables object (state.opts), and a dictionary of command-line targets
7 # used to setup aliases, default targets, and dependencies (state.targets). All four of
8 # these variables are aliased to the main lsst.sconsUtils scope, so there should be no
9 # need for users to deal with the state module directly.
10 #
11 # These are all initialized when the module is imported, but may be modified by other code
12 # (particularly dependencies.configure()).
13 ##
14 import os
15 import re
16 
17 import SCons.Script
18 import SCons.Conftest
19 import eupsForScons
20 
21 SCons.Script.EnsureSConsVersion(2, 1, 0)
22 
23 ##
24 # @brief A dictionary of SCons aliases and targets.
25 #
26 # These are used to setup aliases, default targets, and dependencies by BasicSConstruct.finish().
27 # While one can still use env.Alias to setup aliases (and should for "install"), putting targets
28 # here will generally provide better build-time dependency handling (like ensuring everything
29 # is built before we try to install, and making sure SCons doesn't rebuild the world before
30 # installing).
31 #
32 # Users can add additional keys to the dictionary if desired.
33 #
34 # Targets should be added by calling extend() or using += on the dict values, to keep the lists of
35 # targets from turning into lists-of-lists.
36 ##
37 targets = {"doc": [], "tests": [], "lib": [], "python": [], "examples": [], "include": [], "version": []}
38 
39 ## @cond INTERNAL
40 
41 env = None
42 log = None
43 opts = None
44 
45 
46 def _initOptions():
47  SCons.Script.AddOption('--checkDependencies', dest='checkDependencies',
48  action='store_true', default=False,
49  help="Verify dependencies with autoconf-style tests.")
50  SCons.Script.AddOption('--filterWarn', dest='filterWarn', action='store_true', default=False,
51  help="Filter out a class of warnings deemed irrelevant"),
52  SCons.Script.AddOption('--force', dest='force', action='store_true', default=False,
53  help="Set to force possibly dangerous behaviours")
54  SCons.Script.AddOption('--linkFarmDir', dest='linkFarmDir', action='store', default=None,
55  help="The directory of symbolic links needed to build and use the package")
56  SCons.Script.AddOption('--prefix', dest='prefix', action='store', default=False,
57  help="Specify the install destination")
58  SCons.Script.AddOption('--setenv', dest='setenv', action='store_true', default=False,
59  help="Treat arguments such as Foo=bar as defining construction variables")
60  SCons.Script.AddOption('--tag', dest='tag', action='store', default=None,
61  help="Declare product with this eups tag")
62  SCons.Script.AddOption('--verbose', dest='verbose', action='store_true', default=False,
63  help="Print additional messages for debugging.")
64  SCons.Script.AddOption('--traceback', dest='traceback', action='store_true', default=False,
65  help="Print full exception tracebacks when errors occur.")
66 
67 def _initLog():
68  from . import utils
69  global log
70  log = utils.Log()
71 
72 def _initVariables():
73  files = []
74  if SCons.Script.ARGUMENTS.has_key("optfile"):
75  configfile = SCons.Script.ARGUMENTS["optfile"]
76  if configfile not in files:
77  files.append(configfile)
78  for file in files:
79  if not os.path.isfile(file):
80  log.warn("Warning: Will ignore non-existent options file, %s" % file)
81  if not SCons.Script.ARGUMENTS.has_key("optfile"):
82  files.append("buildOpts.py")
83  global opts
84  opts = SCons.Script.Variables(files)
85  opts.AddVariables(
86  ('archflags', 'Extra architecture specification to add to CC/LINK flags (e.g. -m32)', ''),
87  ('cc', 'Choose the compiler to use', ''),
88  SCons.Script.BoolVariable('debug', 'Set to enable debugging flags (use --debug)', True),
89  ('eupsdb', 'Specify which element of EUPS_PATH should be used', None),
90  ('flavor', 'Set the build flavor', None),
91  SCons.Script.BoolVariable('force', 'Set to force possibly dangerous behaviours', False),
92  ('optfile', 'Specify a file to read default options from', None),
93  ('prefix', 'Specify the install destination', None),
94  SCons.Script.EnumVariable('opt', 'Set the optimisation level', 0,
95  allowed_values=('0', '1', '2', '3')),
96  SCons.Script.EnumVariable('profile', 'Compile/link for profiler', 0,
97  allowed_values=('0', '1', 'pg', 'gcov')),
98  ('version', 'Specify the version to declare', None),
99  ('baseversion', 'Specify the current base version', None),
100  ('optFiles', "Specify a list of files that SHOULD be optimized", None),
101  ('noOptFiles', "Specify a list of files that should NOT be optimized", None),
102  )
103 
104 def _initEnvironment():
105  """Construction and basic setup of the state.env variable."""
106 
107  ourEnv = {}
108  for key in ('EUPS_DIR', 'EUPS_PATH', 'EUPS_SHELL', 'PATH' ,'DYLD_LIBRARY_PATH', 'LD_LIBRARY_PATH',
109  'SHELL', 'TMPDIR', 'TEMP', 'TMP', 'EUPS_LOCK_PID', 'XPA_PORT'):
110  if key in os.environ:
111  ourEnv[key] = os.environ[key]
112 
113  # Find and propagate EUPS environment variables.
114  cfgPath = []
115  for k in os.environ.keys():
116  m = re.search(r"^(?P<name>\w+)_DIR(?P<extra>_EXTRA)?$", k)
117  if not m: continue
118  cfgPath.append(os.path.join(os.environ[k], "ups"))
119  if m.group("extra"):
120  cfgPath.append(os.environ[k])
121  else:
122  cfgPath.append(os.path.join(os.environ[k], "ups"))
123  p = m.group("name")
124  varname = eupsForScons.utils.setupEnvNameFor(p)
125  if os.environ.has_key(varname):
126  ourEnv[varname] = os.environ[varname]
127  ourEnv[k] = os.environ[k]
128 
129  # Recursively walk LSST_CFG_PATH
130  for root in os.environ.get("LSST_CFG_PATH", "").split(":"):
131  for base, dirs, files in os.walk(root):
132  dirs = [d for d in dirs if not d.startswith(".")]
133  cfgPath.insert(0, base)
134  #
135  # Add any values marked as export=FOO=XXX[,GOO=YYY] to ourEnv
136  #
137  exportVal = SCons.Script.ARGUMENTS.pop("export", None)
138  if exportVal:
139  for kv in exportVal.split(','):
140  k, v = kv.split('=')
141  ourEnv[k] = v
142  global env
143  sconsUtilsPath, thisFile = os.path.split(__file__)
144  toolPath = os.path.join(sconsUtilsPath, "tools")
145  env = SCons.Script.Environment(
146  ENV=ourEnv,
147  variables=opts,
148  toolpath=[toolPath],
149  tools=["default", "cuda"]
150  )
151  env.cfgPath = cfgPath
152  #
153  # We don't want "lib" inserted at the beginning of loadable module names;
154  # we'll import them under their given names.
155  #
156  env['LDMODULEPREFIX'] = ""
157  if env['PLATFORM'] == 'darwin':
158  env['LDMODULESUFFIX'] = ".so"
159  if not re.search(r"-install_name", str(env['SHLINKFLAGS'])):
160  env.Append(SHLINKFLAGS = ["-Wl,-install_name", "-Wl,${TARGET.file}"])
161  #
162  # Remove valid options from the arguments
163  #
164  for opt in opts.keys():
165  try:
166  del SCons.Script.ARGUMENTS[opt]
167  except KeyError:
168  pass
169  #
170  # Process those arguments
171  #
172  for k in ("force", "prefix"): # these may now be set as options instead of variables
173  if SCons.Script.GetOption(k):
174  env[k] = SCons.Script.GetOption(k)
175 
176  if env['debug']:
177  env.Append(CCFLAGS = ['-g'])
178  #
179  # Find the eups path, replace 'flavor' in favor of 'PLATFORM' if needed.
180  #
181  eupsPath = None
182  try:
183  db = env['eupsdb']
184  if not os.environ.has_key('EUPS_PATH'):
185  raise RuntimeError("You can't use eupsdb=XXX without an EUPS_PATH set")
186  eupsPath = None
187  for d in os.environ['EUPS_PATH'].split(':'):
188  if re.search(r"/%s$|^%s/|/%s/" % (db, db, db), d):
189  eupsPath = d
190  break
191  if not eupsPath:
192  raise RuntimeError("I cannot find DB \"%s\" in $EUPS_PATH" % db)
193  except KeyError:
194  if os.environ.has_key('EUPS_PATH'):
195  eupsPath = os.environ['EUPS_PATH'].split(':')[0]
196  env['eupsPath'] = eupsPath
197  try:
198  env['PLATFORM'] = env['flavor']
199  del env['flavor']
200  except KeyError:
201  pass
202  #
203  # Check arguments
204  #
205  errorStr = ""
206  #
207  # Process otherwise unknown arguments. If setenv is true,
208  # set construction variables; otherwise generate an error
209  #
210  if SCons.Script.GetOption("setenv"):
211  for key in SCons.Script.ARGUMENTS.keys():
212  env[key] = SCons.Script.Split(SCons.Script.ARGUMENTS[key])
213  else:
214  for key in SCons.Script.ARGUMENTS.keys():
215  errorStr += " %s=%s" % (key, SCons.Script.ARGUMENTS[key])
216  if errorStr:
217  log.fail("Unprocessed arguments:%s" % errorStr)
218  #
219  # We need a binary name, not just "Posix"
220  #
221  env['eupsFlavor'] = eupsForScons.flavor()
222 
223 def _configureCommon():
224  """Configuration checks for the compiler, platform, and standard libraries."""
225  #
226  # Is the C compiler really gcc/g++?
227  #
228  def ClassifyCc(context):
229  """Return a pair of string identifying the compiler in use
230 
231  @return (compiler, version) as a pair of strings, or ("unknown", "unknown") if unknown
232  """
233  versionNameList = (
234  (r"gcc +\(.+\) +([0-9.a-zA-Z]+)", "gcc"),
235  (r"LLVM +version +([0-9.a-zA-Z]+) ", "clang"), # clang on Mac
236  (r"clang +version +([0-9.a-zA-Z]+) ", "clang"), # clang on linux
237  (r"\(ICC\) +([0-9.a-zA-Z]+) ", "icc"),
238  )
239 
240  context.Message("Checking who built the CC compiler...")
241  result = context.TryAction(SCons.Script.Action(r"$CC --version > $TARGET"))
242  ccVersDumpOK, ccVersDump = result[0:2]
243  if ccVersDumpOK:
244  for reStr, compilerName in versionNameList:
245  match = re.search(reStr, ccVersDump)
246  if match:
247  compilerVersion = match.groups()[0]
248  return (compilerName, compilerVersion)
249  return ("unknown", "unknown")
250 
251  if env.GetOption("clean") or env.GetOption("no_exec") or env.GetOption("help") :
252  env.whichCc = "unknown" # who cares? We're cleaning/not execing, not building
253  else:
254  if env['cc'] != '':
255  CC = CXX = None
256  if re.search(r"^gcc(-\d+(\.\d+)*)?( |$)", env['cc']):
257  CC = env['cc']
258  CXX = re.sub(r"^gcc", "g++", CC)
259  elif re.search(r"^icc( |$)", env['cc']):
260  CC = env['cc']
261  CXX = re.sub(r"^icc", "icpc", CC)
262  elif re.search(r"^clang( |$)", env['cc']):
263  CC = env['cc']
264  CXX = re.sub(r"^clang", "clang++", CC)
265  elif re.search(r"^cc( |$)", env['cc']):
266  CC = env['cc']
267  CXX = re.sub(r"^cc", "c++", CC)
268  else:
269  log.fail("Unrecognised compiler:%s" % env['cc'])
270  env0 = SCons.Script.Environment()
271  if CC and env['CC'] == env0['CC']:
272  env['CC'] = CC
273  if CC and env['CXX'] == env0['CXX']:
274  env['CXX'] = CXX
275  conf = env.Configure(custom_tests = {'ClassifyCc' : ClassifyCc,})
276  env.whichCc, env.ccVersion = conf.ClassifyCc()
277  log.info("CC is %s version %s" % (env.whichCc, env.ccVersion))
278  conf.Finish()
279  #
280  # Compiler flags, including CCFLAGS for C and C++ and CXXFLAGS for C++ only
281  #
282  ARCHFLAGS = os.environ.get("ARCHFLAGS", env.get('archflags'))
283  if ARCHFLAGS:
284  env.Append(CCFLAGS = [ARCHFLAGS.split()])
285  env.Append(LINKFLAGS = [ARCHFLAGS.split()])
286  # We'll add warning and optimisation options last
287  if env['profile'] == '1' or env['profile'] == "pg":
288  env.Append(CCFLAGS = ['-pg'])
289  env.Append(LINKFLAGS = ['-pg'])
290  elif env['profile'] == 'gcov':
291  env.Append(CCFLAGS = '--coverage')
292  env.Append(LINKFLAGS = '--coverage')
293 
294  #
295  # Enable C++11 support (and C99 support for gcc)
296  #
297  if not (env.GetOption("clean") or env.GetOption("help") or env.GetOption("no_exec")):
298  log.info("Checking for C++11 support")
299  conf = env.Configure()
300  for cpp11Arg in ("-std=%s" % (val,) for val in ("c++11", "c++0x")):
301  conf.env = env.Clone()
302  conf.env.Append(CXXFLAGS = cpp11Arg)
303  if conf.CheckCXX():
304  env.Append(CXXFLAGS = cpp11Arg)
305  log.info("C++11 supported with %r" % (cpp11Arg,))
306  break
307  else:
308  log.fail("C++11 extensions could not be enabled for compiler %r" % env.whichCc)
309  conf.Finish()
310 
311  #
312  # Is C++'s TR1 available? If not, use e.g. #include "lsst/tr1/foo.h"
313  #
314  # NOTE: previously this was only checked when none of --clean, --help, and --noexec,
315  # but that was causing "no" to be cached and used on later runs.
316  if not (env.GetOption("clean") or env.GetOption("help") or env.GetOption("no_exec")):
317  conf = env.Configure()
318  env.Append(CCFLAGS = ['-DLSST_HAVE_TR1=%d' % int(conf.CheckCXXHeader("tr1/unordered_map"))])
319  conf.Finish()
320  #
321  # Byte order
322  #
323  import socket
324  if socket.htons(1) != 1:
325  env.Append(CCFLAGS = ['-DLSST_LITTLE_ENDIAN=1'])
326  #
327  # If we're linking to libraries that themselves linked to
328  # shareable libraries we need to do something special.
329  #
330  if (re.search(r"^(Linux|Linux64)$", env["eupsFlavor"]) and os.environ.has_key("LD_LIBRARY_PATH")):
331  env.Append(LINKFLAGS = ["-Wl,-rpath-link"])
332  env.Append(LINKFLAGS = ["-Wl,%s" % os.environ["LD_LIBRARY_PATH"]])
333  #
334  # Set the optimization level.
335  #
336  if env['opt']:
337  env["CCFLAGS"] = [o for o in env["CCFLAGS"] if not re.search(r"^-O(\d|s)$", o)]
338  env.MergeFlags('-O%d' % int(env['opt']))
339  #
340  # Set compiler-specific warning flags.
341  #
342  if env.whichCc == "clang":
343  env.Append(CCFLAGS = ['-Wall'])
344  env["CCFLAGS"] = [o for o in env["CCFLAGS"] if not re.search(r"^-mno-fused-madd$", o)]
345 
346  ignoreWarnings = {
347  "unused-function" : 'boost::regex has functions in anon namespaces in headers',
348  }
349  filterWarnings = {
350  "attributes" : "clang pretends to be g++, but complains about g++ attributes such as flatten",
351  "char-subscripts" : 'seems innocous enough, and is used by boost',
352  "constant-logical-operand" : "Used by eigen 2.0.15. Should get this fixed",
353  "format-security" : "format string is not a string literal",
354  "mismatched-tags" : "mixed class and struct. Used by gcc 4.2 RTL and eigen 2.0.15",
355  "parentheses" : "equality comparison with extraneous parentheses",
356  "shorten-64-to-32" : "implicit conversion loses integer precision",
357  "self-assign" : "x = x",
358  "unknown-pragmas" : "unknown pragma ignored",
359  "deprecated-register" : "register is deprecated",
360  }
361  for k in ignoreWarnings.keys():
362  env.Append(CCFLAGS = ["-Wno-%s" % k])
363  if env.GetOption('filterWarn'):
364  for k in filterWarnings.keys():
365  env.Append(CCFLAGS = ["-Wno-%s" % k])
366  elif env.whichCc == "gcc":
367  env.Append(CCFLAGS = ['-Wall'])
368  env.Append(CCFLAGS = ["-Wno-unknown-pragmas"]) # we don't want complaints about icc/clang pragmas
369  elif env.whichCc == "icc":
370  env.Append(CCFLAGS = ['-Wall'])
371  filterWarnings = {
372  21 : 'type qualifiers are meaningless in this declaration',
373  68 : 'integer conversion resulted in a change of sign',
374  111 : 'statement is unreachable',
375  191 : 'type qualifier is meaningless on cast type',
376  193 : 'zero used for undefined preprocessing identifier "SYMB"',
377  279 : 'controlling expression is constant',
378  304 : 'access control not specified ("public" by default)', # comes from boost
379  383 : 'value copied to temporary, reference to temporary used',
380  #424 : 'Extra ";" ignored',
381  444 : 'destructor for base class "CLASS" is not virtual',
382  981 : 'operands are evaluated in unspecified order',
383  1418 : 'external function definition with no prior declaration',
384  1419 : 'external declaration in primary source file',
385  1572 : 'floating-point equality and inequality comparisons are unreliable',
386  1720 : 'function "FUNC" has no corresponding member operator delete (to be called if an exception is thrown during initialization of an allocated object)',
387  2259 : 'non-pointer conversion from "int" to "float" may lose significant bits',
388  }
389  if env.GetOption('filterWarn'):
390  env.Append(CCFLAGS = ["-wd%s" % (",".join([str(k) for k in filterWarnings.keys()]))])
391  # Workaround intel bug; cf. RHL's intel bug report 580167
392  env.Append(LINKFLAGS = ["-Wl,-no_compact_unwind", "-wd,11015"])
393 
394 def _saveState():
395  """Save state such as optimization level used. The scons mailing lists were unable to tell
396  RHL how to get this back from .sconsign.dblite
397  """
398 
399  if env.GetOption("clean"):
400  return
401 
402  import ConfigParser
403 
404  config = ConfigParser.ConfigParser()
405  config.add_section('Build')
406  config.set('Build', 'cc', env.whichCc)
407  if env['opt']:
408  config.set('Build', 'opt', env['opt'])
409 
410  try:
411  confFile = os.path.join(env.Dir(env["CONFIGUREDIR"]).abspath, "build.cfg")
412  with open(confFile, 'wb') as configfile:
413  config.write(configfile)
414  except Exception, e:
415  log.warn("Unexpected exception in _saveState: %s" % e)
416 
417 _initOptions()
418 _initLog()
419 _initVariables()
420 _initEnvironment()
421 _configureCommon()
422 _saveState()
423 
424 ## @endcond
A dead-simple logger for all messages.
Definition: utils.py:19