2 # SConstruct file to build scons packages during development.
4 # See the README file for an overview of how SCons is built and tested.
6 from __future__ import generators ### KEEP FOR COMPATIBILITY FIXERS
8 # When this gets changed, you must also change the copyright_years string
9 # in QMTest/TestSCons.py so the test scripts look for the right string.
10 copyright_years = '2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010'
12 # This gets inserted into the man pages to reflect the month of release.
13 month_year = 'January 2010'
18 # Permission is hereby granted, free of charge, to any person obtaining
19 # a copy of this software and associated documentation files (the
20 # "Software"), to deal in the Software without restriction, including
21 # without limitation the rights to use, copy, modify, merge, publish,
22 # distribute, sublicense, and/or sell copies of the Software, and to
23 # permit persons to whom the Software is furnished to do so, subject to
24 # the following conditions:
26 # The above copyright notice and this permission notice shall be included
27 # in all copies or substantial portions of the Software.
29 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
30 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
31 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
32 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
33 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
34 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
35 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
48 default_version = '1.3.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 os.environ['PATH'].split(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 = socket.gethostname().split('.')[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 [l for l in hg_status_lines if l[0] in 'AMR!']:
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 [l for l in svn_status_lines if l[0] in 'ACDMR']:
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 key in os.environ:
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 = sorted(packaging_flavors + [('doc', 'The SCons documentation.')])
285 for alias, help_text in aliases:
286 tw = textwrap.TextWrapper(
288 initial_indent = indent_fmt % alias,
289 subsequent_indent = indent_fmt % '' + ' ',
291 Help(tw.fill(help_text) + '\n')
294 The following command-line variables can be set:
298 for variable, help_text in command_line_variables:
299 tw = textwrap.TextWrapper(
301 initial_indent = indent_fmt % variable,
302 subsequent_indent = indent_fmt % '' + ' ',
304 Help(tw.fill(help_text) + '\n')
311 # Figure out if we can handle .zip files.
318 def zipit(env, target, source):
319 print "Zipping %s:" % str(target[0])
320 def visit(arg, dirname, names):
322 path = os.path.join(dirname, name)
323 if os.path.isfile(path):
325 zf = zipfile.ZipFile(str(target[0]), 'w')
328 try: os.path.walk(env['PSV'], visit, zf)
329 finally: os.chdir(olddir)
332 def unzipit(env, target, source):
333 print "Unzipping %s:" % str(source[0])
334 zf = zipfile.ZipFile(str(source[0]), 'r')
335 for name in zf.namelist():
336 dest = os.path.join(env['UNPACK_ZIP_DIR'], name)
337 dir = os.path.dirname(dest)
343 # if the file exists, then delete it before writing
344 # to it so that we don't end up trying to write to a symlink:
345 if os.path.isfile(dest) or os.path.islink(dest):
347 if not os.path.isdir(dest):
348 open(dest, 'wb').write(zf.read(name))
352 zipit = "cd $CD && $ZIP $ZIPFLAGS $( ${TARGET.abspath} $) $PSV"
353 unzipit = "$UNZIP $UNZIPFLAGS $SOURCES"
355 def SCons_revision(target, source, env):
356 """Interpolate specific values from the environment into a file.
358 This is used to copy files into a tree that gets packaged up
359 into the source file package.
363 contents = open(s, 'rb').read()
364 # Note: We construct the __*__ substitution strings here
365 # so that they don't get replaced when this file gets
366 # copied into the tree for packaging.
367 contents = contents.replace('__BUILD' + '__', env['BUILD'])
368 contents = contents.replace('__BUILDSYS' + '__', env['BUILDSYS'])
369 contents = contents.replace('__COPYRIGHT' + '__', env['COPYRIGHT'])
370 contents = contents.replace('__DATE' + '__', env['DATE'])
371 contents = contents.replace('__DEVELOPER' + '__', env['DEVELOPER'])
372 contents = contents.replace('__FILE' + '__', str(source[0]))
373 contents = contents.replace('__MONTH_YEAR'+ '__', env['MONTH_YEAR'])
374 contents = contents.replace('__REVISION' + '__', env['REVISION'])
375 contents = contents.replace('__VERSION' + '__', env['VERSION'])
376 contents = contents.replace('__NULL' + '__', '')
377 open(t, 'wb').write(contents)
378 os.chmod(t, os.stat(s)[0])
380 revbuilder = Builder(action = Action(SCons_revision,
381 varlist=['COPYRIGHT', 'VERSION']))
383 def soelim(target, source, env):
385 Interpolate files included in [gnt]roff source files using the
388 This behaves somewhat like the soelim(1) wrapper around groff, but
389 makes us independent of whether the actual underlying implementation
390 includes an soelim() command or the corresponding command-line option
391 to groff(1). The key behavioral difference is that this doesn't
392 recursively include .so files from the include file. Not yet, anyway.
396 dir, f = os.path.split(s)
399 for line in sfp.readlines():
400 if line[:4] in ['.so ', "'so "]:
401 sofile = os.path.join(dir, line[4:-1])
402 tfp.write(open(sofile, 'r').read())
408 def soscan(node, env, path):
409 c = node.get_text_contents()
410 return re.compile(r"^[\.']so\s+(\S+)", re.M).findall(c)
412 soelimbuilder = Builder(action = Action(soelim),
413 source_scanner = Scanner(soscan))
415 # When copying local files from a Repository (Aegis),
416 # just make copies, don't symlink them.
417 SetOption('duplicate', 'copy')
423 BUILDDIR = build_dir,
424 BUILDSYS = build_system,
425 COPYRIGHT = copyright,
427 DEVELOPER = developer,
428 DISTDIR = os.path.join(build_dir, 'dist'),
429 MONTH_YEAR = month_year,
434 TAR_HFLAG = tar_hflag,
439 UNZIPFLAGS = '-o -d $UNPACK_ZIP_DIR',
444 RPM2CPIO = 'rpm2cpio',
446 TEST_DEB_DIR = test_deb_dir,
447 TEST_RPM_DIR = test_rpm_dir,
448 TEST_SRC_TAR_GZ_DIR = test_src_tar_gz_dir,
449 TEST_SRC_ZIP_DIR = test_src_zip_dir,
450 TEST_TAR_GZ_DIR = test_tar_gz_dir,
451 TEST_ZIP_DIR = test_zip_dir,
453 UNPACK_TAR_GZ_DIR = unpack_tar_gz_dir,
454 UNPACK_ZIP_DIR = unpack_zip_dir,
456 BUILDERS = { 'SCons_revision' : revbuilder,
457 'SOElim' : soelimbuilder },
459 PYTHON = '"%s"' % sys.executable,
463 Version_values = [Value(version), Value(build_id)]
466 # Define SCons packages.
468 # In the original, more complicated packaging scheme, we were going
469 # to have separate packages for:
471 # python-scons only the build engine
472 # scons-script only the script
473 # scons the script plus the build engine
475 # We're now only delivering a single "scons" package, but this is still
476 # "built" as two sub-packages (the build engine and the script), so
477 # the definitions remain here, even though we're not using them for
482 'pkg' : 'python-' + project,
483 'src_subdir' : 'engine',
484 'inst_subdir' : os.path.join('lib', 'python1.5', 'site-packages'),
485 'rpm_dir' : '/usr/lib/scons',
498 'files' : [ 'LICENSE.txt',
505 'LICENSE.txt' : '../LICENSE.txt'
510 'extra_rpm_files' : [],
513 'SCons/__init__.py' : Version_values,
517 # Figure out the name of a .egg-info file that might be generated
518 # as part of the RPM package. There are two complicating factors.
520 # First, the RPM spec file we generate will just execute "python", not
521 # necessarily the one in sys.executable. If *that* version of python has
522 # a distutils that knows about Python eggs, then setup.py will generate a
523 # .egg-info file, so we have to execute any distutils logic in a subshell.
525 # Second, we can't just have the subshell check for the existence of the
526 # distutils.command.install_egg_info module and generate the expected
527 # file name by hand, the way we used to, because different systems can
528 # have slightly different .egg-info naming conventions. (Specifically,
529 # Ubuntu overrides the default behavior to remove the Python version
530 # string from the .egg-info file name.) The right way to do this is to
531 # actually call into the install_egg_info() class to have it generate
532 # the expected name for us.
534 # This is all complicated enough that we do it by writing an in-line
535 # script to a temporary file and then feeding it to a separate invocation
536 # of "python" to tell us the actual name of the generated .egg-info file.
538 print_egg_info_name = """
540 from distutils.dist import Distribution
541 from distutils.command.install_egg_info import install_egg_info
545 dist = Distribution({'name' : "scons", 'version' : '%s'})
546 i = install_egg_info(dist)
549 print os.path.split(i.outputs[0])[1]
553 fd, tfname = tempfile.mkstemp()
554 tfp = os.fdopen(fd, "w")
555 tfp.write(print_egg_info_name)
557 egg_info_file = os.popen("python %s" % tfname).read()[:-1]
559 python_scons['extra_rpm_files'].append(egg_info_file)
563 except EnvironmentError:
567 # The original packaging scheme would have have required us to push
568 # the Python version number into the package name (python1.5-scons,
569 # python2.0-scons, etc.), which would have required a definition
570 # like the following. Leave this here in case we ever decide to do
571 # this in the future, but note that this would require some modification
572 # to src/engine/setup.py before it would really work.
575 # 'pkg' : 'python2-' + project,
576 # 'src_subdir' : 'engine',
577 # 'inst_subdir' : os.path.join('lib', 'python2.2', 'site-packages'),
580 # 'debian/changelog',
582 # 'debian/copyright',
597 # 'LICENSE.txt' : '../LICENSE.txt',
604 'pkg' : project + '-script',
605 'src_subdir' : 'script',
606 'inst_subdir' : 'bin',
607 'rpm_dir' : '/usr/bin',
628 'LICENSE.txt' : '../LICENSE.txt',
629 'scons' : 'scons.py',
630 'sconsign' : 'sconsign.py',
631 'scons-time' : 'scons-time.py',
636 'extra_rpm_files' : [
638 'sconsign-' + version,
639 'scons-time-' + version,
643 'scons' : Version_values,
644 'sconsign' : Version_values,
667 'os_spawnv_fix.diff',
672 #'script/scons-post-install.py',
678 'scons.1' : '$BUILDDIR/doc/man/scons.1',
679 'sconsign.1' : '$BUILDDIR/doc/man/sconsign.1',
680 'scons-time.1' : '$BUILDDIR/doc/man/scons-time.1',
684 'scons.1' : env.SOElim,
685 'sconsign.1' : env.SOElim,
686 'scons-time.1' : env.SOElim,
689 'subpkgs' : [ python_scons, scons_script ],
692 'python-' + project : python_project_subinst_dir,
693 project + '-script' : project_script_subinst_dir,
697 scripts = ['scons', 'sconsign', 'scons-time']
704 # Initialize variables with the right directories for this package.
707 pkg_version = "%s-%s" % (pkg, version)
710 if 'src_subdir' in p:
711 src = os.path.join(src, p['src_subdir'])
713 build = os.path.join(build_dir, pkg)
715 tar_gz = os.path.join(build, 'dist', "%s.tar.gz" % pkg_version)
716 platform_tar_gz = os.path.join(build,
718 "%s.%s.tar.gz" % (pkg_version, platform))
719 zip = os.path.join(build, 'dist', "%s.zip" % pkg_version)
720 platform_zip = os.path.join(build,
722 "%s.%s.zip" % (pkg_version, platform))
723 if platform == "win-amd64":
724 win32_exe = os.path.join(build, 'dist', "%s.win-amd64.exe" % pkg_version)
726 win32_exe = os.path.join(build, 'dist', "%s.win32.exe" % pkg_version)
729 # Update the environment with the relevant information
732 # We can get away with calling setup.py using a directory path
733 # like this because we put a preamble in it that will chdir()
734 # to the directory in which setup.py exists.
736 setup_py = os.path.join(build, 'setup.py')
737 env.Replace(PKG = pkg,
738 PKG_VERSION = pkg_version,
739 SETUP_PY = '"%s"' % setup_py)
743 # Read up the list of source files from our MANIFEST.in.
744 # This list should *not* include LICENSE.txt, MANIFEST,
745 # README.txt, or setup.py. Make a copy of the list for the
748 manifest_in = File(os.path.join(src, 'MANIFEST.in')).rstr()
749 src_files = [x[:-1] for x in open(manifest_in).readlines()]
750 raw_files = src_files[:]
751 dst_files = src_files[:]
754 MANIFEST_in_list = []
758 # This package includes some sub-packages. Read up their
759 # MANIFEST.in files, and add them to our source and destination
760 # file lists, modifying them as appropriate to add the
763 for sp in p['subpkgs']:
764 ssubdir = sp['src_subdir']
765 isubdir = p['subinst_dirs'][sp['pkg']]
766 MANIFEST_in = File(os.path.join(src, ssubdir, 'MANIFEST.in')).rstr()
767 MANIFEST_in_list.append(MANIFEST_in)
768 files = [x[:-1] for x in open(MANIFEST_in).readlines()]
769 raw_files.extend(files)
770 src_files.extend([os.path.join(ssubdir, x) for x in files])
772 r = os.path.join(sp['rpm_dir'], f)
775 rpm_files.append(r + 'c')
776 for f in sp.get('extra_rpm_files', []):
777 r = os.path.join(sp['rpm_dir'], f)
779 files = [os.path.join(isubdir, x) for x in files]
780 dst_files.extend(files)
781 for k, f in sp['filemap'].items():
783 k = os.path.join(ssubdir, k)
784 p['filemap'][k] = os.path.join(ssubdir, f)
785 for f, deps in sp['explicit_deps'].items():
786 f = os.path.join(build, ssubdir, f)
790 # Now that we have the "normal" source files, add those files
791 # that are standard for each distribution. Note that we don't
792 # add these to dst_files, because they don't get installed.
793 # And we still have the MANIFEST to add.
795 src_files.extend(p['files'])
798 # Now run everything in src_file through the sed command we
799 # concocted to expand __FILE__, __VERSION__, etc.
802 s = p['filemap'].get(b, b)
803 if not s[0] == '$' and not os.path.isabs(s):
804 s = os.path.join(src, s)
805 builder = p['buildermap'].get(b, env.SCons_revision)
806 x = builder(os.path.join(build, b), s)
810 # NOW, finally, we can create the MANIFEST, which we do
811 # by having Python spit out the contents of the src_files
812 # array we've carefully created. After we've added
813 # MANIFEST itself to the array, of course.
815 src_files.append("MANIFEST")
816 MANIFEST_in_list.append(os.path.join(src, 'MANIFEST.in'))
818 def write_src_files(target, source, **kw):
821 f = open(str(target[0]), 'wb')
822 for file in src_files:
826 env.Command(os.path.join(build, 'MANIFEST'),
831 # Now go through and arrange to create whatever packages we can.
833 build_src_files = [os.path.join(build, x) for x in src_files]
834 Local(*build_src_files)
836 distutils_formats = []
838 distutils_targets = [ win32_exe ]
840 dist_distutils_targets = env.Install('$DISTDIR', distutils_targets)
841 Local(dist_distutils_targets)
842 AddPostAction(dist_distutils_targets, Chmod(dist_distutils_targets, 0644))
845 print "gzip not found in %s; skipping .tar.gz package for %s." % (os.environ['PATH'], pkg)
848 distutils_formats.append('gztar')
850 src_deps.append(tar_gz)
852 distutils_targets.extend([ tar_gz, platform_tar_gz ])
854 dist_tar_gz = env.Install('$DISTDIR', tar_gz)
855 dist_platform_tar_gz = env.Install('$DISTDIR', platform_tar_gz)
856 Local(dist_tar_gz, dist_platform_tar_gz)
857 AddPostAction(dist_tar_gz, Chmod(dist_tar_gz, 0644))
858 AddPostAction(dist_platform_tar_gz, Chmod(dist_platform_tar_gz, 0644))
861 # Unpack the tar.gz archive created by the distutils into
862 # build/unpack-tar-gz/scons-{version}.
864 # We'd like to replace the last three lines with the following:
866 # tar zxf $SOURCES -C $UNPACK_TAR_GZ_DIR
868 # but that gives heartburn to Cygwin's tar, so work around it
869 # with separate zcat-tar-rm commands.
871 unpack_tar_gz_files = [os.path.join(unpack_tar_gz_dir, pkg_version, x)
873 env.Command(unpack_tar_gz_files, dist_tar_gz, [
874 Delete(os.path.join(unpack_tar_gz_dir, pkg_version)),
875 "$ZCAT $SOURCES > .temp",
876 "tar xf .temp -C $UNPACK_TAR_GZ_DIR",
881 # Run setup.py in the unpacked subdirectory to "install" everything
882 # into our build/test subdirectory. The runtest.py script will set
883 # PYTHONPATH so that the tests only look under build/test-{package},
884 # and under etc (for the testing modules TestCmd.py, TestSCons.py,
885 # and unittest.py). This makes sure that our tests pass with what
886 # we really packaged, not because of something hanging around in
887 # the development directory.
889 # We can get away with calling setup.py using a directory path
890 # like this because we put a preamble in it that will chdir()
891 # to the directory in which setup.py exists.
893 dfiles = [os.path.join(test_tar_gz_dir, x) for x in dst_files]
894 env.Command(dfiles, unpack_tar_gz_files, [
895 Delete(os.path.join(unpack_tar_gz_dir, pkg_version, 'build')),
896 Delete("$TEST_TAR_GZ_DIR"),
897 '$PYTHON $PYTHONFLAGS "%s" install "--prefix=$TEST_TAR_GZ_DIR" --standalone-lib' % \
898 os.path.join(unpack_tar_gz_dir, pkg_version, 'setup.py'),
902 # Generate portage files for submission to Gentoo Linux.
904 gentoo = os.path.join(build, 'gentoo')
905 ebuild = os.path.join(gentoo, 'scons-%s.ebuild' % version)
906 digest = os.path.join(gentoo, 'files', 'digest-scons-%s' % version)
907 env.Command(ebuild, os.path.join('gentoo', 'scons.ebuild.in'), SCons_revision)
908 def Digestify(target, source, env):
910 src = source[0].rfile()
911 contents = open(str(src)).read()
912 sig = md5.new(contents).hexdigest()
913 bytes = os.stat(str(src))[6]
914 open(str(target[0]), 'w').write("MD5 %s %s %d\n" % (sig,
917 env.Command(digest, tar_gz, Digestify)
920 print "zip not found; skipping .zip package for %s." % pkg
923 distutils_formats.append('zip')
927 distutils_targets.extend([ zip, platform_zip ])
929 dist_zip = env.Install('$DISTDIR', zip)
930 dist_platform_zip = env.Install('$DISTDIR', platform_zip)
931 Local(dist_zip, dist_platform_zip)
932 AddPostAction(dist_zip, Chmod(dist_zip, 0644))
933 AddPostAction(dist_platform_zip, Chmod(dist_platform_zip, 0644))
936 # Unpack the zip archive created by the distutils into
937 # build/unpack-zip/scons-{version}.
939 unpack_zip_files = [os.path.join(unpack_zip_dir, pkg_version, x)
942 env.Command(unpack_zip_files, dist_zip, [
943 Delete(os.path.join(unpack_zip_dir, pkg_version)),
948 # Run setup.py in the unpacked subdirectory to "install" everything
949 # into our build/test subdirectory. The runtest.py script will set
950 # PYTHONPATH so that the tests only look under build/test-{package},
951 # and under etc (for the testing modules TestCmd.py, TestSCons.py,
952 # and unittest.py). This makes sure that our tests pass with what
953 # we really packaged, not because of something hanging around in
954 # the development directory.
956 # We can get away with calling setup.py using a directory path
957 # like this because we put a preamble in it that will chdir()
958 # to the directory in which setup.py exists.
960 dfiles = [os.path.join(test_zip_dir, x) for x in dst_files]
961 env.Command(dfiles, unpack_zip_files, [
962 Delete(os.path.join(unpack_zip_dir, pkg_version, 'build')),
963 Delete("$TEST_ZIP_DIR"),
964 '$PYTHON $PYTHONFLAGS "%s" install "--prefix=$TEST_ZIP_DIR" --standalone-lib' % \
965 os.path.join(unpack_zip_dir, pkg_version, 'setup.py'),
969 msg = "@echo \"Warning: Can not build 'rpm': no rpmbuild utility found\""
970 AlwaysBuild(Alias('rpm', [], msg))
972 topdir = os.path.join(build, 'build',
973 'bdist.' + platform, 'rpm')
975 buildroot = os.path.join(build_dir, 'rpm-buildroot')
977 BUILDdir = os.path.join(topdir, 'BUILD', pkg + '-' + version)
978 RPMSdir = os.path.join(topdir, 'RPMS', 'noarch')
979 SOURCESdir = os.path.join(topdir, 'SOURCES')
980 SPECSdir = os.path.join(topdir, 'SPECS')
981 SRPMSdir = os.path.join(topdir, 'SRPMS')
983 specfile_in = os.path.join('rpm', "%s.spec.in" % pkg)
984 specfile = os.path.join(SPECSdir, "%s-1.spec" % pkg_version)
985 sourcefile = os.path.join(SOURCESdir, "%s.tar.gz" % pkg_version);
986 noarch_rpm = os.path.join(RPMSdir, "%s-1.noarch.rpm" % pkg_version)
987 src_rpm = os.path.join(SRPMSdir, "%s-1.src.rpm" % pkg_version)
989 def spec_function(target, source, env):
990 """Generate the RPM .spec file from the template file.
992 This fills in the %files portion of the .spec file with a
993 list generated from our MANIFEST(s), so we don't have to
994 maintain multiple lists.
996 c = open(str(source[0]), 'rb').read()
997 c = c.replace('__VERSION' + '__', env['VERSION'])
998 c = c.replace('__RPM_FILES' + '__', env['RPM_FILES'])
999 open(str(target[0]), 'wb').write(c)
1002 rpm_files_str = "\n".join(rpm_files) + "\n"
1003 rpm_spec_env = env.Clone(RPM_FILES = rpm_files_str)
1004 rpm_spec_action = Action(spec_function, varlist=['RPM_FILES'])
1005 rpm_spec_env.Command(specfile, specfile_in, rpm_spec_action)
1007 env.InstallAs(sourcefile, tar_gz)
1010 targets = [ noarch_rpm, src_rpm ]
1011 cmd = "$RPMBUILD --define '_topdir $(%s$)' --buildroot %s -ba $SOURCES" % (topdir, buildroot)
1012 if not os.path.isdir(BUILDdir):
1013 cmd = ("$( mkdir -p %s; $)" % BUILDdir) + cmd
1014 t = env.Command(targets, specfile, cmd)
1015 env.Depends(t, sourcefile)
1017 dist_noarch_rpm = env.Install('$DISTDIR', noarch_rpm)
1018 dist_src_rpm = env.Install('$DISTDIR', src_rpm)
1019 Local(dist_noarch_rpm, dist_src_rpm)
1020 AddPostAction(dist_noarch_rpm, Chmod(dist_noarch_rpm, 0644))
1021 AddPostAction(dist_src_rpm, Chmod(dist_src_rpm, 0644))
1023 dfiles = [os.path.join(test_rpm_dir, 'usr', x) for x in dst_files]
1026 "$RPM2CPIO $SOURCES | (cd $TEST_RPM_DIR && cpio -id)")
1028 if dh_builddeb and fakeroot:
1029 # Our Debian packaging builds directly into build/dist,
1030 # so we don't need to Install() the .debs.
1031 deb = os.path.join(build_dir, 'dist', "%s_%s-1_all.deb" % (pkg, version))
1032 for d in p['debian_deps']:
1033 b = env.SCons_revision(os.path.join(build, d), d)
1036 env.Command(deb, build_src_files, [
1037 "cd %s && fakeroot make -f debian/rules PYTHON=$PYTHON BUILDDEB_OPTIONS=--destdir=../../build/dist binary" % build,
1040 old = os.path.join('lib', 'scons', '')
1041 new = os.path.join('lib', 'python' + python_ver, 'site-packages', '')
1042 def xxx(s, old=old, new=new):
1043 if s[:len(old)] == old:
1044 s = new + s[len(old):]
1045 return os.path.join('usr', s)
1046 dfiles = [os.path.join(test_deb_dir, xxx(x)) for x in dst_files]
1049 "dpkg --fsys-tarfile $SOURCES | (cd $TEST_DEB_DIR && tar -xf -)")
1053 # Use the Python distutils to generate the appropriate packages.
1056 Delete(os.path.join(build, 'build', 'lib')),
1057 Delete(os.path.join(build, 'build', 'scripts')),
1060 if distutils_formats:
1061 commands.append(Delete(os.path.join(build,
1063 'bdist.' + platform,
1065 for format in distutils_formats:
1066 commands.append("$PYTHON $PYTHONFLAGS $SETUP_PY bdist_dumb -f %s" % format)
1068 commands.append("$PYTHON $PYTHONFLAGS $SETUP_PY sdist --formats=%s" % \
1069 ','.join(distutils_formats))
1071 commands.append("$PYTHON $PYTHONFLAGS $SETUP_PY bdist_wininst")
1073 env.Command(distutils_targets, build_src_files, commands)
1076 # Now create local packages for people who want to let people
1077 # build their SCons-buildable packages without having to
1080 s_l_v = '%s-local-%s' % (pkg, version)
1082 local = pkg + '-local'
1083 build_dir_local = os.path.join(build_dir, local)
1084 build_dir_local_slv = os.path.join(build_dir, local, s_l_v)
1086 dist_local_tar_gz = os.path.join("$DISTDIR/%s.tar.gz" % s_l_v)
1087 dist_local_zip = os.path.join("$DISTDIR/%s.zip" % s_l_v)
1088 AddPostAction(dist_local_tar_gz, Chmod(dist_local_tar_gz, 0644))
1089 AddPostAction(dist_local_zip, Chmod(dist_local_zip, 0644))
1092 Delete(build_dir_local),
1093 '$PYTHON $PYTHONFLAGS $SETUP_PY install "--install-script=%s" "--install-lib=%s" --no-install-man --no-compile --standalone-lib --no-version-script' % \
1094 (build_dir_local, build_dir_local_slv),
1097 for script in scripts:
1098 #commands.append("mv %s/%s %s/%s.py" % (local, script, local, script))
1099 local_script = os.path.join(build_dir_local, script)
1100 commands.append(Move(local_script + '.py', local_script))
1102 rf = [x for x in raw_files if not x in scripts]
1103 rf = [os.path.join(s_l_v, x) for x in rf]
1104 for script in scripts:
1105 rf.append("%s.py" % script)
1106 local_targets = [os.path.join(build_dir_local, x) for x in rf]
1108 env.Command(local_targets, build_src_files, commands)
1110 scons_LICENSE = os.path.join(build_dir_local, 'scons-LICENSE')
1111 l = env.SCons_revision(scons_LICENSE, 'LICENSE-local')
1112 local_targets.append(l)
1115 scons_README = os.path.join(build_dir_local, 'scons-README')
1116 l = env.SCons_revision(scons_README, 'README-local')
1117 local_targets.append(l)
1121 env.Command(dist_local_tar_gz,
1123 "cd %s && tar czf $( ${TARGET.abspath} $) *" % build_dir_local)
1125 unpack_targets = [os.path.join(test_local_tar_gz_dir, x) for x in rf]
1126 commands = [Delete(test_local_tar_gz_dir),
1127 Mkdir(test_local_tar_gz_dir),
1128 "cd %s && tar xzf $( ${SOURCE.abspath} $)" % test_local_tar_gz_dir]
1130 env.Command(unpack_targets, dist_local_tar_gz, commands)
1133 env.Command(dist_local_zip, local_targets, zipit,
1134 CD = build_dir_local, PSV = '.')
1136 unpack_targets = [os.path.join(test_local_zip_dir, x) for x in rf]
1137 commands = [Delete(test_local_zip_dir),
1138 Mkdir(test_local_zip_dir),
1141 env.Command(unpack_targets, dist_local_zip, unzipit,
1142 UNPACK_ZIP_DIR = test_local_zip_dir)
1147 Export('build_dir', 'env')
1149 SConscript('QMTest/SConscript')
1158 def copy(target, source, env):
1161 open(t, 'wb').write(open(s, 'rb').read())
1164 # Guarantee that real copies of these files always exist in
1165 # build/. If there's a symlink there, then this is an Aegis
1166 # build and we blow them away now so that they'll get "built" later.
1167 p = os.path.join(build_dir, file)
1168 if os.path.islink(p):
1170 if not os.path.isabs(p):
1172 sp = env.Command(p, file, copy)
1178 Export('build_dir', 'env', 'whereis')
1180 SConscript('doc/SConscript')
1183 # If we're running in a Subversion working directory, pack up a complete
1184 # source archive from the project files and files in the change.
1189 slines = [l for l in hg_status_lines if l[0] in 'ACM']
1190 sfiles = [l.split()[-1] for l in slines]
1191 elif svn_status_lines:
1192 slines = [l for l in svn_status_lines if l[0] in ' MA']
1193 sentries = [l.split()[-1] for l in slines]
1194 sfiles = list(filter(os.path.isfile, sentries))
1196 "Not building in a Mercurial or Subversion tree; skipping building src package."
1208 for p in remove_patterns:
1209 sfiles = [s for s in sfiles if not fnmatch.fnmatch(s, p)]
1212 ps = "%s-src" % project
1213 psv = "%s-%s" % (ps, version)
1214 b_ps = os.path.join(build_dir, ps)
1215 b_psv = os.path.join(build_dir, psv)
1216 b_psv_stamp = b_psv + '-stamp'
1218 src_tar_gz = os.path.join(build_dir, 'dist', '%s.tar.gz' % psv)
1219 src_zip = os.path.join(build_dir, 'dist', '%s.zip' % psv)
1221 Local(src_tar_gz, src_zip)
1224 env.SCons_revision(os.path.join(b_ps, file), file)
1226 b_ps_files = [os.path.join(b_ps, x) for x in sfiles]
1233 env.Command(b_psv_stamp, src_deps + b_ps_files, cmds)
1239 env.Command(src_tar_gz, b_psv_stamp,
1240 "tar cz${TAR_HFLAG} -f $TARGET -C build %s" % psv)
1243 # Unpack the archive into build/unpack/scons-{version}.
1245 unpack_tar_gz_files = [os.path.join(unpack_tar_gz_dir, psv, x)
1249 # We'd like to replace the last three lines with the following:
1251 # tar zxf $SOURCES -C $UNPACK_TAR_GZ_DIR
1253 # but that gives heartburn to Cygwin's tar, so work around it
1254 # with separate zcat-tar-rm commands.
1255 env.Command(unpack_tar_gz_files, src_tar_gz, [
1256 Delete(os.path.join(unpack_tar_gz_dir, psv)),
1257 "$ZCAT $SOURCES > .temp",
1258 "tar xf .temp -C $UNPACK_TAR_GZ_DIR",
1263 # Run setup.py in the unpacked subdirectory to "install" everything
1264 # into our build/test subdirectory. The runtest.py script will set
1265 # PYTHONPATH so that the tests only look under build/test-{package},
1266 # and under etc (for the testing modules TestCmd.py, TestSCons.py,
1267 # and unittest.py). This makes sure that our tests pass with what
1268 # we really packaged, not because of something hanging around in
1269 # the development directory.
1271 # We can get away with calling setup.py using a directory path
1272 # like this because we put a preamble in it that will chdir()
1273 # to the directory in which setup.py exists.
1275 dfiles = [os.path.join(test_src_tar_gz_dir, x) for x in dst_files]
1276 scons_lib_dir = os.path.join(unpack_tar_gz_dir, psv, 'src', 'engine')
1277 ENV = env.Dictionary('ENV').copy()
1278 ENV['SCONS_LIB_DIR'] = scons_lib_dir
1279 ENV['USERNAME'] = developer
1280 env.Command(dfiles, unpack_tar_gz_files,
1282 Delete(os.path.join(unpack_tar_gz_dir,
1287 Delete("$TEST_SRC_TAR_GZ_DIR"),
1288 'cd "%s" && $PYTHON $PYTHONFLAGS "%s" "%s" VERSION="$VERSION"' % \
1289 (os.path.join(unpack_tar_gz_dir, psv),
1290 os.path.join('src', 'script', 'scons.py'),
1291 os.path.join('build', 'scons')),
1292 '$PYTHON $PYTHONFLAGS "%s" install "--prefix=$TEST_SRC_TAR_GZ_DIR" --standalone-lib' % \
1293 os.path.join(unpack_tar_gz_dir,
1303 env.Command(src_zip, b_psv_stamp, zipit, CD = 'build', PSV = psv)
1306 # Unpack the archive into build/unpack/scons-{version}.
1308 unpack_zip_files = [os.path.join(unpack_zip_dir, psv, x)
1311 env.Command(unpack_zip_files, src_zip, [
1312 Delete(os.path.join(unpack_zip_dir, psv)),
1317 # Run setup.py in the unpacked subdirectory to "install" everything
1318 # into our build/test subdirectory. The runtest.py script will set
1319 # PYTHONPATH so that the tests only look under build/test-{package},
1320 # and under etc (for the testing modules TestCmd.py, TestSCons.py,
1321 # and unittest.py). This makes sure that our tests pass with what
1322 # we really packaged, not because of something hanging around in
1323 # the development directory.
1325 # We can get away with calling setup.py using a directory path
1326 # like this because we put a preamble in it that will chdir()
1327 # to the directory in which setup.py exists.
1329 dfiles = [os.path.join(test_src_zip_dir, x) for x in dst_files]
1330 scons_lib_dir = os.path.join(unpack_zip_dir, psv, 'src', 'engine')
1331 ENV = env.Dictionary('ENV').copy()
1332 ENV['SCONS_LIB_DIR'] = scons_lib_dir
1333 ENV['USERNAME'] = developer
1334 env.Command(dfiles, unpack_zip_files,
1336 Delete(os.path.join(unpack_zip_dir,
1341 Delete("$TEST_SRC_ZIP_DIR"),
1342 'cd "%s" && $PYTHON $PYTHONFLAGS "%s" "%s" VERSION="$VERSION"' % \
1343 (os.path.join(unpack_zip_dir, psv),
1344 os.path.join('src', 'script', 'scons.py'),
1345 os.path.join('build', 'scons')),
1346 '$PYTHON $PYTHONFLAGS "%s" install "--prefix=$TEST_SRC_ZIP_DIR" --standalone-lib' % \
1347 os.path.join(unpack_zip_dir,
1355 for pf, help_text in packaging_flavors:
1357 os.path.join(build_dir, 'test-'+pf),
1358 os.path.join(build_dir, 'QMTest'),
1359 os.path.join(build_dir, 'runtest.py'),