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'
11 # This gets inserted into the man pages to reflect the month of release.
12 month_year = 'April 2008'
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 = '0.98.2'
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')
80 unzip = whereis('unzip')
84 # Now grab the information that we "build" into the files.
86 date = ARGUMENTS.get('DATE')
89 date = time.strftime("%Y/%m/%d %H:%M:%S", time.localtime(time.time()))
91 developer = ARGUMENTS.get('DEVELOPER')
93 for variable in ['USERNAME', 'LOGNAME', 'USER']:
94 developer = os.environ.get(variable)
98 build_system = ARGUMENTS.get('BUILD_SYSTEM')
101 build_system = string.split(socket.gethostname(), '.')[0]
103 version = ARGUMENTS.get('VERSION', '')
105 version = default_version
107 revision = ARGUMENTS.get('REVISION', '')
108 if not revision and svn:
109 svn_info = os.popen("%s info 2> /dev/null" % svn, "r").read()
110 m = re.search('Revision: (\d+)', svn_info)
112 revision = m.group(1)
114 checkpoint = ARGUMENTS.get('CHECKPOINT', '')
116 if checkpoint == 'd':
118 checkpoint = time.strftime('d%Y%m%d', time.localtime(time.time()))
119 elif checkpoint == 'r':
120 checkpoint = 'r' + revision
121 version = version + '.' + checkpoint
124 svn_status_lines = []
127 svn_status = os.popen("%s status --verbose 2> /dev/null" % svn, "r").read()
128 svn_status_lines = svn_status[:-1].split('\n')
130 build_id = ARGUMENTS.get('BUILD_ID')
133 build_id = 'r' + revision
134 if filter(lambda l: l[0] in 'ACDMR', svn_status_lines):
135 build_id = build_id + '[MODIFIED]'
139 python_ver = sys.version[0:3]
141 platform = distutils.util.get_platform()
143 # Re-exporting LD_LIBRARY_PATH is necessary if the Python version was
144 # built with the --enable-shared option.
146 ENV = { 'PATH' : os.environ['PATH'] }
147 for key in ['LOGNAME', 'PYTHONPATH', 'LD_LIBRARY_PATH']:
148 if os.environ.has_key(key):
149 ENV[key] = os.environ[key]
151 build_dir = ARGUMENTS.get('BUILDDIR', 'build')
152 if not os.path.isabs(build_dir):
153 build_dir = os.path.normpath(os.path.join(os.getcwd(), build_dir))
155 command_line_variables = [
156 ("BUILDDIR=", "The directory in which to build the packages. " +
157 "The default is the './build' subdirectory."),
159 ("BUILD_ID=", "An identifier for the specific build." +
160 "The default is the Subversion revision number."),
162 ("BUILD_SYSTEM=", "The system on which the packages were built. " +
163 "The default is whatever hostname is returned " +
164 "by socket.gethostname()."),
166 ("CHECKPOINT=", "The specific checkpoint release being packaged, " +
167 "which will be appended to the VERSION string. " +
168 "A value of CHECKPOINT=d will generate a string " +
169 "of 'd' plus today's date in the format YYYMMDD. " +
170 "A value of CHECKPOINT=r will generate a " +
171 "string of 'r' plus the Subversion revision " +
172 "number. Any other CHECKPOINT= string will be " +
173 "used as is. There is no default value."),
175 ("DATE=", "The date string representing when the packaging " +
176 "build occurred. The default is the day and time " +
177 "the SConstruct file was invoked, in the format " +
178 "YYYY/MM/DD HH:MM:SS."),
180 ("DEVELOPER=", "The developer who created the packages. " +
181 "The default is the first set environment " +
182 "variable from the list $USERNAME, $LOGNAME, $USER."),
184 ("REVISION=", "The revision number of the source being built. " +
185 "The default is the Subversion revision returned " +
186 "'svn info', with an appended string of " +
187 "'[MODIFIED]' if there are any changes in the " +
190 ("VERSION=", "The SCons version being packaged. The default " +
191 "is the hard-coded value '%s' " % default_version +
192 "from this SConstruct file."),
195 Default('.', build_dir)
197 packaging_flavors = [
198 ('deb', "A .deb package. (This is currently not supported.)"),
200 ('rpm', "A RedHat Package Manager file."),
202 ('tar-gz', "The normal .tar.gz file for end-user installation."),
204 ('src-tar-gz', "A .tar.gz file containing all the source " +
205 "(including tests and documentation)."),
207 ('local-tar-gz', "A .tar.gz file for dropping into other software " +
210 ('zip', "The normal .zip file for end-user installation."),
212 ('src-zip', "A .zip file containing all the source " +
213 "(including tests and documentation)."),
215 ('local-zip', "A .zip file for dropping into other software " +
219 test_deb_dir = os.path.join(build_dir, "test-deb")
220 test_rpm_dir = os.path.join(build_dir, "test-rpm")
221 test_tar_gz_dir = os.path.join(build_dir, "test-tar-gz")
222 test_src_tar_gz_dir = os.path.join(build_dir, "test-src-tar-gz")
223 test_local_tar_gz_dir = os.path.join(build_dir, "test-local-tar-gz")
224 test_zip_dir = os.path.join(build_dir, "test-zip")
225 test_src_zip_dir = os.path.join(build_dir, "test-src-zip")
226 test_local_zip_dir = os.path.join(build_dir, "test-local-zip")
228 unpack_tar_gz_dir = os.path.join(build_dir, "unpack-tar-gz")
229 unpack_zip_dir = os.path.join(build_dir, "unpack-zip")
231 if platform == "win32":
233 python_project_subinst_dir = os.path.join("Lib", "site-packages", project)
234 project_script_subinst_dir = 'Scripts'
237 python_project_subinst_dir = os.path.join("lib", project)
238 project_script_subinst_dir = 'bin'
244 indent_fmt = ' %-26s '
247 The following aliases build packages of various types, and unpack the
248 contents into build/test-$PACKAGE subdirectories, which can be used by the
249 runtest.py -p option to run tests against what's been actually packaged:
253 aliases = packaging_flavors + [('doc', 'The SCons documentation.')]
256 for alias, help_text in aliases:
257 tw = textwrap.TextWrapper(
259 initial_indent = indent_fmt % alias,
260 subsequent_indent = indent_fmt % '' + ' ',
262 Help(tw.fill(help_text) + '\n')
265 The following command-line variables can be set:
269 for variable, help_text in command_line_variables:
270 tw = textwrap.TextWrapper(
272 initial_indent = indent_fmt % variable,
273 subsequent_indent = indent_fmt % '' + ' ',
275 Help(tw.fill(help_text) + '\n')
282 # Figure out if we can handle .zip files.
289 def zipit(env, target, source):
290 print "Zipping %s:" % str(target[0])
291 def visit(arg, dirname, names):
293 path = os.path.join(dirname, name)
294 if os.path.isfile(path):
296 zf = zipfile.ZipFile(str(target[0]), 'w')
299 try: os.path.walk(env['PSV'], visit, zf)
300 finally: os.chdir(olddir)
303 def unzipit(env, target, source):
304 print "Unzipping %s:" % str(source[0])
305 zf = zipfile.ZipFile(str(source[0]), 'r')
306 for name in zf.namelist():
307 dest = os.path.join(env['UNPACK_ZIP_DIR'], name)
308 dir = os.path.dirname(dest)
314 # if the file exists, then delete it before writing
315 # to it so that we don't end up trying to write to a symlink:
316 if os.path.isfile(dest) or os.path.islink(dest):
318 if not os.path.isdir(dest):
319 open(dest, 'wb').write(zf.read(name))
323 zipit = "cd $CD && $ZIP $ZIPFLAGS $( ${TARGET.abspath} $) $PSV"
324 unzipit = "$UNZIP $UNZIPFLAGS $SOURCES"
326 def SCons_revision(target, source, env):
327 """Interpolate specific values from the environment into a file.
329 This is used to copy files into a tree that gets packaged up
330 into the source file package.
334 contents = open(s, 'rb').read()
335 # Note: We construct the __*__ substitution strings here
336 # so that they don't get replaced when this file gets
337 # copied into the tree for packaging.
338 contents = string.replace(contents, '__BUILD' + '__', env['BUILD'])
339 contents = string.replace(contents, '__BUILDSYS' + '__', env['BUILDSYS'])
340 contents = string.replace(contents, '__COPYRIGHT' + '__', env['COPYRIGHT'])
341 contents = string.replace(contents, '__DATE' + '__', env['DATE'])
342 contents = string.replace(contents, '__DEVELOPER' + '__', env['DEVELOPER'])
343 contents = string.replace(contents, '__FILE' + '__', str(source[0]))
344 contents = string.replace(contents, '__MONTH_YEAR'+ '__', env['MONTH_YEAR'])
345 contents = string.replace(contents, '__REVISION' + '__', env['REVISION'])
346 contents = string.replace(contents, '__VERSION' + '__', env['VERSION'])
347 contents = string.replace(contents, '__NULL' + '__', '')
348 open(t, 'wb').write(contents)
349 os.chmod(t, os.stat(s)[0])
351 revbuilder = Builder(action = Action(SCons_revision,
352 varlist=['COPYRIGHT', 'VERSION']))
354 def soelim(target, source, env):
356 Interpolate files included in [gnt]roff source files using the
359 This behaves somewhat like the soelim(1) wrapper around groff, but
360 makes us independent of whether the actual underlying implementation
361 includes an soelim() command or the corresponding command-line option
362 to groff(1). The key behavioral difference is that this doesn't
363 recursively include .so files from the include file. Not yet, anyway.
367 dir, f = os.path.split(s)
370 for line in sfp.readlines():
371 if line[:4] in ['.so ', "'so "]:
372 sofile = os.path.join(dir, line[4:-1])
373 tfp.write(open(sofile, 'r').read())
379 def soscan(node, env, path):
380 c = node.get_contents()
381 return re.compile(r"^[\.']so\s+(\S+)", re.M).findall(c)
383 soelimbuilder = Builder(action = Action(soelim),
384 source_scanner = Scanner(soscan))
386 # When copying local files from a Repository (Aegis),
387 # just make copies, don't symlink them.
388 SetOption('duplicate', 'copy')
394 BUILDDIR = build_dir,
395 BUILDSYS = build_system,
396 COPYRIGHT = copyright,
398 DEVELOPER = developer,
399 DISTDIR = os.path.join(build_dir, 'dist'),
400 MONTH_YEAR = month_year,
405 TAR_HFLAG = tar_hflag,
410 UNZIPFLAGS = '-o -d $UNPACK_ZIP_DIR',
415 RPM2CPIO = 'rpm2cpio',
417 TEST_DEB_DIR = test_deb_dir,
418 TEST_RPM_DIR = test_rpm_dir,
419 TEST_SRC_TAR_GZ_DIR = test_src_tar_gz_dir,
420 TEST_SRC_ZIP_DIR = test_src_zip_dir,
421 TEST_TAR_GZ_DIR = test_tar_gz_dir,
422 TEST_ZIP_DIR = test_zip_dir,
424 UNPACK_TAR_GZ_DIR = unpack_tar_gz_dir,
425 UNPACK_ZIP_DIR = unpack_zip_dir,
427 BUILDERS = { 'SCons_revision' : revbuilder,
428 'SOElim' : soelimbuilder },
430 PYTHON = '"%s"' % sys.executable,
434 Version_values = [Value(version), Value(build_id)]
437 # Define SCons packages.
439 # In the original, more complicated packaging scheme, we were going
440 # to have separate packages for:
442 # python-scons only the build engine
443 # scons-script only the script
444 # scons the script plus the build engine
446 # We're now only delivering a single "scons" package, but this is still
447 # "built" as two sub-packages (the build engine and the script), so
448 # the definitions remain here, even though we're not using them for
453 'pkg' : 'python-' + project,
454 'src_subdir' : 'engine',
455 'inst_subdir' : os.path.join('lib', 'python1.5', 'site-packages'),
456 'rpm_dir' : '/usr/lib/scons',
469 'files' : [ 'LICENSE.txt',
476 'LICENSE.txt' : '../LICENSE.txt'
481 'extra_rpm_files' : [],
484 'SCons/__init__.py' : Version_values,
488 # Figure out the name of a .egg-info file that might be generated
489 # as part of the RPM package. There are two complicating factors.
491 # First, the RPM spec file we generate will just execute "python", not
492 # necessarily the one in sys.executable. If *that* version of python has
493 # a distutils that knows about Python eggs, then setup.py will generate a
494 # .egg-info file, so we have to execute any distutils logic in a subshell.
496 # Second, we can't just have the subshell check for the existence of the
497 # distutils.command.install_egg_info module and generate the expected
498 # file name by hand, the way we used to, because different systems can
499 # have slightly different .egg-info naming conventions. (Specifically,
500 # Ubuntu overrides the default behavior to remove the Python version
501 # string from the .egg-info file name.) The right way to do this is to
502 # actually call into the install_egg_info() class to have it generate
503 # the expected name for us.
505 # This is all complicated enough that we do it by writing an in-line
506 # script to a temporary file and then feeding it to a separate invocation
507 # of "python" to tell us the actual name of the generated .egg-info file.
509 print_egg_info_name = """
511 from distutils.dist import Distribution
512 from distutils.command.install_egg_info import install_egg_info
516 dist = Distribution({'name' : "scons", 'version' : '%s'})
517 i = install_egg_info(dist)
520 print os.path.split(i.outputs[0])[1]
524 fd, tfname = tempfile.mkstemp()
525 tfp = os.fdopen(fd, "w")
526 tfp.write(print_egg_info_name)
528 egg_info_file = os.popen("python %s" % tfname).read()[:-1]
530 python_scons['extra_rpm_files'].append(egg_info_file)
534 except EnvironmentError:
538 # The original packaging scheme would have have required us to push
539 # the Python version number into the package name (python1.5-scons,
540 # python2.0-scons, etc.), which would have required a definition
541 # like the following. Leave this here in case we ever decide to do
542 # this in the future, but note that this would require some modification
543 # to src/engine/setup.py before it would really work.
546 # 'pkg' : 'python2-' + project,
547 # 'src_subdir' : 'engine',
548 # 'inst_subdir' : os.path.join('lib', 'python2.2', 'site-packages'),
551 # 'debian/changelog',
553 # 'debian/copyright',
568 # 'LICENSE.txt' : '../LICENSE.txt',
575 'pkg' : project + '-script',
576 'src_subdir' : 'script',
577 'inst_subdir' : 'bin',
578 'rpm_dir' : '/usr/bin',
599 'LICENSE.txt' : '../LICENSE.txt',
600 'scons' : 'scons.py',
601 'sconsign' : 'sconsign.py',
602 'scons-time' : 'scons-time.py',
607 'extra_rpm_files' : [
609 'sconsign-' + version,
610 'scons-time-' + version,
614 'scons' : Version_values,
615 'sconsign' : Version_values,
638 'os_spawnv_fix.diff',
643 #'script/scons-post-install.py',
649 'scons.1' : '$BUILDDIR/doc/man/scons.1',
650 'sconsign.1' : '$BUILDDIR/doc/man/sconsign.1',
651 'scons-time.1' : '$BUILDDIR/doc/man/scons-time.1',
655 'scons.1' : env.SOElim,
656 'sconsign.1' : env.SOElim,
657 'scons-time.1' : env.SOElim,
660 'subpkgs' : [ python_scons, scons_script ],
663 'python-' + project : python_project_subinst_dir,
664 project + '-script' : project_script_subinst_dir,
668 scripts = ['scons', 'sconsign', 'scons-time']
675 # Initialize variables with the right directories for this package.
678 pkg_version = "%s-%s" % (pkg, version)
681 if p.has_key('src_subdir'):
682 src = os.path.join(src, p['src_subdir'])
684 build = os.path.join(build_dir, pkg)
686 tar_gz = os.path.join(build, 'dist', "%s.tar.gz" % pkg_version)
687 platform_tar_gz = os.path.join(build,
689 "%s.%s.tar.gz" % (pkg_version, platform))
690 zip = os.path.join(build, 'dist', "%s.zip" % pkg_version)
691 platform_zip = os.path.join(build,
693 "%s.%s.zip" % (pkg_version, platform))
694 win32_exe = os.path.join(build, 'dist', "%s.win32.exe" % pkg_version)
697 # Update the environment with the relevant information
700 # We can get away with calling setup.py using a directory path
701 # like this because we put a preamble in it that will chdir()
702 # to the directory in which setup.py exists.
704 setup_py = os.path.join(build, 'setup.py')
705 env.Replace(PKG = pkg,
706 PKG_VERSION = pkg_version,
707 SETUP_PY = '"%s"' % setup_py)
711 # Read up the list of source files from our MANIFEST.in.
712 # This list should *not* include LICENSE.txt, MANIFEST,
713 # README.txt, or setup.py. Make a copy of the list for the
716 manifest_in = File(os.path.join(src, 'MANIFEST.in')).rstr()
717 src_files = map(lambda x: x[:-1],
718 open(manifest_in).readlines())
719 raw_files = src_files[:]
720 dst_files = src_files[:]
723 MANIFEST_in_list = []
725 if p.has_key('subpkgs'):
727 # This package includes some sub-packages. Read up their
728 # MANIFEST.in files, and add them to our source and destination
729 # file lists, modifying them as appropriate to add the
732 for sp in p['subpkgs']:
733 ssubdir = sp['src_subdir']
734 isubdir = p['subinst_dirs'][sp['pkg']]
735 MANIFEST_in = File(os.path.join(src, ssubdir, 'MANIFEST.in')).rstr()
736 MANIFEST_in_list.append(MANIFEST_in)
737 files = map(lambda x: x[:-1], open(MANIFEST_in).readlines())
738 raw_files.extend(files)
739 src_files.extend(map(lambda x, s=ssubdir: os.path.join(s, x), files))
741 r = os.path.join(sp['rpm_dir'], f)
744 rpm_files.append(r + 'c')
745 for f in sp.get('extra_rpm_files', []):
746 r = os.path.join(sp['rpm_dir'], f)
748 files = map(lambda x, i=isubdir: os.path.join(i, x), files)
749 dst_files.extend(files)
750 for k, f in sp['filemap'].items():
752 k = os.path.join(ssubdir, k)
753 p['filemap'][k] = os.path.join(ssubdir, f)
754 for f, deps in sp['explicit_deps'].items():
755 f = os.path.join(build, ssubdir, f)
759 # Now that we have the "normal" source files, add those files
760 # that are standard for each distribution. Note that we don't
761 # add these to dst_files, because they don't get installed.
762 # And we still have the MANIFEST to add.
764 src_files.extend(p['files'])
767 # Now run everything in src_file through the sed command we
768 # concocted to expand __FILE__, __VERSION__, etc.
771 s = p['filemap'].get(b, b)
772 if not s[0] == '$' and not os.path.isabs(s):
773 s = os.path.join(src, s)
774 builder = p['buildermap'].get(b, env.SCons_revision)
775 x = builder(os.path.join(build, b), s)
779 # NOW, finally, we can create the MANIFEST, which we do
780 # by having Python spit out the contents of the src_files
781 # array we've carefully created. After we've added
782 # MANIFEST itself to the array, of course.
784 src_files.append("MANIFEST")
785 MANIFEST_in_list.append(os.path.join(src, 'MANIFEST.in'))
787 def write_src_files(target, source, **kw):
790 f = open(str(target[0]), 'wb')
791 for file in src_files:
795 env.Command(os.path.join(build, 'MANIFEST'),
800 # Now go through and arrange to create whatever packages we can.
802 build_src_files = map(lambda x, b=build: os.path.join(b, x), src_files)
803 apply(Local, build_src_files, {})
805 distutils_formats = []
807 distutils_targets = [ win32_exe ]
809 dist_distutils_targets = env.Install('$DISTDIR', distutils_targets)
810 Local(dist_distutils_targets)
811 AddPostAction(dist_distutils_targets, Chmod(dist_distutils_targets, 0644))
814 print "gzip not found; skipping .tar.gz package for %s." % pkg
817 distutils_formats.append('gztar')
819 src_deps.append(tar_gz)
821 distutils_targets.extend([ tar_gz, platform_tar_gz ])
823 dist_tar_gz = env.Install('$DISTDIR', tar_gz)
824 dist_platform_tar_gz = env.Install('$DISTDIR', platform_tar_gz)
825 Local(dist_tar_gz, dist_platform_tar_gz)
826 AddPostAction(dist_tar_gz, Chmod(dist_tar_gz, 0644))
827 AddPostAction(dist_platform_tar_gz, Chmod(dist_platform_tar_gz, 0644))
830 # Unpack the tar.gz archive created by the distutils into
831 # build/unpack-tar-gz/scons-{version}.
833 # We'd like to replace the last three lines with the following:
835 # tar zxf $SOURCES -C $UNPACK_TAR_GZ_DIR
837 # but that gives heartburn to Cygwin's tar, so work around it
838 # with separate zcat-tar-rm commands.
840 unpack_tar_gz_files = map(lambda x, u=unpack_tar_gz_dir, pv=pkg_version:
841 os.path.join(u, pv, x),
843 env.Command(unpack_tar_gz_files, dist_tar_gz, [
844 Delete(os.path.join(unpack_tar_gz_dir, pkg_version)),
845 "$ZCAT $SOURCES > .temp",
846 "tar xf .temp -C $UNPACK_TAR_GZ_DIR",
851 # Run setup.py in the unpacked subdirectory to "install" everything
852 # into our build/test subdirectory. The runtest.py script will set
853 # PYTHONPATH so that the tests only look under build/test-{package},
854 # and under etc (for the testing modules TestCmd.py, TestSCons.py,
855 # and unittest.py). This makes sure that our tests pass with what
856 # we really packaged, not because of something hanging around in
857 # the development directory.
859 # We can get away with calling setup.py using a directory path
860 # like this because we put a preamble in it that will chdir()
861 # to the directory in which setup.py exists.
863 dfiles = map(lambda x, d=test_tar_gz_dir: os.path.join(d, x), dst_files)
864 env.Command(dfiles, unpack_tar_gz_files, [
865 Delete(os.path.join(unpack_tar_gz_dir, pkg_version, 'build')),
866 Delete("$TEST_TAR_GZ_DIR"),
867 '$PYTHON $PYTHONFLAGS "%s" install "--prefix=$TEST_TAR_GZ_DIR" --standalone-lib' % \
868 os.path.join(unpack_tar_gz_dir, pkg_version, 'setup.py'),
872 # Generate portage files for submission to Gentoo Linux.
874 gentoo = os.path.join(build, 'gentoo')
875 ebuild = os.path.join(gentoo, 'scons-%s.ebuild' % version)
876 digest = os.path.join(gentoo, 'files', 'digest-scons-%s' % version)
877 env.Command(ebuild, os.path.join('gentoo', 'scons.ebuild.in'), SCons_revision)
878 def Digestify(target, source, env):
881 """Return a signature as a string of hex characters.
883 # NOTE: This routine is a method in the Python 2.0 interface
884 # of the native md5 module, but we want SCons to operate all
885 # the way back to at least Python 1.5.2, which doesn't have it.
890 r = r + h[(i >> 4) & 0xF] + h[i & 0xF]
892 src = source[0].rfile()
893 contents = open(str(src)).read()
894 sig = hexdigest(md5.new(contents).digest())
895 bytes = os.stat(str(src))[6]
896 open(str(target[0]), 'w').write("MD5 %s %s %d\n" % (sig,
899 env.Command(digest, tar_gz, Digestify)
902 print "zip not found; skipping .zip package for %s." % pkg
905 distutils_formats.append('zip')
909 distutils_targets.extend([ zip, platform_zip ])
911 dist_zip = env.Install('$DISTDIR', zip)
912 dist_platform_zip = env.Install('$DISTDIR', platform_zip)
913 Local(dist_zip, dist_platform_zip)
914 AddPostAction(dist_zip, Chmod(dist_zip, 0644))
915 AddPostAction(dist_platform_zip, Chmod(dist_platform_zip, 0644))
918 # Unpack the zip archive created by the distutils into
919 # build/unpack-zip/scons-{version}.
921 unpack_zip_files = map(lambda x, u=unpack_zip_dir, pv=pkg_version:
922 os.path.join(u, pv, x),
925 env.Command(unpack_zip_files, dist_zip, [
926 Delete(os.path.join(unpack_zip_dir, pkg_version)),
931 # Run setup.py in the unpacked subdirectory to "install" everything
932 # into our build/test subdirectory. The runtest.py script will set
933 # PYTHONPATH so that the tests only look under build/test-{package},
934 # and under etc (for the testing modules TestCmd.py, TestSCons.py,
935 # and unittest.py). This makes sure that our tests pass with what
936 # we really packaged, not because of something hanging around in
937 # the development directory.
939 # We can get away with calling setup.py using a directory path
940 # like this because we put a preamble in it that will chdir()
941 # to the directory in which setup.py exists.
943 dfiles = map(lambda x, d=test_zip_dir: os.path.join(d, x), dst_files)
944 env.Command(dfiles, unpack_zip_files, [
945 Delete(os.path.join(unpack_zip_dir, pkg_version, 'build')),
946 Delete("$TEST_ZIP_DIR"),
947 '$PYTHON $PYTHONFLAGS "%s" install "--prefix=$TEST_ZIP_DIR" --standalone-lib' % \
948 os.path.join(unpack_zip_dir, pkg_version, 'setup.py'),
952 msg = "@echo \"Warning: Can not build 'rpm': no rpmbuild utility found\""
953 AlwaysBuild(Alias('rpm', [], msg))
955 topdir = os.path.join(build, 'build',
956 'bdist.' + platform, 'rpm')
958 buildroot = os.path.join(build_dir, 'rpm-buildroot')
960 BUILDdir = os.path.join(topdir, 'BUILD', pkg + '-' + version)
961 RPMSdir = os.path.join(topdir, 'RPMS', 'noarch')
962 SOURCESdir = os.path.join(topdir, 'SOURCES')
963 SPECSdir = os.path.join(topdir, 'SPECS')
964 SRPMSdir = os.path.join(topdir, 'SRPMS')
966 specfile_in = os.path.join('rpm', "%s.spec.in" % pkg)
967 specfile = os.path.join(SPECSdir, "%s-1.spec" % pkg_version)
968 sourcefile = os.path.join(SOURCESdir, "%s.tar.gz" % pkg_version);
969 noarch_rpm = os.path.join(RPMSdir, "%s-1.noarch.rpm" % pkg_version)
970 src_rpm = os.path.join(SRPMSdir, "%s-1.src.rpm" % pkg_version)
972 def spec_function(target, source, env):
973 """Generate the RPM .spec file from the template file.
975 This fills in the %files portion of the .spec file with a
976 list generated from our MANIFEST(s), so we don't have to
977 maintain multiple lists.
979 c = open(str(source[0]), 'rb').read()
980 c = string.replace(c, '__VERSION' + '__', env['VERSION'])
981 c = string.replace(c, '__RPM_FILES' + '__', env['RPM_FILES'])
982 open(str(target[0]), 'wb').write(c)
985 rpm_files_str = string.join(rpm_files, "\n") + "\n"
986 rpm_spec_env = env.Clone(RPM_FILES = rpm_files_str)
987 rpm_spec_action = Action(spec_function, varlist=['RPM_FILES'])
988 rpm_spec_env.Command(specfile, specfile_in, rpm_spec_action)
990 env.InstallAs(sourcefile, tar_gz)
993 targets = [ noarch_rpm, src_rpm ]
994 cmd = "$RPMBUILD --define '_topdir $(%s$)' --buildroot %s -ba $SOURCES" % (topdir, buildroot)
995 if not os.path.isdir(BUILDdir):
996 cmd = ("$( mkdir -p %s; $)" % BUILDdir) + cmd
997 t = env.Command(targets, specfile, cmd)
998 env.Depends(t, sourcefile)
1000 dist_noarch_rpm = env.Install('$DISTDIR', noarch_rpm)
1001 dist_src_rpm = env.Install('$DISTDIR', src_rpm)
1002 Local(dist_noarch_rpm, dist_src_rpm)
1003 AddPostAction(dist_noarch_rpm, Chmod(dist_noarch_rpm, 0644))
1004 AddPostAction(dist_src_rpm, Chmod(dist_src_rpm, 0644))
1006 dfiles = map(lambda x, d=test_rpm_dir: os.path.join(d, 'usr', x),
1010 "$RPM2CPIO $SOURCES | (cd $TEST_RPM_DIR && cpio -id)")
1012 if dh_builddeb and fakeroot:
1013 # Our Debian packaging builds directly into build/dist,
1014 # so we don't need to Install() the .debs.
1015 deb = os.path.join(build_dir, 'dist', "%s_%s-1_all.deb" % (pkg, version))
1016 for d in p['debian_deps']:
1017 b = env.SCons_revision(os.path.join(build, d), d)
1020 env.Command(deb, build_src_files, [
1021 "cd %s && fakeroot make -f debian/rules PYTHON=$PYTHON BUILDDEB_OPTIONS=--destdir=../../build/dist binary" % build,
1024 old = os.path.join('lib', 'scons', '')
1025 new = os.path.join('lib', 'python' + python_ver, 'site-packages', '')
1026 def xxx(s, old=old, new=new):
1027 if s[:len(old)] == old:
1028 s = new + s[len(old):]
1029 return os.path.join('usr', s)
1030 dfiles = map(lambda x, t=test_deb_dir: os.path.join(t, x),
1031 map(xxx, dst_files))
1034 "dpkg --fsys-tarfile $SOURCES | (cd $TEST_DEB_DIR && tar -xf -)")
1038 # Use the Python distutils to generate the appropriate packages.
1041 Delete(os.path.join(build, 'build', 'lib')),
1042 Delete(os.path.join(build, 'build', 'scripts')),
1045 if distutils_formats:
1046 commands.append(Delete(os.path.join(build,
1048 'bdist.' + platform,
1050 for format in distutils_formats:
1051 commands.append("$PYTHON $PYTHONFLAGS $SETUP_PY bdist_dumb -f %s" % format)
1053 commands.append("$PYTHON $PYTHONFLAGS $SETUP_PY sdist --formats=%s" % \
1054 string.join(distutils_formats, ','))
1056 commands.append("$PYTHON $PYTHONFLAGS $SETUP_PY bdist_wininst")
1058 env.Command(distutils_targets, build_src_files, commands)
1061 # Now create local packages for people who want to let people
1062 # build their SCons-buildable packages without having to
1065 s_l_v = '%s-local-%s' % (pkg, version)
1067 local = pkg + '-local'
1068 build_dir_local = os.path.join(build_dir, local)
1069 build_dir_local_slv = os.path.join(build_dir, local, s_l_v)
1071 dist_local_tar_gz = os.path.join("$DISTDIR/%s.tar.gz" % s_l_v)
1072 dist_local_zip = os.path.join("$DISTDIR/%s.zip" % s_l_v)
1073 AddPostAction(dist_local_tar_gz, Chmod(dist_local_tar_gz, 0644))
1074 AddPostAction(dist_local_zip, Chmod(dist_local_zip, 0644))
1077 Delete(build_dir_local),
1078 '$PYTHON $PYTHONFLAGS $SETUP_PY install "--install-script=%s" "--install-lib=%s" --no-install-man --no-compile --standalone-lib --no-version-script' % \
1079 (build_dir_local, build_dir_local_slv),
1082 for script in scripts:
1083 #commands.append("mv %s/%s %s/%s.py" % (local, script, local, script))
1084 local_script = os.path.join(build_dir_local, script)
1085 commands.append(Move(local_script + '.py', local_script))
1087 rf = filter(lambda x: not x in scripts, raw_files)
1088 rf = map(lambda x, slv=s_l_v: os.path.join(slv, x), rf)
1089 for script in scripts:
1090 rf.append("%s.py" % script)
1091 local_targets = map(lambda x, s=build_dir_local: os.path.join(s, x), rf)
1093 env.Command(local_targets, build_src_files, commands)
1095 scons_LICENSE = os.path.join(build_dir_local, 'scons-LICENSE')
1096 l = env.SCons_revision(scons_LICENSE, 'LICENSE-local')
1097 local_targets.append(l)
1100 scons_README = os.path.join(build_dir_local, 'scons-README')
1101 l = env.SCons_revision(scons_README, 'README-local')
1102 local_targets.append(l)
1106 env.Command(dist_local_tar_gz,
1108 "cd %s && tar czf $( ${TARGET.abspath} $) *" % build_dir_local)
1110 unpack_targets = map(lambda x, d=test_local_tar_gz_dir:
1113 commands = [Delete(test_local_tar_gz_dir),
1114 Mkdir(test_local_tar_gz_dir),
1115 "cd %s && tar xzf $( ${SOURCE.abspath} $)" % test_local_tar_gz_dir]
1117 env.Command(unpack_targets, dist_local_tar_gz, commands)
1120 env.Command(dist_local_zip, local_targets, zipit,
1121 CD = build_dir_local, PSV = '.')
1123 unpack_targets = map(lambda x, d=test_local_zip_dir:
1126 commands = [Delete(test_local_zip_dir),
1127 Mkdir(test_local_zip_dir),
1130 env.Command(unpack_targets, dist_local_zip, unzipit,
1131 UNPACK_ZIP_DIR = test_local_zip_dir)
1136 Export('build_dir', 'env')
1138 SConscript('QMTest/SConscript')
1147 def copy(target, source, env):
1150 open(t, 'wb').write(open(s, 'rb').read())
1153 # Guarantee that real copies of these files always exist in
1154 # build/. If there's a symlink there, then this is an Aegis
1155 # build and we blow them away now so that they'll get "built" later.
1156 p = os.path.join(build_dir, file)
1157 if os.path.islink(p):
1159 if not os.path.isabs(p):
1161 sp = env.Command(p, file, copy)
1167 Export('build_dir', 'env', 'whereis')
1169 SConscript('doc/SConscript')
1172 # If we're running in a Subversion working directory, pack up a complete
1173 # source archive from the project files and files in the change.
1177 "Not building in a Subversion tree; skipping building src package."
1179 slines = filter(lambda l: l[0] in ' MA', svn_status_lines)
1180 sentries = map(lambda l: l.split()[-1], slines)
1181 sfiles = filter(os.path.isfile, sentries)
1190 for p in remove_patterns:
1191 sfiles = filter(lambda s, p=p: not fnmatch.fnmatch(s, p), sfiles)
1194 ps = "%s-src" % project
1195 psv = "%s-%s" % (ps, version)
1196 b_ps = os.path.join(build_dir, ps)
1197 b_psv = os.path.join(build_dir, psv)
1198 b_psv_stamp = b_psv + '-stamp'
1200 src_tar_gz = os.path.join(build_dir, 'dist', '%s.tar.gz' % psv)
1201 src_zip = os.path.join(build_dir, 'dist', '%s.zip' % psv)
1203 Local(src_tar_gz, src_zip)
1206 env.SCons_revision(os.path.join(b_ps, file), file)
1208 b_ps_files = map(lambda x, d=b_ps: os.path.join(d, x), sfiles)
1215 env.Command(b_psv_stamp, src_deps + b_ps_files, cmds)
1217 apply(Local, b_ps_files, {})
1221 env.Command(src_tar_gz, b_psv_stamp,
1222 "tar cz${TAR_HFLAG} -f $TARGET -C build %s" % psv)
1225 # Unpack the archive into build/unpack/scons-{version}.
1227 unpack_tar_gz_files = map(lambda x, u=unpack_tar_gz_dir, psv=psv:
1228 os.path.join(u, psv, x),
1232 # We'd like to replace the last three lines with the following:
1234 # tar zxf $SOURCES -C $UNPACK_TAR_GZ_DIR
1236 # but that gives heartburn to Cygwin's tar, so work around it
1237 # with separate zcat-tar-rm commands.
1238 env.Command(unpack_tar_gz_files, src_tar_gz, [
1239 Delete(os.path.join(unpack_tar_gz_dir, psv)),
1240 "$ZCAT $SOURCES > .temp",
1241 "tar xf .temp -C $UNPACK_TAR_GZ_DIR",
1246 # Run setup.py in the unpacked subdirectory to "install" everything
1247 # into our build/test subdirectory. The runtest.py script will set
1248 # PYTHONPATH so that the tests only look under build/test-{package},
1249 # and under etc (for the testing modules TestCmd.py, TestSCons.py,
1250 # and unittest.py). This makes sure that our tests pass with what
1251 # we really packaged, not because of something hanging around in
1252 # the development directory.
1254 # We can get away with calling setup.py using a directory path
1255 # like this because we put a preamble in it that will chdir()
1256 # to the directory in which setup.py exists.
1258 dfiles = map(lambda x, d=test_src_tar_gz_dir: os.path.join(d, x),
1260 scons_lib_dir = os.path.join(unpack_tar_gz_dir, psv, 'src', 'engine')
1261 ENV = env.Dictionary('ENV').copy()
1262 ENV['SCONS_LIB_DIR'] = scons_lib_dir
1263 ENV['USERNAME'] = developer
1264 env.Command(dfiles, unpack_tar_gz_files,
1266 Delete(os.path.join(unpack_tar_gz_dir,
1271 Delete("$TEST_SRC_TAR_GZ_DIR"),
1272 'cd "%s" && $PYTHON $PYTHONFLAGS "%s" "%s" VERSION="$VERSION"' % \
1273 (os.path.join(unpack_tar_gz_dir, psv),
1274 os.path.join('src', 'script', 'scons.py'),
1275 os.path.join('build', 'scons')),
1276 '$PYTHON $PYTHONFLAGS "%s" install "--prefix=$TEST_SRC_TAR_GZ_DIR" --standalone-lib' % \
1277 os.path.join(unpack_tar_gz_dir,
1287 env.Command(src_zip, b_psv_stamp, zipit, CD = 'build', PSV = psv)
1290 # Unpack the archive into build/unpack/scons-{version}.
1292 unpack_zip_files = map(lambda x, u=unpack_zip_dir, psv=psv:
1293 os.path.join(u, psv, x),
1296 env.Command(unpack_zip_files, src_zip, [
1297 Delete(os.path.join(unpack_zip_dir, psv)),
1302 # Run setup.py in the unpacked subdirectory to "install" everything
1303 # into our build/test subdirectory. The runtest.py script will set
1304 # PYTHONPATH so that the tests only look under build/test-{package},
1305 # and under etc (for the testing modules TestCmd.py, TestSCons.py,
1306 # and unittest.py). This makes sure that our tests pass with what
1307 # we really packaged, not because of something hanging around in
1308 # the development directory.
1310 # We can get away with calling setup.py using a directory path
1311 # like this because we put a preamble in it that will chdir()
1312 # to the directory in which setup.py exists.
1314 dfiles = map(lambda x, d=test_src_zip_dir: os.path.join(d, x),
1316 scons_lib_dir = os.path.join(unpack_zip_dir, psv, 'src', 'engine')
1317 ENV = env.Dictionary('ENV').copy()
1318 ENV['SCONS_LIB_DIR'] = scons_lib_dir
1319 ENV['USERNAME'] = developer
1320 env.Command(dfiles, unpack_zip_files,
1322 Delete(os.path.join(unpack_zip_dir,
1327 Delete("$TEST_SRC_ZIP_DIR"),
1328 'cd "%s" && $PYTHON $PYTHONFLAGS "%s" "%s" VERSION="$VERSION"' % \
1329 (os.path.join(unpack_zip_dir, psv),
1330 os.path.join('src', 'script', 'scons.py'),
1331 os.path.join('build', 'scons')),
1332 '$PYTHON $PYTHONFLAGS "%s" install "--prefix=$TEST_SRC_ZIP_DIR" --standalone-lib' % \
1333 os.path.join(unpack_zip_dir,
1341 for pf, help_text in packaging_flavors:
1343 os.path.join(build_dir, 'test-'+pf),
1344 os.path.join(build_dir, 'QMTest'),
1345 os.path.join(build_dir, 'runtest.py'),