2 # SConstruct file to build scons during development.
4 # THIS IS NOT READY YET. DO NOT TRY TO BUILD SCons WITH ITSELF YET.
8 # Copyright (c) 2001 Steven Knight
10 # Permission is hereby granted, free of charge, to any person obtaining
11 # a copy of this software and associated documentation files (the
12 # "Software"), to deal in the Software without restriction, including
13 # without limitation the rights to use, copy, modify, merge, publish,
14 # distribute, sublicense, and/or sell copies of the Software, and to
15 # permit persons to whom the Software is furnished to do so, subject to
16 # the following conditions:
18 # The above copyright notice and this permission notice shall be included
19 # in all copies or substantial portions of the Software.
21 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
22 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
23 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
25 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
27 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
43 # An internal "whereis" routine to figure out if we have a
44 # given program available. Put it in the "cons::" package
45 # so subsidiary Conscript files can get at it easily, too.
49 for dir in string.split(os.environ['PATH'], os.pathsep):
50 f = os.path.join(dir, file)
55 if stat.S_IMODE(st[stat.ST_MODE]) & 0111:
60 # We let the presence or absence of various utilities determine
61 # whether or not we bother to build certain pieces of things.
62 # This will allow people to still do SCons work even if they
63 # don't have Aegis or RPM installed, for example.
65 aegis = whereis('aegis')
66 aesub = whereis('aesub')
68 dh_builddeb = whereis('dh_builddeb')
69 fakeroot = whereis('fakeroot')
71 # My installation on Red Hat doesn't like any debhelper version
72 # beyond 2, so let's use 2 as the default on any non-Debian build.
73 if os.path.isfile('/etc/debian_version'):
79 # Now grab the information that we "build" into the files (using sed).
82 date = ARGUMENTS['date']
84 date = time.strftime("%Y/%m/%d %H:%M:%S", time.localtime(time.time()))
86 if ARGUMENTS.has_key('developer'):
87 developer = ARGUMENTS['developer']
88 elif os.environ.has_key('USERNAME'):
89 developer = os.environ['USERNAME']
90 elif os.environ.has_key('LOGNAME'):
91 developer = os.environ['LOGNAME']
92 elif os.environ.has_key('USER'):
93 developer = os.environ['USER']
96 revision = ARGUMENTS['version']
99 revision = os.popen(aesub + " \\$version", "r").read()[:-1]
103 a = string.split(revision, '.')
109 revision = string.join(arr, '.')
111 # Here's how we'd turn the calculated $revision into our package $version.
112 # This makes it difficult to coordinate with other files (debian/changelog
113 # and rpm/scons.spec) that hard-code the version number, so just go with
114 # the flow for now and hard code it here, too.
118 # if str[0] == 'C' or str[0] == 'D':
120 # while len(str) > 2 and str[0] == '0':
123 #arr = map(lambda x, xxx=xxx: xxx(x), arr)
124 #version = string.join(arr, '.')
128 change = ARGUMENTS['change']
131 change = os.popen(aesub + " \\$change", "r").read()[:-1]
135 python_ver = sys.version[0:3]
137 platform = distutils.util.get_platform()
139 if platform == "win32":
142 archsuffix = "tar.gz"
144 ENV = { 'PATH' : os.environ['PATH'] }
145 if os.environ.has_key('AEGIS_PROJECT'):
146 ENV['AEGIS_PROJECT'] = os.environ['AEGIS_PROJECT']
148 test1_dir = os.path.join(os.getcwd(), "build", "test1")
149 test2_dir = os.path.join(os.getcwd(), "build", "test2")
151 lib_project = os.path.join("lib", project)
153 # Originally, we were going to package the build engine in a
154 # private SCons library that contained the version number, so
155 # we could easily have multiple side-by-side versions of SCons
156 # installed. Keep this around in case we ever want to go back
157 # to that scheme. Note that this also requires changes to
158 # runtest.py and src/setup.py.
159 #lib_project = os.path.join("lib", project + '-' + version)
161 test1_lib_dir = os.path.join(test1_dir, lib_project)
163 test2_lib_dir = os.path.join(test2_dir,
165 "python" + python_ver,
168 unpack_dir = os.path.join(os.getcwd(), "build", "unpack")
173 TEST1_LIB_DIR = test1_lib_dir,
174 TEST2_LIB_DIR = test2_lib_dir,
177 DEVELOPER = developer,
180 DH_COMPAT = dh_compat,
183 SEDFLAGS = "$( -e 's+__DATE__+$DATE+' $)" + \
184 " -e 's+__DEVELOPER__+$DEVELOPER+'" + \
185 " -e 's+__FILE__+$SOURCES'+" + \
186 " -e 's+__REVISION__+$REVISION'+" + \
187 " -e 's+__VERSION__+$VERSION'+",
188 SEDCOM = '$SED $SEDFLAGS $SOURCES > $TARGET',
192 # Define SCons packages.
194 # In the original, more complicated packaging scheme, we were going
195 # to have separate packages for:
197 # python-scons only the build engine
198 # scons-script only the script
199 # scons the script plus the build engine
201 # We're now only delivering a single "scons" package, but this is still
202 # "built" as two sub-packages (the build engine and the script), so
203 # the definitions remain here, even though we're not using them for
208 'pkg' : 'python-' + project,
209 'src_subdir' : 'engine',
210 'inst_subdir' : os.path.join('lib', 'python1.5', 'site-packages'),
211 'prefix' : test2_dir,
218 'debian/python-scons.postinst',
219 'debian/python-scons.prerm',
222 'files' : [ 'LICENSE.txt',
229 'LICENSE.txt' : '../LICENSE.txt'
234 # The original packaging scheme would have have required us to push
235 # the Python version number into the package name (python1.5-scons,
236 # python2.0-scons, etc.), which would have required a definition
237 # like the following. Leave this here in case we ever decide to do
238 # this in the future, but note that this would require some modification
239 # to src/engine/setup.py before it would really work.
242 # 'pkg' : 'python2-' + project,
243 # 'src_subdir' : 'engine',
244 # 'inst_subdir' : os.path.join('lib', 'python2.1', 'site-packages'),
245 # 'prefix' : test2_dir,
250 # 'debian/changelog',
251 # 'debian/copyright',
252 # 'debian/python2-scons.postinst',
253 # 'debian/python2-scons.prerm',
263 # 'LICENSE.txt' : '../LICENSE.txt',
269 'pkg' : project + '-script',
270 'src_subdir' : 'script',
271 'inst_subdir' : 'bin',
272 'prefix' : test2_dir,
279 'debian/python-scons.postinst',
280 'debian/python-scons.prerm',
291 'LICENSE.txt' : '../LICENSE.txt',
292 'scons' : 'scons.py',
298 #'inst_subdir' : None,
299 'prefix' : test1_dir,
306 'debian/scons.postinst',
307 'debian/scons.prerm',
315 'os_spawnv_fix.diff',
323 'scons.1' : '../doc/man/scons.1',
326 'subpkgs' : [ python_scons, scons_script ],
329 'python-' + project : lib_project,
330 project + '-script' : 'bin',
339 # Initialize variables with the right directories for this package.
344 if p.has_key('src_subdir'):
345 src = os.path.join(src, p['src_subdir'])
347 build = os.path.join('build', pkg)
351 if p.has_key('inst_subdir'):
352 install = os.path.join(install, p['inst_subdir'])
355 # Read up the list of source files from our MANIFEST.in.
356 # This list should *not* include LICENSE.txt, MANIFEST,
357 # README.txt, or setup.py. Make a copy of the list for the
361 src_files = map(lambda x: x[:-1],
362 open(os.path.join(src, 'MANIFEST.in')).readlines())
363 dst_files = map(lambda x: os.path.join(install, x), src_files)
365 if p.has_key('subpkgs'):
367 # This package includes some sub-packages. Read up their
368 # MANIFEST.in files, and add them to our source and destination
369 # file lists, modifying them as appropriate to add the
372 for sp in p['subpkgs']:
373 ssubdir = sp['src_subdir']
374 isubdir = p['subinst_dirs'][sp['pkg']]
375 f = map(lambda x: x[:-1],
376 open(os.path.join(src, ssubdir, 'MANIFEST.in')).readlines())
377 src_files.extend(map(lambda x, s=sp['src_subdir']:
380 dst_files.extend(map(lambda x, i=install, s=isubdir:
381 os.path.join(i, s, x),
383 for k in sp['filemap'].keys():
386 k = os.path.join(sp['src_subdir'], k)
387 p['filemap'][k] = os.path.join(sp['src_subdir'], f)
390 # Now that we have the "normal" source files, add those files
391 # that are standard for each distribution. Note that we don't
392 # add these to dst_files, because they don't get installed.
393 # And we still have the MANIFEST to add.
395 src_files.extend(p['files'])
398 # Now run everything in src_file through the sed command we
399 # concocted to expand __FILE__, __VERSION__, etc.
402 s = p['filemap'].get(b, b)
403 env.Command(os.path.join(build, b),
404 os.path.join(src, s),
408 # NOW, finally, we can create the MANIFEST, which we do
409 # by having Python spit out the contents of the src_files
410 # array we've carefully created. After we've added
411 # MANIFEST itself to the array, of course.
413 src_files.append("MANIFEST")
414 def copy(target, source, **kw):
417 f = open(target, 'wb')
418 for file in src_files:
422 env.Command(os.path.join(build, 'MANIFEST'),
423 os.path.join(src, 'MANIFEST.in'),
427 # Use the Python distutils to generate the packages.
429 archive = os.path.join(build,
431 "%s-%s.%s" % (pkg, version, archsuffix))
433 src_deps.append(archive)
436 os.path.join(build, 'dist', "%s-%s.%s.%s" % (pkg, version, platform, archsuffix)),
438 os.path.join(build, 'dist', "%s-%s.win32.exe" % (pkg, version)),
440 install_targets = build_targets[:]
442 # We can get away with calling setup.py using a directory path
443 # like this because we put a preamble in it that will chdir()
444 # to the directory in which setup.py exists.
446 os.path.join(build, 'build', 'lib'),
447 os.path.join(build, 'build', 'scripts'),
449 setup_py = os.path.join(build, 'setup.py')
451 "rm -rf %s && python %s bdist" %
452 (string.join(map(lambda x: str(x), bdist_dirs)), setup_py),
453 "python %s sdist" % setup_py,
454 "python %s bdist_wininst" % setup_py,
458 topdir = os.path.join(os.getcwd(), build, 'build',
459 'bdist.' + platform, 'rpm')
461 BUILDdir = os.path.join(topdir, 'BUILD', pkg + '-' + version)
462 RPMSdir = os.path.join(topdir, 'RPMS', 'noarch')
463 SOURCESdir = os.path.join(topdir, 'SOURCES')
464 SPECSdir = os.path.join(topdir, 'SPECS')
465 SRPMSdir = os.path.join(topdir, 'SRPMS')
467 specfile = os.path.join(SPECSdir, "%s-%s-1.spec" % (pkg, version))
468 sourcefile = os.path.join(SOURCESdir, "%s-%s.%s" % (pkg, version, archsuffix));
469 rpm = os.path.join(RPMSdir, "%s-%s-1.noarch.rpm" % (pkg, version))
470 src_rpm = os.path.join(SRPMSdir, "%s-%s-1.src.rpm" % (pkg, version))
472 env.InstallAs(specfile, os.path.join('rpm', "%s.spec" % pkg))
473 env.InstallAs(sourcefile, archive)
475 targets = [ rpm, src_rpm ]
476 cmd = "rpm --define '_topdir %s' -ba $SOURCES" % topdir
477 if not os.path.isdir(BUILDdir):
478 cmd = "mkdir -p " + BUILDdir + "; " + cmd
479 env.Command(targets, specfile, cmd)
480 env.Depends(targets, sourcefile)
482 install_targets.extend(targets)
484 build_src_files = map(lambda x, b=build: os.path.join(b, x), src_files)
486 if dh_builddeb and fakeroot:
487 # Debian builds directly into build/dist, so we don't
488 # need to add the .debs to the install_targets.
489 deb = os.path.join('build', 'dist', "%s_%s-1_all.deb" % (pkg, version))
490 env.Command(deb, build_src_files, [
491 "fakeroot make -f debian/rules VERSION=$VERSION DH_COMPAT=$DH_COMPAT ENVOKED_BY_CONSTRUCT=1 binary-%s" % pkg,
492 "env DH_COMPAT=$DH_COMPAT dh_clean"
494 env.Depends(deb, p['debian_deps'])
498 # Now set up creation and installation of the packages.
500 env.Command(build_targets, build_src_files, commands)
501 env.Install(os.path.join('build', 'dist'), install_targets)
504 # Unpack the archive created by the distutils into build/unpack.
506 d = os.path.join(unpack_dir, "%s-%s" % (pkg, version))
507 unpack_files = map(lambda x, d=d: os.path.join(d, x), src_files)
509 # We'd like to replace the last three lines with the following:
511 # tar zxf %< -C $unpack_dir
513 # but that gives heartburn to Cygwin's tar, so work around it
514 # with separate zcat-tar-rm commands.
515 env.Command(unpack_files, archive, [
516 "rm -rf " + os.path.join(unpack_dir, '%s-%s' % (pkg, version)),
517 "zcat $SOURCES > .temp",
518 "tar xf .temp -C %s" % unpack_dir,
523 # Run setup.py in the unpacked subdirectory to "install" everything
524 # into our build/test subdirectory. Auxiliary modules that we need
525 # (TestCmd.py, TestSCons.py, unittest.py) will be copied in by
526 # etc/Conscript. The runtest.py script will set PYTHONPATH so that
527 # the tests only look under build/test. This makes sure that our
528 # tests pass with what we really packaged, not because of something
529 # hanging around in the development directory.
531 # We can get away with calling setup.py using a directory path
532 # like this because we put a preamble in it that will chdir()
533 # to the directory in which setup.py exists.
534 env.Command(dst_files, unpack_files, [
535 "rm -rf %s" % install,
536 "python %s install --prefix=%s" % (os.path.join(unpack_dir,
537 '%s-%s' % (pkg, version),
544 # Arrange for supporting packages to be installed in the test directories.
546 Export('env', 'whereis')
548 SConscript('etc/SConscript')
553 BuildDir('build/doc', 'doc')
555 SConscript('build/doc/SConscript');
559 # If we're running in the actual Aegis project, pack up a complete
560 # source archive from the project files and files in the change,
561 # so we can share it with helpful developers who don't use Aegis.
563 # First, lie and say that we've seen any files removed by this
564 # change, so they don't get added to the source files list
565 # that goes into the archive.
570 cmd = "aegis -list -unf -c %s cf 2>/dev/null" % change
571 for line in map(lambda x: x[:-1], os.popen(cmd, "r").readlines()):
572 a = string.split(line)
576 cmd = "aegis -list -terse pf 2>/dev/null"
577 pf = map(lambda x: x[:-1], os.popen(cmd, "r").readlines())
578 cmd = "aegis -list -terse cf 2>/dev/null"
579 cf = map(lambda x: x[:-1], os.popen(cmd, "r").readlines())
585 sfiles = filter(lambda x: x[-9:] != '.aeignore' and x[-7:] != '.consign',
589 ps = "%s-src" % project
590 psv = "%s-src-%s" % (project, version)
591 b_ps = os.path.join('build', ps)
592 b_psv = os.path.join('build', psv)
595 env.Command(os.path.join(b_ps, file), file,
596 [ "$SEDCOM", "chmod --reference=$SOURCES $TARGET" ])
598 b_ps_files = map(lambda x, d=b_ps: os.path.join(d, x), sfiles)
601 "cp -rp %s %s" % (b_ps, b_psv),
602 "find %s -name .consign -exec rm {} \\;" % b_psv,
603 "tar zcf $TARGET -C build %s" % psv,
605 env.Command(os.path.join('build',
607 '%s-src-%s.tar.gz' % (project, version)),
608 src_deps + b_ps_files, cmds)