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 = 'January 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
54 # An internal "whereis" routine to figure out if a given program
55 # is available on this system.
58 for dir in string.split(os.environ['PATH'], os.pathsep):
59 f = os.path.join(dir, file)
65 if stat.S_IMODE(st[stat.ST_MODE]) & 0111:
70 # We let the presence or absence of various utilities determine whether
71 # or not we bother to build certain pieces of things. This should allow
72 # people to still do SCons packaging work even if they don't have all
73 # of the utilities installed (e.g. RPM).
75 dh_builddeb = whereis('dh_builddeb')
76 fakeroot = whereis('fakeroot')
77 gzip = whereis('gzip')
78 rpmbuild = whereis('rpmbuild') or whereis('rpm')
81 unzip = whereis('unzip')
85 # Now grab the information that we "build" into the files.
87 date = ARGUMENTS.get('DATE')
90 date = time.strftime("%Y/%m/%d %H:%M:%S", time.localtime(time.time()))
92 developer = ARGUMENTS.get('DEVELOPER')
94 for variable in ['USERNAME', 'LOGNAME', 'USER']:
95 developer = os.environ.get(variable)
99 build_system = ARGUMENTS.get('BUILD_SYSTEM')
102 build_system = string.split(socket.gethostname(), '.')[0]
104 version = ARGUMENTS.get('VERSION', '')
106 version = default_version
109 svn_status_lines = []
112 cmd = "%s status --all 2> /dev/null" % hg
113 hg_status_lines = os.popen(cmd, "r").readlines()
116 cmd = "%s status --verbose 2> /dev/null" % svn
117 svn_status_lines = os.popen(cmd, "r").readlines()
119 revision = ARGUMENTS.get('REVISION', '')
120 def generate_build_id(revision):
123 if not revision and hg:
124 hg_heads = os.popen("%s heads 2> /dev/null" % hg, "r").read()
125 cs = re.search('changeset:\s+(\S+)', hg_heads)
127 revision = cs.group(1)
128 b = re.search('branch:\s+(\S+)', hg_heads)
130 revision = b.group(1) + ':' + revision
131 def generate_build_id(revision):
133 if filter(lambda l: l[0] in 'AMR!', hg_status_lines):
134 result = result + '[MODIFIED]'
137 if not revision and svn:
138 svn_info = os.popen("%s info 2> /dev/null" % svn, "r").read()
139 m = re.search('Revision: (\d+)', svn_info)
141 revision = m.group(1)
142 def generate_build_id(revision):
143 result = 'r' + revision
144 if filter(lambda l: l[0] in 'ACDMR', svn_status_lines):
145 result = result + '[MODIFIED]'
148 checkpoint = ARGUMENTS.get('CHECKPOINT', '')
150 if checkpoint == 'd':
152 checkpoint = time.strftime('d%Y%m%d', time.localtime(time.time()))
153 elif checkpoint == 'r':
154 checkpoint = 'r' + revision
155 version = version + '.' + checkpoint
157 build_id = ARGUMENTS.get('BUILD_ID')
160 build_id = generate_build_id(revision)
164 python_ver = sys.version[0:3]
166 platform = distutils.util.get_platform()
168 # Re-exporting LD_LIBRARY_PATH is necessary if the Python version was
169 # built with the --enable-shared option.
171 ENV = { 'PATH' : os.environ['PATH'] }
172 for key in ['LOGNAME', 'PYTHONPATH', 'LD_LIBRARY_PATH']:
173 if os.environ.has_key(key):
174 ENV[key] = os.environ[key]
176 build_dir = ARGUMENTS.get('BUILDDIR', 'build')
177 if not os.path.isabs(build_dir):
178 build_dir = os.path.normpath(os.path.join(os.getcwd(), build_dir))
180 command_line_variables = [
181 ("BUILDDIR=", "The directory in which to build the packages. " +
182 "The default is the './build' subdirectory."),
184 ("BUILD_ID=", "An identifier for the specific build." +
185 "The default is the Subversion revision number."),
187 ("BUILD_SYSTEM=", "The system on which the packages were built. " +
188 "The default is whatever hostname is returned " +
189 "by socket.gethostname()."),
191 ("CHECKPOINT=", "The specific checkpoint release being packaged, " +
192 "which will be appended to the VERSION string. " +
193 "A value of CHECKPOINT=d will generate a string " +
194 "of 'd' plus today's date in the format YYYMMDD. " +
195 "A value of CHECKPOINT=r will generate a " +
196 "string of 'r' plus the Subversion revision " +
197 "number. Any other CHECKPOINT= string will be " +
198 "used as is. There is no default value."),
200 ("DATE=", "The date string representing when the packaging " +
201 "build occurred. The default is the day and time " +
202 "the SConstruct file was invoked, in the format " +
203 "YYYY/MM/DD HH:MM:SS."),
205 ("DEVELOPER=", "The developer who created the packages. " +
206 "The default is the first set environment " +
207 "variable from the list $USERNAME, $LOGNAME, $USER."),
209 ("REVISION=", "The revision number of the source being built. " +
210 "The default is the Subversion revision returned " +
211 "'svn info', with an appended string of " +
212 "'[MODIFIED]' if there are any changes in the " +
215 ("VERSION=", "The SCons version being packaged. The default " +
216 "is the hard-coded value '%s' " % default_version +
217 "from this SConstruct file."),
220 Default('.', build_dir)
222 packaging_flavors = [
223 ('deb', "A .deb package. (This is currently not supported.)"),
225 ('rpm', "A RedHat Package Manager file."),
227 ('tar-gz', "The normal .tar.gz file for end-user installation."),
229 ('src-tar-gz', "A .tar.gz file containing all the source " +
230 "(including tests and documentation)."),
232 ('local-tar-gz', "A .tar.gz file for dropping into other software " +
235 ('zip', "The normal .zip file for end-user installation."),
237 ('src-zip', "A .zip file containing all the source " +
238 "(including tests and documentation)."),
240 ('local-zip', "A .zip file for dropping into other software " +
244 test_deb_dir = os.path.join(build_dir, "test-deb")
245 test_rpm_dir = os.path.join(build_dir, "test-rpm")
246 test_tar_gz_dir = os.path.join(build_dir, "test-tar-gz")
247 test_src_tar_gz_dir = os.path.join(build_dir, "test-src-tar-gz")
248 test_local_tar_gz_dir = os.path.join(build_dir, "test-local-tar-gz")
249 test_zip_dir = os.path.join(build_dir, "test-zip")
250 test_src_zip_dir = os.path.join(build_dir, "test-src-zip")
251 test_local_zip_dir = os.path.join(build_dir, "test-local-zip")
253 unpack_tar_gz_dir = os.path.join(build_dir, "unpack-tar-gz")
254 unpack_zip_dir = os.path.join(build_dir, "unpack-zip")
256 if platform == "win32":
258 python_project_subinst_dir = os.path.join("Lib", "site-packages", project)
259 project_script_subinst_dir = 'Scripts'
262 python_project_subinst_dir = os.path.join("lib", project)
263 project_script_subinst_dir = 'bin'
269 indent_fmt = ' %-26s '
272 The following aliases build packages of various types, and unpack the
273 contents into build/test-$PACKAGE subdirectories, which can be used by the
274 runtest.py -p option to run tests against what's been actually packaged:
278 aliases = packaging_flavors + [('doc', 'The SCons documentation.')]
281 for alias, help_text in aliases:
282 tw = textwrap.TextWrapper(
284 initial_indent = indent_fmt % alias,
285 subsequent_indent = indent_fmt % '' + ' ',
287 Help(tw.fill(help_text) + '\n')
290 The following command-line variables can be set:
294 for variable, help_text in command_line_variables:
295 tw = textwrap.TextWrapper(
297 initial_indent = indent_fmt % variable,
298 subsequent_indent = indent_fmt % '' + ' ',
300 Help(tw.fill(help_text) + '\n')
307 # Figure out if we can handle .zip files.
314 def zipit(env, target, source):
315 print "Zipping %s:" % str(target[0])
316 def visit(arg, dirname, names):
318 path = os.path.join(dirname, name)
319 if os.path.isfile(path):
321 zf = zipfile.ZipFile(str(target[0]), 'w')
324 try: os.path.walk(env['PSV'], visit, zf)
325 finally: os.chdir(olddir)
328 def unzipit(env, target, source):
329 print "Unzipping %s:" % str(source[0])
330 zf = zipfile.ZipFile(str(source[0]), 'r')
331 for name in zf.namelist():
332 dest = os.path.join(env['UNPACK_ZIP_DIR'], name)
333 dir = os.path.dirname(dest)
339 # if the file exists, then delete it before writing
340 # to it so that we don't end up trying to write to a symlink:
341 if os.path.isfile(dest) or os.path.islink(dest):
343 if not os.path.isdir(dest):
344 open(dest, 'wb').write(zf.read(name))
348 zipit = "cd $CD && $ZIP $ZIPFLAGS $( ${TARGET.abspath} $) $PSV"
349 unzipit = "$UNZIP $UNZIPFLAGS $SOURCES"
351 def SCons_revision(target, source, env):
352 """Interpolate specific values from the environment into a file.
354 This is used to copy files into a tree that gets packaged up
355 into the source file package.
359 contents = open(s, 'rb').read()
360 # Note: We construct the __*__ substitution strings here
361 # so that they don't get replaced when this file gets
362 # copied into the tree for packaging.
363 contents = string.replace(contents, '__BUILD' + '__', env['BUILD'])
364 contents = string.replace(contents, '__BUILDSYS' + '__', env['BUILDSYS'])
365 contents = string.replace(contents, '__COPYRIGHT' + '__', env['COPYRIGHT'])
366 contents = string.replace(contents, '__DATE' + '__', env['DATE'])
367 contents = string.replace(contents, '__DEVELOPER' + '__', env['DEVELOPER'])
368 contents = string.replace(contents, '__FILE' + '__', str(source[0]))
369 contents = string.replace(contents, '__MONTH_YEAR'+ '__', env['MONTH_YEAR'])
370 contents = string.replace(contents, '__REVISION' + '__', env['REVISION'])
371 contents = string.replace(contents, '__VERSION' + '__', env['VERSION'])
372 contents = string.replace(contents, '__NULL' + '__', '')
373 open(t, 'wb').write(contents)
374 os.chmod(t, os.stat(s)[0])
376 revbuilder = Builder(action = Action(SCons_revision,
377 varlist=['COPYRIGHT', 'VERSION']))
379 def soelim(target, source, env):
381 Interpolate files included in [gnt]roff source files using the
384 This behaves somewhat like the soelim(1) wrapper around groff, but
385 makes us independent of whether the actual underlying implementation
386 includes an soelim() command or the corresponding command-line option
387 to groff(1). The key behavioral difference is that this doesn't
388 recursively include .so files from the include file. Not yet, anyway.
392 dir, f = os.path.split(s)
395 for line in sfp.readlines():
396 if line[:4] in ['.so ', "'so "]:
397 sofile = os.path.join(dir, line[4:-1])
398 tfp.write(open(sofile, 'r').read())
404 def soscan(node, env, path):
405 c = node.get_text_contents()
406 return re.compile(r"^[\.']so\s+(\S+)", re.M).findall(c)
408 soelimbuilder = Builder(action = Action(soelim),
409 source_scanner = Scanner(soscan))
411 # When copying local files from a Repository (Aegis),
412 # just make copies, don't symlink them.
413 SetOption('duplicate', 'copy')
419 BUILDDIR = build_dir,
420 BUILDSYS = build_system,
421 COPYRIGHT = copyright,
423 DEVELOPER = developer,
424 DISTDIR = os.path.join(build_dir, 'dist'),
425 MONTH_YEAR = month_year,
430 TAR_HFLAG = tar_hflag,
435 UNZIPFLAGS = '-o -d $UNPACK_ZIP_DIR',
440 RPM2CPIO = 'rpm2cpio',
442 TEST_DEB_DIR = test_deb_dir,
443 TEST_RPM_DIR = test_rpm_dir,
444 TEST_SRC_TAR_GZ_DIR = test_src_tar_gz_dir,
445 TEST_SRC_ZIP_DIR = test_src_zip_dir,
446 TEST_TAR_GZ_DIR = test_tar_gz_dir,
447 TEST_ZIP_DIR = test_zip_dir,
449 UNPACK_TAR_GZ_DIR = unpack_tar_gz_dir,
450 UNPACK_ZIP_DIR = unpack_zip_dir,
452 BUILDERS = { 'SCons_revision' : revbuilder,
453 'SOElim' : soelimbuilder },
455 PYTHON = '"%s"' % sys.executable,
459 Version_values = [Value(version), Value(build_id)]
462 # Define SCons packages.
464 # In the original, more complicated packaging scheme, we were going
465 # to have separate packages for:
467 # python-scons only the build engine
468 # scons-script only the script
469 # scons the script plus the build engine
471 # We're now only delivering a single "scons" package, but this is still
472 # "built" as two sub-packages (the build engine and the script), so
473 # the definitions remain here, even though we're not using them for
478 'pkg' : 'python-' + project,
479 'src_subdir' : 'engine',
480 'inst_subdir' : os.path.join('lib', 'python1.5', 'site-packages'),
481 'rpm_dir' : '/usr/lib/scons',
494 'files' : [ 'LICENSE.txt',
501 'LICENSE.txt' : '../LICENSE.txt'
506 'extra_rpm_files' : [],
509 'SCons/__init__.py' : Version_values,
513 # Figure out the name of a .egg-info file that might be generated
514 # as part of the RPM package. There are two complicating factors.
516 # First, the RPM spec file we generate will just execute "python", not
517 # necessarily the one in sys.executable. If *that* version of python has
518 # a distutils that knows about Python eggs, then setup.py will generate a
519 # .egg-info file, so we have to execute any distutils logic in a subshell.
521 # Second, we can't just have the subshell check for the existence of the
522 # distutils.command.install_egg_info module and generate the expected
523 # file name by hand, the way we used to, because different systems can
524 # have slightly different .egg-info naming conventions. (Specifically,
525 # Ubuntu overrides the default behavior to remove the Python version
526 # string from the .egg-info file name.) The right way to do this is to
527 # actually call into the install_egg_info() class to have it generate
528 # the expected name for us.
530 # This is all complicated enough that we do it by writing an in-line
531 # script to a temporary file and then feeding it to a separate invocation
532 # of "python" to tell us the actual name of the generated .egg-info file.
534 print_egg_info_name = """
536 from distutils.dist import Distribution
537 from distutils.command.install_egg_info import install_egg_info
541 dist = Distribution({'name' : "scons", 'version' : '%s'})
542 i = install_egg_info(dist)
545 print os.path.split(i.outputs[0])[1]
549 fd, tfname = tempfile.mkstemp()
550 tfp = os.fdopen(fd, "w")
551 tfp.write(print_egg_info_name)
553 egg_info_file = os.popen("python %s" % tfname).read()[:-1]
555 python_scons['extra_rpm_files'].append(egg_info_file)
559 except EnvironmentError:
563 # The original packaging scheme would have have required us to push
564 # the Python version number into the package name (python1.5-scons,
565 # python2.0-scons, etc.), which would have required a definition
566 # like the following. Leave this here in case we ever decide to do
567 # this in the future, but note that this would require some modification
568 # to src/engine/setup.py before it would really work.
571 # 'pkg' : 'python2-' + project,
572 # 'src_subdir' : 'engine',
573 # 'inst_subdir' : os.path.join('lib', 'python2.2', 'site-packages'),
576 # 'debian/changelog',
578 # 'debian/copyright',
593 # 'LICENSE.txt' : '../LICENSE.txt',
600 'pkg' : project + '-script',
601 'src_subdir' : 'script',
602 'inst_subdir' : 'bin',
603 'rpm_dir' : '/usr/bin',
624 'LICENSE.txt' : '../LICENSE.txt',
625 'scons' : 'scons.py',
626 'sconsign' : 'sconsign.py',
627 'scons-time' : 'scons-time.py',
632 'extra_rpm_files' : [
634 'sconsign-' + version,
635 'scons-time-' + version,
639 'scons' : Version_values,
640 'sconsign' : Version_values,
663 'os_spawnv_fix.diff',
668 #'script/scons-post-install.py',
674 'scons.1' : '$BUILDDIR/doc/man/scons.1',
675 'sconsign.1' : '$BUILDDIR/doc/man/sconsign.1',
676 'scons-time.1' : '$BUILDDIR/doc/man/scons-time.1',
680 'scons.1' : env.SOElim,
681 'sconsign.1' : env.SOElim,
682 'scons-time.1' : env.SOElim,
685 'subpkgs' : [ python_scons, scons_script ],
688 'python-' + project : python_project_subinst_dir,
689 project + '-script' : project_script_subinst_dir,
693 scripts = ['scons', 'sconsign', 'scons-time']
700 # Initialize variables with the right directories for this package.
703 pkg_version = "%s-%s" % (pkg, version)
706 if p.has_key('src_subdir'):
707 src = os.path.join(src, p['src_subdir'])
709 build = os.path.join(build_dir, pkg)
711 tar_gz = os.path.join(build, 'dist', "%s.tar.gz" % pkg_version)
712 platform_tar_gz = os.path.join(build,
714 "%s.%s.tar.gz" % (pkg_version, platform))
715 zip = os.path.join(build, 'dist', "%s.zip" % pkg_version)
716 platform_zip = os.path.join(build,
718 "%s.%s.zip" % (pkg_version, platform))
719 win32_exe = os.path.join(build, 'dist', "%s.win32.exe" % pkg_version)
722 # Update the environment with the relevant information
725 # We can get away with calling setup.py using a directory path
726 # like this because we put a preamble in it that will chdir()
727 # to the directory in which setup.py exists.
729 setup_py = os.path.join(build, 'setup.py')
730 env.Replace(PKG = pkg,
731 PKG_VERSION = pkg_version,
732 SETUP_PY = '"%s"' % setup_py)
736 # Read up the list of source files from our MANIFEST.in.
737 # This list should *not* include LICENSE.txt, MANIFEST,
738 # README.txt, or setup.py. Make a copy of the list for the
741 manifest_in = File(os.path.join(src, 'MANIFEST.in')).rstr()
742 src_files = map(lambda x: x[:-1],
743 open(manifest_in).readlines())
744 raw_files = src_files[:]
745 dst_files = src_files[:]
748 MANIFEST_in_list = []
750 if p.has_key('subpkgs'):
752 # This package includes some sub-packages. Read up their
753 # MANIFEST.in files, and add them to our source and destination
754 # file lists, modifying them as appropriate to add the
757 for sp in p['subpkgs']:
758 ssubdir = sp['src_subdir']
759 isubdir = p['subinst_dirs'][sp['pkg']]
760 MANIFEST_in = File(os.path.join(src, ssubdir, 'MANIFEST.in')).rstr()
761 MANIFEST_in_list.append(MANIFEST_in)
762 files = map(lambda x: x[:-1], open(MANIFEST_in).readlines())
763 raw_files.extend(files)
764 src_files.extend(map(lambda x, s=ssubdir: os.path.join(s, x), files))
766 r = os.path.join(sp['rpm_dir'], f)
769 rpm_files.append(r + 'c')
770 for f in sp.get('extra_rpm_files', []):
771 r = os.path.join(sp['rpm_dir'], f)
773 files = map(lambda x, i=isubdir: os.path.join(i, x), files)
774 dst_files.extend(files)
775 for k, f in sp['filemap'].items():
777 k = os.path.join(ssubdir, k)
778 p['filemap'][k] = os.path.join(ssubdir, f)
779 for f, deps in sp['explicit_deps'].items():
780 f = os.path.join(build, ssubdir, f)
784 # Now that we have the "normal" source files, add those files
785 # that are standard for each distribution. Note that we don't
786 # add these to dst_files, because they don't get installed.
787 # And we still have the MANIFEST to add.
789 src_files.extend(p['files'])
792 # Now run everything in src_file through the sed command we
793 # concocted to expand __FILE__, __VERSION__, etc.
796 s = p['filemap'].get(b, b)
797 if not s[0] == '$' and not os.path.isabs(s):
798 s = os.path.join(src, s)
799 builder = p['buildermap'].get(b, env.SCons_revision)
800 x = builder(os.path.join(build, b), s)
804 # NOW, finally, we can create the MANIFEST, which we do
805 # by having Python spit out the contents of the src_files
806 # array we've carefully created. After we've added
807 # MANIFEST itself to the array, of course.
809 src_files.append("MANIFEST")
810 MANIFEST_in_list.append(os.path.join(src, 'MANIFEST.in'))
812 def write_src_files(target, source, **kw):
815 f = open(str(target[0]), 'wb')
816 for file in src_files:
820 env.Command(os.path.join(build, 'MANIFEST'),
825 # Now go through and arrange to create whatever packages we can.
827 build_src_files = map(lambda x, b=build: os.path.join(b, x), src_files)
828 apply(Local, build_src_files, {})
830 distutils_formats = []
832 distutils_targets = [ win32_exe ]
834 dist_distutils_targets = env.Install('$DISTDIR', distutils_targets)
835 Local(dist_distutils_targets)
836 AddPostAction(dist_distutils_targets, Chmod(dist_distutils_targets, 0644))
839 print "gzip not found; skipping .tar.gz package for %s." % pkg
842 distutils_formats.append('gztar')
844 src_deps.append(tar_gz)
846 distutils_targets.extend([ tar_gz, platform_tar_gz ])
848 dist_tar_gz = env.Install('$DISTDIR', tar_gz)
849 dist_platform_tar_gz = env.Install('$DISTDIR', platform_tar_gz)
850 Local(dist_tar_gz, dist_platform_tar_gz)
851 AddPostAction(dist_tar_gz, Chmod(dist_tar_gz, 0644))
852 AddPostAction(dist_platform_tar_gz, Chmod(dist_platform_tar_gz, 0644))
855 # Unpack the tar.gz archive created by the distutils into
856 # build/unpack-tar-gz/scons-{version}.
858 # We'd like to replace the last three lines with the following:
860 # tar zxf $SOURCES -C $UNPACK_TAR_GZ_DIR
862 # but that gives heartburn to Cygwin's tar, so work around it
863 # with separate zcat-tar-rm commands.
865 unpack_tar_gz_files = map(lambda x, u=unpack_tar_gz_dir, pv=pkg_version:
866 os.path.join(u, pv, x),
868 env.Command(unpack_tar_gz_files, dist_tar_gz, [
869 Delete(os.path.join(unpack_tar_gz_dir, pkg_version)),
870 "$ZCAT $SOURCES > .temp",
871 "tar xf .temp -C $UNPACK_TAR_GZ_DIR",
876 # Run setup.py in the unpacked subdirectory to "install" everything
877 # into our build/test subdirectory. The runtest.py script will set
878 # PYTHONPATH so that the tests only look under build/test-{package},
879 # and under etc (for the testing modules TestCmd.py, TestSCons.py,
880 # and unittest.py). This makes sure that our tests pass with what
881 # we really packaged, not because of something hanging around in
882 # the development directory.
884 # We can get away with calling setup.py using a directory path
885 # like this because we put a preamble in it that will chdir()
886 # to the directory in which setup.py exists.
888 dfiles = map(lambda x, d=test_tar_gz_dir: os.path.join(d, x), dst_files)
889 env.Command(dfiles, unpack_tar_gz_files, [
890 Delete(os.path.join(unpack_tar_gz_dir, pkg_version, 'build')),
891 Delete("$TEST_TAR_GZ_DIR"),
892 '$PYTHON $PYTHONFLAGS "%s" install "--prefix=$TEST_TAR_GZ_DIR" --standalone-lib' % \
893 os.path.join(unpack_tar_gz_dir, pkg_version, 'setup.py'),
897 # Generate portage files for submission to Gentoo Linux.
899 gentoo = os.path.join(build, 'gentoo')
900 ebuild = os.path.join(gentoo, 'scons-%s.ebuild' % version)
901 digest = os.path.join(gentoo, 'files', 'digest-scons-%s' % version)
902 env.Command(ebuild, os.path.join('gentoo', 'scons.ebuild.in'), SCons_revision)
903 def Digestify(target, source, env):
906 """Return a signature as a string of hex characters.
908 # NOTE: This routine is a method in the Python 2.0 interface
909 # of the native md5 module, but we want SCons to operate all
910 # the way back to at least Python 1.5.2, which doesn't have it.
915 r = r + h[(i >> 4) & 0xF] + h[i & 0xF]
917 src = source[0].rfile()
918 contents = open(str(src)).read()
919 sig = hexdigest(md5.new(contents).digest())
920 bytes = os.stat(str(src))[6]
921 open(str(target[0]), 'w').write("MD5 %s %s %d\n" % (sig,
924 env.Command(digest, tar_gz, Digestify)
927 print "zip not found; skipping .zip package for %s." % pkg
930 distutils_formats.append('zip')
934 distutils_targets.extend([ zip, platform_zip ])
936 dist_zip = env.Install('$DISTDIR', zip)
937 dist_platform_zip = env.Install('$DISTDIR', platform_zip)
938 Local(dist_zip, dist_platform_zip)
939 AddPostAction(dist_zip, Chmod(dist_zip, 0644))
940 AddPostAction(dist_platform_zip, Chmod(dist_platform_zip, 0644))
943 # Unpack the zip archive created by the distutils into
944 # build/unpack-zip/scons-{version}.
946 unpack_zip_files = map(lambda x, u=unpack_zip_dir, pv=pkg_version:
947 os.path.join(u, pv, x),
950 env.Command(unpack_zip_files, dist_zip, [
951 Delete(os.path.join(unpack_zip_dir, pkg_version)),
956 # Run setup.py in the unpacked subdirectory to "install" everything
957 # into our build/test subdirectory. The runtest.py script will set
958 # PYTHONPATH so that the tests only look under build/test-{package},
959 # and under etc (for the testing modules TestCmd.py, TestSCons.py,
960 # and unittest.py). This makes sure that our tests pass with what
961 # we really packaged, not because of something hanging around in
962 # the development directory.
964 # We can get away with calling setup.py using a directory path
965 # like this because we put a preamble in it that will chdir()
966 # to the directory in which setup.py exists.
968 dfiles = map(lambda x, d=test_zip_dir: os.path.join(d, x), dst_files)
969 env.Command(dfiles, unpack_zip_files, [
970 Delete(os.path.join(unpack_zip_dir, pkg_version, 'build')),
971 Delete("$TEST_ZIP_DIR"),
972 '$PYTHON $PYTHONFLAGS "%s" install "--prefix=$TEST_ZIP_DIR" --standalone-lib' % \
973 os.path.join(unpack_zip_dir, pkg_version, 'setup.py'),
977 msg = "@echo \"Warning: Can not build 'rpm': no rpmbuild utility found\""
978 AlwaysBuild(Alias('rpm', [], msg))
980 topdir = os.path.join(build, 'build',
981 'bdist.' + platform, 'rpm')
983 buildroot = os.path.join(build_dir, 'rpm-buildroot')
985 BUILDdir = os.path.join(topdir, 'BUILD', pkg + '-' + version)
986 RPMSdir = os.path.join(topdir, 'RPMS', 'noarch')
987 SOURCESdir = os.path.join(topdir, 'SOURCES')
988 SPECSdir = os.path.join(topdir, 'SPECS')
989 SRPMSdir = os.path.join(topdir, 'SRPMS')
991 specfile_in = os.path.join('rpm', "%s.spec.in" % pkg)
992 specfile = os.path.join(SPECSdir, "%s-1.spec" % pkg_version)
993 sourcefile = os.path.join(SOURCESdir, "%s.tar.gz" % pkg_version);
994 noarch_rpm = os.path.join(RPMSdir, "%s-1.noarch.rpm" % pkg_version)
995 src_rpm = os.path.join(SRPMSdir, "%s-1.src.rpm" % pkg_version)
997 def spec_function(target, source, env):
998 """Generate the RPM .spec file from the template file.
1000 This fills in the %files portion of the .spec file with a
1001 list generated from our MANIFEST(s), so we don't have to
1002 maintain multiple lists.
1004 c = open(str(source[0]), 'rb').read()
1005 c = string.replace(c, '__VERSION' + '__', env['VERSION'])
1006 c = string.replace(c, '__RPM_FILES' + '__', env['RPM_FILES'])
1007 open(str(target[0]), 'wb').write(c)
1010 rpm_files_str = string.join(rpm_files, "\n") + "\n"
1011 rpm_spec_env = env.Clone(RPM_FILES = rpm_files_str)
1012 rpm_spec_action = Action(spec_function, varlist=['RPM_FILES'])
1013 rpm_spec_env.Command(specfile, specfile_in, rpm_spec_action)
1015 env.InstallAs(sourcefile, tar_gz)
1018 targets = [ noarch_rpm, src_rpm ]
1019 cmd = "$RPMBUILD --define '_topdir $(%s$)' --buildroot %s -ba $SOURCES" % (topdir, buildroot)
1020 if not os.path.isdir(BUILDdir):
1021 cmd = ("$( mkdir -p %s; $)" % BUILDdir) + cmd
1022 t = env.Command(targets, specfile, cmd)
1023 env.Depends(t, sourcefile)
1025 dist_noarch_rpm = env.Install('$DISTDIR', noarch_rpm)
1026 dist_src_rpm = env.Install('$DISTDIR', src_rpm)
1027 Local(dist_noarch_rpm, dist_src_rpm)
1028 AddPostAction(dist_noarch_rpm, Chmod(dist_noarch_rpm, 0644))
1029 AddPostAction(dist_src_rpm, Chmod(dist_src_rpm, 0644))
1031 dfiles = map(lambda x, d=test_rpm_dir: os.path.join(d, 'usr', x),
1035 "$RPM2CPIO $SOURCES | (cd $TEST_RPM_DIR && cpio -id)")
1037 if dh_builddeb and fakeroot:
1038 # Our Debian packaging builds directly into build/dist,
1039 # so we don't need to Install() the .debs.
1040 deb = os.path.join(build_dir, 'dist', "%s_%s-1_all.deb" % (pkg, version))
1041 for d in p['debian_deps']:
1042 b = env.SCons_revision(os.path.join(build, d), d)
1045 env.Command(deb, build_src_files, [
1046 "cd %s && fakeroot make -f debian/rules PYTHON=$PYTHON BUILDDEB_OPTIONS=--destdir=../../build/dist binary" % build,
1049 old = os.path.join('lib', 'scons', '')
1050 new = os.path.join('lib', 'python' + python_ver, 'site-packages', '')
1051 def xxx(s, old=old, new=new):
1052 if s[:len(old)] == old:
1053 s = new + s[len(old):]
1054 return os.path.join('usr', s)
1055 dfiles = map(lambda x, t=test_deb_dir: os.path.join(t, x),
1056 map(xxx, dst_files))
1059 "dpkg --fsys-tarfile $SOURCES | (cd $TEST_DEB_DIR && tar -xf -)")
1063 # Use the Python distutils to generate the appropriate packages.
1066 Delete(os.path.join(build, 'build', 'lib')),
1067 Delete(os.path.join(build, 'build', 'scripts')),
1070 if distutils_formats:
1071 commands.append(Delete(os.path.join(build,
1073 'bdist.' + platform,
1075 for format in distutils_formats:
1076 commands.append("$PYTHON $PYTHONFLAGS $SETUP_PY bdist_dumb -f %s" % format)
1078 commands.append("$PYTHON $PYTHONFLAGS $SETUP_PY sdist --formats=%s" % \
1079 string.join(distutils_formats, ','))
1081 commands.append("$PYTHON $PYTHONFLAGS $SETUP_PY bdist_wininst")
1083 env.Command(distutils_targets, build_src_files, commands)
1086 # Now create local packages for people who want to let people
1087 # build their SCons-buildable packages without having to
1090 s_l_v = '%s-local-%s' % (pkg, version)
1092 local = pkg + '-local'
1093 build_dir_local = os.path.join(build_dir, local)
1094 build_dir_local_slv = os.path.join(build_dir, local, s_l_v)
1096 dist_local_tar_gz = os.path.join("$DISTDIR/%s.tar.gz" % s_l_v)
1097 dist_local_zip = os.path.join("$DISTDIR/%s.zip" % s_l_v)
1098 AddPostAction(dist_local_tar_gz, Chmod(dist_local_tar_gz, 0644))
1099 AddPostAction(dist_local_zip, Chmod(dist_local_zip, 0644))
1102 Delete(build_dir_local),
1103 '$PYTHON $PYTHONFLAGS $SETUP_PY install "--install-script=%s" "--install-lib=%s" --no-install-man --no-compile --standalone-lib --no-version-script' % \
1104 (build_dir_local, build_dir_local_slv),
1107 for script in scripts:
1108 #commands.append("mv %s/%s %s/%s.py" % (local, script, local, script))
1109 local_script = os.path.join(build_dir_local, script)
1110 commands.append(Move(local_script + '.py', local_script))
1112 rf = filter(lambda x: not x in scripts, raw_files)
1113 rf = map(lambda x, slv=s_l_v: os.path.join(slv, x), rf)
1114 for script in scripts:
1115 rf.append("%s.py" % script)
1116 local_targets = map(lambda x, s=build_dir_local: os.path.join(s, x), rf)
1118 env.Command(local_targets, build_src_files, commands)
1120 scons_LICENSE = os.path.join(build_dir_local, 'scons-LICENSE')
1121 l = env.SCons_revision(scons_LICENSE, 'LICENSE-local')
1122 local_targets.append(l)
1125 scons_README = os.path.join(build_dir_local, 'scons-README')
1126 l = env.SCons_revision(scons_README, 'README-local')
1127 local_targets.append(l)
1131 env.Command(dist_local_tar_gz,
1133 "cd %s && tar czf $( ${TARGET.abspath} $) *" % build_dir_local)
1135 unpack_targets = map(lambda x, d=test_local_tar_gz_dir:
1138 commands = [Delete(test_local_tar_gz_dir),
1139 Mkdir(test_local_tar_gz_dir),
1140 "cd %s && tar xzf $( ${SOURCE.abspath} $)" % test_local_tar_gz_dir]
1142 env.Command(unpack_targets, dist_local_tar_gz, commands)
1145 env.Command(dist_local_zip, local_targets, zipit,
1146 CD = build_dir_local, PSV = '.')
1148 unpack_targets = map(lambda x, d=test_local_zip_dir:
1151 commands = [Delete(test_local_zip_dir),
1152 Mkdir(test_local_zip_dir),
1155 env.Command(unpack_targets, dist_local_zip, unzipit,
1156 UNPACK_ZIP_DIR = test_local_zip_dir)
1161 Export('build_dir', 'env')
1163 SConscript('QMTest/SConscript')
1172 def copy(target, source, env):
1175 open(t, 'wb').write(open(s, 'rb').read())
1178 # Guarantee that real copies of these files always exist in
1179 # build/. If there's a symlink there, then this is an Aegis
1180 # build and we blow them away now so that they'll get "built" later.
1181 p = os.path.join(build_dir, file)
1182 if os.path.islink(p):
1184 if not os.path.isabs(p):
1186 sp = env.Command(p, file, copy)
1192 Export('build_dir', 'env', 'whereis')
1194 SConscript('doc/SConscript')
1197 # If we're running in a Subversion working directory, pack up a complete
1198 # source archive from the project files and files in the change.
1203 slines = filter(lambda l: l[0] in 'ACM', hg_status_lines)
1204 sfiles = map(lambda l: l.split()[-1], slines)
1205 elif svn_status_lines:
1206 slines = filter(lambda l: l[0] in ' MA', svn_status_lines)
1207 sentries = map(lambda l: l.split()[-1], slines)
1208 sfiles = filter(os.path.isfile, sentries)
1210 "Not building in a Mercurial or Subversion tree; skipping building src package."
1222 for p in remove_patterns:
1223 sfiles = filter(lambda s, p=p: not fnmatch.fnmatch(s, p), sfiles)
1226 ps = "%s-src" % project
1227 psv = "%s-%s" % (ps, version)
1228 b_ps = os.path.join(build_dir, ps)
1229 b_psv = os.path.join(build_dir, psv)
1230 b_psv_stamp = b_psv + '-stamp'
1232 src_tar_gz = os.path.join(build_dir, 'dist', '%s.tar.gz' % psv)
1233 src_zip = os.path.join(build_dir, 'dist', '%s.zip' % psv)
1235 Local(src_tar_gz, src_zip)
1238 env.SCons_revision(os.path.join(b_ps, file), file)
1240 b_ps_files = map(lambda x, d=b_ps: os.path.join(d, x), sfiles)
1247 env.Command(b_psv_stamp, src_deps + b_ps_files, cmds)
1249 apply(Local, b_ps_files, {})
1253 env.Command(src_tar_gz, b_psv_stamp,
1254 "tar cz${TAR_HFLAG} -f $TARGET -C build %s" % psv)
1257 # Unpack the archive into build/unpack/scons-{version}.
1259 unpack_tar_gz_files = map(lambda x, u=unpack_tar_gz_dir, psv=psv:
1260 os.path.join(u, psv, x),
1264 # We'd like to replace the last three lines with the following:
1266 # tar zxf $SOURCES -C $UNPACK_TAR_GZ_DIR
1268 # but that gives heartburn to Cygwin's tar, so work around it
1269 # with separate zcat-tar-rm commands.
1270 env.Command(unpack_tar_gz_files, src_tar_gz, [
1271 Delete(os.path.join(unpack_tar_gz_dir, psv)),
1272 "$ZCAT $SOURCES > .temp",
1273 "tar xf .temp -C $UNPACK_TAR_GZ_DIR",
1278 # Run setup.py in the unpacked subdirectory to "install" everything
1279 # into our build/test subdirectory. The runtest.py script will set
1280 # PYTHONPATH so that the tests only look under build/test-{package},
1281 # and under etc (for the testing modules TestCmd.py, TestSCons.py,
1282 # and unittest.py). This makes sure that our tests pass with what
1283 # we really packaged, not because of something hanging around in
1284 # the development directory.
1286 # We can get away with calling setup.py using a directory path
1287 # like this because we put a preamble in it that will chdir()
1288 # to the directory in which setup.py exists.
1290 dfiles = map(lambda x, d=test_src_tar_gz_dir: os.path.join(d, x),
1292 scons_lib_dir = os.path.join(unpack_tar_gz_dir, psv, 'src', 'engine')
1293 ENV = env.Dictionary('ENV').copy()
1294 ENV['SCONS_LIB_DIR'] = scons_lib_dir
1295 ENV['USERNAME'] = developer
1296 env.Command(dfiles, unpack_tar_gz_files,
1298 Delete(os.path.join(unpack_tar_gz_dir,
1303 Delete("$TEST_SRC_TAR_GZ_DIR"),
1304 'cd "%s" && $PYTHON $PYTHONFLAGS "%s" "%s" VERSION="$VERSION"' % \
1305 (os.path.join(unpack_tar_gz_dir, psv),
1306 os.path.join('src', 'script', 'scons.py'),
1307 os.path.join('build', 'scons')),
1308 '$PYTHON $PYTHONFLAGS "%s" install "--prefix=$TEST_SRC_TAR_GZ_DIR" --standalone-lib' % \
1309 os.path.join(unpack_tar_gz_dir,
1319 env.Command(src_zip, b_psv_stamp, zipit, CD = 'build', PSV = psv)
1322 # Unpack the archive into build/unpack/scons-{version}.
1324 unpack_zip_files = map(lambda x, u=unpack_zip_dir, psv=psv:
1325 os.path.join(u, psv, x),
1328 env.Command(unpack_zip_files, src_zip, [
1329 Delete(os.path.join(unpack_zip_dir, psv)),
1334 # Run setup.py in the unpacked subdirectory to "install" everything
1335 # into our build/test subdirectory. The runtest.py script will set
1336 # PYTHONPATH so that the tests only look under build/test-{package},
1337 # and under etc (for the testing modules TestCmd.py, TestSCons.py,
1338 # and unittest.py). This makes sure that our tests pass with what
1339 # we really packaged, not because of something hanging around in
1340 # the development directory.
1342 # We can get away with calling setup.py using a directory path
1343 # like this because we put a preamble in it that will chdir()
1344 # to the directory in which setup.py exists.
1346 dfiles = map(lambda x, d=test_src_zip_dir: os.path.join(d, x),
1348 scons_lib_dir = os.path.join(unpack_zip_dir, psv, 'src', 'engine')
1349 ENV = env.Dictionary('ENV').copy()
1350 ENV['SCONS_LIB_DIR'] = scons_lib_dir
1351 ENV['USERNAME'] = developer
1352 env.Command(dfiles, unpack_zip_files,
1354 Delete(os.path.join(unpack_zip_dir,
1359 Delete("$TEST_SRC_ZIP_DIR"),
1360 'cd "%s" && $PYTHON $PYTHONFLAGS "%s" "%s" VERSION="$VERSION"' % \
1361 (os.path.join(unpack_zip_dir, psv),
1362 os.path.join('src', 'script', 'scons.py'),
1363 os.path.join('build', 'scons')),
1364 '$PYTHON $PYTHONFLAGS "%s" install "--prefix=$TEST_SRC_ZIP_DIR" --standalone-lib' % \
1365 os.path.join(unpack_zip_dir,
1373 for pf, help_text in packaging_flavors:
1375 os.path.join(build_dir, 'test-'+pf),
1376 os.path.join(build_dir, 'QMTest'),
1377 os.path.join(build_dir, 'runtest.py'),