LSSTApplications  11.0-13-gbb96280,12.1.rc1,12.1.rc1+1,12.1.rc1+2,12.1.rc1+5,12.1.rc1+8,12.1.rc1-1-g06d7636+1,12.1.rc1-1-g253890b+5,12.1.rc1-1-g3d31b68+7,12.1.rc1-1-g3db6b75+1,12.1.rc1-1-g5c1385a+3,12.1.rc1-1-g83b2247,12.1.rc1-1-g90cb4cf+6,12.1.rc1-1-g91da24b+3,12.1.rc1-2-g3521f8a,12.1.rc1-2-g39433dd+4,12.1.rc1-2-g486411b+2,12.1.rc1-2-g4c2be76,12.1.rc1-2-gc9c0491,12.1.rc1-2-gda2cd4f+6,12.1.rc1-3-g3391c73+2,12.1.rc1-3-g8c1bd6c+1,12.1.rc1-3-gcf4b6cb+2,12.1.rc1-4-g057223e+1,12.1.rc1-4-g19ed13b+2,12.1.rc1-4-g30492a7
LSSTDataManagementBasePackage
dependencies.py
Go to the documentation of this file.
1 ##
2 # @file dependencies.py
3 #
4 # Dependency configuration and definition.
5 #
6 # @defgroup sconsUtilsDependencies Dependencies and Configuration
7 # @{
8 ##
9 
10 from __future__ import absolute_import
11 import os.path
12 import collections
13 import imp
14 import SCons.Script
15 from . import eupsForScons
16 from SCons.Script.SConscript import SConsEnvironment
17 
18 from . import installation
19 from . import state
20 
21 
22 ##
23 # @brief Recursively configure a package using ups/.cfg files.
24 #
25 # Aliased as lsst.sconsUtils.configure().
26 #
27 # Usually, LSST packages will call this function through scripts.BasicSConstruct.
28 #
29 # @param packageName Name of the package being built; must correspond to a .cfg file in ups/.
30 # @param versionString Version-control system string to be parsed for version information
31 # ($HeadURL$ for SVN).
32 # @param eupsProduct Name of the EUPS product being built. Defaults to and is almost always
33 # the name of the package.
34 # @param eupsProductPath An alternate directory where the package should be installed.
35 # @param noCfgFile If True, this package has no .cfg file
36 #
37 # @return an SCons Environment object (which is also available as lsst.sconsUtils.env).
38 ##
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:
45  versionString = "git"
46  state.env['eupsProduct'] = eupsProduct
47  state.env['packageName'] = packageName
48  #
49  # Setup installation directories and variables
50  #
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
61  #
62  # Process dependencies
63  #
64  state.log.traceback = state.env.GetOption("traceback")
65  state.log.verbose = state.env.GetOption("verbose")
66  packages = PackageTree(packageName, noCfgFile=noCfgFile)
67  state.log.flush() # if we've already hit a fatal error, die now.
68  state.env.libs = {"main": [], "python": [], "test": []}
69  state.env.doxygen = {"tags": [], "includes": []}
70  state.env['CPPPATH'] = []
71  state.env['LIBPATH'] = []
72 
73  # XCPPPATH is a new variable defined by sconsUtils - it's like CPPPATH, but the headers
74  # found there aren't treated as dependencies. This can make scons a lot faster.
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")
80 
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']
86 
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
92  state.log.flush()
93 
94 
95 # -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
96 
97 ##
98 # @brief Base class for defining how to configure an LSST sconsUtils package.
99 #
100 # Aliased as lsst.sconsUtils.Configuration.
101 #
102 # An ups/*.cfg file should contain an instance of this class called
103 # "config". Most LSST packages will be able to use this class directly
104 # instead of subclassing it.
105 #
106 # The only important method is configure(), which modifies an SCons
107 # environment to use the package. If a subclass overrides configure,
108 # it may not need to call the base class __init__(), whose only
109 # purpose is to define a number of instance variables used by configure().
110 ##
111 class Configuration(object):
112 
113  # @brief Parse the name of a .cfg file, returning the package name and root directory.
114  @staticmethod
115  def parseFilename(cfgFile):
116  dir, file = os.path.split(cfgFile)
117  name, ext = os.path.splitext(file)
118  return name, os.path.abspath(os.path.join(dir, ".."))
119 
120  @staticmethod
121  def getEupsData(eupsProduct):
122  version, eupsPathDir, productDir, table, flavor = eupsForScons.getEups().findSetupVersion(eupsProduct)
123  if productDir is None:
124  productDir = eupsForScons.productDir(eupsProduct)
125  return version, productDir
126 
127  ##
128  # @brief Initialize the configuration object.
129  #
130  # @param cfgFile The name of the calling .cfg file, usually just passed in with the special
131  # variable __file__. This will be parsed to extract the package name and
132  # root.
133  # @param headers A list of headers provided by the package, to be used in autoconf-style
134  # tests.
135  # @param libs A list or dictionary of libraries provided by the package. If a dictionary
136  # is provided, libs["main"] should contain a list of regular libraries
137  # provided
138  # by the library. Other keys are "python" and "test", which refer to
139  # libraries that are only linked against compiled Python modules and unit
140  # tests, respectively. If a list is provided, the list is used as "main".
141  # These are used both for autoconf-style tests and to support
142  # env.getLibs(...), which recursively computes the libraries a package
143  # must be linked with.
144  # @param hasSwigFiles If True, the package provides SWIG interface files in "<root>/python".
145  # @param hasDoxygenInclude If True, the package provides a Doxygen include file with the
146  # name "<root>/doc/<name>.inc".
147  # @param hasDoxygenTag If True, the package generates a Doxygen TAG file.
148  # @param includeFileDirs List of directories that should be searched for include files
149  # @param libFileDirs List of directories that should be searched for libraries
150  # @param eupsProduct Name of the EUPS product for the package, if different from the name of the
151  # .cfg file.
152  ##
153  def __init__(self, cfgFile, headers=(), libs=None, hasSwigFiles=True,
154  includeFileDirs=["include"], libFileDirs=["lib"],
155  hasDoxygenInclude=False, hasDoxygenTag=True, eupsProduct=None):
156  self.name, self.root = self.parseFilename(cfgFile)
157  if eupsProduct is None:
158  eupsProduct = self.name
159  self.eupsProduct = eupsProduct
160  version, productDir = self.getEupsData(self.eupsProduct)
161  if version is not None:
162  self.version = version
163  if productDir is None:
164  state.log.warn("Could not find EUPS product dir for '%s'; using %s."
165  % (self.eupsProduct, self.root))
166  else:
167  self.root = os.path.realpath(productDir)
168  self.doxygen = {
169  # Doxygen tag files generated by this package
170  "tags": ([os.path.join(self.root, "doc", "%s.tag" % self.name)]
171  if hasDoxygenTag else []),
172  # Doxygen include files to include in the configuration of dependent products
173  "includes": ([os.path.join(self.root, "doc", "%s.inc" % self.name)]
174  if hasDoxygenInclude else [])
175  }
176  if libs is None:
177  self.libs = {
178  # Normal libraries provided by this package
179  "main": [self.name],
180  # Libraries provided that should only be linked with Python modules
181  "python": [],
182  # Libraries provided that should only be linked with unit test code
183  "test": [],
184  }
185  elif "main" in libs:
186  self.libs = libs
187  else:
188  self.libs = {"main": libs, "python": [], "test": []}
189  self.paths = {}
190  if hasSwigFiles:
191  self.paths["SWIGPATH"] = [os.path.join(self.root, "python")]
192  else:
193  self.paths["SWIGPATH"] = []
194 
195  for pathName, subDirs in [("CPPPATH", includeFileDirs),
196  ("LIBPATH", libFileDirs)]:
197  self.paths[pathName] = []
198 
199  if state.env.linkFarmDir:
200  continue
201 
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)
206 
207  self.provides = {
208  "headers": tuple(headers),
209  "libs": tuple(self.libs["main"])
210  }
211 
212  ##
213  # @brief Add custom SCons configuration tests to the Configure Context passed to the
214  # configure() method.
215  #
216  # This needs to be done up-front so we can pass in a dictionary of custom tests when
217  # calling env.Configure(), and use the same configure context for all packages.
218  #
219  # @param tests A dictionary to add custom tests to. This will be passed as the
220  # custom_tests argument to env.Configure().
221  ##
222  def addCustomTests(self, tests):
223  pass
224 
225  ##
226  # @brief Update an SCons environment to make use of the package.
227  #
228  # @param conf An SCons Configure context. The SCons Environment conf.env should be updated
229  # by the configure function.
230  # @param packages A dictionary containing the configuration modules of all dependencies (or None if
231  # the dependency was optional and was not found). The <module>.config.configure(...)
232  # method will have already been called on all dependencies.
233  # @param check If True, perform autoconf-style tests to verify that key components are in
234  # fact in place.
235  # @param build If True, this is the package currently being built, and packages in
236  # "buildRequired" and "buildOptional" dependencies will also be present in
237  # the packages dict.
238  ##
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"])
244  if not build:
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))
250  else:
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))
255  if check:
256  for header in self.provides["headers"]:
257  if not conf.CheckCXXHeader(header):
258  return False
259  for lib in self.libs["main"]:
260  if not conf.CheckLib(lib, autoadd=False, language="C++"):
261  return False
262  return True
263 
264 
265 # -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
266 
267 ##
268 # @brief A Configuration subclass for external (third-party) packages.
269 #
270 # Aliased as lsst.sconsUtils.ExternalConfiguration.
271 #
272 # ExternalConfiguration doesn't assume the package uses SWIG or Doxygen,
273 # and tells SCons not to consider header files this package provides as dependencies
274 # (by setting XCPPPATH instead of CPPPATH). This means things SCons won't waste time
275 # looking for changes in it every time you build.
276 ##
278 
279  ##
280  # @brief Initialize the configuration object.
281  #
282  # @param cfgFile The name of the calling .cfg file, usually just passed in with the special
283  # variable __file__. This will be parsed to extract the package name and root.
284  # @param headers A list of headers provided by the package, to be used in autoconf-style tests.
285  # @param libs A list or dictionary of libraries provided by the package. If a dictionary
286  # is provided, libs["main"] should contain a list of regular libraries provided
287  # by the library. Other keys are "python" and "test", which refer to libraries
288  # that are only linked against compiled Python modules and unit tests, respectively.
289  # If a list is provided, the list is used as "main". These are used both for
290  # autoconf-style tests and to support env.getLibs(...), which recursively computes
291  # the libraries a package must be linked with.
292  ##
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"]
298 
299 
300 # -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
301 
302 ##
303 # @brief A configuration test that checks whether a C compiler supports
304 # a particular flag.
305 #
306 # @param context Configuration context.
307 # @param flag Flag to test, e.g. "-fvisibility-inlines-hidden".
308 # @param append Automatically append the flag to context.env["CCFLAGS"]
309 # if the compiler supports it?
310 ##
311 def CustomCFlagCheck(context, flag, append=True):
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)
319  return result
320 
321 
322 ##
323 # @brief A configuration test that checks whether a C++ compiler supports
324 # a particular flag.
325 #
326 # @param context Configuration context.
327 # @param flag Flag to test, e.g. "-fvisibility-inlines-hidden".
328 # @param append Automatically append the flag to context.env["CXXFLAGS"]
329 # if the compiler supports it?
330 ##
331 def CustomCppFlagCheck(context, flag, append=True):
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)
339  return result
340 
341 
342 ##
343 # @brief A configuration test that checks whether the given source code
344 # compiles.
345 # @param context Configuration context.
346 # @param message Message disaplyed on console prior to running the test.
347 # @param source Source code to compile.
348 # param extension Identifies the language of the source code. Use ".c" for C, and ".cc"
349 # for C++ (the default).
350 ##
351 def CustomCompileCheck(context, message, source, extension=".cc"):
352  context.Message(message)
353 
354  env = context.env
355  if (env.GetOption("clean") or env.GetOption("help") or env.GetOption("no_exec")):
356  result = True
357  else:
358  result = context.TryCompile(source, extension)
359 
360  context.Result(result)
361 
362  return result
363 
364 
365 ##
366 # @brief A configuration test that checks whether the given source code
367 # compiles and links.
368 # @param context Configuration context.
369 # @param message Message disaplyed on console prior to running the test.
370 # @param source Source code to compile.
371 # param extension Identifies the language of the source code. Use ".c" for C, and ".cc"
372 # for C++ (the default).
373 ##
374 def CustomLinkCheck(context, message, source, extension=".cc"):
375  context.Message(message)
376  result = context.TryLink(source, extension)
377  context.Result(result)
378  return result
379 
380 
381 # -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
382 
383 ##
384 # @brief A class for loading and managing the dependency tree of a package, as defined by its
385 # configuration module (.cfg) file.
386 #
387 # This tree isn't actually stored as a tree; it's flattened into an ordered dictionary
388 # as it is recursively loaded.
389 #
390 # The main SCons produced by configure() and available as sconsUtils.env will contain
391 # an instance of this class as env.dependencies.
392 #
393 # Its can be used like a read-only dictionary to check whether an optional package has been
394 # configured; a package that was not found will have a value of None, while a configured
395 # package's value will be its imported .cfg module.
396 ##
397 class PackageTree(object):
398 
399  ##
400  # @brief Recursively load *.cfg files for packageName and all its dependencies.
401  #
402  # @param primaryName The name of the primary package being built.
403  # @param noCfgFile If True, this package has no .cfg file
404  #
405  # After __init__, self.primary will be set to the configuration module for the primary package,
406  # and self.packages will be an OrderedDict of dependencies (excluding self.primary), ordered
407  # such that configuration can proceed in iteration order.
408  ##
409  def __init__(self, primaryName, noCfgFile=False):
410  self.cfgPath = state.env.cfgPath
411  self.packages = collections.OrderedDict()
412  self.customTests = {
413  "CustomCFlagCheck": CustomCFlagCheck,
414  "CustomCppFlagCheck": CustomCppFlagCheck,
415  "CustomCompileCheck": CustomCompileCheck,
416  "CustomLinkCheck": CustomLinkCheck,
417  }
418  self._current = set([primaryName])
419  if noCfgFile:
420  self.primary = None
421  return
422 
423  self.primary = self._tryImport(primaryName)
424  if self.primary is None:
425  state.log.fail("Failed to load primary package configuration for %s." % primaryName)
426 
427  missingDeps = []
428  for dependency in self.primary.dependencies.get("required", ()):
429  if not self._recurse(dependency):
430  missingDeps.append(dependency)
431  if missingDeps:
432  state.log.fail("Failed to load required dependencies: \"%s\"" % '", "'.join(missingDeps))
433 
434  missingDeps = []
435  for dependency in self.primary.dependencies.get("buildRequired", ()):
436  if not self._recurse(dependency):
437  missingDeps.append(dependency)
438  if missingDeps:
439  state.log.fail("Failed to load required build dependencies: \"%s\"" % '", "'.join(missingDeps))
440 
441  for dependency in self.primary.dependencies.get("optional", ()):
442  self._recurse(dependency)
443 
444  for dependency in self.primary.dependencies.get("buildOptional", ()):
445  self._recurse(dependency)
446 
447  name = property(lambda self: self.primary.config.name)
448 
449  # @brief Configure the entire dependency tree in order. and return an updated environment."""
450  def configure(self, env, check=False):
451  conf = env.Configure(custom_tests=self.customTests)
452  for name, module in self.packages.items():
453  if module is None:
454  state.log.info("Skipping missing optional package %s." % name)
455  continue
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)
458  if self.primary:
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"])
462  # reverse the order of libraries in env.libs, so libraries that fulfill a dependency
463  # of another appear after it. required by the linker to successfully resolve symbols
464  # in static libraries.
465  for target in env.libs:
466  env.libs[target].reverse()
467  env = conf.Finish()
468  return env
469 
470  def __contains__(self, name):
471  return name == self.name or name in self.packages
472 
473  has_key = __contains__
474 
475  def __getitem__(self, name):
476  if name == self.name:
477  return self.primary
478  else:
479  return self.packages[name]
480 
481  def get(self, name, default=None):
482  if name == self.name:
483  return self.primary
484  else:
485  return self.packages.get(name)
486 
487  def keys(self):
488  k = self.packages.keys()
489  k.append(self.name)
490  return k
491 
492  def _tryImport(self, name):
493  """Search for and import an individual configuration module from file."""
494  for path in self.cfgPath:
495  filename = os.path.join(path, name + ".cfg")
496  if os.path.exists(filename):
497  try:
498  module = imp.load_source(name + "_cfg", filename)
499  except Exception as e:
500  state.log.warn("Error loading configuration %s (%s)" % (filename, e))
501  continue
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)
505  return None
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)
508  return None
509  else:
510  module.config.addCustomTests(self.customTests)
511  return module
512  state.log.info("Failed to import configuration for optional package '%s'." % name)
513 
514  def _recurse(self, name):
515  """Recursively load a dependency."""
516  if name in self._current:
517  state.log.fail("Detected recursive dependency involving package '%s'" % name)
518  else:
519  self._current.add(name)
520  if name in self.packages:
521  self._current.remove(name)
522  return self.packages[name] is not None
523  module = self._tryImport(name)
524  if module is None:
525  self.packages[name] = None
526  self._current.remove(name)
527  return False
528  for dependency in module.dependencies.get("required", ()):
529  if not self._recurse(dependency):
530  # We can't configure this package because a required dependency wasn't found.
531  # But this package might itself be optional, so we don't die yet.
532  self.packages[name] = None
533  self._current.remove(name)
534  state.log.warn("Could not load all dependencies for package '%s' (missing %s)." %
535  (name, dependency))
536  return False
537  for dependency in module.dependencies.get("optional", ()):
538  self._recurse(dependency)
539  # This comes last to ensure the ordering puts all dependencies first.
540  self.packages[name] = module
541  self._current.remove(name)
542  return True
543 
544 
545 # -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
546 
547 ##
548 # @brief Get the libraries the package should be linked with.
549 #
550 # @param categories A string containing whitespace-delimited categories. Standard
551 # categories are "main", "python", and "test". Default is "main".
552 # A special virtual category "self" can be provided, returning
553 # the results of targets="main" with the env["packageName"] removed.
554 #
555 # Typically, main libraries will be linked with LIBS=getLibs("self"),
556 # Python modules will be linked with LIBS=getLibs("main python") and
557 # C++-coded test programs will be linked with LIBS=getLibs("main test").
558 # """
559 def getLibs(env, categories="main"):
560  libs = []
561  removeSelf = False
562  for category in categories.split():
563  if category == "self":
564  category = "main"
565  removeSelf = True
566  for lib in env.libs[category]:
567  if lib not in libs:
568  libs.append(lib)
569  if removeSelf:
570  try:
571  libs.remove(env["packageName"])
572  except ValueError:
573  pass
574  return libs
575 
576 SConsEnvironment.getLibs = getLibs
577 
578 ## @}
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.
Definition: dependencies.py:39
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...