9 from __future__
import absolute_import, division, print_function
13 from stat
import ST_MODE
14 from SCons.Script import SConscript, File, Dir, Glob, BUILD_TARGETS
15 from distutils.spawn
import find_executable
17 from .
import dependencies
22 DEFAULT_TARGETS = (
"lib",
"python",
"tests",
"examples",
"doc",
"shebang")
26 name, ext = os.path.splitext(os.path.basename(str(node)))
58 def __new__(cls, packageName, versionString=None, eupsProduct=None, eupsProductPath=None, cleanExt=None,
59 defaultTargets=DEFAULT_TARGETS,
60 subDirList=
None, ignoreRegex=
None,
61 versionModuleName=
"python/lsst/%s/version.py", noCfgFile=
False,
62 sconscriptOrder=
None):
63 cls.initialize(packageName, versionString, eupsProduct, eupsProductPath, cleanExt,
64 versionModuleName, noCfgFile=noCfgFile, sconscriptOrder=sconscriptOrder)
65 cls.finish(defaultTargets, subDirList, ignoreRegex)
99 def initialize(cls, packageName, versionString=None, eupsProduct=None, eupsProductPath=None,
100 cleanExt=
None, versionModuleName=
"python/lsst/%s/version.py", noCfgFile=
False,
101 sconscriptOrder=
None):
102 if cls._initializing:
103 state.log.fail(
"Recursion detected; an SConscript file should not call BasicSConstruct.")
104 cls._initializing =
True
106 cleanExt =
r"*~ core *.so *.os *.o *.pyc *.pkgc"
107 dependencies.configure(packageName, versionString, eupsProduct, eupsProductPath, noCfgFile)
108 state.env.BuildETags()
109 state.env.CleanTree(cleanExt)
110 if versionModuleName
is not None:
112 versionModuleName = versionModuleName %
"/".join(packageName.split(
"_"))
115 state.targets[
"version"] = state.env.VersionModule(versionModuleName)
117 for root, dirs, files
in os.walk(
"."):
118 if "SConstruct" in files
and root !=
".":
121 dirs[:] = [d
for d
in dirs
if not d.startswith(
'.')]
123 if "SConscript" in files:
124 scripts.append(os.path.join(root,
"SConscript"))
125 if sconscriptOrder
is None:
126 sconscriptOrder = (
"lib",
"python",
"tests",
"examples",
"doc")
128 for i, item
in enumerate(sconscriptOrder):
129 if path.startswith(item):
131 return len(sconscriptOrder)
132 scripts.sort(key=key)
133 for script
in scripts:
134 state.log.info(
"Using SConscript at %s" % script)
136 cls._initializing =
False
157 def finish(defaultTargets=DEFAULT_TARGETS,
158 subDirList=
None, ignoreRegex=
None):
159 if ignoreRegex
is None:
160 ignoreRegex =
r"(~$|\.pyc$|^\.svn$|\.o|\.os$)"
161 if subDirList
is None:
163 for path
in os.listdir(
"."):
164 if os.path.isdir(path)
and not path.startswith(
"."):
165 subDirList.append(path)
166 install = state.env.InstallLSST(state.env[
"prefix"],
167 [subDir
for subDir
in subDirList],
168 ignoreRegex=ignoreRegex)
169 for name, target
in state.targets.items():
170 state.env.Requires(install, target)
171 state.env.Alias(name, target)
172 state.env.Requires(state.targets[
"python"], state.targets[
"version"])
173 declarer = state.env.Declare()
174 state.env.Requires(declarer, install)
175 state.env.Default([t
for t
in defaultTargets
if os.path.exists(t)])
177 if "shebang" in defaultTargets
and os.path.exists(
"bin.src"):
178 state.env.Default(
"shebang")
179 if "version" in state.targets:
180 state.env.Default(state.targets[
"version"])
181 state.env.Requires(state.targets[
"tests"], state.targets[
"version"])
182 state.env.Decider(
"MD5-timestamp")
189 if "tests" in [str(t)
for t
in BUILD_TARGETS]:
190 testsDir = pipes.quote(os.path.join(os.getcwd(),
"tests",
".tests"))
191 checkTestStatus_command = state.env.Command(
'checkTestStatus', [],
"""
192 @ if [ -d {0} ]; then \
193 nfail=`find {0} -name \*.failed | wc -l | sed -e 's/ //g'`; \
194 if [ $$nfail -gt 0 ]; then \
195 echo "Failed test output:" >&2; \
196 find {0} -name \*.failed -exec cat {{}} \; >&2; \
197 echo "The following tests failed:" >&2;\
198 find {0} -name \*.failed >&2; \
199 echo "$$nfail tests failed" >&2; exit 1; \
204 state.env.Depends(checkTestStatus_command, BUILD_TARGETS)
205 BUILD_TARGETS.extend(checkTestStatus_command)
206 state.env.AlwaysBuild(checkTestStatus_command)
231 def lib(libName=None, src=None, libs="self"):
233 libName = state.env[
"packageName"]
235 src = Glob(
"#src/*.cc") + Glob(
"#src/*/*.cc") + Glob(
"#src/*/*/*.cc") + Glob(
"#src/*/*/*/*.cc")
236 src = state.env.SourcesForSharedLibrary(src)
237 if isinstance(libs, basestring):
238 libs = state.env.getLibs(libs)
241 result = state.env.SharedLibrary(libName, src, LIBS=libs)
242 state.targets[
"lib"].extend(result)
261 FIRST_LINE_RE = re.compile(
r'^#!.*python[0-9.]*([ \t].*)?$')
262 doRewrite = utils.needShebangRewrite()
264 def rewrite_shebang(target, source, env):
265 """Copy source to target, rewriting the shebang"""
267 usepython = utils.whichPython()
268 for targ, src
in zip(target, source):
269 with open(str(src),
"r") as srcfd:
270 with open(str(targ), "w")
as outfd:
271 first_line = srcfd.readline()
275 match = FIRST_LINE_RE.match(first_line)
276 if match
and doRewrite:
277 post_interp = match.group(1)
or ''
278 outfd.write(
"#!{}{}\n".
format(usepython, post_interp))
281 state.log.warn(
"Could not rewrite shebang of {}. Please check"
282 " file or move it to bin directory.".
format(str(src)))
283 outfd.write(first_line)
284 for line
in srcfd.readlines():
287 oldmode = os.stat(str(targ))[ST_MODE] & 0o7777
288 newmode = (oldmode | 0o555) & 0o7777
289 if newmode != oldmode:
290 state.log.info(
"changing mode of {} from {} to {}".
format(
291 str(targ), oldmode, newmode))
292 os.chmod(str(targ), newmode)
295 src = Glob(
"#bin.src/*")
297 if str(s) !=
"SConscript":
298 result = state.env.Command(target=os.path.join(Dir(
"#bin").abspath, str(s)),
299 source=s, action=rewrite_shebang)
300 state.targets[
"shebang"].extend(result)
317 def python(swigNameList=None, libs="main python", swigSrc=None):
318 if swigNameList
is None:
319 swigNameList = [state.env[
"packageName"].split(
"_")[-1] +
"Lib"]
320 swigFileList = [File(name +
".i")
for name
in swigNameList]
323 for name, node
in zip(swigNameList, swigFileList):
324 swigSrc.setdefault(name, []).append(node)
325 if isinstance(libs, basestring):
326 libs = state.env.getLibs(libs)
330 for name, src
in swigSrc.items():
331 result.extend(state.env.SwigLoadableModule(
"_" + name, src, LIBS=libs))
332 state.targets[
"python"].extend(result)
346 def doc(config="doxygen.conf.in", projectName=None, projectNumber=None, **kw):
347 if not find_executable(
"doxygen"):
348 state.log.warn(
"doxygen executable not found; skipping documentation build.")
350 if projectName
is None:
351 projectName =
".".join([
"lsst"] + state.env[
"packageName"].split(
"_"))
352 if projectNumber
is None:
353 projectNumber = state.env[
"version"]
354 result = state.env.Doxygen(
355 config, projectName=projectName, projectNumber=projectNumber,
356 includes=state.env.doxygen[
"includes"],
357 useTags=state.env.doxygen[
"tags"],
358 makeTag=(state.env[
"packageName"] +
".tag"),
361 state.targets[
"doc"].extend(result)
392 def tests(pyList=None, ccList=None, swigNameList=None, swigSrc=None,
393 ignoreList=
None, noBuildList=
None,
395 if noBuildList
is None:
397 if swigNameList
is None:
398 swigFileList = Glob(
"*.i")
399 swigNameList = [
_getFileBase(node)
for node
in swigFileList]
401 swigFileList = [File(name +
".i")
for name
in swigNameList]
405 for name, node
in zip(swigNameList, swigFileList):
406 src = swigSrc.setdefault(name, [])
407 allSwigSrc.update(str(element)
for element
in src)
410 pyList = [node
for node
in Glob(
"*.py")
412 os.path.basename(str(node))
not in noBuildList]
414 ccList = [node
for node
in Glob(
"*.cc")
415 if (
not str(node).endswith(
"_wrap.cc"))
and str(node)
not in allSwigSrc
and
416 os.path.basename(str(node))
not in noBuildList]
417 if ignoreList
is None:
421 return [str(i)
for i
in l]
422 state.log.info(
"SWIG modules for tests: %s" % s(swigFileList))
423 state.log.info(
"Python tests: %s" % s(pyList))
424 state.log.info(
"C++ tests: %s" % s(ccList))
425 state.log.info(
"Files that will not be built: %s" % noBuildList)
426 state.log.info(
"Ignored tests: %s" % ignoreList)
427 control =
tests.Control(state.env, ignoreList=ignoreList, args=args, verbose=
True)
428 for ccTest
in ccList:
429 state.env.Program(ccTest, LIBS=state.env.getLibs(
"main test"))
431 for name, src
in swigSrc.items():
433 state.env.SwigLoadableModule(
"_" + name, src, LIBS=state.env.getLibs(
"main python"))
435 ccList = [control.run(str(node))
for node
in ccList]
436 pyList = [control.run(str(node))
for node
in pyList]
437 for pyTest
in pyList:
438 state.env.Depends(pyTest, swigMods)
439 state.env.Depends(pyTest, state.targets[
"python"])
440 state.env.Depends(pyTest, state.targets[
"shebang"])
441 result = ccList + pyList
442 state.targets[
"tests"].extend(result)
457 def examples(ccList=None, swigNameList=None, swigSrc=None):
458 if swigNameList
is None:
459 swigFileList = Glob(
"*.i")
460 swigNameList = [
_getFileBase(node)
for node
in swigFileList]
462 swigFileList = [File(name)
for name
in swigNameList]
466 for name, node
in zip(swigNameList, swigFileList):
467 src = swigSrc.setdefault(name, [])
468 allSwigSrc.update(str(element)
for element
in src)
471 ccList = [node
for node
in Glob(
"*.cc")
472 if (
not str(node).endswith(
"_wrap.cc"))
and str(node)
not in allSwigSrc]
473 state.log.info(
"SWIG modules for examples: %s" % swigFileList)
474 state.log.info(
"C++ examples: %s" % ccList)
477 results.extend(state.env.Program(src, LIBS=state.env.getLibs(
"main")))
478 for name, src
in swigSrc.items():
480 state.env.SwigLoadableModule(
"_" + name, src, LIBS=state.env.getLibs(
"main python"))
482 for result
in results:
483 state.env.Depends(result, state.targets[
"lib"])
484 state.targets[
"examples"].extend(results)
def lib
Convenience function to replace standard lib/SConscript boilerplate.
def initialize
Convenience function to replace standard SConstruct boilerplate (step 1).
def finish
Convenience function to replace standard SConstruct boilerplate (step 2).
def doc
Convenience function to replace standard doc/SConscript boilerplate.
A scope-only class for SConstruct-replacement convenience functions.
A scope-only class for SConscript-replacement convenience functions.
def shebang
Handles shebang rewriting.
def examples
Convenience function to replace standard examples/SConscript boilerplate.
def __new__
Convenience function to replace standard SConstruct boilerplate.
A class to control unit tests.
def tests
Convenience function to replace standard tests/SConscript boilerplate.
def python
Convenience function to replace standard python/*/SConscript boilerplate.