10 from __future__
import absolute_import
15 from .
import eupsForScons
16 from SCons.Script.SConscript
import SConsEnvironment
18 from .
import installation
39 def configure(packageName, versionString=None, eupsProduct=None, eupsProductPath=None, noCfgFile=False):
40 if not state.env.GetOption(
"no_progress"):
41 state.log.info(
"Setting up environment to build package '%s'." % packageName)
42 if eupsProduct
is None:
43 eupsProduct = packageName
44 if versionString
is None:
46 state.env[
'eupsProduct'] = eupsProduct
47 state.env[
'packageName'] = packageName
51 SCons.Script.Help(state.opts.GenerateHelpText(state.env))
52 state.env.installing = [t
for t
in SCons.Script.BUILD_TARGETS
if t ==
"install"]
53 state.env.declaring = [t
for t
in SCons.Script.BUILD_TARGETS
if t ==
"declare" or t ==
"current"]
54 state.env.linkFarmDir = state.env.GetOption(
"linkFarmDir")
55 if state.env.linkFarmDir:
56 state.env.linkFarmDir = os.path.abspath(os.path.expanduser(state.env.linkFarmDir))
57 prefix = installation.setPrefix(state.env, versionString, eupsProductPath)
58 state.env[
'prefix'] = prefix
59 state.env[
"libDir"] =
"%s/lib" % prefix
60 state.env[
"pythonDir"] =
"%s/python" % prefix
64 state.log.traceback = state.env.GetOption(
"traceback")
65 state.log.verbose = state.env.GetOption(
"verbose")
66 packages =
PackageTree(packageName, noCfgFile=noCfgFile)
68 state.env.libs = {
"main": [],
"python": [],
"test": []}
69 state.env.doxygen = {
"tags": [],
"includes": []}
70 state.env[
'CPPPATH'] = []
71 state.env[
'LIBPATH'] = []
75 state.env[
'XCPPPATH'] = []
76 state.env[
'_CPPINCFLAGS'] = \
77 "$( ${_concat(INCPREFIX, CPPPATH, INCSUFFIX, __env__, RDirs, TARGET, SOURCE)}"\
78 " ${_concat(INCPREFIX, XCPPPATH, INCSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)"
79 state.env[
'_SWIGINCFLAGS'] = state.env[
'_CPPINCFLAGS'].replace(
"CPPPATH",
"SWIGPATH")
81 if state.env.linkFarmDir:
82 for d
in [state.env.linkFarmDir,
"#"]:
83 state.env.Append(CPPPATH=os.path.join(d,
"include"))
84 state.env.Append(LIBPATH=os.path.join(d,
"lib"))
85 state.env[
'SWIGPATH'] = state.env[
'CPPPATH']
87 if not state.env.GetOption(
"clean")
and not state.env.GetOption(
"help"):
88 packages.configure(state.env, check=state.env.GetOption(
"checkDependencies"))
89 for target
in state.env.libs:
90 state.log.info(
"Libraries in target '%s': %s" % (target, state.env.libs[target]))
91 state.env.dependencies = packages
116 dir, file = os.path.split(cfgFile)
117 name, ext = os.path.splitext(file)
118 return name, os.path.abspath(os.path.join(dir,
".."))
122 version, eupsPathDir, productDir, table, flavor = eupsForScons.getEups().
findSetupVersion(eupsProduct)
123 if productDir
is None:
124 productDir = eupsForScons.productDir(eupsProduct)
125 return version, productDir
153 def __init__(self, cfgFile, headers=(), libs=
None, hasSwigFiles=
True,
154 includeFileDirs=[
"include"], libFileDirs=[
"lib"],
155 hasDoxygenInclude=
False, hasDoxygenTag=
True, eupsProduct=
None):
157 if eupsProduct
is None:
158 eupsProduct = self.name
161 if version
is not None:
163 if productDir
is None:
164 state.log.warn(
"Could not find EUPS product dir for '%s'; using %s."
167 self.
root = os.path.realpath(productDir)
170 "tags": ([os.path.join(self.
root,
"doc",
"%s.tag" % self.name)]
171 if hasDoxygenTag
else []),
173 "includes": ([os.path.join(self.
root,
"doc",
"%s.inc" % self.name)]
174 if hasDoxygenInclude
else [])
188 self.
libs = {
"main": libs,
"python": [],
"test": []}
191 self.
paths[
"SWIGPATH"] = [os.path.join(self.
root,
"python")]
193 self.
paths[
"SWIGPATH"] = []
195 for pathName, subDirs
in [(
"CPPPATH", includeFileDirs),
196 (
"LIBPATH", libFileDirs)]:
197 self.
paths[pathName] = []
199 if state.env.linkFarmDir:
202 for subDir
in subDirs:
203 pathDir = os.path.join(self.
root, subDir)
204 if os.path.isdir(pathDir):
205 self.
paths[pathName].append(pathDir)
208 "headers": tuple(headers),
209 "libs": tuple(self.
libs[
"main"])
239 def configure(self, conf, packages, check=False, build=True):
240 assert(
not (check
and build))
241 conf.env.PrependUnique(**self.
paths)
242 state.log.info(
"Configuring package '%s'." % self.name)
243 conf.env.doxygen[
"includes"].extend(self.
doxygen[
"includes"])
245 conf.env.doxygen[
"tags"].extend(self.
doxygen[
"tags"])
246 for target
in self.
libs:
247 if target
not in conf.env.libs:
248 conf.env.libs[target] = self.
libs[target].copy()
249 state.log.info(
"Adding '%s' libraries to target '%s'." % (self.
libs[target], target))
251 for lib
in self.
libs[target]:
252 if lib
not in conf.env.libs[target]:
253 conf.env.libs[target].append(lib)
254 state.log.info(
"Adding '%s' library to target '%s'." % (lib, target))
256 for header
in self.
provides[
"headers"]:
257 if not conf.CheckCXXHeader(header):
259 for lib
in self.
libs[
"main"]:
260 if not conf.CheckLib(lib, autoadd=
False, language=
"C++"):
293 def __init__(self, cfgFile, headers=(), libs=
None, eupsProduct=
None):
294 Configuration.__init__(self, cfgFile, headers, libs, eupsProduct=eupsProduct, hasSwigFiles=
False,
295 hasDoxygenTag=
False, hasDoxygenInclude=
False)
296 self.
paths[
"XCPPPATH"] = self.
paths[
"CPPPATH"]
297 del self.
paths[
"CPPPATH"]
312 context.Message(
"Checking if C compiler supports " + flag +
" flag ")
313 ccflags = context.env[
"CCFLAGS"]
314 context.env.Append(CCFLAGS=flag)
315 result = context.TryCompile(
"int main(int argc, char **argv) { return 0; }",
".c")
316 context.Result(result)
317 if not append
or not result:
318 context.env.Replace(CCFLAGS=ccflags)
332 context.Message(
"Checking if C++ compiler supports " + flag +
" flag ")
333 cxxflags = context.env[
"CXXFLAGS"]
334 context.env.Append(CXXFLAGS=flag)
335 result = context.TryCompile(
"int main(int argc, char **argv) { return 0; }",
".cc")
336 context.Result(result)
337 if not append
or not result:
338 context.env.Replace(CXXFLAGS=cxxflags)
352 context.Message(message)
355 if (env.GetOption(
"clean")
or env.GetOption(
"help")
or env.GetOption(
"no_exec")):
358 result = context.TryCompile(source, extension)
360 context.Result(result)
375 context.Message(message)
376 result = context.TryLink(source, extension)
377 context.Result(result)
413 "CustomCFlagCheck": CustomCFlagCheck,
414 "CustomCppFlagCheck": CustomCppFlagCheck,
415 "CustomCompileCheck": CustomCompileCheck,
416 "CustomLinkCheck": CustomLinkCheck,
425 state.log.fail(
"Failed to load primary package configuration for %s." % primaryName)
428 for dependency
in self.primary.dependencies.get(
"required", ()):
430 missingDeps.append(dependency)
432 state.log.fail(
"Failed to load required dependencies: \"%s\"" %
'", "'.join(missingDeps))
435 for dependency
in self.primary.dependencies.get(
"buildRequired", ()):
437 missingDeps.append(dependency)
439 state.log.fail(
"Failed to load required build dependencies: \"%s\"" %
'", "'.join(missingDeps))
441 for dependency
in self.primary.dependencies.get(
"optional", ()):
444 for dependency
in self.primary.dependencies.get(
"buildOptional", ()):
447 name = property(
lambda self: self.primary.config.name)
451 conf = env.Configure(custom_tests=self.
customTests)
452 for name, module
in self.packages.items():
454 state.log.info(
"Skipping missing optional package %s." % name)
456 if not module.config.configure(conf, packages=self.
packages, check=check, build=
False):
457 state.log.fail(
"%s was found but did not pass configuration checks." % name)
459 self.primary.config.configure(conf, packages=self.
packages, check=
False, build=
True)
460 env.AppendUnique(SWIGPATH=env[
"CPPPATH"])
461 env.AppendUnique(XSWIGPATH=env[
"XCPPPATH"])
465 for target
in env.libs:
466 env.libs[target].reverse()
473 has_key = __contains__
476 if name == self.
name:
481 def get(self, name, default=None):
482 if name == self.
name:
485 return self.packages.get(name)
488 k = self.packages.keys()
493 """Search for and import an individual configuration module from file."""
495 filename = os.path.join(path, name +
".cfg")
496 if os.path.exists(filename):
498 module = imp.load_source(name +
"_cfg", filename)
499 except Exception
as e:
500 state.log.warn(
"Error loading configuration %s (%s)" % (filename, e))
502 state.log.info(
"Using configuration for package '%s' at '%s'." % (name, filename))
503 if not hasattr(module,
"dependencies")
or not isinstance(module.dependencies, dict):
504 state.log.warn(
"Configuration module for package '%s' lacks a dependencies dict." % name)
506 if not hasattr(module,
"config")
or not isinstance(module.config, Configuration):
507 state.log.warn(
"Configuration module for package '%s' lacks a config object." % name)
512 state.log.info(
"Failed to import configuration for optional package '%s'." % name)
515 """Recursively load a dependency."""
517 state.log.fail(
"Detected recursive dependency involving package '%s'" % name)
519 self._current.add(name)
521 self._current.remove(name)
522 return self.
packages[name]
is not None
526 self._current.remove(name)
528 for dependency
in module.dependencies.get(
"required", ()):
533 self._current.remove(name)
534 state.log.warn(
"Could not load all dependencies for package '%s' (missing %s)." %
537 for dependency
in module.dependencies.get(
"optional", ()):
541 self._current.remove(name)
562 for category
in categories.split():
563 if category ==
"self":
566 for lib
in env.libs[category]:
571 libs.remove(env[
"packageName"])
576 SConsEnvironment.getLibs = getLibs
Base class for defining how to configure an LSST sconsUtils package.
def CustomCppFlagCheck
A configuration test that checks whether a C++ compiler supports a particular flag.
def CustomLinkCheck
A configuration test that checks whether the given source code compiles and links.
def configure
Recursively configure a package using ups/.cfg files.
def getLibs
Get the libraries the package should be linked with.
def configure
Update an SCons environment to make use of the package.
def __init__
Recursively load *.cfg files for packageName and all its dependencies.
def CustomCompileCheck
A configuration test that checks whether the given source code compiles.
def __init__
Initialize the configuration object.
def CustomCFlagCheck
A configuration test that checks whether a C compiler supports a particular flag. ...
def __init__
Initialize the configuration object.
A class for loading and managing the dependency tree of a package, as defined by its configuration mo...
A Configuration subclass for external (third-party) packages.
def addCustomTests
Add custom SCons configuration tests to the Configure Context passed to the configure() method...