2 # SConstruct file to build scons packages during development.
4 # See the README file for an overview of how SCons is built and tested.
7 # When this gets changed, you must also change the copyright_years string
8 # in QMTest/TestSCons.py so the test scripts look for the right string.
9 copyright_years = '2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009'
11 # This gets inserted into the man pages to reflect the month of release.
12 month_year = 'February 2009'
17 # Permission is hereby granted, free of charge, to any person obtaining
18 # a copy of this software and associated documentation files (the
19 # "Software"), to deal in the Software without restriction, including
20 # without limitation the rights to use, copy, modify, merge, publish,
21 # distribute, sublicense, and/or sell copies of the Software, and to
22 # permit persons to whom the Software is furnished to do so, subject to
23 # the following conditions:
25 # The above copyright notice and this permission notice shall be included
26 # in all copies or substantial portions of the Software.
28 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
29 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
30 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
31 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
32 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
33 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
34 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
48 default_version = '1.2.0'
49 copyright = "Copyright (c) %s The SCons Foundation" % copyright_years
51 platform = distutils.util.get_platform()
56 # An internal "whereis" routine to figure out if a given program
57 # is available on this system.
61 if platform == "win32":
63 for dir in string.split(os.environ['PATH'], os.pathsep):
64 f = os.path.join(dir, file)
67 if os.path.isfile(f_ext):
72 if stat.S_IMODE(st[stat.ST_MODE]) & 0111:
77 # We let the presence or absence of various utilities determine whether
78 # or not we bother to build certain pieces of things. This should allow
79 # people to still do SCons packaging work even if they don't have all
80 # of the utilities installed (e.g. RPM).
82 dh_builddeb = whereis('dh_builddeb')
83 fakeroot = whereis('fakeroot')
84 gzip = whereis('gzip')
85 rpmbuild = whereis('rpmbuild') or whereis('rpm')
86 hg = os.path.exists('.hg') and whereis('hg')
87 svn = os.path.exists('.svn') and whereis('svn')
88 unzip = whereis('unzip')
92 # Now grab the information that we "build" into the files.
94 date = ARGUMENTS.get('DATE')
97 date = time.strftime("%Y/%m/%d %H:%M:%S", time.localtime(time.time()))
99 developer = ARGUMENTS.get('DEVELOPER')
101 for variable in ['USERNAME', 'LOGNAME', 'USER']:
102 developer = os.environ.get(variable)
106 build_system = ARGUMENTS.get('BUILD_SYSTEM')
109 build_system = string.split(socket.gethostname(), '.')[0]
111 version = ARGUMENTS.get('VERSION', '')
113 version = default_version
116 svn_status_lines = []
119 cmd = "%s status --all 2> /dev/null" % hg
120 hg_status_lines = os.popen(cmd, "r").readlines()
123 cmd = "%s status --verbose 2> /dev/null" % svn
124 svn_status_lines = os.popen(cmd, "r").readlines()
126 revision = ARGUMENTS.get('REVISION', '')
127 def generate_build_id(revision):
130 if not revision and hg:
131 hg_heads = os.popen("%s heads 2> /dev/null" % hg, "r").read()
132 cs = re.search('changeset:\s+(\S+)', hg_heads)
134 revision = cs.group(1)
135 b = re.search('branch:\s+(\S+)', hg_heads)
137 revision = b.group(1) + ':' + revision
138 def generate_build_id(revision):
140 if filter(lambda l: l[0] in 'AMR!', hg_status_lines):
141 result = result + '[MODIFIED]'
144 if not revision and svn:
145 svn_info = os.popen("%s info 2> /dev/null" % svn, "r").read()
146 m = re.search('Revision: (\d+)', svn_info)
148 revision = m.group(1)
149 def generate_build_id(revision):
150 result = 'r' + revision
151 if filter(lambda l: l[0] in 'ACDMR', svn_status_lines):
152 result = result + '[MODIFIED]'
155 checkpoint = ARGUMENTS.get('CHECKPOINT', '')
157 if checkpoint == 'd':
159 checkpoint = time.strftime('d%Y%m%d', time.localtime(time.time()))
160 elif checkpoint == 'r':
161 checkpoint = 'r' + revision
162 version = version + '.' + checkpoint
164 build_id = ARGUMENTS.get('BUILD_ID')
167 build_id = generate_build_id(revision)
171 python_ver = sys.version[0:3]
173 # Re-exporting LD_LIBRARY_PATH is necessary if the Python version was
174 # built with the --enable-shared option.
176 ENV = { 'PATH' : os.environ['PATH'] }
177 for key in ['LOGNAME', 'PYTHONPATH', 'LD_LIBRARY_PATH']:
178 if os.environ.has_key(key):
179 ENV[key] = os.environ[key]
181 build_dir = ARGUMENTS.get('BUILDDIR', 'build')
182 if not os.path.isabs(build_dir):
183 build_dir = os.path.normpath(os.path.join(os.getcwd(), build_dir))
185 command_line_variables = [
186 ("BUILDDIR=", "The directory in which to build the packages. " +
187 "The default is the './build' subdirectory."),
189 ("BUILD_ID=", "An identifier for the specific build." +
190 "The default is the Subversion revision number."),
192 ("BUILD_SYSTEM=", "The system on which the packages were built. " +
193 "The default is whatever hostname is returned " +
194 "by socket.gethostname()."),
196 ("CHECKPOINT=", "The specific checkpoint release being packaged, " +
197 "which will be appended to the VERSION string. " +
198 "A value of CHECKPOINT=d will generate a string " +
199 "of 'd' plus today's date in the format YYYMMDD. " +
200 "A value of CHECKPOINT=r will generate a " +
201 "string of 'r' plus the Subversion revision " +
202 "number. Any other CHECKPOINT= string will be " +
203 "used as is. There is no default value."),
205 ("DATE=", "The date string representing when the packaging " +
206 "build occurred. The default is the day and time " +
207 "the SConstruct file was invoked, in the format " +
208 "YYYY/MM/DD HH:MM:SS."),
210 ("DEVELOPER=", "The developer who created the packages. " +
211 "The default is the first set environment " +
212 "variable from the list $USERNAME, $LOGNAME, $USER."),
214 ("REVISION=", "The revision number of the source being built. " +
215 "The default is the Subversion revision returned " +
216 "'svn info', with an appended string of " +
217 "'[MODIFIED]' if there are any changes in the " +
220 ("VERSION=", "The SCons version being packaged. The default " +
221 "is the hard-coded value '%s' " % default_version +
222 "from this SConstruct file."),
225 Default('.', build_dir)
227 packaging_flavors = [
228 ('deb', "A .deb package. (This is currently not supported.)"),
230 ('rpm', "A RedHat Package Manager file."),
232 ('tar-gz', "The normal .tar.gz file for end-user installation."),
234 ('src-tar-gz', "A .tar.gz file containing all the source " +
235 "(including tests and documentation)."),
237 ('local-tar-gz', "A .tar.gz file for dropping into other software " +
240 ('zip', "The normal .zip file for end-user installation."),
242 ('src-zip', "A .zip file containing all the source " +
243 "(including tests and documentation)."),
245 ('local-zip', "A .zip file for dropping into other software " +
249 test_deb_dir = os.path.join(build_dir, "test-deb")
250 test_rpm_dir = os.path.join(build_dir, "test-rpm")
251 test_tar_gz_dir = os.path.join(build_dir, "test-tar-gz")
252 test_src_tar_gz_dir = os.path.join(build_dir, "test-src-tar-gz")
253 test_local_tar_gz_dir = os.path.join(build_dir, "test-local-tar-gz")
254 test_zip_dir = os.path.join(build_dir, "test-zip")
255 test_src_zip_dir = os.path.join(build_dir, "test-src-zip")
256 test_local_zip_dir = os.path.join(build_dir, "test-local-zip")
258 unpack_tar_gz_dir = os.path.join(build_dir, "unpack-tar-gz")
259 unpack_zip_dir = os.path.join(build_dir, "unpack-zip")
261 if platform == "win32":
263 python_project_subinst_dir = os.path.join("Lib", "site-packages", project)
264 project_script_subinst_dir = 'Scripts'
267 python_project_subinst_dir = os.path.join("lib", project)
268 project_script_subinst_dir = 'bin'
274 indent_fmt = ' %-26s '
277 The following aliases build packages of various types, and unpack the
278 contents into build/test-$PACKAGE subdirectories, which can be used by the
279 runtest.py -p option to run tests against what's been actually packaged:
283 aliases = packaging_flavors + [('doc', 'The SCons documentation.')]
286 for alias, help_text in aliases:
287 tw = textwrap.TextWrapper(
289 initial_indent = indent_fmt % alias,
290 subsequent_indent = indent_fmt % '' + ' ',
292 Help(tw.fill(help_text) + '\n')
295 The following command-line variables can be set:
299 for variable, help_text in command_line_variables:
300 tw = textwrap.TextWrapper(
302 initial_indent = indent_fmt % variable,
303 subsequent_indent = indent_fmt % '' + ' ',
305 Help(tw.fill(help_text) + '\n')
312 # Figure out if we can handle .zip files.
319 def zipit(env, target, source):
320 print "Zipping %s:" % str(target[0])
321 def visit(arg, dirname, names):
323 path = os.path.join(dirname, name)
324 if os.path.isfile(path):
326 zf = zipfile.ZipFile(str(target[0]), 'w')
329 try: os.path.walk(env['PSV'], visit, zf)
330 finally: os.chdir(olddir)
333 def unzipit(env, target, source):
334 print "Unzipping %s:" % str(source[0])
335 zf = zipfile.ZipFile(str(source[0]), 'r')
336 for name in zf.namelist():
337 dest = os.path.join(env['UNPACK_ZIP_DIR'], name)
338 dir = os.path.dirname(dest)
344 # if the file exists, then delete it before writing
345 # to it so that we don't end up trying to write to a symlink:
346 if os.path.isfile(dest) or os.path.islink(dest):
348 if not os.path.isdir(dest):
349 open(dest, 'wb').write(zf.read(name))
353 zipit = "cd $CD && $ZIP $ZIPFLAGS $( ${TARGET.abspath} $) $PSV"
354 unzipit = "$UNZIP $UNZIPFLAGS $SOURCES"
356 def SCons_revision(target, source, env):
357 """Interpolate specific values from the environment into a file.
359 This is used to copy files into a tree that gets packaged up
360 into the source file package.
364 contents = open(s, 'rb').read()
365 # Note: We construct the __*__ substitution strings here
366 # so that they don't get replaced when this file gets
367 # copied into the tree for packaging.
368 contents = string.replace(contents, '__BUILD' + '__', env['BUILD'])
369 contents = string.replace(contents, '__BUILDSYS' + '__', env['BUILDSYS'])
370 contents = string.replace(contents, '__COPYRIGHT' + '__', env['COPYRIGHT'])
371 contents = string.replace(contents, '__DATE' + '__', env['DATE'])
372 contents = string.replace(contents, '__DEVELOPER' + '__', env['DEVELOPER'])
373 contents = string.replace(contents, '__FILE' + '__', str(source[0]))
374 contents = string.replace(contents, '__MONTH_YEAR'+ '__', env['MONTH_YEAR'])
375 contents = string.replace(contents, '__REVISION' + '__', env['REVISION'])
376 contents = string.replace(contents, '__VERSION' + '__', env['VERSION'])
377 contents = string.replace(contents, '__NULL' + '__', '')
378 open(t, 'wb').write(contents)
379 os.chmod(t, os.stat(s)[0])
381 revbuilder = Builder(action = Action(SCons_revision,
382 varlist=['COPYRIGHT', 'VERSION']))
384 def soelim(target, source, env):
386 Interpolate files included in [gnt]roff source files using the
389 This behaves somewhat like the soelim(1) wrapper around groff, but
390 makes us independent of whether the actual underlying implementation
391 includes an soelim() command or the corresponding command-line option
392 to groff(1). The key behavioral difference is that this doesn't
393 recursively include .so files from the include file. Not yet, anyway.
397 dir, f = os.path.split(s)
400 for line in sfp.readlines():
401 if line[:4] in ['.so ', "'so "]:
402 sofile = os.path.join(dir, line[4:-1])
403 tfp.write(open(sofile, 'r').read())
409 def soscan(node, env, path):
410 c = node.get_text_contents()
411 return re.compile(r"^[\.']so\s+(\S+)", re.M).findall(c)
413 soelimbuilder = Builder(action = Action(soelim),
414 source_scanner = Scanner(soscan))
416 # When copying local files from a Repository (Aegis),
417 # just make copies, don't symlink them.
418 SetOption('duplicate', 'copy')
424 BUILDDIR = build_dir,
425 BUILDSYS = build_system,
426 COPYRIGHT = copyright,
428 DEVELOPER = developer,
429 DISTDIR = os.path.join(build_dir, 'dist'),
430 MONTH_YEAR = month_year,
435 TAR_HFLAG = tar_hflag,
440 UNZIPFLAGS = '-o -d $UNPACK_ZIP_DIR',
445 RPM2CPIO = 'rpm2cpio',
447 TEST_DEB_DIR = test_deb_dir,
448 TEST_RPM_DIR = test_rpm_dir,
449 TEST_SRC_TAR_GZ_DIR = test_src_tar_gz_dir,
450 TEST_SRC_ZIP_DIR = test_src_zip_dir,
451 TEST_TAR_GZ_DIR = test_tar_gz_dir,
452 TEST_ZIP_DIR = test_zip_dir,
454 UNPACK_TAR_GZ_DIR = unpack_tar_gz_dir,
455 UNPACK_ZIP_DIR = unpack_zip_dir,
457 BUILDERS = { 'SCons_revision' : revbuilder,
458 'SOElim' : soelimbuilder },
460 PYTHON = '"%s"' % sys.executable,
464 Version_values = [Value(version), Value(build_id)]
467 # Define SCons packages.
469 # In the original, more complicated packaging scheme, we were going
470 # to have separate packages for:
472 # python-scons only the build engine
473 # scons-script only the script
474 # scons the script plus the build engine
476 # We're now only delivering a single "scons" package, but this is still
477 # "built" as two sub-packages (the build engine and the script), so
478 # the definitions remain here, even though we're not using them for
483 'pkg' : 'python-' + project,
484 'src_subdir' : 'engine',
485 'inst_subdir' : os.path.join('lib', 'python1.5', 'site-packages'),
486 'rpm_dir' : '/usr/lib/scons',
499 'files' : [ 'LICENSE.txt',
506 'LICENSE.txt' : '../LICENSE.txt'
511 'extra_rpm_files' : [],
514 'SCons/__init__.py' : Version_values,
518 # Figure out the name of a .egg-info file that might be generated
519 # as part of the RPM package. There are two complicating factors.
521 # First, the RPM spec file we generate will just execute "python", not
522 # necessarily the one in sys.executable. If *that* version of python has
523 # a distutils that knows about Python eggs, then setup.py will generate a
524 # .egg-info file, so we have to execute any distutils logic in a subshell.
526 # Second, we can't just have the subshell check for the existence of the
527 # distutils.command.install_egg_info module and generate the expected
528 # file name by hand, the way we used to, because different systems can
529 # have slightly different .egg-info naming conventions. (Specifically,
530 # Ubuntu overrides the default behavior to remove the Python version
531 # string from the .egg-info file name.) The right way to do this is to
532 # actually call into the install_egg_info() class to have it generate
533 # the expected name for us.
535 # This is all complicated enough that we do it by writing an in-line
536 # script to a temporary file and then feeding it to a separate invocation
537 # of "python" to tell us the actual name of the generated .egg-info file.
539 print_egg_info_name = """
541 from distutils.dist import Distribution
542 from distutils.command.install_egg_info import install_egg_info
546 dist = Distribution({'name' : "scons", 'version' : '%s'})
547 i = install_egg_info(dist)
550 print os.path.split(i.outputs[0])[1]
554 fd, tfname = tempfile.mkstemp()
555 tfp = os.fdopen(fd, "w")
556 tfp.write(print_egg_info_name)
558 egg_info_file = os.popen("python %s" % tfname).read()[:-1]
560 python_scons['extra_rpm_files'].append(egg_info_file)
564 except EnvironmentError:
568 # The original packaging scheme would have have required us to push
569 # the Python version number into the package name (python1.5-scons,
570 # python2.0-scons, etc.), which would have required a definition
571 # like the following. Leave this here in case we ever decide to do
572 # this in the future, but note that this would require some modification
573 # to src/engine/setup.py before it would really work.
576 # 'pkg' : 'python2-' + project,
577 # 'src_subdir' : 'engine',
578 # 'inst_subdir' : os.path.join('lib', 'python2.2', 'site-packages'),
581 # 'debian/changelog',
583 # 'debian/copyright',
598 # 'LICENSE.txt' : '../LICENSE.txt',
605 'pkg' : project + '-script',
606 'src_subdir' : 'script',
607 'inst_subdir' : 'bin',
608 'rpm_dir' : '/usr/bin',
629 'LICENSE.txt' : '../LICENSE.txt',
630 'scons' : 'scons.py',
631 'sconsign' : 'sconsign.py',
632 'scons-time' : 'scons-time.py',
637 'extra_rpm_files' : [
639 'sconsign-' + version,
640 'scons-time-' + version,
644 'scons' : Version_values,
645 'sconsign' : Version_values,
668 'os_spawnv_fix.diff',
673 #'script/scons-post-install.py',
679 'scons.1' : '$BUILDDIR/doc/man/scons.1',
680 'sconsign.1' : '$BUILDDIR/doc/man/sconsign.1',
681 'scons-time.1' : '$BUILDDIR/doc/man/scons-time.1',
685 'scons.1' : env.SOElim,
686 'sconsign.1' : env.SOElim,
687 'scons-time.1' : env.SOElim,
690 'subpkgs' : [ python_scons, scons_script ],
693 'python-' + project : python_project_subinst_dir,
694 project + '-script' : project_script_subinst_dir,
698 scripts = ['scons', 'sconsign', 'scons-time']
705 # Initialize variables with the right directories for this package.
708 pkg_version = "%s-%s" % (pkg, version)
711 if p.has_key('src_subdir'):
712 src = os.path.join(src, p['src_subdir'])
714 build = os.path.join(build_dir, pkg)
716 tar_gz = os.path.join(build, 'dist', "%s.tar.gz" % pkg_version)
717 platform_tar_gz = os.path.join(build,
719 "%s.%s.tar.gz" % (pkg_version, platform))
720 zip = os.path.join(build, 'dist', "%s.zip" % pkg_version)
721 platform_zip = os.path.join(build,
723 "%s.%s.zip" % (pkg_version, platform))
724 if platform == "win-amd64":
725 win32_exe = os.path.join(build, 'dist', "%s.win-amd64.exe" % pkg_version)
727 win32_exe = os.path.join(build, 'dist', "%s.win32.exe" % pkg_version)
730 # Update the environment with the relevant information
733 # We can get away with calling setup.py using a directory path
734 # like this because we put a preamble in it that will chdir()
735 # to the directory in which setup.py exists.
737 setup_py = os.path.join(build, 'setup.py')
738 env.Replace(PKG = pkg,
739 PKG_VERSION = pkg_version,
740 SETUP_PY = '"%s"' % setup_py)
744 # Read up the list of source files from our MANIFEST.in.
745 # This list should *not* include LICENSE.txt, MANIFEST,
746 # README.txt, or setup.py. Make a copy of the list for the
749 manifest_in = File(os.path.join(src, 'MANIFEST.in')).rstr()
750 src_files = map(lambda x: x[:-1],
751 open(manifest_in).readlines())
752 raw_files = src_files[:]
753 dst_files = src_files[:]
756 MANIFEST_in_list = []
758 if p.has_key('subpkgs'):
760 # This package includes some sub-packages. Read up their
761 # MANIFEST.in files, and add them to our source and destination
762 # file lists, modifying them as appropriate to add the
765 for sp in p['subpkgs']:
766 ssubdir = sp['src_subdir']
767 isubdir = p['subinst_dirs'][sp['pkg']]
768 MANIFEST_in = File(os.path.join(src, ssubdir, 'MANIFEST.in')).rstr()
769 MANIFEST_in_list.append(MANIFEST_in)
770 files = map(lambda x: x[:-1], open(MANIFEST_in).readlines())
771 raw_files.extend(files)
772 src_files.extend(map(lambda x, s=ssubdir: os.path.join(s, x), files))
774 r = os.path.join(sp['rpm_dir'], f)
777 rpm_files.append(r + 'c')
778 for f in sp.get('extra_rpm_files', []):
779 r = os.path.join(sp['rpm_dir'], f)
781 files = map(lambda x, i=isubdir: os.path.join(i, x), files)
782 dst_files.extend(files)
783 for k, f in sp['filemap'].items():
785 k = os.path.join(ssubdir, k)
786 p['filemap'][k] = os.path.join(ssubdir, f)
787 for f, deps in sp['explicit_deps'].items():
788 f = os.path.join(build, ssubdir, f)
792 # Now that we have the "normal" source files, add those files
793 # that are standard for each distribution. Note that we don't
794 # add these to dst_files, because they don't get installed.
795 # And we still have the MANIFEST to add.
797 src_files.extend(p['files'])
800 # Now run everything in src_file through the sed command we
801 # concocted to expand __FILE__, __VERSION__, etc.
804 s = p['filemap'].get(b, b)
805 if not s[0] == '$' and not os.path.isabs(s):
806 s = os.path.join(src, s)
807 builder = p['buildermap'].get(b, env.SCons_revision)
808 x = builder(os.path.join(build, b), s)
812 # NOW, finally, we can create the MANIFEST, which we do
813 # by having Python spit out the contents of the src_files
814 # array we've carefully created. After we've added
815 # MANIFEST itself to the array, of course.
817 src_files.append("MANIFEST")
818 MANIFEST_in_list.append(os.path.join(src, 'MANIFEST.in'))
820 def write_src_files(target, source, **kw):
823 f = open(str(target[0]), 'wb')
824 for file in src_files:
828 env.Command(os.path.join(build, 'MANIFEST'),
833 # Now go through and arrange to create whatever packages we can.
835 build_src_files = map(lambda x, b=build: os.path.join(b, x), src_files)
836 apply(Local, build_src_files, {})
838 distutils_formats = []
840 distutils_targets = [ win32_exe ]
842 dist_distutils_targets = env.Install('$DISTDIR', distutils_targets)
843 Local(dist_distutils_targets)
844 AddPostAction(dist_distutils_targets, Chmod(dist_distutils_targets, 0644))
847 print "gzip not found in %s; skipping .tar.gz package for %s." % (os.environ['PATH'], pkg)
850 distutils_formats.append('gztar')
852 src_deps.append(tar_gz)
854 distutils_targets.extend([ tar_gz, platform_tar_gz ])
856 dist_tar_gz = env.Install('$DISTDIR', tar_gz)
857 dist_platform_tar_gz = env.Install('$DISTDIR', platform_tar_gz)
858 Local(dist_tar_gz, dist_platform_tar_gz)
859 AddPostAction(dist_tar_gz, Chmod(dist_tar_gz, 0644))
860 AddPostAction(dist_platform_tar_gz, Chmod(dist_platform_tar_gz, 0644))
863 # Unpack the tar.gz archive created by the distutils into
864 # build/unpack-tar-gz/scons-{version}.
866 # We'd like to replace the last three lines with the following:
868 # tar zxf $SOURCES -C $UNPACK_TAR_GZ_DIR
870 # but that gives heartburn to Cygwin's tar, so work around it
871 # with separate zcat-tar-rm commands.
873 unpack_tar_gz_files = map(lambda x, u=unpack_tar_gz_dir, pv=pkg_version:
874 os.path.join(u, pv, x),
876 env.Command(unpack_tar_gz_files, dist_tar_gz, [
877 Delete(os.path.join(unpack_tar_gz_dir, pkg_version)),
878 "$ZCAT $SOURCES > .temp",
879 "tar xf .temp -C $UNPACK_TAR_GZ_DIR",
884 # Run setup.py in the unpacked subdirectory to "install" everything
885 # into our build/test subdirectory. The runtest.py script will set
886 # PYTHONPATH so that the tests only look under build/test-{package},
887 # and under etc (for the testing modules TestCmd.py, TestSCons.py,
888 # and unittest.py). This makes sure that our tests pass with what
889 # we really packaged, not because of something hanging around in
890 # the development directory.
892 # We can get away with calling setup.py using a directory path
893 # like this because we put a preamble in it that will chdir()
894 # to the directory in which setup.py exists.
896 dfiles = map(lambda x, d=test_tar_gz_dir: os.path.join(d, x), dst_files)
897 env.Command(dfiles, unpack_tar_gz_files, [
898 Delete(os.path.join(unpack_tar_gz_dir, pkg_version, 'build')),
899 Delete("$TEST_TAR_GZ_DIR"),
900 '$PYTHON $PYTHONFLAGS "%s" install "--prefix=$TEST_TAR_GZ_DIR" --standalone-lib' % \
901 os.path.join(unpack_tar_gz_dir, pkg_version, 'setup.py'),
905 # Generate portage files for submission to Gentoo Linux.
907 gentoo = os.path.join(build, 'gentoo')
908 ebuild = os.path.join(gentoo, 'scons-%s.ebuild' % version)
909 digest = os.path.join(gentoo, 'files', 'digest-scons-%s' % version)
910 env.Command(ebuild, os.path.join('gentoo', 'scons.ebuild.in'), SCons_revision)
911 def Digestify(target, source, env):
914 """Return a signature as a string of hex characters.
916 # NOTE: This routine is a method in the Python 2.0 interface
917 # of the native md5 module, but we want SCons to operate all
918 # the way back to at least Python 1.5.2, which doesn't have it.
923 r = r + h[(i >> 4) & 0xF] + h[i & 0xF]
925 src = source[0].rfile()
926 contents = open(str(src)).read()
927 sig = hexdigest(md5.new(contents).digest())
928 bytes = os.stat(str(src))[6]
929 open(str(target[0]), 'w').write("MD5 %s %s %d\n" % (sig,
932 env.Command(digest, tar_gz, Digestify)
935 print "zip not found; skipping .zip package for %s." % pkg
938 distutils_formats.append('zip')
942 distutils_targets.extend([ zip, platform_zip ])
944 dist_zip = env.Install('$DISTDIR', zip)
945 dist_platform_zip = env.Install('$DISTDIR', platform_zip)
946 Local(dist_zip, dist_platform_zip)
947 AddPostAction(dist_zip, Chmod(dist_zip, 0644))
948 AddPostAction(dist_platform_zip, Chmod(dist_platform_zip, 0644))
951 # Unpack the zip archive created by the distutils into
952 # build/unpack-zip/scons-{version}.
954 unpack_zip_files = map(lambda x, u=unpack_zip_dir, pv=pkg_version:
955 os.path.join(u, pv, x),
958 env.Command(unpack_zip_files, dist_zip, [
959 Delete(os.path.join(unpack_zip_dir, pkg_version)),
964 # Run setup.py in the unpacked subdirectory to "install" everything
965 # into our build/test subdirectory. The runtest.py script will set
966 # PYTHONPATH so that the tests only look under build/test-{package},
967 # and under etc (for the testing modules TestCmd.py, TestSCons.py,
968 # and unittest.py). This makes sure that our tests pass with what
969 # we really packaged, not because of something hanging around in
970 # the development directory.
972 # We can get away with calling setup.py using a directory path
973 # like this because we put a preamble in it that will chdir()
974 # to the directory in which setup.py exists.
976 dfiles = map(lambda x, d=test_zip_dir: os.path.join(d, x), dst_files)
977 env.Command(dfiles, unpack_zip_files, [
978 Delete(os.path.join(unpack_zip_dir, pkg_version, 'build')),
979 Delete("$TEST_ZIP_DIR"),
980 '$PYTHON $PYTHONFLAGS "%s" install "--prefix=$TEST_ZIP_DIR" --standalone-lib' % \
981 os.path.join(unpack_zip_dir, pkg_version, 'setup.py'),
985 msg = "@echo \"Warning: Can not build 'rpm': no rpmbuild utility found\""
986 AlwaysBuild(Alias('rpm', [], msg))
988 topdir = os.path.join(build, 'build',
989 'bdist.' + platform, 'rpm')
991 buildroot = os.path.join(build_dir, 'rpm-buildroot')
993 BUILDdir = os.path.join(topdir, 'BUILD', pkg + '-' + version)
994 RPMSdir = os.path.join(topdir, 'RPMS', 'noarch')
995 SOURCESdir = os.path.join(topdir, 'SOURCES')
996 SPECSdir = os.path.join(topdir, 'SPECS')
997 SRPMSdir = os.path.join(topdir, 'SRPMS')
999 specfile_in = os.path.join('rpm', "%s.spec.in" % pkg)
1000 specfile = os.path.join(SPECSdir, "%s-1.spec" % pkg_version)
1001 sourcefile = os.path.join(SOURCESdir, "%s.tar.gz" % pkg_version);
1002 noarch_rpm = os.path.join(RPMSdir, "%s-1.noarch.rpm" % pkg_version)
1003 src_rpm = os.path.join(SRPMSdir, "%s-1.src.rpm" % pkg_version)
1005 def spec_function(target, source, env):
1006 """Generate the RPM .spec file from the template file.
1008 This fills in the %files portion of the .spec file with a
1009 list generated from our MANIFEST(s), so we don't have to
1010 maintain multiple lists.
1012 c = open(str(source[0]), 'rb').read()
1013 c = string.replace(c, '__VERSION' + '__', env['VERSION'])
1014 c = string.replace(c, '__RPM_FILES' + '__', env['RPM_FILES'])
1015 open(str(target[0]), 'wb').write(c)
1018 rpm_files_str = string.join(rpm_files, "\n") + "\n"
1019 rpm_spec_env = env.Clone(RPM_FILES = rpm_files_str)
1020 rpm_spec_action = Action(spec_function, varlist=['RPM_FILES'])
1021 rpm_spec_env.Command(specfile, specfile_in, rpm_spec_action)
1023 env.InstallAs(sourcefile, tar_gz)
1026 targets = [ noarch_rpm, src_rpm ]
1027 cmd = "$RPMBUILD --define '_topdir $(%s$)' --buildroot %s -ba $SOURCES" % (topdir, buildroot)
1028 if not os.path.isdir(BUILDdir):
1029 cmd = ("$( mkdir -p %s; $)" % BUILDdir) + cmd
1030 t = env.Command(targets, specfile, cmd)
1031 env.Depends(t, sourcefile)
1033 dist_noarch_rpm = env.Install('$DISTDIR', noarch_rpm)
1034 dist_src_rpm = env.Install('$DISTDIR', src_rpm)
1035 Local(dist_noarch_rpm, dist_src_rpm)
1036 AddPostAction(dist_noarch_rpm, Chmod(dist_noarch_rpm, 0644))
1037 AddPostAction(dist_src_rpm, Chmod(dist_src_rpm, 0644))
1039 dfiles = map(lambda x, d=test_rpm_dir: os.path.join(d, 'usr', x),
1043 "$RPM2CPIO $SOURCES | (cd $TEST_RPM_DIR && cpio -id)")
1045 if dh_builddeb and fakeroot:
1046 # Our Debian packaging builds directly into build/dist,
1047 # so we don't need to Install() the .debs.
1048 deb = os.path.join(build_dir, 'dist', "%s_%s-1_all.deb" % (pkg, version))
1049 for d in p['debian_deps']:
1050 b = env.SCons_revision(os.path.join(build, d), d)
1053 env.Command(deb, build_src_files, [
1054 "cd %s && fakeroot make -f debian/rules PYTHON=$PYTHON BUILDDEB_OPTIONS=--destdir=../../build/dist binary" % build,
1057 old = os.path.join('lib', 'scons', '')
1058 new = os.path.join('lib', 'python' + python_ver, 'site-packages', '')
1059 def xxx(s, old=old, new=new):
1060 if s[:len(old)] == old:
1061 s = new + s[len(old):]
1062 return os.path.join('usr', s)
1063 dfiles = map(lambda x, t=test_deb_dir: os.path.join(t, x),
1064 map(xxx, dst_files))
1067 "dpkg --fsys-tarfile $SOURCES | (cd $TEST_DEB_DIR && tar -xf -)")
1071 # Use the Python distutils to generate the appropriate packages.
1074 Delete(os.path.join(build, 'build', 'lib')),
1075 Delete(os.path.join(build, 'build', 'scripts')),
1078 if distutils_formats:
1079 commands.append(Delete(os.path.join(build,
1081 'bdist.' + platform,
1083 for format in distutils_formats:
1084 commands.append("$PYTHON $PYTHONFLAGS $SETUP_PY bdist_dumb -f %s" % format)
1086 commands.append("$PYTHON $PYTHONFLAGS $SETUP_PY sdist --formats=%s" % \
1087 string.join(distutils_formats, ','))
1089 commands.append("$PYTHON $PYTHONFLAGS $SETUP_PY bdist_wininst")
1091 env.Command(distutils_targets, build_src_files, commands)
1094 # Now create local packages for people who want to let people
1095 # build their SCons-buildable packages without having to
1098 s_l_v = '%s-local-%s' % (pkg, version)
1100 local = pkg + '-local'
1101 build_dir_local = os.path.join(build_dir, local)
1102 build_dir_local_slv = os.path.join(build_dir, local, s_l_v)
1104 dist_local_tar_gz = os.path.join("$DISTDIR/%s.tar.gz" % s_l_v)
1105 dist_local_zip = os.path.join("$DISTDIR/%s.zip" % s_l_v)
1106 AddPostAction(dist_local_tar_gz, Chmod(dist_local_tar_gz, 0644))
1107 AddPostAction(dist_local_zip, Chmod(dist_local_zip, 0644))
1110 Delete(build_dir_local),
1111 '$PYTHON $PYTHONFLAGS $SETUP_PY install "--install-script=%s" "--install-lib=%s" --no-install-man --no-compile --standalone-lib --no-version-script' % \
1112 (build_dir_local, build_dir_local_slv),
1115 for script in scripts:
1116 #commands.append("mv %s/%s %s/%s.py" % (local, script, local, script))
1117 local_script = os.path.join(build_dir_local, script)
1118 commands.append(Move(local_script + '.py', local_script))
1120 rf = filter(lambda x: not x in scripts, raw_files)
1121 rf = map(lambda x, slv=s_l_v: os.path.join(slv, x), rf)
1122 for script in scripts:
1123 rf.append("%s.py" % script)
1124 local_targets = map(lambda x, s=build_dir_local: os.path.join(s, x), rf)
1126 env.Command(local_targets, build_src_files, commands)
1128 scons_LICENSE = os.path.join(build_dir_local, 'scons-LICENSE')
1129 l = env.SCons_revision(scons_LICENSE, 'LICENSE-local')
1130 local_targets.append(l)
1133 scons_README = os.path.join(build_dir_local, 'scons-README')
1134 l = env.SCons_revision(scons_README, 'README-local')
1135 local_targets.append(l)
1139 env.Command(dist_local_tar_gz,
1141 "cd %s && tar czf $( ${TARGET.abspath} $) *" % build_dir_local)
1143 unpack_targets = map(lambda x, d=test_local_tar_gz_dir:
1146 commands = [Delete(test_local_tar_gz_dir),
1147 Mkdir(test_local_tar_gz_dir),
1148 "cd %s && tar xzf $( ${SOURCE.abspath} $)" % test_local_tar_gz_dir]
1150 env.Command(unpack_targets, dist_local_tar_gz, commands)
1153 env.Command(dist_local_zip, local_targets, zipit,
1154 CD = build_dir_local, PSV = '.')
1156 unpack_targets = map(lambda x, d=test_local_zip_dir:
1159 commands = [Delete(test_local_zip_dir),
1160 Mkdir(test_local_zip_dir),
1163 env.Command(unpack_targets, dist_local_zip, unzipit,
1164 UNPACK_ZIP_DIR = test_local_zip_dir)
1169 Export('build_dir', 'env')
1171 SConscript('QMTest/SConscript')
1180 def copy(target, source, env):
1183 open(t, 'wb').write(open(s, 'rb').read())
1186 # Guarantee that real copies of these files always exist in
1187 # build/. If there's a symlink there, then this is an Aegis
1188 # build and we blow them away now so that they'll get "built" later.
1189 p = os.path.join(build_dir, file)
1190 if os.path.islink(p):
1192 if not os.path.isabs(p):
1194 sp = env.Command(p, file, copy)
1200 Export('build_dir', 'env', 'whereis')
1202 SConscript('doc/SConscript')
1205 # If we're running in a Subversion working directory, pack up a complete
1206 # source archive from the project files and files in the change.
1211 slines = filter(lambda l: l[0] in 'ACM', hg_status_lines)
1212 sfiles = map(lambda l: l.split()[-1], slines)
1213 elif svn_status_lines:
1214 slines = filter(lambda l: l[0] in ' MA', svn_status_lines)
1215 sentries = map(lambda l: l.split()[-1], slines)
1216 sfiles = filter(os.path.isfile, sentries)
1218 "Not building in a Mercurial or Subversion tree; skipping building src package."
1230 for p in remove_patterns:
1231 sfiles = filter(lambda s, p=p: not fnmatch.fnmatch(s, p), sfiles)
1234 ps = "%s-src" % project
1235 psv = "%s-%s" % (ps, version)
1236 b_ps = os.path.join(build_dir, ps)
1237 b_psv = os.path.join(build_dir, psv)
1238 b_psv_stamp = b_psv + '-stamp'
1240 src_tar_gz = os.path.join(build_dir, 'dist', '%s.tar.gz' % psv)
1241 src_zip = os.path.join(build_dir, 'dist', '%s.zip' % psv)
1243 Local(src_tar_gz, src_zip)
1246 env.SCons_revision(os.path.join(b_ps, file), file)
1248 b_ps_files = map(lambda x, d=b_ps: os.path.join(d, x), sfiles)
1255 env.Command(b_psv_stamp, src_deps + b_ps_files, cmds)
1257 apply(Local, b_ps_files, {})
1261 env.Command(src_tar_gz, b_psv_stamp,
1262 "tar cz${TAR_HFLAG} -f $TARGET -C build %s" % psv)
1265 # Unpack the archive into build/unpack/scons-{version}.
1267 unpack_tar_gz_files = map(lambda x, u=unpack_tar_gz_dir, psv=psv:
1268 os.path.join(u, psv, x),
1272 # We'd like to replace the last three lines with the following:
1274 # tar zxf $SOURCES -C $UNPACK_TAR_GZ_DIR
1276 # but that gives heartburn to Cygwin's tar, so work around it
1277 # with separate zcat-tar-rm commands.
1278 env.Command(unpack_tar_gz_files, src_tar_gz, [
1279 Delete(os.path.join(unpack_tar_gz_dir, psv)),
1280 "$ZCAT $SOURCES > .temp",
1281 "tar xf .temp -C $UNPACK_TAR_GZ_DIR",
1286 # Run setup.py in the unpacked subdirectory to "install" everything
1287 # into our build/test subdirectory. The runtest.py script will set
1288 # PYTHONPATH so that the tests only look under build/test-{package},
1289 # and under etc (for the testing modules TestCmd.py, TestSCons.py,
1290 # and unittest.py). This makes sure that our tests pass with what
1291 # we really packaged, not because of something hanging around in
1292 # the development directory.
1294 # We can get away with calling setup.py using a directory path
1295 # like this because we put a preamble in it that will chdir()
1296 # to the directory in which setup.py exists.
1298 dfiles = map(lambda x, d=test_src_tar_gz_dir: os.path.join(d, x),
1300 scons_lib_dir = os.path.join(unpack_tar_gz_dir, psv, 'src', 'engine')
1301 ENV = env.Dictionary('ENV').copy()
1302 ENV['SCONS_LIB_DIR'] = scons_lib_dir
1303 ENV['USERNAME'] = developer
1304 env.Command(dfiles, unpack_tar_gz_files,
1306 Delete(os.path.join(unpack_tar_gz_dir,
1311 Delete("$TEST_SRC_TAR_GZ_DIR"),
1312 'cd "%s" && $PYTHON $PYTHONFLAGS "%s" "%s" VERSION="$VERSION"' % \
1313 (os.path.join(unpack_tar_gz_dir, psv),
1314 os.path.join('src', 'script', 'scons.py'),
1315 os.path.join('build', 'scons')),
1316 '$PYTHON $PYTHONFLAGS "%s" install "--prefix=$TEST_SRC_TAR_GZ_DIR" --standalone-lib' % \
1317 os.path.join(unpack_tar_gz_dir,
1327 env.Command(src_zip, b_psv_stamp, zipit, CD = 'build', PSV = psv)
1330 # Unpack the archive into build/unpack/scons-{version}.
1332 unpack_zip_files = map(lambda x, u=unpack_zip_dir, psv=psv:
1333 os.path.join(u, psv, x),
1336 env.Command(unpack_zip_files, src_zip, [
1337 Delete(os.path.join(unpack_zip_dir, psv)),
1342 # Run setup.py in the unpacked subdirectory to "install" everything
1343 # into our build/test subdirectory. The runtest.py script will set
1344 # PYTHONPATH so that the tests only look under build/test-{package},
1345 # and under etc (for the testing modules TestCmd.py, TestSCons.py,
1346 # and unittest.py). This makes sure that our tests pass with what
1347 # we really packaged, not because of something hanging around in
1348 # the development directory.
1350 # We can get away with calling setup.py using a directory path
1351 # like this because we put a preamble in it that will chdir()
1352 # to the directory in which setup.py exists.
1354 dfiles = map(lambda x, d=test_src_zip_dir: os.path.join(d, x),
1356 scons_lib_dir = os.path.join(unpack_zip_dir, psv, 'src', 'engine')
1357 ENV = env.Dictionary('ENV').copy()
1358 ENV['SCONS_LIB_DIR'] = scons_lib_dir
1359 ENV['USERNAME'] = developer
1360 env.Command(dfiles, unpack_zip_files,
1362 Delete(os.path.join(unpack_zip_dir,
1367 Delete("$TEST_SRC_ZIP_DIR"),
1368 'cd "%s" && $PYTHON $PYTHONFLAGS "%s" "%s" VERSION="$VERSION"' % \
1369 (os.path.join(unpack_zip_dir, psv),
1370 os.path.join('src', 'script', 'scons.py'),
1371 os.path.join('build', 'scons')),
1372 '$PYTHON $PYTHONFLAGS "%s" install "--prefix=$TEST_SRC_ZIP_DIR" --standalone-lib' % \
1373 os.path.join(unpack_zip_dir,
1381 for pf, help_text in packaging_flavors:
1383 os.path.join(build_dir, 'test-'+pf),
1384 os.path.join(build_dir, 'QMTest'),
1385 os.path.join(build_dir, 'runtest.py'),