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 = packaging_flavors + [('doc', 'The SCons documentation.')]
286 for alias, help_text in aliases:
287 tw = textwrap.TextWrapper(
289 initial_indent = indent_fmt % alias,
290 subsequent_indent = indent_fmt % '' + ' ',
292 Help(tw.fill(help_text) + '\n')
295 The following command-line variables can be set:
299 for variable, help_text in command_line_variables:
300 tw = textwrap.TextWrapper(
302 initial_indent = indent_fmt % variable,
303 subsequent_indent = indent_fmt % '' + ' ',
305 Help(tw.fill(help_text) + '\n')
312 # Figure out if we can handle .zip files.
319 def zipit(env, target, source):
320 print "Zipping %s:" % str(target[0])
321 def visit(arg, dirname, names):
323 path = os.path.join(dirname, name)
324 if os.path.isfile(path):
326 zf = zipfile.ZipFile(str(target[0]), 'w')
329 try: os.path.walk(env['PSV'], visit, zf)
330 finally: os.chdir(olddir)
333 def unzipit(env, target, source):
334 print "Unzipping %s:" % str(source[0])
335 zf = zipfile.ZipFile(str(source[0]), 'r')
336 for name in zf.namelist():
337 dest = os.path.join(env['UNPACK_ZIP_DIR'], name)
338 dir = os.path.dirname(dest)
344 # if the file exists, then delete it before writing
345 # to it so that we don't end up trying to write to a symlink:
346 if os.path.isfile(dest) or os.path.islink(dest):
348 if not os.path.isdir(dest):
349 open(dest, 'wb').write(zf.read(name))
353 zipit = "cd $CD && $ZIP $ZIPFLAGS $( ${TARGET.abspath} $) $PSV"
354 unzipit = "$UNZIP $UNZIPFLAGS $SOURCES"
356 def SCons_revision(target, source, env):
357 """Interpolate specific values from the environment into a file.
359 This is used to copy files into a tree that gets packaged up
360 into the source file package.
364 contents = open(s, 'rb').read()
365 # Note: We construct the __*__ substitution strings here
366 # so that they don't get replaced when this file gets
367 # copied into the tree for packaging.
368 contents = contents.replace('__BUILD' + '__', env['BUILD'])
369 contents = contents.replace('__BUILDSYS' + '__', env['BUILDSYS'])
370 contents = contents.replace('__COPYRIGHT' + '__', env['COPYRIGHT'])
371 contents = contents.replace('__DATE' + '__', env['DATE'])
372 contents = contents.replace('__DEVELOPER' + '__', env['DEVELOPER'])
373 contents = contents.replace('__FILE' + '__', str(source[0]))
374 contents = contents.replace('__MONTH_YEAR'+ '__', env['MONTH_YEAR'])
375 contents = contents.replace('__REVISION' + '__', env['REVISION'])
376 contents = contents.replace('__VERSION' + '__', env['VERSION'])
377 contents = contents.replace('__NULL' + '__', '')
378 open(t, 'wb').write(contents)
379 os.chmod(t, os.stat(s)[0])
381 revbuilder = Builder(action = Action(SCons_revision,
382 varlist=['COPYRIGHT', 'VERSION']))
384 def soelim(target, source, env):
386 Interpolate files included in [gnt]roff source files using the
389 This behaves somewhat like the soelim(1) wrapper around groff, but
390 makes us independent of whether the actual underlying implementation
391 includes an soelim() command or the corresponding command-line option
392 to groff(1). The key behavioral difference is that this doesn't
393 recursively include .so files from the include file. Not yet, anyway.
397 dir, f = os.path.split(s)
400 for line in sfp.readlines():
401 if line[:4] in ['.so ', "'so "]:
402 sofile = os.path.join(dir, line[4:-1])
403 tfp.write(open(sofile, 'r').read())
409 def soscan(node, env, path):
410 c = node.get_text_contents()
411 return re.compile(r"^[\.']so\s+(\S+)", re.M).findall(c)
413 soelimbuilder = Builder(action = Action(soelim),
414 source_scanner = Scanner(soscan))
416 # When copying local files from a Repository (Aegis),
417 # just make copies, don't symlink them.
418 SetOption('duplicate', 'copy')
424 BUILDDIR = build_dir,
425 BUILDSYS = build_system,
426 COPYRIGHT = copyright,
428 DEVELOPER = developer,
429 DISTDIR = os.path.join(build_dir, 'dist'),
430 MONTH_YEAR = month_year,
435 TAR_HFLAG = tar_hflag,
440 UNZIPFLAGS = '-o -d $UNPACK_ZIP_DIR',
445 RPM2CPIO = 'rpm2cpio',
447 TEST_DEB_DIR = test_deb_dir,
448 TEST_RPM_DIR = test_rpm_dir,
449 TEST_SRC_TAR_GZ_DIR = test_src_tar_gz_dir,
450 TEST_SRC_ZIP_DIR = test_src_zip_dir,
451 TEST_TAR_GZ_DIR = test_tar_gz_dir,
452 TEST_ZIP_DIR = test_zip_dir,
454 UNPACK_TAR_GZ_DIR = unpack_tar_gz_dir,
455 UNPACK_ZIP_DIR = unpack_zip_dir,
457 BUILDERS = { 'SCons_revision' : revbuilder,
458 'SOElim' : soelimbuilder },
460 PYTHON = '"%s"' % sys.executable,
464 Version_values = [Value(version), Value(build_id)]
467 # Define SCons packages.
469 # In the original, more complicated packaging scheme, we were going
470 # to have separate packages for:
472 # python-scons only the build engine
473 # scons-script only the script
474 # scons the script plus the build engine
476 # We're now only delivering a single "scons" package, but this is still
477 # "built" as two sub-packages (the build engine and the script), so
478 # the definitions remain here, even though we're not using them for
483 'pkg' : 'python-' + project,
484 'src_subdir' : 'engine',
485 'inst_subdir' : os.path.join('lib', 'python1.5', 'site-packages'),
486 'rpm_dir' : '/usr/lib/scons',
499 'files' : [ 'LICENSE.txt',
506 'LICENSE.txt' : '../LICENSE.txt'
511 'extra_rpm_files' : [],
514 'SCons/__init__.py' : Version_values,
518 # Figure out the name of a .egg-info file that might be generated
519 # as part of the RPM package. There are two complicating factors.
521 # First, the RPM spec file we generate will just execute "python", not
522 # necessarily the one in sys.executable. If *that* version of python has
523 # a distutils that knows about Python eggs, then setup.py will generate a
524 # .egg-info file, so we have to execute any distutils logic in a subshell.
526 # Second, we can't just have the subshell check for the existence of the
527 # distutils.command.install_egg_info module and generate the expected
528 # file name by hand, the way we used to, because different systems can
529 # have slightly different .egg-info naming conventions. (Specifically,
530 # Ubuntu overrides the default behavior to remove the Python version
531 # string from the .egg-info file name.) The right way to do this is to
532 # actually call into the install_egg_info() class to have it generate
533 # the expected name for us.
535 # This is all complicated enough that we do it by writing an in-line
536 # script to a temporary file and then feeding it to a separate invocation
537 # of "python" to tell us the actual name of the generated .egg-info file.
539 print_egg_info_name = """
541 from distutils.dist import Distribution
542 from distutils.command.install_egg_info import install_egg_info
546 dist = Distribution({'name' : "scons", 'version' : '%s'})
547 i = install_egg_info(dist)
550 print os.path.split(i.outputs[0])[1]
554 fd, tfname = tempfile.mkstemp()
555 tfp = os.fdopen(fd, "w")
556 tfp.write(print_egg_info_name)
558 egg_info_file = os.popen("python %s" % tfname).read()[:-1]
560 python_scons['extra_rpm_files'].append(egg_info_file)
564 except EnvironmentError:
568 # The original packaging scheme would have have required us to push
569 # the Python version number into the package name (python1.5-scons,
570 # python2.0-scons, etc.), which would have required a definition
571 # like the following. Leave this here in case we ever decide to do
572 # this in the future, but note that this would require some modification
573 # to src/engine/setup.py before it would really work.
576 # 'pkg' : 'python2-' + project,
577 # 'src_subdir' : 'engine',
578 # 'inst_subdir' : os.path.join('lib', 'python2.2', 'site-packages'),
581 # 'debian/changelog',
583 # 'debian/copyright',
598 # 'LICENSE.txt' : '../LICENSE.txt',
605 'pkg' : project + '-script',
606 'src_subdir' : 'script',
607 'inst_subdir' : 'bin',
608 'rpm_dir' : '/usr/bin',
629 'LICENSE.txt' : '../LICENSE.txt',
630 'scons' : 'scons.py',
631 'sconsign' : 'sconsign.py',
632 'scons-time' : 'scons-time.py',
637 'extra_rpm_files' : [
639 'sconsign-' + version,
640 'scons-time-' + version,
644 'scons' : Version_values,
645 'sconsign' : Version_values,
668 'os_spawnv_fix.diff',
673 #'script/scons-post-install.py',
679 'scons.1' : '$BUILDDIR/doc/man/scons.1',
680 'sconsign.1' : '$BUILDDIR/doc/man/sconsign.1',
681 'scons-time.1' : '$BUILDDIR/doc/man/scons-time.1',
685 'scons.1' : env.SOElim,
686 'sconsign.1' : env.SOElim,
687 'scons-time.1' : env.SOElim,
690 'subpkgs' : [ python_scons, scons_script ],
693 'python-' + project : python_project_subinst_dir,
694 project + '-script' : project_script_subinst_dir,
698 scripts = ['scons', 'sconsign', 'scons-time']
705 # Initialize variables with the right directories for this package.
708 pkg_version = "%s-%s" % (pkg, version)
711 if 'src_subdir' in p:
712 src = os.path.join(src, p['src_subdir'])
714 build = os.path.join(build_dir, pkg)
716 tar_gz = os.path.join(build, 'dist', "%s.tar.gz" % pkg_version)
717 platform_tar_gz = os.path.join(build,
719 "%s.%s.tar.gz" % (pkg_version, platform))
720 zip = os.path.join(build, 'dist', "%s.zip" % pkg_version)
721 platform_zip = os.path.join(build,
723 "%s.%s.zip" % (pkg_version, platform))
724 if platform == "win-amd64":
725 win32_exe = os.path.join(build, 'dist', "%s.win-amd64.exe" % pkg_version)
727 win32_exe = os.path.join(build, 'dist', "%s.win32.exe" % pkg_version)
730 # Update the environment with the relevant information
733 # We can get away with calling setup.py using a directory path
734 # like this because we put a preamble in it that will chdir()
735 # to the directory in which setup.py exists.
737 setup_py = os.path.join(build, 'setup.py')
738 env.Replace(PKG = pkg,
739 PKG_VERSION = pkg_version,
740 SETUP_PY = '"%s"' % setup_py)
744 # Read up the list of source files from our MANIFEST.in.
745 # This list should *not* include LICENSE.txt, MANIFEST,
746 # README.txt, or setup.py. Make a copy of the list for the
749 manifest_in = File(os.path.join(src, 'MANIFEST.in')).rstr()
750 src_files = [x[:-1] for x in open(manifest_in).readlines()]
751 raw_files = src_files[:]
752 dst_files = src_files[:]
755 MANIFEST_in_list = []
759 # This package includes some sub-packages. Read up their
760 # MANIFEST.in files, and add them to our source and destination
761 # file lists, modifying them as appropriate to add the
764 for sp in p['subpkgs']:
765 ssubdir = sp['src_subdir']
766 isubdir = p['subinst_dirs'][sp['pkg']]
767 MANIFEST_in = File(os.path.join(src, ssubdir, 'MANIFEST.in')).rstr()
768 MANIFEST_in_list.append(MANIFEST_in)
769 files = [x[:-1] for x in open(MANIFEST_in).readlines()]
770 raw_files.extend(files)
771 src_files.extend([os.path.join(subdir, x) for x in files])
773 r = os.path.join(sp['rpm_dir'], f)
776 rpm_files.append(r + 'c')
777 for f in sp.get('extra_rpm_files', []):
778 r = os.path.join(sp['rpm_dir'], f)
780 files = [os.path.join(isubdir, x) for x in files]
781 dst_files.extend(files)
782 for k, f in sp['filemap'].items():
784 k = os.path.join(ssubdir, k)
785 p['filemap'][k] = os.path.join(ssubdir, f)
786 for f, deps in sp['explicit_deps'].items():
787 f = os.path.join(build, ssubdir, f)
791 # Now that we have the "normal" source files, add those files
792 # that are standard for each distribution. Note that we don't
793 # add these to dst_files, because they don't get installed.
794 # And we still have the MANIFEST to add.
796 src_files.extend(p['files'])
799 # Now run everything in src_file through the sed command we
800 # concocted to expand __FILE__, __VERSION__, etc.
803 s = p['filemap'].get(b, b)
804 if not s[0] == '$' and not os.path.isabs(s):
805 s = os.path.join(src, s)
806 builder = p['buildermap'].get(b, env.SCons_revision)
807 x = builder(os.path.join(build, b), s)
811 # NOW, finally, we can create the MANIFEST, which we do
812 # by having Python spit out the contents of the src_files
813 # array we've carefully created. After we've added
814 # MANIFEST itself to the array, of course.
816 src_files.append("MANIFEST")
817 MANIFEST_in_list.append(os.path.join(src, 'MANIFEST.in'))
819 def write_src_files(target, source, **kw):
822 f = open(str(target[0]), 'wb')
823 for file in src_files:
827 env.Command(os.path.join(build, 'MANIFEST'),
832 # Now go through and arrange to create whatever packages we can.
834 build_src_files = [os.path.join(build, x) for x in src_files]
835 Local(*build_src_files)
837 distutils_formats = []
839 distutils_targets = [ win32_exe ]
841 dist_distutils_targets = env.Install('$DISTDIR', distutils_targets)
842 Local(dist_distutils_targets)
843 AddPostAction(dist_distutils_targets, Chmod(dist_distutils_targets, 0644))
846 print "gzip not found in %s; skipping .tar.gz package for %s." % (os.environ['PATH'], pkg)
849 distutils_formats.append('gztar')
851 src_deps.append(tar_gz)
853 distutils_targets.extend([ tar_gz, platform_tar_gz ])
855 dist_tar_gz = env.Install('$DISTDIR', tar_gz)
856 dist_platform_tar_gz = env.Install('$DISTDIR', platform_tar_gz)
857 Local(dist_tar_gz, dist_platform_tar_gz)
858 AddPostAction(dist_tar_gz, Chmod(dist_tar_gz, 0644))
859 AddPostAction(dist_platform_tar_gz, Chmod(dist_platform_tar_gz, 0644))
862 # Unpack the tar.gz archive created by the distutils into
863 # build/unpack-tar-gz/scons-{version}.
865 # We'd like to replace the last three lines with the following:
867 # tar zxf $SOURCES -C $UNPACK_TAR_GZ_DIR
869 # but that gives heartburn to Cygwin's tar, so work around it
870 # with separate zcat-tar-rm commands.
872 unpack_tar_gz_files = [os.path.join(unpack_tar_gz_dir, pkg_version, x)
874 env.Command(unpack_tar_gz_files, dist_tar_gz, [
875 Delete(os.path.join(unpack_tar_gz_dir, pkg_version)),
876 "$ZCAT $SOURCES > .temp",
877 "tar xf .temp -C $UNPACK_TAR_GZ_DIR",
882 # Run setup.py in the unpacked subdirectory to "install" everything
883 # into our build/test subdirectory. The runtest.py script will set
884 # PYTHONPATH so that the tests only look under build/test-{package},
885 # and under etc (for the testing modules TestCmd.py, TestSCons.py,
886 # and unittest.py). This makes sure that our tests pass with what
887 # we really packaged, not because of something hanging around in
888 # the development directory.
890 # We can get away with calling setup.py using a directory path
891 # like this because we put a preamble in it that will chdir()
892 # to the directory in which setup.py exists.
894 dfiles = [os.path.join(test_tar_gz_dir, x) for x in dst_files]
895 env.Command(dfiles, unpack_tar_gz_files, [
896 Delete(os.path.join(unpack_tar_gz_dir, pkg_version, 'build')),
897 Delete("$TEST_TAR_GZ_DIR"),
898 '$PYTHON $PYTHONFLAGS "%s" install "--prefix=$TEST_TAR_GZ_DIR" --standalone-lib' % \
899 os.path.join(unpack_tar_gz_dir, pkg_version, 'setup.py'),
903 # Generate portage files for submission to Gentoo Linux.
905 gentoo = os.path.join(build, 'gentoo')
906 ebuild = os.path.join(gentoo, 'scons-%s.ebuild' % version)
907 digest = os.path.join(gentoo, 'files', 'digest-scons-%s' % version)
908 env.Command(ebuild, os.path.join('gentoo', 'scons.ebuild.in'), SCons_revision)
909 def Digestify(target, source, env):
911 src = source[0].rfile()
912 contents = open(str(src)).read()
913 sig = md5.new(contents).hexdigest()
914 bytes = os.stat(str(src))[6]
915 open(str(target[0]), 'w').write("MD5 %s %s %d\n" % (sig,
918 env.Command(digest, tar_gz, Digestify)
921 print "zip not found; skipping .zip package for %s." % pkg
924 distutils_formats.append('zip')
928 distutils_targets.extend([ zip, platform_zip ])
930 dist_zip = env.Install('$DISTDIR', zip)
931 dist_platform_zip = env.Install('$DISTDIR', platform_zip)
932 Local(dist_zip, dist_platform_zip)
933 AddPostAction(dist_zip, Chmod(dist_zip, 0644))
934 AddPostAction(dist_platform_zip, Chmod(dist_platform_zip, 0644))
937 # Unpack the zip archive created by the distutils into
938 # build/unpack-zip/scons-{version}.
940 unpack_zip_files = [os.path.join(unpack_zip_dir, pkg_version, x)
943 env.Command(unpack_zip_files, dist_zip, [
944 Delete(os.path.join(unpack_zip_dir, pkg_version)),
949 # Run setup.py in the unpacked subdirectory to "install" everything
950 # into our build/test subdirectory. The runtest.py script will set
951 # PYTHONPATH so that the tests only look under build/test-{package},
952 # and under etc (for the testing modules TestCmd.py, TestSCons.py,
953 # and unittest.py). This makes sure that our tests pass with what
954 # we really packaged, not because of something hanging around in
955 # the development directory.
957 # We can get away with calling setup.py using a directory path
958 # like this because we put a preamble in it that will chdir()
959 # to the directory in which setup.py exists.
961 dfiles = [os.path.join(test_zip_dir, x) for x in dst_files]
962 env.Command(dfiles, unpack_zip_files, [
963 Delete(os.path.join(unpack_zip_dir, pkg_version, 'build')),
964 Delete("$TEST_ZIP_DIR"),
965 '$PYTHON $PYTHONFLAGS "%s" install "--prefix=$TEST_ZIP_DIR" --standalone-lib' % \
966 os.path.join(unpack_zip_dir, pkg_version, 'setup.py'),
970 msg = "@echo \"Warning: Can not build 'rpm': no rpmbuild utility found\""
971 AlwaysBuild(Alias('rpm', [], msg))
973 topdir = os.path.join(build, 'build',
974 'bdist.' + platform, 'rpm')
976 buildroot = os.path.join(build_dir, 'rpm-buildroot')
978 BUILDdir = os.path.join(topdir, 'BUILD', pkg + '-' + version)
979 RPMSdir = os.path.join(topdir, 'RPMS', 'noarch')
980 SOURCESdir = os.path.join(topdir, 'SOURCES')
981 SPECSdir = os.path.join(topdir, 'SPECS')
982 SRPMSdir = os.path.join(topdir, 'SRPMS')
984 specfile_in = os.path.join('rpm', "%s.spec.in" % pkg)
985 specfile = os.path.join(SPECSdir, "%s-1.spec" % pkg_version)
986 sourcefile = os.path.join(SOURCESdir, "%s.tar.gz" % pkg_version);
987 noarch_rpm = os.path.join(RPMSdir, "%s-1.noarch.rpm" % pkg_version)
988 src_rpm = os.path.join(SRPMSdir, "%s-1.src.rpm" % pkg_version)
990 def spec_function(target, source, env):
991 """Generate the RPM .spec file from the template file.
993 This fills in the %files portion of the .spec file with a
994 list generated from our MANIFEST(s), so we don't have to
995 maintain multiple lists.
997 c = open(str(source[0]), 'rb').read()
998 c = c.replace('__VERSION' + '__', env['VERSION'])
999 c = c.replace('__RPM_FILES' + '__', env['RPM_FILES'])
1000 open(str(target[0]), 'wb').write(c)
1003 rpm_files_str = "\n".join(rpm_files) + "\n"
1004 rpm_spec_env = env.Clone(RPM_FILES = rpm_files_str)
1005 rpm_spec_action = Action(spec_function, varlist=['RPM_FILES'])
1006 rpm_spec_env.Command(specfile, specfile_in, rpm_spec_action)
1008 env.InstallAs(sourcefile, tar_gz)
1011 targets = [ noarch_rpm, src_rpm ]
1012 cmd = "$RPMBUILD --define '_topdir $(%s$)' --buildroot %s -ba $SOURCES" % (topdir, buildroot)
1013 if not os.path.isdir(BUILDdir):
1014 cmd = ("$( mkdir -p %s; $)" % BUILDdir) + cmd
1015 t = env.Command(targets, specfile, cmd)
1016 env.Depends(t, sourcefile)
1018 dist_noarch_rpm = env.Install('$DISTDIR', noarch_rpm)
1019 dist_src_rpm = env.Install('$DISTDIR', src_rpm)
1020 Local(dist_noarch_rpm, dist_src_rpm)
1021 AddPostAction(dist_noarch_rpm, Chmod(dist_noarch_rpm, 0644))
1022 AddPostAction(dist_src_rpm, Chmod(dist_src_rpm, 0644))
1024 dfiles = [os.path.join(test_rpm_dir, 'usr', x) for x in dst_files]
1027 "$RPM2CPIO $SOURCES | (cd $TEST_RPM_DIR && cpio -id)")
1029 if dh_builddeb and fakeroot:
1030 # Our Debian packaging builds directly into build/dist,
1031 # so we don't need to Install() the .debs.
1032 deb = os.path.join(build_dir, 'dist', "%s_%s-1_all.deb" % (pkg, version))
1033 for d in p['debian_deps']:
1034 b = env.SCons_revision(os.path.join(build, d), d)
1037 env.Command(deb, build_src_files, [
1038 "cd %s && fakeroot make -f debian/rules PYTHON=$PYTHON BUILDDEB_OPTIONS=--destdir=../../build/dist binary" % build,
1041 old = os.path.join('lib', 'scons', '')
1042 new = os.path.join('lib', 'python' + python_ver, 'site-packages', '')
1043 def xxx(s, old=old, new=new):
1044 if s[:len(old)] == old:
1045 s = new + s[len(old):]
1046 return os.path.join('usr', s)
1047 dfiles = [os.path.join(test_deb_dir, xxx(x)) for x in dst_files]
1050 "dpkg --fsys-tarfile $SOURCES | (cd $TEST_DEB_DIR && tar -xf -)")
1054 # Use the Python distutils to generate the appropriate packages.
1057 Delete(os.path.join(build, 'build', 'lib')),
1058 Delete(os.path.join(build, 'build', 'scripts')),
1061 if distutils_formats:
1062 commands.append(Delete(os.path.join(build,
1064 'bdist.' + platform,
1066 for format in distutils_formats:
1067 commands.append("$PYTHON $PYTHONFLAGS $SETUP_PY bdist_dumb -f %s" % format)
1069 commands.append("$PYTHON $PYTHONFLAGS $SETUP_PY sdist --formats=%s" % \
1070 ','.join(distutils_formats))
1072 commands.append("$PYTHON $PYTHONFLAGS $SETUP_PY bdist_wininst")
1074 env.Command(distutils_targets, build_src_files, commands)
1077 # Now create local packages for people who want to let people
1078 # build their SCons-buildable packages without having to
1081 s_l_v = '%s-local-%s' % (pkg, version)
1083 local = pkg + '-local'
1084 build_dir_local = os.path.join(build_dir, local)
1085 build_dir_local_slv = os.path.join(build_dir, local, s_l_v)
1087 dist_local_tar_gz = os.path.join("$DISTDIR/%s.tar.gz" % s_l_v)
1088 dist_local_zip = os.path.join("$DISTDIR/%s.zip" % s_l_v)
1089 AddPostAction(dist_local_tar_gz, Chmod(dist_local_tar_gz, 0644))
1090 AddPostAction(dist_local_zip, Chmod(dist_local_zip, 0644))
1093 Delete(build_dir_local),
1094 '$PYTHON $PYTHONFLAGS $SETUP_PY install "--install-script=%s" "--install-lib=%s" --no-install-man --no-compile --standalone-lib --no-version-script' % \
1095 (build_dir_local, build_dir_local_slv),
1098 for script in scripts:
1099 #commands.append("mv %s/%s %s/%s.py" % (local, script, local, script))
1100 local_script = os.path.join(build_dir_local, script)
1101 commands.append(Move(local_script + '.py', local_script))
1103 rf = [x for x in raw_files if not x in scripts]
1104 rf = [os.path.join(s_l_v, x) for x in rf]
1105 for script in scripts:
1106 rf.append("%s.py" % script)
1107 local_targets = [os.path.join(build_dir_local, x) for x in rf]
1109 env.Command(local_targets, build_src_files, commands)
1111 scons_LICENSE = os.path.join(build_dir_local, 'scons-LICENSE')
1112 l = env.SCons_revision(scons_LICENSE, 'LICENSE-local')
1113 local_targets.append(l)
1116 scons_README = os.path.join(build_dir_local, 'scons-README')
1117 l = env.SCons_revision(scons_README, 'README-local')
1118 local_targets.append(l)
1122 env.Command(dist_local_tar_gz,
1124 "cd %s && tar czf $( ${TARGET.abspath} $) *" % build_dir_local)
1126 unpack_targets = [os.path.join(test_local_tar_gz_dir, x) for x in rf]
1127 commands = [Delete(test_local_tar_gz_dir),
1128 Mkdir(test_local_tar_gz_dir),
1129 "cd %s && tar xzf $( ${SOURCE.abspath} $)" % test_local_tar_gz_dir]
1131 env.Command(unpack_targets, dist_local_tar_gz, commands)
1134 env.Command(dist_local_zip, local_targets, zipit,
1135 CD = build_dir_local, PSV = '.')
1137 unpack_targets = [os.path.join(test_local_zip_dir, x) for x in rf]
1138 commands = [Delete(test_local_zip_dir),
1139 Mkdir(test_local_zip_dir),
1142 env.Command(unpack_targets, dist_local_zip, unzipit,
1143 UNPACK_ZIP_DIR = test_local_zip_dir)
1148 Export('build_dir', 'env')
1150 SConscript('QMTest/SConscript')
1159 def copy(target, source, env):
1162 open(t, 'wb').write(open(s, 'rb').read())
1165 # Guarantee that real copies of these files always exist in
1166 # build/. If there's a symlink there, then this is an Aegis
1167 # build and we blow them away now so that they'll get "built" later.
1168 p = os.path.join(build_dir, file)
1169 if os.path.islink(p):
1171 if not os.path.isabs(p):
1173 sp = env.Command(p, file, copy)
1179 Export('build_dir', 'env', 'whereis')
1181 SConscript('doc/SConscript')
1184 # If we're running in a Subversion working directory, pack up a complete
1185 # source archive from the project files and files in the change.
1190 slines = [l for l in hg_status_lines if l[0] in 'ACM']
1191 sfiles = [l.split()[-1] for l in slines]
1192 elif svn_status_lines:
1193 slines = [l for l in svn_status_lines if l[0] in ' MA']
1194 sentries = [l.split()[-1] for l in slines]
1195 sfiles = list(filter(os.path.isfile, sentries))
1197 "Not building in a Mercurial or Subversion tree; skipping building src package."
1209 for p in remove_patterns:
1210 sfiles = [s for s in sfiles if not fnmatch.fnmatch(s, p)]
1213 ps = "%s-src" % project
1214 psv = "%s-%s" % (ps, version)
1215 b_ps = os.path.join(build_dir, ps)
1216 b_psv = os.path.join(build_dir, psv)
1217 b_psv_stamp = b_psv + '-stamp'
1219 src_tar_gz = os.path.join(build_dir, 'dist', '%s.tar.gz' % psv)
1220 src_zip = os.path.join(build_dir, 'dist', '%s.zip' % psv)
1222 Local(src_tar_gz, src_zip)
1225 env.SCons_revision(os.path.join(b_ps, file), file)
1227 b_ps_files = [os.path.join(b_ps, x) for x in sfiles]
1234 env.Command(b_psv_stamp, src_deps + b_ps_files, cmds)
1240 env.Command(src_tar_gz, b_psv_stamp,
1241 "tar cz${TAR_HFLAG} -f $TARGET -C build %s" % psv)
1244 # Unpack the archive into build/unpack/scons-{version}.
1246 unpack_tar_gz_files = [os.path.join(unpack_tar_gz_dir, psv, x)
1250 # We'd like to replace the last three lines with the following:
1252 # tar zxf $SOURCES -C $UNPACK_TAR_GZ_DIR
1254 # but that gives heartburn to Cygwin's tar, so work around it
1255 # with separate zcat-tar-rm commands.
1256 env.Command(unpack_tar_gz_files, src_tar_gz, [
1257 Delete(os.path.join(unpack_tar_gz_dir, psv)),
1258 "$ZCAT $SOURCES > .temp",
1259 "tar xf .temp -C $UNPACK_TAR_GZ_DIR",
1264 # Run setup.py in the unpacked subdirectory to "install" everything
1265 # into our build/test subdirectory. The runtest.py script will set
1266 # PYTHONPATH so that the tests only look under build/test-{package},
1267 # and under etc (for the testing modules TestCmd.py, TestSCons.py,
1268 # and unittest.py). This makes sure that our tests pass with what
1269 # we really packaged, not because of something hanging around in
1270 # the development directory.
1272 # We can get away with calling setup.py using a directory path
1273 # like this because we put a preamble in it that will chdir()
1274 # to the directory in which setup.py exists.
1276 dfiles = [os.path.join(test_src_tar_gz_dir, x) for x in dst_files]
1277 scons_lib_dir = os.path.join(unpack_tar_gz_dir, psv, 'src', 'engine')
1278 ENV = env.Dictionary('ENV').copy()
1279 ENV['SCONS_LIB_DIR'] = scons_lib_dir
1280 ENV['USERNAME'] = developer
1281 env.Command(dfiles, unpack_tar_gz_files,
1283 Delete(os.path.join(unpack_tar_gz_dir,
1288 Delete("$TEST_SRC_TAR_GZ_DIR"),
1289 'cd "%s" && $PYTHON $PYTHONFLAGS "%s" "%s" VERSION="$VERSION"' % \
1290 (os.path.join(unpack_tar_gz_dir, psv),
1291 os.path.join('src', 'script', 'scons.py'),
1292 os.path.join('build', 'scons')),
1293 '$PYTHON $PYTHONFLAGS "%s" install "--prefix=$TEST_SRC_TAR_GZ_DIR" --standalone-lib' % \
1294 os.path.join(unpack_tar_gz_dir,
1304 env.Command(src_zip, b_psv_stamp, zipit, CD = 'build', PSV = psv)
1307 # Unpack the archive into build/unpack/scons-{version}.
1309 unpack_zip_files = [os.path.join(unpack_zip_dir, psv, x)
1312 env.Command(unpack_zip_files, src_zip, [
1313 Delete(os.path.join(unpack_zip_dir, psv)),
1318 # Run setup.py in the unpacked subdirectory to "install" everything
1319 # into our build/test subdirectory. The runtest.py script will set
1320 # PYTHONPATH so that the tests only look under build/test-{package},
1321 # and under etc (for the testing modules TestCmd.py, TestSCons.py,
1322 # and unittest.py). This makes sure that our tests pass with what
1323 # we really packaged, not because of something hanging around in
1324 # the development directory.
1326 # We can get away with calling setup.py using a directory path
1327 # like this because we put a preamble in it that will chdir()
1328 # to the directory in which setup.py exists.
1330 dfiles = [os.path.join(test_src_zip_dir, x) for x in dst_files]
1331 scons_lib_dir = os.path.join(unpack_zip_dir, psv, 'src', 'engine')
1332 ENV = env.Dictionary('ENV').copy()
1333 ENV['SCONS_LIB_DIR'] = scons_lib_dir
1334 ENV['USERNAME'] = developer
1335 env.Command(dfiles, unpack_zip_files,
1337 Delete(os.path.join(unpack_zip_dir,
1342 Delete("$TEST_SRC_ZIP_DIR"),
1343 'cd "%s" && $PYTHON $PYTHONFLAGS "%s" "%s" VERSION="$VERSION"' % \
1344 (os.path.join(unpack_zip_dir, psv),
1345 os.path.join('src', 'script', 'scons.py'),
1346 os.path.join('build', 'scons')),
1347 '$PYTHON $PYTHONFLAGS "%s" install "--prefix=$TEST_SRC_ZIP_DIR" --standalone-lib' % \
1348 os.path.join(unpack_zip_dir,
1356 for pf, help_text in packaging_flavors:
1358 os.path.join(build_dir, 'test-'+pf),
1359 os.path.join(build_dir, 'QMTest'),
1360 os.path.join(build_dir, 'runtest.py'),