LSSTApplications  16.0-10-g0ee56ad+4,16.0-11-ga33d1f2+4,16.0-12-g3ef5c14+2,16.0-12-g71e5ef5+17,16.0-12-gbdf3636+2,16.0-13-g118c103+2,16.0-13-g8f68b0a+2,16.0-15-gbf5c1cb+3,16.0-16-gfd17674+2,16.0-17-g7c01f5c+2,16.0-18-g0a50484,16.0-20-ga20f992+7,16.0-21-g0e05fd4+5,16.0-21-g15e2d33+3,16.0-22-g62d8060+3,16.0-22-g847a80f+3,16.0-25-gf00d9b8,16.0-28-g3990c221+3,16.0-3-gf928089+2,16.0-32-g88a4f23+4,16.0-34-gd7987ad+2,16.0-37-gc7333cb+1,16.0-4-g10fc685+1,16.0-4-g18f3627+25,16.0-4-g5f3a788+25,16.0-5-gaf5c3d7+3,16.0-5-gcc1f4bb,16.0-6-g3b92700+3,16.0-6-g4412fcd+2,16.0-6-g7235603+3,16.0-69-g2562ce1b+1,16.0-7-g0913a87,16.0-8-g14ebd58+3,16.0-8-g2df868b,16.0-8-g4cec79c+5,16.0-8-gadf6c7a,16.0-82-g59ec2a54a,16.0-9-g5400cdc+1,16.0-9-ge6233d7+4,master-g2880f2d8cf+2,v17.0.rc1
LSSTDataManagementBasePackage
state.py
Go to the documentation of this file.
1 
14 
15 import os
16 import re
17 
18 import SCons.Script
19 import SCons.Conftest
20 from . import eupsForScons
21 
22 SCons.Script.EnsureSConsVersion(2, 1, 0)
23 
24 
38 targets = {"doc": [], "tests": [], "lib": [], "python": [], "examples": [], "include": [], "version": [],
39  "shebang": []}
40 
41 # @cond INTERNAL
42 
43 env = None
44 log = None
45 opts = None
46 
47 
48 def _initOptions():
49  SCons.Script.AddOption('--checkDependencies', dest='checkDependencies',
50  action='store_true', default=False,
51  help="Verify dependencies with autoconf-style tests.")
52  SCons.Script.AddOption('--filterWarn', dest='filterWarn', action='store_true', default=False,
53  help="Filter out a class of warnings deemed irrelevant"),
54  SCons.Script.AddOption('--force', dest='force', action='store_true', default=False,
55  help="Set to force possibly dangerous behaviours")
56  SCons.Script.AddOption('--linkFarmDir', dest='linkFarmDir', action='store', default=None,
57  help="The directory of symbolic links needed to build and use the package")
58  SCons.Script.AddOption('--prefix', dest='prefix', action='store', default=False,
59  help="Specify the install destination")
60  SCons.Script.AddOption('--setenv', dest='setenv', action='store_true', default=False,
61  help="Treat arguments such as Foo=bar as defining construction variables")
62  SCons.Script.AddOption('--tag', dest='tag', action='store', default=None,
63  help="Declare product with this eups tag")
64  SCons.Script.AddOption('--verbose', dest='verbose', action='store_true', default=False,
65  help="Print additional messages for debugging.")
66  SCons.Script.AddOption('--traceback', dest='traceback', action='store_true', default=False,
67  help="Print full exception tracebacks when errors occur.")
68  SCons.Script.AddOption('--no-eups', dest='no_eups', action='store_true', default=False,
69  help="Do not use EUPS for configuration")
70 
71 
72 def _initLog():
73  from . import utils
74  global log
75  log = utils.Log()
76 
77 
78 def _initVariables():
79  files = []
80  if "optfile" in SCons.Script.ARGUMENTS:
81  configfile = SCons.Script.ARGUMENTS["optfile"]
82  if configfile not in files:
83  files.append(configfile)
84  for file in files:
85  if not os.path.isfile(file):
86  log.warn("Warning: Will ignore non-existent options file, %s" % file)
87  if "optfile" not in SCons.Script.ARGUMENTS:
88  files.append("buildOpts.py")
89  global opts
90  opts = SCons.Script.Variables(files)
91  opts.AddVariables(
92  ('archflags', 'Extra architecture specification to add to CC/LINK flags (e.g. -m32)', ''),
93  ('cc', 'Choose the compiler to use', ''),
94  SCons.Script.BoolVariable('debug', 'Set to enable debugging flags (use --debug)', True),
95  ('eupsdb', 'Specify which element of EUPS_PATH should be used', None),
96  ('flavor', 'Set the build flavor', None),
97  SCons.Script.BoolVariable('force', 'Set to force possibly dangerous behaviours', False),
98  ('optfile', 'Specify a file to read default options from', None),
99  ('prefix', 'Specify the install destination', None),
100  SCons.Script.EnumVariable('opt', 'Set the optimisation level', 3,
101  allowed_values=('g', '0', '1', '2', '3')),
102  SCons.Script.EnumVariable('profile', 'Compile/link for profiler', 0,
103  allowed_values=('0', '1', 'pg', 'gcov')),
104  ('version', 'Specify the version to declare', None),
105  ('baseversion', 'Specify the current base version', None),
106  ('optFiles', "Specify a list of files that SHOULD be optimized", None),
107  ('noOptFiles', "Specify a list of files that should NOT be optimized", None),
108  ('macosx_deployment_target', 'Deployment target for Mac OS X', '10.9'),
109  )
110 
111 
112 def _initEnvironment():
113  """Construction and basic setup of the state.env variable."""
114 
115  ourEnv = {}
116  preserveVars = """
117  DYLD_LIBRARY_PATH
118  EUPS_DIR
119  EUPS_LOCK_PID
120  EUPS_PATH
121  EUPS_SHELL
122  EUPS_USERDATA
123  LD_LIBRARY_PATH
124  PATH
125  SHELL
126  TEMP
127  TMP
128  TMPDIR
129  XPA_PORT
130  """.split()
131 
132  for key in preserveVars:
133  if key in os.environ:
134  ourEnv[key] = os.environ[key]
135 
136  # Find and propagate EUPS environment variables.
137  cfgPath = []
138  for k in os.environ:
139  m = re.search(r"^(?P<name>\w+)_DIR(?P<extra>_EXTRA)?$", k)
140  if not m:
141  continue
142  cfgPath.append(os.path.join(os.environ[k], "ups"))
143  if m.group("extra"):
144  cfgPath.append(os.environ[k])
145  else:
146  cfgPath.append(os.path.join(os.environ[k], "ups"))
147  p = m.group("name")
148  varname = eupsForScons.utils.setupEnvNameFor(p)
149  if varname in os.environ:
150  ourEnv[varname] = os.environ[varname]
151  ourEnv[k] = os.environ[k]
152 
153  # add <build root>/ups directory to the configuration search path
154  # this allows the .cfg file for the package being built to be found without
155  # requiring <product name>_DIR to be in the env
156  cfgPath.append(os.path.join(SCons.Script.Dir('#').abspath, 'ups'))
157 
158  # Recursively walk LSST_CFG_PATH
159  for root in os.environ.get("LSST_CFG_PATH", "").split(":"):
160  for base, dirs, files in os.walk(root):
161  dirs = [d for d in dirs if not d.startswith(".")]
162  cfgPath.insert(0, base)
163  #
164  # Add any values marked as export=FOO=XXX[,GOO=YYY] to ourEnv
165  #
166  exportVal = SCons.Script.ARGUMENTS.pop("export", None)
167  if exportVal:
168  for kv in exportVal.split(','):
169  k, v = kv.split('=')
170  ourEnv[k] = v
171  global env
172  sconsUtilsPath, thisFile = os.path.split(__file__)
173  toolPath = os.path.join(sconsUtilsPath, "tools")
174  env = SCons.Script.Environment(
175  ENV=ourEnv,
176  variables=opts,
177  toolpath=[toolPath],
178  tools=["default", "cuda"]
179  )
180  env.cfgPath = cfgPath
181  #
182  # We don't want "lib" inserted at the beginning of loadable module names;
183  # we'll import them under their given names.
184  #
185  env['LDMODULEPREFIX'] = ""
186  if env['PLATFORM'] == 'darwin':
187  env['LDMODULESUFFIX'] = ".so"
188  if not re.search(r"-install_name", str(env['SHLINKFLAGS'])):
189  env.Append(SHLINKFLAGS=["-install_name", "${TARGET.file}"])
190  if not re.search(r"-headerpad_max_install_names", str(env['SHLINKFLAGS'])):
191  env.Append(SHLINKFLAGS=["-Wl,-headerpad_max_install_names"])
192  #
193  # We want to be explicit about the OS X version we're targeting
194  #
195  env['ENV']['MACOSX_DEPLOYMENT_TARGET'] = env['macosx_deployment_target']
196  log.info("Setting OS X binary compatibility level: %s" % env['ENV']['MACOSX_DEPLOYMENT_TARGET'])
197  #
198  # For XCode 7.3 we need to explicitly add a trailing slash to library paths.
199  # This does not hurt things on older XCodes. We can remove this once XCode
200  # is fixed. See Apple radar rdr://25313838
201  #
202  env['LIBDIRSUFFIX'] = '/'
203 
204  #
205  # Remove valid options from the arguments
206  #
207  # SCons Variables do not behave like dicts
208  for opt in opts.keys():
209  try:
210  del SCons.Script.ARGUMENTS[opt]
211  except KeyError:
212  pass
213  #
214  # Process those arguments
215  #
216  for k in ("force", "prefix"): # these may now be set as options instead of variables
217  if SCons.Script.GetOption(k):
218  env[k] = SCons.Script.GetOption(k)
219 
220  if env['debug']:
221  env.Append(CCFLAGS=['-g'])
222 
223  #
224  # determine if EUPS is present
225  #
226 
227  # --no-eups overides probing
228  # XXX is it possible to test python snippets as a scons action?
229  if SCons.Script.GetOption("no_eups"):
230  env['no_eups'] = True
231  else:
232  env['no_eups'] = not eupsForScons.haveEups()
233 
234  if not env.GetOption("no_progress"):
235  if env['no_eups']:
236  log.info('EUPS integration: disabled')
237  else:
238  log.info('EUPS integration: enabled')
239 
240  #
241  # Find the eups path, replace 'flavor' in favor of 'PLATFORM' if needed.
242  #
243  eupsPath = None
244  try:
245  db = env['eupsdb']
246  if 'EUPS_PATH' not in os.environ:
247  raise RuntimeError("You can't use eupsdb=XXX without an EUPS_PATH set")
248  eupsPath = None
249  for d in os.environ['EUPS_PATH'].split(':'):
250  if re.search(r"/%s$|^%s/|/%s/" % (db, db, db), d):
251  eupsPath = d
252  break
253  if not eupsPath:
254  raise RuntimeError("I cannot find DB \"%s\" in $EUPS_PATH" % db)
255  except KeyError:
256  if 'EUPS_PATH' in os.environ:
257  eupsPath = os.environ['EUPS_PATH'].split(':')[0]
258  env['eupsPath'] = eupsPath
259  try:
260  env['PLATFORM'] = env['flavor']
261  del env['flavor']
262  except KeyError:
263  pass
264  #
265  # Check arguments
266  #
267  errorStr = ""
268  #
269  # Process otherwise unknown arguments. If setenv is true,
270  # set construction variables; otherwise generate an error
271  #
272  if SCons.Script.GetOption("setenv"):
273  for key in SCons.Script.ARGUMENTS:
274  env[key] = SCons.Script.Split(SCons.Script.ARGUMENTS[key])
275  else:
276  for key in SCons.Script.ARGUMENTS:
277  errorStr += " %s=%s" % (key, SCons.Script.ARGUMENTS[key])
278  if errorStr:
279  log.fail("Unprocessed arguments:%s" % errorStr)
280  #
281  # We need a binary name, not just "Posix"
282  #
283  env['eupsFlavor'] = eupsForScons.flavor()
284 
285 
286 _configured = False
287 
288 
289 def _configureCommon():
290  """Configuration checks for the compiler, platform, and standard libraries."""
291  global _configured
292  if _configured:
293  return
294  _configured = True
295 
296  #
297  # Is the C compiler really gcc/g++?
298  #
299  def ClassifyCc(context):
300  """Return a pair of string identifying the compiler in use
301 
302  @return (compiler, version) as a pair of strings, or ("unknown", "unknown") if unknown
303  """
304  versionNameList = (
305  (r"gcc(?:\-.+)? +\(.+\) +([0-9.a-zA-Z]+)", "gcc"),
306  (r"\(GCC\) +([0-9.a-zA-Z]+) ", "gcc"),
307  (r"LLVM +version +([0-9.a-zA-Z]+) ", "clang"), # clang on Mac
308  (r"clang +version +([0-9.a-zA-Z]+) ", "clang"), # clang on linux
309  (r"\(ICC\) +([0-9.a-zA-Z]+) ", "icc"),
310  (r"cc \(Ubuntu +([0-9\~\-.a-zA-Z]+)\)", "gcc"), # gcc on Ubuntu (not always caught by #2 above)
311  )
312 
313  context.Message("Checking who built the CC compiler...")
314  result = context.TryAction(SCons.Script.Action(r"$CC --version > $TARGET"))
315  ccVersDumpOK, ccVersDump = result[0:2]
316  if ccVersDumpOK:
317  for reStr, compilerName in versionNameList:
318  match = re.search(reStr, ccVersDump)
319  if match:
320  compilerVersion = match.groups()[0]
321  context.Result("%s=%s" % (compilerName, compilerVersion))
322  return (compilerName, compilerVersion)
323  context.Result("unknown")
324  return ("unknown", "unknown")
325 
326  if env.GetOption("clean") or env.GetOption("no_exec") or env.GetOption("help"):
327  env.whichCc = "unknown" # who cares? We're cleaning/not execing, not building
328  else:
329  if env['cc'] != '':
330  CC = CXX = None
331  if re.search(r"^gcc(-\d+(\.\d+)*)?( |$)", env['cc']):
332  CC = env['cc']
333  CXX = re.sub(r"^gcc", "g++", CC)
334  elif re.search(r"^icc( |$)", env['cc']):
335  CC = env['cc']
336  CXX = re.sub(r"^icc", "icpc", CC)
337  elif re.search(r"^clang( |$)", env['cc']):
338  CC = env['cc']
339  CXX = re.sub(r"^clang", "clang++", CC)
340  elif re.search(r"^cc( |$)", env['cc']):
341  CC = env['cc']
342  CXX = re.sub(r"^cc", "c++", CC)
343  else:
344  log.fail("Unrecognised compiler:%s" % env['cc'])
345  env0 = SCons.Script.Environment()
346  if CC and env['CC'] == env0['CC']:
347  env['CC'] = CC
348  if CC and env['CXX'] == env0['CXX']:
349  env['CXX'] = CXX
350  conf = env.Configure(custom_tests={'ClassifyCc': ClassifyCc})
351  env.whichCc, env.ccVersion = conf.ClassifyCc()
352 
353  # If we have picked up a default compiler called gcc that is really
354  # clang, we call it clang to avoid confusion (gcc on macOS has subtly
355  # different options)
356  if not env['cc'] and env.whichCc == "clang" and env['CC'] == "gcc":
357  env['CC'] = "clang"
358  env['CXX'] = "clang++"
359 
360  if not env.GetOption("no_progress"):
361  log.info("CC is %s version %s" % (env.whichCc, env.ccVersion))
362  conf.Finish()
363  #
364  # Compiler flags, including CCFLAGS for C and C++ and CXXFLAGS for C++ only
365  #
366  ARCHFLAGS = os.environ.get("ARCHFLAGS", env.get('archflags'))
367  if ARCHFLAGS:
368  env.Append(CCFLAGS=ARCHFLAGS.split())
369  env.Append(LINKFLAGS=ARCHFLAGS.split())
370  # We'll add warning and optimisation options last
371  if env['profile'] == '1' or env['profile'] == "pg":
372  env.Append(CCFLAGS=['-pg'])
373  env.Append(LINKFLAGS=['-pg'])
374  elif env['profile'] == 'gcov':
375  env.Append(CCFLAGS='--coverage')
376  env.Append(LINKFLAGS='--coverage')
377 
378  #
379  # Enable C++14 support (and C99 support for gcc)
380  #
381  if not (env.GetOption("clean") or env.GetOption("help") or env.GetOption("no_exec")):
382  if not env.GetOption("no_progress"):
383  log.info("Checking for C++14 support")
384  conf = env.Configure()
385  for cpp14Arg in ("-std=%s" % (val,) for val in ("c++14",)):
386  conf.env = env.Clone()
387  conf.env.Append(CXXFLAGS=cpp14Arg)
388  if conf.CheckCXX():
389  env.Append(CXXFLAGS=cpp14Arg)
390  if not env.GetOption("no_progress"):
391  log.info("C++14 supported with %r" % (cpp14Arg,))
392  break
393  else:
394  log.fail("C++14 extensions could not be enabled for compiler %r" % env.whichCc)
395  conf.Finish()
396 
397  #
398  # Byte order
399  #
400  import socket
401  if socket.htons(1) != 1:
402  env.Append(CCFLAGS=['-DLSST_LITTLE_ENDIAN=1'])
403  #
404  # If we're linking to libraries that themselves linked to
405  # shareable libraries we need to do something special.
406  #
407  if (re.search(r"^(Linux|Linux64)$", env["eupsFlavor"]) and "LD_LIBRARY_PATH" in os.environ):
408  env.Append(LINKFLAGS=["-Wl,-rpath-link"])
409  env.Append(LINKFLAGS=["-Wl,%s" % os.environ["LD_LIBRARY_PATH"]])
410  #
411  # Set the optimization level.
412  #
413  if env['opt']:
414  env["CCFLAGS"] = [o for o in env["CCFLAGS"] if not re.search(r"^-O(\d|s|g|fast)$", o)]
415  env.MergeFlags('-O%s' % env['opt'])
416  #
417  # Set compiler-specific warning flags.
418  #
419  if env.whichCc == "clang":
420  env.Append(CCFLAGS=['-Wall'])
421  env["CCFLAGS"] = [o for o in env["CCFLAGS"] if not re.search(r"^-mno-fused-madd$", o)]
422 
423  ignoreWarnings = {
424  "unused-function": 'boost::regex has functions in anon namespaces in headers',
425  }
426  filterWarnings = {
427  "attributes": "clang pretends to be g++, but complains about g++ attributes such as flatten",
428  "char-subscripts": 'seems innocous enough, and is used by boost',
429  "constant-logical-operand": "Used by eigen 2.0.15. Should get this fixed",
430  "format-security": "format string is not a string literal",
431  "mismatched-tags": "mixed class and struct. Used by gcc 4.2 RTL and eigen 2.0.15",
432  "parentheses": "equality comparison with extraneous parentheses",
433  "shorten-64-to-32": "implicit conversion loses integer precision",
434  "self-assign": "x = x",
435  "unused-local-typedefs": "unused typedef", # lots from boost
436  "unknown-pragmas": "unknown pragma ignored",
437  "deprecated-register": "register is deprecated",
438  }
439  for k in ignoreWarnings:
440  env.Append(CCFLAGS=["-Wno-%s" % k])
441  if env.GetOption('filterWarn'):
442  for k in filterWarnings:
443  env.Append(CCFLAGS=["-Wno-%s" % k])
444  elif env.whichCc == "gcc":
445  env.Append(CCFLAGS=['-Wall'])
446  env.Append(CCFLAGS=["-Wno-unknown-pragmas"]) # we don't want complaints about icc/clang pragmas
447  env.Append(CCFLAGS=["-Wno-unused-local-typedefs"]) # boost generates a lot of these
448  elif env.whichCc == "icc":
449  env.Append(CCFLAGS=['-Wall'])
450  filterWarnings = {
451  21: 'type qualifiers are meaningless in this declaration',
452  68: 'integer conversion resulted in a change of sign',
453  111: 'statement is unreachable',
454  191: 'type qualifier is meaningless on cast type',
455  193: 'zero used for undefined preprocessing identifier "SYMB"',
456  279: 'controlling expression is constant',
457  304: 'access control not specified ("public" by default)', # comes from boost
458  383: 'value copied to temporary, reference to temporary used',
459  # 424: 'Extra ";" ignored',
460  444: 'destructor for base class "CLASS" is not virtual',
461  981: 'operands are evaluated in unspecified order',
462  1418: 'external function definition with no prior declaration',
463  1419: 'external declaration in primary source file',
464  1572: 'floating-point equality and inequality comparisons are unreliable',
465  1720: 'function "FUNC" has no corresponding member operator delete'
466  '(to be called if an exception is thrown during initialization of an allocated object)',
467  2259: 'non-pointer conversion from "int" to "float" may lose significant bits',
468  }
469  if env.GetOption('filterWarn'):
470  env.Append(CCFLAGS=["-wd%s" % (",".join([str(k) for k in filterWarnings]))])
471  # Workaround intel bug; cf. RHL's intel bug report 580167
472  env.Append(LINKFLAGS=["-Wl,-no_compact_unwind", "-wd,11015"])
473  #
474  # Disable link-time-optimization on GCC, for compatibility with conda binaries
475  #
476  if env.whichCc == "gcc":
477  env.Append(CCFLAGS=['-fno-lto'])
478  env.Append(LINKFLAGS=['-fno-lto'])
479 
480 
481 def _saveState():
482  """Save state such as optimization level used. The scons mailing lists were unable to tell
483  RHL how to get this back from .sconsign.dblite
484  """
485 
486  if env.GetOption("clean"):
487  return
488 
489  # Python 2 uses ConfigParser, Python 3 uses configparser
490  try:
491  from configparser import ConfigParser
492  except ImportError:
493  from ConfigParser import ConfigParser
494 
495  config = ConfigParser()
496  config.add_section('Build')
497  config.set('Build', 'cc', env.whichCc)
498  if env['opt']:
499  config.set('Build', 'opt', env['opt'])
500 
501  try:
502  confFile = os.path.join(env.Dir(env["CONFIGUREDIR"]).abspath, "build.cfg")
503  with open(confFile, 'w') as configfile:
504  config.write(configfile)
505  except Exception as e:
506  log.warn("Unexpected exception in _saveState: %s" % e)
507 
508 
509 _initOptions()
510 _initLog()
511 _initVariables()
512 _initEnvironment()
513 
514 # @endcond
A dead-simple logger for all messages.
Definition: utils.py:22