From: stevenknight Date: Sun, 15 Jul 2007 13:42:20 +0000 (+0000) Subject: Merged revisions 1502-1677,1679-1682,1684-1918,1920-1968,1970-2116,2118-2125,2127... X-Git-Url: http://git.tremily.us/?a=commitdiff_plain;h=a757d747fd9d9d6b181dd1dfbe037f6d90909472;p=scons.git Merged revisions 1502-1677,1679-1682,1684-1918,1920-1968,1970-2116,2118-2125,2127-2136,2138 via svnmerge from http://scons.tigris.org/svn/scons/branches/packaging ................ r1509 | pscholl | 2006-05-30 13:33:58 -0500 (Tue, 30 May 2006) | 5 lines The first commit: * a failing testcase. * a Package() Builder. ................ r1510 | pscholl | 2006-05-31 15:39:42 -0500 (Wed, 31 May 2006) | 4 lines The test passes, so this doesn't do much but calling the Tar Builder through a different interface. ................ r1511 | pscholl | 2006-06-01 15:18:21 -0500 (Thu, 01 Jun 2006) | 3 lines Add targz and tarbz2 packagers. ................ r1512 | pscholl | 2006-06-02 14:16:09 -0500 (Fri, 02 Jun 2006) | 4 lines * package name generation from "projectname" and "version" * targz and tarbz2 packager ................ r1513 | pscholl | 2006-06-02 14:24:53 -0500 (Fri, 02 Jun 2006) | 4 lines * forgot the test for package name generation. * add the zip packager. ................ r1514 | pscholl | 2006-06-10 15:02:50 -0500 (Sat, 10 Jun 2006) | 6 lines * add support for "subdir". * add support for more than one "type" of packager. * add more testcases. * add an example/. ................ r1516 | pscholl | 2006-06-19 14:51:44 -0500 (Mon, 19 Jun 2006) | 2 lines * add a simple rpm testcase * activate the builddir testcase ................ r1519 | pscholl | 2006-06-22 05:38:40 -0500 (Thu, 22 Jun 2006) | 7 lines * add a rpm Tool. * add a rpm Packager abstraction. * add a new Error Class: EnvironmentError, for errors where the Environment doesn't supply neccessary tools. * Change Environment to include the rpm tool per default. ................ r1520 | pscholl | 2006-06-23 13:11:23 -0500 (Fri, 23 Jun 2006) | 5 lines * create a fake_root_emitter, which puts source file under a new root * create a rpm_spec_file emitter and actions to create a rpm specfile from a list of files and a dict with specific keywords. ................ r1521 | pscholl | 2006-06-23 16:19:11 -0500 (Fri, 23 Jun 2006) | 8 lines * fix the testcase/example. Problem: Proper Installation needs to be done form rpm. Proper does mean that at least the "Installation Prefix" of the install target can be specified. * add an assertion to the rpm builder, that the target of the builder are the files created by the rpmbuild call. * hack the rpm_emitter to add possibly created targets of the rpmbuild call. ................ r1522 | pscholl | 2006-06-24 16:41:08 -0500 (Sat, 24 Jun 2006) | 11 lines * make create_default_target() able to call a create_default_target() in a chosen packager. This makes it possible to change the default targets for rpm for example. * rename fake_root to package_root * rename subdir argument to package_root * add method create_default_package_root() ................ r1523 | pscholl | 2006-06-25 09:12:45 -0500 (Sun, 25 Jun 2006) | 4 lines * make international tags work. * able to build a rpms iff with a highly specialized "install" target ................ r1524 | pscholl | 2006-06-25 09:55:00 -0500 (Sun, 25 Jun 2006) | 3 lines * fix older testcases and change to new api ................ r1525 | pscholl | 2006-06-25 13:48:11 -0500 (Sun, 25 Jun 2006) | 1 line set the rpm _topdir to something under the current build directory, so /usr/src/redhat does not need to be writable ................ r1526 | pscholl | 2006-06-27 09:16:31 -0500 (Tue, 27 Jun 2006) | 4 lines * towards file tagging * seperated source and binary packages automatically. ................ r1527 | pscholl | 2006-06-27 15:37:24 -0500 (Tue, 27 Jun 2006) | 1 line better layout for specfile ................ r1528 | pscholl | 2006-06-28 02:43:29 -0500 (Wed, 28 Jun 2006) | 3 lines use copy function instead of InstallAs for package_root_emitter() ................ r1529 | pscholl | 2006-06-28 06:40:17 -0500 (Wed, 28 Jun 2006) | 6 lines * add a Tag() Builder to add packager tag to files. * add examples for the packaging builder. * prevent SCons from showing the stderr pipe from rpmbuild. * clarify the compile() function of rpm specfile builder. ................ r1530 | pscholl | 2006-06-28 09:31:28 -0500 (Wed, 28 Jun 2006) | 3 lines encoding ... ................ r1531 | pscholl | 2006-07-02 10:35:34 -0500 (Sun, 02 Jul 2006) | 8 lines * introduce a DESTDIR command line arguments that is honored by the Install() builders. Major caveat: SCons calculates default targets from the current working directory. So if DESTDIR is outside of cwd, those files will not be installed unless part of their path is given as a target ("/" will not work) * adapt the rpm specfile accordingly. ................ r1532 | pscholl | 2006-07-09 15:52:58 -0500 (Sun, 09 Jul 2006) | 4 lines Possible to generate WiX xml files and using the wix toolset from http://wix.sf.net/ it is possible to create non-interactive installs. ................ r1545 | pscholl | 2006-07-26 02:34:38 -0500 (Wed, 26 Jul 2006) | 6 lines * kill the packageversion tag. * possible to create msi packages with the default Wix_mondo gui. * msi honours the installation location of a File. * x_msi_feature tag is available to split the package up into several parts. ................ r1548 | pscholl | 2006-07-27 10:32:48 -0500 (Thu, 27 Jul 2006) | 4 lines * fix testcases * fix wix toolchain lookup ................ r1549 | pscholl | 2006-07-27 11:13:58 -0500 (Thu, 27 Jul 2006) | 4 lines * add license and license_tag file tags. * add an example for msi package building. ................ r1591 | pscholl | 2006-08-14 13:32:07 -0500 (Mon, 14 Aug 2006) | 6 lines Packaging framework refactoring: * move common code to a superclass. * Create BinaryPackager and SourcePackager. * move TagCompiler from rpm to package.py ................ r1592 | pscholl | 2006-08-14 13:42:39 -0500 (Mon, 14 Aug 2006) | 3 lines change DESTDIR to --install-sandbox ................ r1593 | pscholl | 2006-08-14 14:18:59 -0500 (Mon, 14 Aug 2006) | 1 line fix msi builder. ................ r1595 | pscholl | 2006-08-15 09:19:05 -0500 (Tue, 15 Aug 2006) | 3 lines add a ipkg packager. ................ r1596 | pscholl | 2006-08-15 12:09:17 -0500 (Tue, 15 Aug 2006) | 3 lines small fix for ipk. ................ r1597 | pscholl | 2006-08-15 12:10:13 -0500 (Tue, 15 Aug 2006) | 3 lines add some forgotten tests. ................ r1598 | pscholl | 2006-08-16 08:49:19 -0500 (Wed, 16 Aug 2006) | 11 lines * add an ipk packaging example * fix outstanding problems with ipk: * split the strip_install_emitter, which strips the InstallBuilder of the given source list and adds the install_location tag, from the specfile_emitter. * therefore the .ipk file is now independent of the *installed* files. * add a 'architecture' PackageTag for ipk and rpm packages. * rpm support is currently not working. ................ r1599 | pscholl | 2006-08-16 08:59:39 -0500 (Wed, 16 Aug 2006) | 3 lines rename x_ipk_architecture -> architecture ................ r1601 | pscholl | 2006-08-17 04:35:00 -0500 (Thu, 17 Aug 2006) | 7 lines First implementation of new Installer: * is split off as a Tool in Tool/install.py and added to the 'default' tools. * honors the --install-sandbox option * fix the rpm package building in the process ................ r1602 | pscholl | 2006-08-17 15:20:35 -0500 (Thu, 17 Aug 2006) | 1 line change default INSTALLSTR ................ r1603 | pscholl | 2006-08-17 16:28:44 -0500 (Thu, 17 Aug 2006) | 6 lines * strip Install_old and InstallAs_old from Environment.py * fix the source package building part of the rpm builder. * add '--package-type=' option. * add 'package' default alias. ................ r1604 | pscholl | 2006-08-17 16:39:12 -0500 (Thu, 17 Aug 2006) | 3 lines * forgot the test ................ r1605 | pscholl | 2006-08-17 17:37:07 -0500 (Thu, 17 Aug 2006) | 5 lines * introduce 'src_zip', 'src_tarbz2', 'src_targz' packagers. Their difference to 'zip', 'tarbz2', 'targz' is an emitter that only accepts files that have an 'InstallBuilder' attached. ................ r1606 | pscholl | 2006-08-17 17:58:54 -0500 (Thu, 17 Aug 2006) | 4 lines * rename x_ipk_source and x_ipk_source to source_url. * let rpm honor source_url accordingly. ................ r1607 | pscholl | 2006-08-18 09:35:53 -0500 (Fri, 18 Aug 2006) | 7 lines * merge with trunk from r1501 to 1594 * add Environment.FindInstalledFiles(), which returns the env['_INSTALLEDFILES'] list. * add Environment.FindSourceFiles() which tries to find all files by inspecting the '.' directory. ................ r1608 | pscholl | 2006-08-19 09:04:15 -0500 (Sat, 19 Aug 2006) | 3 lines Create a Tool/filesystem.py, which creates the CopyTo() and CopyAs() builders. ................ r1609 | pscholl | 2006-08-19 09:51:50 -0500 (Sat, 19 Aug 2006) | 5 lines add a 'change_specfile' packagetag which takes a function, that will be called with the target and source parameters to enable the user to change the specfile after it has been generated. ................ r1610 | pscholl | 2006-08-19 09:58:42 -0500 (Sat, 19 Aug 2006) | 1 line fix msi packaging ................ r1612 | pscholl | 2006-08-20 12:03:28 -0500 (Sun, 20 Aug 2006) | 1 line To fix issues with a set BuildDir, copy all tag information to the srcnode() of the file the tags are attached to. ................ r1613 | pscholl | 2006-08-20 15:20:12 -0500 (Sun, 20 Aug 2006) | 3 lines last minute fixes. ................ r1614 | pscholl | 2006-08-20 21:38:02 -0500 (Sun, 20 Aug 2006) | 1 line fix some smaller problems with msi. ................ r1615 | pscholl | 2006-08-20 21:44:04 -0500 (Sun, 20 Aug 2006) | 1 line get rid of ambiguous shortname warnings. ................ r1616 | pscholl | 2006-08-21 05:55:10 -0500 (Mon, 21 Aug 2006) | 3 lines change conf filetag in ipk to config ................ r1618 | pscholl | 2006-08-22 06:09:52 -0500 (Tue, 22 Aug 2006) | 1 line fix bug where no builder is attached to source files ................ r1624 | pscholl | 2006-09-09 07:14:40 -0500 (Sat, 09 Sep 2006) | 35 lines * move stip_abs.path() to SCons.Util * Optik.option_parsers does not raise an error on interspersed option that start with '--' or '-', while loosing the capability to raise an error on a given commandline option that is not defined. * change Tool/install.py to use the builder functions which were in Environment.py * Tools might now define their own command line options, by modifying a env-wide Options() object. Their help text is automatically generated for each Tool initialization. For example running 'scons -h' in a directory with a SConstruct that uses the install builder gives this result: scons: Reading SConscript files ... scons: done reading SConscript files. DESTDIR: A directory under which all installed files will be placed. ( /path/to/DESTDIR ) default: None actual: None aliases: ['--install-sandbox'] prefix: The prefix which can be configured for every installed files. ( /path/to/prefix ) default: /usr/local actual: /usr/local aliases: ['--install-prefix'] Use scons -H for help about command-line options. * fix packaging and filesystem from unneeded references to install internals. ................ r1669 | stevenknight | 2006-11-02 12:33:48 -0600 (Thu, 02 Nov 2006) | 2 lines Ignore the *.pyc files. ................ r1670 | stevenknight | 2006-11-02 19:42:36 -0600 (Thu, 02 Nov 2006) | 2 lines Changes to fix various test failures on the packaging branch. ................ r1671 | stevenknight | 2006-11-03 00:00:59 -0600 (Fri, 03 Nov 2006) | 2 lines Map the i[456]86 flavors to i386 for RPM testing purposes. ................ r1679 | pscholl | 2006-11-04 05:34:44 -0600 (Sat, 04 Nov 2006) | 14 lines * exclude the packaging from the default tool listing, change tests accordingly. * remove get_tags and set_tags method from FS/Node.py, replaced by hasattr(), setattr() and getattr() * rename strip_abs_path to make_path_relative * make tool initilisation only try to zipimport if the exception thrown is "No module named ". This way you get the real error that causes problems importing the module. * Start the packaging.xml doc ................ r1680 | pscholl | 2006-11-04 05:42:12 -0600 (Sat, 04 Nov 2006) | 1 line fix the filesystem tool ................ r1681 | pscholl | 2006-11-04 07:51:11 -0600 (Sat, 04 Nov 2006) | 3 lines Packaging -> packaging ................ r1682 | pscholl | 2006-11-04 07:52:58 -0600 (Sat, 04 Nov 2006) | 3 lines somehow I managed to let svn forget about those file through a rename ;) ................ r1685 | pscholl | 2006-11-05 03:42:52 -0600 (Sun, 05 Nov 2006) | 3 lines Those have been missing on the last commit ................ r1686 | pscholl | 2006-11-05 15:12:13 -0600 (Sun, 05 Nov 2006) | 3 lines add test skipping ................ r1734 | pscholl | 2006-12-15 09:07:51 -0600 (Fri, 15 Dec 2006) | 5 lines * change rpm, tarbz, targz, ipk, zip packager to new api * instead of hardcoding the packager type mapping use a __all__ of the packaging submodule. ................ r1911 | pscholl | 2007-05-19 09:13:57 -0500 (Sat, 19 May 2007) | 10 lines * convert Package() build parameters to uppercase (incomplete) * let optik_parser pass undefined command line options starting with "-" and "--" to the Options() parses, instead of failing with a undefined option error. * Small fix for a subsequent bug in handling gnu long opts, see comment in code. ................ r1912 | stevenknight | 2007-05-19 10:21:45 -0500 (Sat, 19 May 2007) | 4 lines Minor fixes for Python 1.5 (no list comprehensions, no +=, string.split() instead of .split() string method). Python 2.x code has been left in place, commented out, for when we drop 1.5. ................ r1913 | stevenknight | 2007-05-19 21:40:51 -0500 (Sat, 19 May 2007) | 2 lines Remove the leftover "import SCons.Tool.Packaging" line so tests can pass. ................ r1915 | pscholl | 2007-05-20 10:00:20 -0500 (Sun, 20 May 2007) | 6 lines * uppercasing construction variables complete. change tests accordingly. * start of the man page doc xml, see SCons/Tool/packaging/__init.xml, which lists all construction variables and all builders. ................ r1918 | stevenknight | 2007-05-21 07:20:46 -0500 (Mon, 21 May 2007) | 4 lines Propagate a fix from the trunk: prepend './' when interpreting the file name to install so we don't treat a file actually named '#...' as being relative to the top-level SConstruct directory. ................ r1922 | pscholl | 2007-05-22 18:16:38 -0500 (Tue, 22 May 2007) | 3 lines fix CopyTo builder. ................ r1923 | pscholl | 2007-05-22 19:02:33 -0500 (Tue, 22 May 2007) | 1 line fix the try block ................ r1926 | stevenknight | 2007-05-22 22:45:48 -0500 (Tue, 22 May 2007) | 747 lines Merged revisions 1679-1917 via svnmerge from http://scons.tigris.org/svn/scons/trunk (Not completely clean; some tests still fail. The failures are the same, though, before and after the merge.) ................ r1684 | stevenknight | 2006-11-04 14:26:57 -0600 (Sat, 04 Nov 2006) | 3 lines Update the instructions to correct a few errors in setting up svnmerge on branches. Add a few clarifying comments. ................ r1688 | stevenknight | 2006-11-06 13:27:39 -0600 (Mon, 06 Nov 2006) | 2 lines Change announcement etc. for 0.96.93. ................ r1738 | stevenknight | 2006-12-15 19:43:01 -0600 (Fri, 15 Dec 2006) | 195 lines Merged revisions 1675-1736 via svnmerge from http://scons.tigris.org/svn/scons/branches/core ........ r1689 | stevenknight | 2006-11-06 20:56:29 -0600 (Mon, 06 Nov 2006) | 1 line 0.96.D483 - Merge changes for 0.96.93 packaging from the subsidiary branch. ........ r1690 | stevenknight | 2006-11-06 20:59:30 -0600 (Mon, 06 Nov 2006) | 1 line 0.96.D484 - Update HOWTO for releases. Fix name type in src/CHANGES.txt. ........ r1691 | stevenknight | 2006-11-08 13:55:36 -0600 (Wed, 08 Nov 2006) | 1 line 0.96.D485 - Fix MergeFlags() handling of None values. (John Pye) ........ r1692 | stevenknight | 2006-11-08 17:15:05 -0600 (Wed, 08 Nov 2006) | 1 line 0.96.D486 - Directly execute commands on Windows when possible. (Jay Kint) ........ r1693 | stevenknight | 2006-11-08 18:54:49 -0600 (Wed, 08 Nov 2006) | 1 line 0.96.D487 - Remove the semi-colon from the list of characters that determine when we use cmd ........ r1694 | stevenknight | 2006-11-09 01:34:06 -0600 (Thu, 09 Nov 2006) | 1 line 0.96.D488 - Pick up latex/bibtex 'Rerun to get citations correct' messages. (Dmitry Mikhin) ........ r1695 | stevenknight | 2006-11-11 08:36:33 -0600 (Sat, 11 Nov 2006) | 1 line 0.96.D489 - Back out the direct-execution-on-Windows change until we solve a corner case. ........ r1696 | stevenknight | 2006-11-15 10:33:10 -0600 (Wed, 15 Nov 2006) | 1 line 0.96.D490 - Fix the sconsign script when the .sconsign.dblite file is specified with its suf ........ r1697 | stevenknight | 2006-11-18 10:45:50 -0600 (Sat, 18 Nov 2006) | 4 lines Complete move of test/sconsign/script.py to underneath test/sconsign/script/. (This got left out of the previous checkin due to an error in the script that resubmits Aegis changes to Subversion.) ........ r1698 | stevenknight | 2006-11-18 11:05:26 -0600 (Sat, 18 Nov 2006) | 1 line 0.96.D491 - Allow an Options converter to take the construction environment as a parameter. ........ r1699 | stevenknight | 2006-11-30 15:34:37 -0600 (Thu, 30 Nov 2006) | 1 line 0.96.D492 - Reverse the order in which we try the arguments Options converters, first a sing ........ r1700 | stevenknight | 2006-11-30 16:03:09 -0600 (Thu, 30 Nov 2006) | 1 line 0.96.D493 - Speed up rel_path() by avoiding recomputation of intermediate directory relative ........ r1701 | stevenknight | 2006-11-30 16:14:16 -0600 (Thu, 30 Nov 2006) | 1 line 0.96.D494 - More efficient get_suffix(): compute it once when we set the name. ........ r1702 | stevenknight | 2006-11-30 16:22:55 -0600 (Thu, 30 Nov 2006) | 1 line 0.96.D495 - Fix missing XML end tags. ........ r1703 | stevenknight | 2006-11-30 17:15:25 -0600 (Thu, 30 Nov 2006) | 1 line 0.96.D496 - Turn Memoizer into a simple counter for --debug=memoizer, not something that doe ........ r1704 | stevenknight | 2006-11-30 20:30:50 -0600 (Thu, 30 Nov 2006) | 1 line 0.96.D497 - Add the scons-time script, with doc and tests. ........ r1705 | stevenknight | 2006-11-30 23:28:20 -0600 (Thu, 30 Nov 2006) | 1 line 0.96.D498 - Update the copyright years string. ........ r1706 | stevenknight | 2006-12-01 11:54:22 -0600 (Fri, 01 Dec 2006) | 1 line 0.96.D499 - Fix _do_Lookup => _doLookup value-caching misspellings. (Ben Leslie) ........ r1707 | stevenknight | 2006-12-01 12:03:46 -0600 (Fri, 01 Dec 2006) | 1 line 0.96.D500 - Fix copyright test against debian build. (Walter Franzini) ........ r1708 | stevenknight | 2006-12-01 14:23:29 -0600 (Fri, 01 Dec 2006) | 1 line 0.96.D501 - Add #include lines for test portability. (Gary Oberbrunner) ........ r1709 | stevenknight | 2006-12-01 14:51:12 -0600 (Fri, 01 Dec 2006) | 1 line 0.96.D502 - Fix tests under Python versions with no profiler (pstats module). ........ r1710 | stevenknight | 2006-12-01 20:04:49 -0600 (Fri, 01 Dec 2006) | 1 line 0.96.D503 - Remove unnecessary os.path.normpath() calls. (Gary Oberbrunner) ........ r1711 | stevenknight | 2006-12-01 20:34:31 -0600 (Fri, 01 Dec 2006) | 1 line 0.96.D504 - Accomodate arbitray white space after a SWIG %module keyword. (Anonymous) ........ r1712 | stevenknight | 2006-12-05 14:49:54 -0600 (Tue, 05 Dec 2006) | 1 line 0.96.D506 - Cache substitutions of of Builder source suffixes. Use a new PathList module, and a refactor Node.FS.Rfindalldirs() method, to cache calculations of values like CPPPATH. ........ r1713 | stevenknight | 2006-12-05 18:43:36 -0600 (Tue, 05 Dec 2006) | 1 line 0.96.D507 - Use cached stat() values in diskchecks. ........ r1714 | stevenknight | 2006-12-05 21:11:24 -0600 (Tue, 05 Dec 2006) | 1 line 0.96.D508 - Fix Memoizer hit counts for methods memoizing simple values. Clean up the code for memoizing return values in a dictionary. Fix comments. ........ r1715 | stevenknight | 2006-12-06 07:23:18 -0600 (Wed, 06 Dec 2006) | 1 line 0.96.D369 - More efficient Node.FS.Dir.current() check. Fix some Windows test portability issues. ........ r1716 | stevenknight | 2006-12-06 12:24:32 -0600 (Wed, 06 Dec 2006) | 2 lines Undo previous checkin (distributed incorrect Aegis change number). ........ r1717 | stevenknight | 2006-12-06 12:34:53 -0600 (Wed, 06 Dec 2006) | 1 line 0.96.D505 - Update ae-{cvs,svn}-ci for newer versions of aetar, and to not truncate descriptions. ........ r1718 | stevenknight | 2006-12-07 23:01:41 -0600 (Thu, 07 Dec 2006) | 1 line 0.96.D509 - Only look for mslink on Windows systems. (Sohail Somani) ........ r1719 | stevenknight | 2006-12-07 23:18:33 -0600 (Thu, 07 Dec 2006) | 1 line 0.96.D510 - Have the D compiler Tool use the same logic for shared libraries, too. (Paolo Invernizzi) ........ r1720 | stevenknight | 2006-12-07 23:29:47 -0600 (Thu, 07 Dec 2006) | 1 line 0.96.D511 - Generalize a JobTests.py test so it doesn't assume a specific order in which the operating system executes the threads. ........ r1721 | stevenknight | 2006-12-07 23:39:37 -0600 (Thu, 07 Dec 2006) | 1 line 0.96.D512 - Back out the Tool/dmd.py change; it breaks shared library linking for other lanuages beside D in the construction environment. ........ r1722 | stevenknight | 2006-12-07 23:47:11 -0600 (Thu, 07 Dec 2006) | 1 line 0.96.D513 - Test fixes: Windows portability, handle changes to Python 2.5 messages. ........ r1723 | stevenknight | 2006-12-08 00:00:13 -0600 (Fri, 08 Dec 2006) | 1 line 0.96.D514 - Change how the 'as' Tool is imported to accomodate the Python 2.6 'as' keyword. ........ r1724 | stevenknight | 2006-12-08 11:19:27 -0600 (Fri, 08 Dec 2006) | 1 line 0.96.D515 - Cache both Node.FS.find_file() and Node.FS.Dri.srcdir_find_file(). ........ r1725 | stevenknight | 2006-12-08 17:27:35 -0600 (Fri, 08 Dec 2006) | 1 line 0.96.D516 - Better error when we try to fetch contents from an Entry that doesn't exist. (Tom Parker) ........ r1726 | stevenknight | 2006-12-08 23:28:55 -0600 (Fri, 08 Dec 2006) | 1 line 0.96.D517 - Make sure we pick up the scons-local directory regardless of where we chdir internally. ........ r1727 | stevenknight | 2006-12-11 16:25:53 -0600 (Mon, 11 Dec 2006) | 1 line 0.96.D518 - Cache results of Executor.get_unignored_sources() and Executor.process_sources(). Eliminate some map() and disambiguate() calls when scanning for implicit dependencies. ........ r1728 | stevenknight | 2006-12-12 14:32:22 -0600 (Tue, 12 Dec 2006) | 1 line 0.96.D519 - Fix SideEffect() when -j is used. ........ r1729 | stevenknight | 2006-12-12 16:58:15 -0600 (Tue, 12 Dec 2006) | 1 line 0.96.D520 - Add a srcdir keyword to Builder calls. ........ r1730 | stevenknight | 2006-12-12 21:40:59 -0600 (Tue, 12 Dec 2006) | 1 line 0.96.D521 - TeX/LaTeX updates, including handling files in subdirectories. (Joel B. Mohler, Rob Managan, Dmitry Mikhin) ........ r1731 | stevenknight | 2006-12-14 15:01:02 -0600 (Thu, 14 Dec 2006) | 1 line 0.96.D522 - Propogate TypeErrors during variable substitution for display to the user. ........ r1732 | stevenknight | 2006-12-14 20:01:49 -0600 (Thu, 14 Dec 2006) | 1 line 0.96.D523 - Fix the os.path.join() calls in EnvironmentTests.py. ........ r1733 | stevenknight | 2006-12-15 07:48:22 -0600 (Fri, 15 Dec 2006) | 1 line 0.96.D524 - Fix source directories as dependencies of an Alias (0.96.93 problem found by LilyPond). ........ r1735 | stevenknight | 2006-12-15 12:43:45 -0600 (Fri, 15 Dec 2006) | 1 line 0.96.D525 - Allow printing Debug.caller() output (or other end-of-run debugging info) when using -h. ........ r1736 | stevenknight | 2006-12-15 16:30:08 -0600 (Fri, 15 Dec 2006) | 1 line 0.96.D526 - Add an option to debug IndexError and NameError exceptions during variable substitution. ........ ................ r1740 | stevenknight | 2006-12-15 21:17:20 -0600 (Fri, 15 Dec 2006) | 13 lines Merged revisions 1440-1541,1543-1582,1584-1667,1669-1675,1677-1738 via svnmerge from http://scons.tigris.org/svn/scons/branches/tools ........ r1646 | stevenknight | 2006-10-17 17:21:58 -0500 (Tue, 17 Oct 2006) | 6 lines Move all the scons.org stuff from the scons source tree itself to a directory next to the trunk, and delete the copies from the branches. There's a lot of stuff there (what with all of the documentation of the different versions) and it's ridiculous to make everyone sync it just to work on the code. ........ ................ r1757 | stevenknight | 2007-01-07 01:23:05 -0600 (Sun, 07 Jan 2007) | 65 lines Merged revisions 1738-1754,1756 via svnmerge from http://scons.tigris.org/svn/scons/branches/core ........ r1741 | stevenknight | 2006-12-16 22:51:07 -0600 (Sat, 16 Dec 2006) | 1 line 0.96.D527 - Give the f90 and f95 Tool modules knowledge of how to build source files of earlier Fortran versions. ........ r1742 | stevenknight | 2006-12-16 23:22:54 -0600 (Sat, 16 Dec 2006) | 1 line 0.96.D528 - Better handling of timestamp fallback if there's no md5 module. ........ r1743 | stevenknight | 2006-12-17 00:21:31 -0600 (Sun, 17 Dec 2006) | 1 line 0.96.D529 - Fix portability of new tests on systems that don't have TeX installed. ........ r1744 | stevenknight | 2006-12-19 15:30:16 -0600 (Tue, 19 Dec 2006) | 1 line 0.96.D530 - Eliminate the ListBuilder subclass in favor of using the Executor's target lists. ........ r1745 | stevenknight | 2006-12-19 18:54:26 -0600 (Tue, 19 Dec 2006) | 1 line 0.96.D531 - Eliminate of MultiStepBuilder as a separate Builder subclass. ........ r1746 | garyo | 2006-12-21 13:21:08 -0600 (Thu, 21 Dec 2006) | 1 line Minor doc fix, thanks to Douglas Landgraf. ........ r1747 | stevenknight | 2006-12-21 17:13:55 -0600 (Thu, 21 Dec 2006) | 1 line 0.96.D533 - Add CFLAGS for options common to C/C++. (Gary Oberbrunner) ........ r1748 | stevenknight | 2007-01-03 19:48:05 -0600 (Wed, 03 Jan 2007) | 1 line 0.96.D534 - Fix signature storage when targets are retrieved from CacheDir(). ........ r1749 | stevenknight | 2007-01-04 16:48:47 -0600 (Thu, 04 Jan 2007) | 1 line 0.96.D535 - Teach the lex and yacc tools about target files generated by different flex/bison options, and about Objective C suffixes. (Pupeno) ........ r1750 | stevenknight | 2007-01-04 17:14:38 -0600 (Thu, 04 Jan 2007) | 1 line 0.96.D536 - Refactor duplicate disambiguation logic in Entry.get_contents(). ........ r1751 | stevenknight | 2007-01-05 13:00:54 -0600 (Fri, 05 Jan 2007) | 1 line 0.96.D537 - Fix lprof regression from 0.96.92. ........ r1752 | stevenknight | 2007-01-05 20:43:48 -0600 (Fri, 05 Jan 2007) | 1 line 0.96.D538 - Fix caching of Builder suffix matching (to fix lprof regression). ........ r1753 | stevenknight | 2007-01-06 00:03:16 -0600 (Sat, 06 Jan 2007) | 1 line 0.96.D539 - Fix --include-dir when using MinGW. (Paul) ........ r1754 | stevenknight | 2007-01-06 00:24:53 -0600 (Sat, 06 Jan 2007) | 1 line 0.96.D540 - Make bootstrap.py something useful to execute SCons out of a source directory. ........ r1756 | stevenknight | 2007-01-06 21:32:11 -0600 (Sat, 06 Jan 2007) | 1 line 0.96.D541 - Update the Copyright year string to include 2007. Automate updating the month+year string in man page title headers. Fix hard-coded __revision__ strings that crept into some older tests. ........ ................ r1764 | stevenknight | 2007-01-08 01:13:59 -0600 (Mon, 08 Jan 2007) | 2 lines Add announcements for 0.96.94. ................ r1767 | stevenknight | 2007-01-08 11:33:58 -0600 (Mon, 08 Jan 2007) | 13 lines Merged revisions 1757-1765 via svnmerge from http://scons.tigris.org/svn/scons/branches/core ........ r1759 | stevenknight | 2007-01-07 09:36:37 -0600 (Sun, 07 Jan 2007) | 1 line 0.96.D541 - Update the Copyright year string to include 2007. Automate updating the month+year string in man page title headers. Fix hard-coded __revision__ strings that crept into some older tests. ........ r1765 | stevenknight | 2007-01-08 11:28:02 -0600 (Mon, 08 Jan 2007) | 1 line 0.96.D543 - Update development line for 0.96.94. ........ ................ r1784 | stevenknight | 2007-01-21 14:13:49 -0600 (Sun, 21 Jan 2007) | 57 lines Merged revisions 1767-1783 via svnmerge from http://scons.tigris.org/svn/scons/branches/core ........ r1771 | stevenknight | 2007-01-11 10:42:17 -0600 (Thu, 11 Jan 2007) | 1 line 0.96.D544 - Fix maximum recursion depth exceeded when writing .sconsign files after using Nodes on two different Windows drive letters. ........ r1772 | stevenknight | 2007-01-11 12:15:07 -0600 (Thu, 11 Jan 2007) | 1 line 0.96.D545 - Restore caching of file contents in Node.FS.File.get_contents(). ........ r1773 | stevenknight | 2007-01-12 10:22:40 -0600 (Fri, 12 Jan 2007) | 1 line 0.96.D405 - Add MergeFlags() and AddFlags() methods. (Greg Noel) Support recognizing compiler flags that begin with +. (Kent Boortz) ........ r1774 | stevenknight | 2007-01-16 15:58:39 -0600 (Tue, 16 Jan 2007) | 2 lines Back out previous modification; the wrong change was distributed. ........ r1775 | stevenknight | 2007-01-16 16:08:26 -0600 (Tue, 16 Jan 2007) | 1 line 0.96.D546 - Back out previous change that cached get_contents(). ........ r1776 | stevenknight | 2007-01-17 14:30:59 -0600 (Wed, 17 Jan 2007) | 1 line 0.96.D547 - Document the use of ${} to evaluate arbitrary Python code. (Gary Oberbrunner) ........ r1777 | stevenknight | 2007-01-17 15:43:18 -0600 (Wed, 17 Jan 2007) | 1 line 0.96.D548 - Better man page Scanner example (Matt Doar); add FindPathDirs() to the publicly-available functions, and document it. ........ r1778 | stevenknight | 2007-01-17 17:01:03 -0600 (Wed, 17 Jan 2007) | 1 line 0.96.D549 - Return MSVC default paths for versions >= 8.0. (Anonymous) ........ r1779 | stevenknight | 2007-01-18 08:37:58 -0600 (Thu, 18 Jan 2007) | 1 line 0.96.D550 - Windows fix for the rel_path() unit test. ........ r1780 | stevenknight | 2007-01-18 14:01:32 -0600 (Thu, 18 Jan 2007) | 1 line 0.96.D551 - Fix use of __builtins__ in Subst.py. ........ r1781 | stevenknight | 2007-01-18 16:00:27 -0600 (Thu, 18 Jan 2007) | 1 line 0.96.D552 - Collect compatibility code in its own SCons.compat subpackage. ........ r1782 | stevenknight | 2007-01-18 16:22:52 -0600 (Thu, 18 Jan 2007) | 1 line 0.96.D553 - Add the vanilla Python 2.5 subprocess.py module (currently unused) as a baseline for our backwards-compatibility changes to that module. ........ r1783 | stevenknight | 2007-01-18 17:09:39 -0600 (Thu, 18 Jan 2007) | 1 line 0.96.D554 - Change env.ParseConfig() to use the new subprocess module. ........ ................ r1814 | stevenknight | 2007-02-08 09:40:05 -0600 (Thu, 08 Feb 2007) | 2 lines Add links to queries for ENHANCEMENT issues. ................ r1826 | stevenknight | 2007-02-10 23:02:05 -0600 (Sat, 10 Feb 2007) | 137 lines Merged revisions 1784-1824 via svnmerge from http://scons.tigris.org/svn/scons/branches/core ........ r1786 | stevenknight | 2007-01-25 15:29:15 -0600 (Thu, 25 Jan 2007) | 1 line 0.96.D555 - Doc string updates for TaskMaster to better describe the architecture. Get rid of some no-longer-used code. ........ r1787 | stevenknight | 2007-01-25 23:24:31 -0600 (Thu, 25 Jan 2007) | 1 line 0.96.D556 - Packaging changes to support building packages in an arbitrary directory. ........ r1788 | stevenknight | 2007-01-30 20:35:39 -0600 (Tue, 30 Jan 2007) | 1 line 0.96.D557 - Make the Scanner.Base class able to handle Scanner.Selector functionality (i.e., a dictionary to select other scanners) and prepare to deprecate Scanner.Selector and Scanner.Scanner() in the future. ........ r1789 | stevenknight | 2007-01-30 20:45:23 -0600 (Tue, 30 Jan 2007) | 1 line 0.96.D558 - Add support for a site-scons subdirectory. (Gary Oberbrunner) ........ r1790 | stevenknight | 2007-01-31 00:36:20 -0600 (Wed, 31 Jan 2007) | 1 line 0.96.D559 - Clean up various module imports and other pychecker-detected problems. ........ r1791 | stevenknight | 2007-01-31 11:51:04 -0600 (Wed, 31 Jan 2007) | 1 line 0.96.D560 - Fix detection of Java anonymous classes if a newline precedes the opening brace. (Leanid Nazdrynau) ........ r1792 | stevenknight | 2007-02-02 11:57:59 -0600 (Fri, 02 Feb 2007) | 1 line 0.96.D561 - Add a --bootstrap_src option to bootstrap.py; search the bootstrap.py directory by default. (Greg Noel) ........ r1793 | stevenknight | 2007-02-02 18:04:52 -0600 (Fri, 02 Feb 2007) | 1 line 0.96.D562 - Don't check the build/*/gentoo directories for copyright strings. ........ r1794 | stevenknight | 2007-02-02 18:12:43 -0600 (Fri, 02 Feb 2007) | 1 line 0.96.D563 - Fix pychecker clean-ups on later Python versions. ........ r1795 | stevenknight | 2007-02-02 18:38:47 -0600 (Fri, 02 Feb 2007) | 1 line 0.96.D564 - Add a NoCache() function to allow marking targets as not being suitable for propagating/ to (or retrieving from) CacheDir(). (Dave Vitek) ........ r1796 | stevenknight | 2007-02-02 20:08:33 -0600 (Fri, 02 Feb 2007) | 1 line 0.96.D565 - Add a --tree= option, to make the interface to dumping dependency graphs a little clearner, and give it a 'prune' option to avoid repeating the dependency walk for Nodes we've already visited. ........ r1797 | stevenknight | 2007-02-03 20:37:25 -0600 (Sat, 03 Feb 2007) | 1 line 0.96.D566 - Packaging fix: don't add '#' to the beginning of a build_dir name if it's already an absolute path. ........ r1798 | stevenknight | 2007-02-03 20:53:42 -0600 (Sat, 03 Feb 2007) | 1 line 0.96.D567 - Add backwards-compatibility for set() types, and restore the line that used them in the compatibility _subprocess.py module. ........ r1799 | stevenknight | 2007-02-05 10:30:28 -0600 (Mon, 05 Feb 2007) | 1 line 0.96.D568 - Capture outline and build configuration for a possible future Developer's Guide. ........ r1800 | stevenknight | 2007-02-05 11:03:37 -0600 (Mon, 05 Feb 2007) | 1 line 0.96.D569 - Exclude the new developer guide MANIFEST from Copyright string checks. ........ r1801 | stevenknight | 2007-02-05 11:16:04 -0600 (Mon, 05 Feb 2007) | 1 line 0.96.D570 - Track fix in upstream subprocess.py. (Ralf W. Grosse-Kunstleve) ........ r1802 | stevenknight | 2007-02-05 20:05:59 -0600 (Mon, 05 Feb 2007) | 1 line 0.96.D571 - Change the Windows installer to register scons.bat as an 'App Path', so the directory doesn't need to be added to %PATH%. ........ r1803 | stevenknight | 2007-02-05 21:33:01 -0600 (Mon, 05 Feb 2007) | 1 line 0.96.D572 - Prepare SConf for use with the subprocess module by refactoring the Unbuffered class. ........ r1804 | stevenknight | 2007-02-05 22:37:09 -0600 (Mon, 05 Feb 2007) | 1 line 0.96.D573 - Get rid of left-over Node.Node.{pre,post}_actions attributes. ........ r1805 | stevenknight | 2007-02-06 12:04:14 -0600 (Tue, 06 Feb 2007) | 1 line 0.96.D574 - Remove leftover print in the test/CacheDir/NoCache.py test. ........ r1806 | stevenknight | 2007-02-06 17:11:03 -0600 (Tue, 06 Feb 2007) | 1 line 0.96.D575 - Support the ability to evaluate a ${} construction variable to select the spawner function. ........ r1809 | stevenknight | 2007-02-07 00:30:10 -0600 (Wed, 07 Feb 2007) | 1 line 0.96.D576 - Documentation fixes and updates. ........ r1810 | stevenknight | 2007-02-07 15:51:20 -0600 (Wed, 07 Feb 2007) | 1 line 0.96.D577 - Don't expect a bootstrap.py runtest.py file to be generated when swig -noproxy is used. ........ r1811 | stevenknight | 2007-02-07 16:55:06 -0600 (Wed, 07 Feb 2007) | 1 line 0.96.D578 - Make --srcdir a synonym for -Y/--repository. ........ r1812 | stevenknight | 2007-02-07 23:46:22 -0600 (Wed, 07 Feb 2007) | 1 line 0.96.D579 - More doc updates: PathAccept, #include+Repository double-quote limitation. ........ r1817 | stevenknight | 2007-02-08 12:46:42 -0600 (Thu, 08 Feb 2007) | 1 line 0.96.D580 - Fix use of toolpath with BuildDir. ........ r1818 | stevenknight | 2007-02-08 12:59:23 -0600 (Thu, 08 Feb 2007) | 1 line 0.96.D581 - Enable build of the text version of the User's Guide. ........ r1819 | stevenknight | 2007-02-08 14:21:31 -0600 (Thu, 08 Feb 2007) | 1 line 0.96.D582 - Fix handling of Java inner classes with JARCHDIR. ........ r1820 | stevenknight | 2007-02-08 14:39:33 -0600 (Thu, 08 Feb 2007) | 1 line 0.96.D583 - Add linking with -lm to the dmd.py Tool smart linker. (Anonymous) ........ r1821 | stevenknight | 2007-02-08 17:26:29 -0600 (Thu, 08 Feb 2007) | 1 line 0.96.D584 - Fix ParseConfig() when *FLAGS variables have been replaced with strings. ........ r1822 | stevenknight | 2007-02-09 12:21:17 -0600 (Fri, 09 Feb 2007) | 1 line 0.96.D585 - Fix installation of file names beginning with #. (Dave Weber) ........ r1823 | stevenknight | 2007-02-09 14:14:47 -0600 (Fri, 09 Feb 2007) | 1 line 0.96.D586 - Post-review improvements to recent toolpath and MergeFlags() changes. (Gary Oberbrunner, Greg Noel) ........ r1824 | stevenknight | 2007-02-10 00:53:13 -0600 (Sat, 10 Feb 2007) | 1 line 0.96.D587 - Commonize flags initialization between the c and c++ Tools. ........ ................ r1834 | stevenknight | 2007-02-15 00:58:45 -0600 (Thu, 15 Feb 2007) | 2 lines Update project highlights for 0.96.95. ................ r1863 | stevenknight | 2007-03-15 17:16:54 -0500 (Thu, 15 Mar 2007) | 2 lines Add banner about SoC2007 to the project highlights. ................ r1884 | stevenknight | 2007-04-13 18:36:39 -0500 (Fri, 13 Apr 2007) | 185 lines Merged revisions 1826-1882 via svnmerge from http://scons.tigris.org/svn/scons/branches/core ........ r1828 | stevenknight | 2007-02-12 13:29:17 -0600 (Mon, 12 Feb 2007) | 1 line 0.96.D588 - Speed up Builder suffix-matching. ........ r1829 | stevenknight | 2007-02-14 08:12:32 -0600 (Wed, 14 Feb 2007) | 1 line 0.96.D589 - The scons command, branch 0.96.94. ........ r1830 | stevenknight | 2007-02-14 09:49:44 -0600 (Wed, 14 Feb 2007) | 1 line 0.96.D590 - Fix the scons-doc .tar.gz file packaging. ........ r1835 | stevenknight | 2007-02-15 11:03:20 -0600 (Thu, 15 Feb 2007) | 1 line 0.96.D591 - Update the release HOWTO. ........ r1836 | stevenknight | 2007-02-15 13:39:24 -0600 (Thu, 15 Feb 2007) | 1 line 0.96.D592 - The scons command, branch 0.96.95. ........ r1837 | stevenknight | 2007-02-15 18:34:18 -0600 (Thu, 15 Feb 2007) | 1 line 0.96.D593 - Back out (comment out) Windows registry installer changes. ........ r1838 | stevenknight | 2007-02-16 10:37:28 -0600 (Fri, 16 Feb 2007) | 1 line 0.96.D594 - Update Debian packaging to remove hard-coded references to Python 2.2. (Jean-Baptiste Lab) ........ r1839 | stevenknight | 2007-02-20 09:34:23 -0600 (Tue, 20 Feb 2007) | 1 line 0.96.D595 - Documentation fixes. In the construction variable appendix, use cross-referenced links to entries. ........ r1840 | stevenknight | 2007-02-21 05:11:35 -0600 (Wed, 21 Feb 2007) | 1 line 0.96.D596 - Handle Java '.class' attributes after non-word tokens without assuming it introduces an inner class. ........ r1841 | stevenknight | 2007-02-21 22:33:28 -0600 (Wed, 21 Feb 2007) | 1 line 0.96.D597 - CPPDEFINES regression ........ r1842 | stevenknight | 2007-02-22 14:19:10 -0600 (Thu, 22 Feb 2007) | 1 line 0.96.D598 - Do not detect a Java anonymous class when the first non-skipped token after "new" is a closing brace. ........ r1843 | stevenknight | 2007-02-23 10:45:06 -0600 (Fri, 23 Feb 2007) | 1 line 0.96.D599 - Better [Errno 21] Is a directory error message. ........ r1844 | stevenknight | 2007-02-23 13:32:11 -0600 (Fri, 23 Feb 2007) | 1 line 0.96.D600 - Fix expansion of non-Node objects within a PathList (maximum recursion / unhashable type bug). ........ r1847 | stevenknight | 2007-03-02 00:12:27 -0600 (Fri, 02 Mar 2007) | 1 line 0.96.D601 - Generate SCons API documentation from the docstrings using epydoc. ........ r1848 | stevenknight | 2007-03-02 14:10:06 -0600 (Fri, 02 Mar 2007) | 1 line 0.96.D602 - Fix use of custom include and lib paths with Visual Studio 8. (Richard Viney) ........ r1849 | stevenknight | 2007-03-03 01:00:22 -0600 (Sat, 03 Mar 2007) | 1 line 0.96.D603 - Man page fix: ParseDepends(). User's Guide updates: NoCache(), Clean(), fix CPPDEFINES output, markers for to-be-documented features, white space clean-up. ........ r1850 | stevenknight | 2007-03-06 02:29:08 -0600 (Tue, 06 Mar 2007) | 1 line 0.96.D604 - Fix use of --debug=presub with the Actions for our out-of-the-box Builders. ........ r1851 | stevenknight | 2007-03-06 09:10:43 -0600 (Tue, 06 Mar 2007) | 1 line 0.96.D605 - User Guide updates: --random, AlwaysBuild(), --tree=, --debug=presub, --debug=stacktrace. ........ r1852 | stevenknight | 2007-03-06 15:38:06 -0600 (Tue, 06 Mar 2007) | 1 line 0.96.D606 - Have the Intel toolchain use the default smart linking logic. (Dmitry Grigorenko and Gary Oberbrunner) ........ r1853 | stevenknight | 2007-03-06 17:56:44 -0600 (Tue, 06 Mar 2007) | 1 line 0.96.D607 - Fix tests: ActionTests.py for presub change, command detection in test/Intel/icpc-link.py. ........ r1854 | stevenknight | 2007-03-08 09:35:25 -0600 (Thu, 08 Mar 2007) | 1 line 0.96.D608 - Better selection of .NET Framework SDK paths. (Richard Viney) ........ r1855 | stevenknight | 2007-03-08 10:34:37 -0600 (Thu, 08 Mar 2007) | 1 line 0.96.D609 - Don't re-run TeX if the triggering strings (\makeindex, \bibliography, \tableofcontents) are commented out. (Matthias Troffaes) ........ r1856 | stevenknight | 2007-03-09 16:18:36 -0600 (Fri, 09 Mar 2007) | 1 line 0.96.D610 - Teach the new PathList module to handle nested lists within CPPPATH and the like. ........ r1857 | stevenknight | 2007-03-10 23:30:29 -0600 (Sat, 10 Mar 2007) | 1 line 0.96.D611 - Qt builders_used failure. ........ r1858 | stevenknight | 2007-03-11 15:33:34 -0500 (Sun, 11 Mar 2007) | 1 line 0.96.D612 - Document limitations of --implicit-cache w.r.t. CPPPATH/LIBPATH/etc. ........ r1859 | stevenknight | 2007-03-11 21:11:26 -0500 (Sun, 11 Mar 2007) | 1 line 0.96.D613 - Document --debug=findlibs and --taskmastertrace in the User's Guide. ........ r1860 | stevenknight | 2007-03-12 13:28:42 -0500 (Mon, 12 Mar 2007) | 1 line 0.96.D614 - Remove deleted cons file from the User's Guide MANIFEST. Fix epydoc API build if the build directory is outside the current directory. ........ r1861 | stevenknight | 2007-03-13 13:03:56 -0500 (Tue, 13 Mar 2007) | 2 lines Ignore '*.pyc' files in the compat/ subdirectory. ........ r1862 | stevenknight | 2007-03-13 19:08:19 -0500 (Tue, 13 Mar 2007) | 1 line 0.96.D615 - Fix use of $VAR expansions within CPPPATH/LIBPATH values when the expansion is itself a Dir node concatenated with a string. ........ r1866 | stevenknight | 2007-03-16 01:46:10 -0500 (Fri, 16 Mar 2007) | 1 line 0.96.D616 - Back off to the 0.96.94 of Builder.py (with some performance improvements). ........ r1867 | stevenknight | 2007-03-16 11:20:39 -0500 (Fri, 16 Mar 2007) | 1 line 0.96.D617 - Fix an unnamed variable error if we can't map the Visual Studio version to a default framework version. ........ r1868 | stevenknight | 2007-03-16 12:08:18 -0500 (Fri, 16 Mar 2007) | 1 line 0.96.D618 - Quote the MSVS build target in command lines to handle spaces target name. (Jeff Mahovsky) ........ r1869 | stevenknight | 2007-03-16 13:30:06 -0500 (Fri, 16 Mar 2007) | 1 line 0.96.D619 - Portability fixes for tests run on Windows. ........ r1870 | stevenknight | 2007-03-20 00:18:04 -0500 (Tue, 20 Mar 2007) | 1 line 0.96.D620 - Windows portability fixes: test scripts and infrastructure, detect vcexpress.exe. ........ r1871 | garyo | 2007-03-21 18:32:54 -0500 (Wed, 21 Mar 2007) | 1 line Fix bug where site_scons dir was added to sys.path as relative, not absolute. Added test case. Bug reported by Timothy Woods; thanks for the test case! ........ r1872 | stevenknight | 2007-03-22 09:43:23 -0500 (Thu, 22 Mar 2007) | 1 line 0.96.D622 - Add mention of site_scons fix to src/CHANGES.txt. ........ r1873 | stevenknight | 2007-04-02 23:49:36 -0500 (Mon, 02 Apr 2007) | 1 line 0.96.D623 - Parallel build dependencies with multiple entries in children. (Adam Simpkins) ........ r1874 | stevenknight | 2007-04-04 07:45:05 -0500 (Wed, 04 Apr 2007) | 1 line 0.96.D624 - Make all necessary LaTeX auxiliary files Precious, so bibliography contents aren't affected by whether the auxiliary files exist or not. (Joel B. Mohler) ........ r1875 | stevenknight | 2007-04-04 13:15:39 -0500 (Wed, 04 Apr 2007) | 1 line 0.96.D625 - Fix --debug-time value when -j option is used. ........ r1876 | stevenknight | 2007-04-09 19:40:08 -0500 (Mon, 09 Apr 2007) | 1 line 0.96.D626 - Fix man page example of propagating external user environment. Eliminate cut-and-paste sentence in NoCache() description. (Helmut Grohne, Joe Bloggs) [Issue 1626] [Issue 1627] ........ r1877 | stevenknight | 2007-04-09 23:20:14 -0500 (Mon, 09 Apr 2007) | 1 line 0.96.D627 - Re-run latex after bibtex runs. (Rob Managan) ........ r1878 | stevenknight | 2007-04-11 23:38:17 -0500 (Wed, 11 Apr 2007) | 1 line 0.96.D628 - Fix typo in the User's Guide. [issue 1600] ........ r1879 | stevenknight | 2007-04-12 01:06:35 -0500 (Thu, 12 Apr 2007) | 1 line 0.96.D629 - Avoid name conflicts with compat/ modules (specifically _subprocess.py). ........ r1880 | stevenknight | 2007-04-12 01:33:42 -0500 (Thu, 12 Apr 2007) | 1 line 0.96.D630 - Portability fixes and other improvements in test scripts. ........ r1882 | stevenknight | 2007-04-13 16:42:02 -0500 (Fri, 13 Apr 2007) | 1 line 0.96.D631 - The scons command, branch 0.96.96. ........ ................ r1905 | stevenknight | 2007-05-17 16:27:36 -0500 (Thu, 17 May 2007) | 2 lines Update project highlights and the roadmap for the release of 0.97. ................ r1907 | stevenknight | 2007-05-18 00:40:31 -0500 (Fri, 18 May 2007) | 29 lines Merged revisions 1884-1905 via svnmerge from http://scons.tigris.org/svn/scons/branches/core ........ r1891 | stevenknight | 2007-04-24 08:57:03 -0500 (Tue, 24 Apr 2007) | 1 line 0.96.D632 - Fix 0.96.96 reference count regression during parallel builds. ........ r1892 | stevenknight | 2007-04-24 12:51:05 -0500 (Tue, 24 Apr 2007) | 1 line 0.96.D633 - Fix documented default value(s) of $MSVS_USE_MFC_DIRS. ........ r1893 | stevenknight | 2007-04-24 16:12:14 -0500 (Tue, 24 Apr 2007) | 1 line 0.96.D634 - Make the DirEntryScanner tolerant of non-Dir nodes. ........ r1898 | stevenknight | 2007-05-09 15:07:15 -0500 (Wed, 09 May 2007) | 1 line 0.96.D635 - Portability fixes in test scripts. ........ r1899 | stevenknight | 2007-05-12 08:19:13 -0500 (Sat, 12 May 2007) | 1 line 0.96.D636 - Update documentation with rudimentary Tool module descriptions. ........ r1901 | stevenknight | 2007-05-17 14:32:14 -0500 (Thu, 17 May 2007) | 1 line 0.97.D001 - Initialize 0.97 for release. ........ ................ ................ r1931 | pscholl | 2007-05-23 18:41:24 -0500 (Wed, 23 May 2007) | 5 lines Install, InstallAs, CopyTo and CopyAs will not get instantiated for each environment. ................ r1936 | stevenknight | 2007-05-26 14:56:36 -0500 (Sat, 26 May 2007) | 3 lines Windows has no os.uname() method, so just leave "machine" initialized to None on those systems. ................ r1946 | pscholl | 2007-05-30 11:44:51 -0500 (Wed, 30 May 2007) | 3 lines * let FindSourceFile use '.' as the default target ................ r1948 | pscholl | 2007-05-30 12:23:42 -0500 (Wed, 30 May 2007) | 3 lines * use has_builder() instead of len(node.sources)==0 to select leaves in the DAG ................ r1950 | stevenknight | 2007-05-31 09:03:19 -0500 (Thu, 31 May 2007) | 6 lines Clean ups: Add a missing test.pass_test() call. Use test.skip_test() when a utility isn't found. Un-indent the test.*() calls in the main bodies of the test. Use test.must_exist() instead of test.fail_test(not os.path.exists()). ................ r1965 | stevenknight | 2007-06-01 15:24:14 -0500 (Fri, 01 Jun 2007) | 3 lines Remove the Install() and InstallAs() unit tests, since those are no longer native construction environment methods. ................ r1966 | stevenknight | 2007-06-01 15:25:47 -0500 (Fri, 01 Jun 2007) | 3 lines Remove an extra line interfering with a fix for installing files that begin with '#'. (Probably left over from a hasty merge.) ................ r1967 | stevenknight | 2007-06-01 15:32:36 -0500 (Fri, 01 Jun 2007) | 2 lines Remove a duplicate line. ................ r1974 | stevenknight | 2007-06-04 12:41:16 -0500 (Mon, 04 Jun 2007) | 7 lines Change FindSourceFiles so it disambiguates Entry nodes into File nodes while performing its walk. Use isinstance() instead explicit __class__ comparison to allow for future subclassing. Use set() to find uniq elements in a list (available to earlier Python versions through our compatibility library). ................ r2122 | stevenknight | 2007-07-11 15:52:15 -0500 (Wed, 11 Jul 2007) | 3 lines 1.5 portability issues (nested scope, comment out Help-text stuff that will be superceded by the AddOption() support). ................ r2124 | stevenknight | 2007-07-12 11:55:38 -0500 (Thu, 12 Jul 2007) | 2 lines Skip MSI tests if xml.dom.minidom isn't installed (early Python version) ................ r2125 | stevenknight | 2007-07-12 12:27:36 -0500 (Thu, 12 Jul 2007) | 13 lines Set TAR_OPTIONS=--wildcards when executing rpmbuild, so the extraction of the .spec file from the tarball will work with newer versions of tar. Change the RPM tool to initialize variables using env.SetDefault(), so the re-application of the tools to the construction environment (?) doesn't wipe out our $RPM setting (to add TAR_OPTIONS=--wildcards). Have the .spec files generated by the tests build/install the software using the version of SCons under test, not whatever happens to be installed as "scons" on the system. Python 1.5 fixes in various RPM code. ................ r2127 | stevenknight | 2007-07-12 15:03:45 -0500 (Thu, 12 Jul 2007) | 10 lines Use the new AddOption() method to support --install-sandbox, restoring the ability to call the options() function of a Tool module. Python 2.2 fix in packaging/test/packaging/rpm/tagging.py. Earlier-version Python fixes in other tests. Removal of unnecessary "import os" lines, and use of TestCmd.must_exist() where it can be. ................ r2129 | stevenknight | 2007-07-13 09:39:26 -0500 (Fri, 13 Jul 2007) | 7 lines Pre-2.2 Python fixes: Pull a recursively-called embedded function into the global Environment.py namespace. Avoid .split() and .replace() string object methods. ................ git-svn-id: http://scons.tigris.org/svn/scons/trunk@2143 fdb21ef1-2011-0410-befe-b5e4ea1792b1 --- diff --git a/QMTest/TestSCons.py b/QMTest/TestSCons.py index e1884acc..100d8ba6 100644 --- a/QMTest/TestSCons.py +++ b/QMTest/TestSCons.py @@ -45,6 +45,7 @@ from TestCommon import __all__ SConsVersion = '0.97' __all__.extend([ 'TestSCons', + 'machine', 'python', '_exe', '_obj', @@ -55,6 +56,23 @@ __all__.extend([ 'TestSCons', '_dll' ]) +machine_map = { + 'i686' : 'i386', + 'i586' : 'i386', + 'i486' : 'i386', +} + +try: + uname = os.uname +except AttributeError: + # Windows doesn't have a uname() function. We could use something like + # sys.platform as a fallback, but that's not really a "machine," so + # just leave it as None. + machine = None +else: + machine = uname()[4] + machine = machine_map.get(machine, machine) + python = python_executable _python_ = '"' + python_executable + '"' _exe = exe_suffix diff --git a/examples/ipk_packaging/SConstruct b/examples/ipk_packaging/SConstruct new file mode 100644 index 00000000..c8c54ebd --- /dev/null +++ b/examples/ipk_packaging/SConstruct @@ -0,0 +1,15 @@ +prog = Install( '/bin/', Program( 'main.c') ) + +Package( projectname = 'foo', + version = '1.2.3', + architecture = 'arm', + x_ipk_maintainer = 'user ', + x_ipk_priority = 'optional', + source_url = 'http://ftp.gnu.org/foo-1.2.3.tar.gz', + x_ipk_depends = 'libc6, grep', + type = 'ipk', + summary = 'bla bla bla', + x_ipk_section = 'extras', + description = 'this should be reallly really long', + source = [ prog ], + ) diff --git a/examples/ipk_packaging/main.c b/examples/ipk_packaging/main.c new file mode 100644 index 00000000..5940dd6c --- /dev/null +++ b/examples/ipk_packaging/main.c @@ -0,0 +1,4 @@ +int main( int argc, char *argv[] ) +{ + return 0; +} diff --git a/examples/msi_packaging/README b/examples/msi_packaging/README new file mode 100644 index 00000000..2e54e702 --- /dev/null +++ b/examples/msi_packaging/README @@ -0,0 +1 @@ +This is the README file. diff --git a/examples/msi_packaging/SConstruct b/examples/msi_packaging/SConstruct new file mode 100644 index 00000000..cabe70eb --- /dev/null +++ b/examples/msi_packaging/SConstruct @@ -0,0 +1,19 @@ +# +# Build a minimal msi installer with two features. +# + +f1 = Install( '/bin/', File('main.exe') ) +f2 = Install( '/lib/', File('helloworld.dll') ) +f3 = Install( '/doc/', File('README') ) + +Tag( f2, x_msi_feature = 'Resuable Components' ) +Tag( f3, 'doc' ) + +Package( projectname = 'helloworld', + version = '1.0', + packageversion = '1', + license = 'gpl', + type = 'msi', + vendor = 'Nanosoft', + summary = 'A HelloWorld implementation', + source = [ f1, f2, f3 ], ) diff --git a/examples/msi_packaging/helloworld.dll b/examples/msi_packaging/helloworld.dll new file mode 100644 index 00000000..1404bcf3 --- /dev/null +++ b/examples/msi_packaging/helloworld.dll @@ -0,0 +1 @@ +a fake .dll diff --git a/examples/msi_packaging/main.exe b/examples/msi_packaging/main.exe new file mode 100644 index 00000000..36c366c2 --- /dev/null +++ b/examples/msi_packaging/main.exe @@ -0,0 +1 @@ +a fake .exe diff --git a/examples/rpm_packaging/international/SConstruct b/examples/rpm_packaging/international/SConstruct new file mode 100644 index 00000000..0d149328 --- /dev/null +++ b/examples/rpm_packaging/international/SConstruct @@ -0,0 +1,25 @@ +# coding: utf-8 +import os + +prog_install = Install( os.path.join( ARGUMENTS.get('prefix', '/'), 'bin'), Program( 'main.c' ) ) +Tag( prog_install, unix_attr='(0755, root, users)' ) + +Default( Package( projectname = 'foo', + version = '1.2.3', + type = 'rpm', + license = 'gpl', + summary = 'hello', + summary_de = 'hallo', + summary_fr = 'bonjour', + packageversion = 0, + x_rpm_Group = 'Application/office', + x_rpm_Group_de = 'Applikation/büro', + x_rpm_Group_fr = 'Application/bureau', + description = 'this should be really long', + description_de = 'das sollte wirklich lang sein', + description_fr = 'ceci devrait être vraiment long', + source = [ prog_install ], + source_url = 'http://foo.org/foo-1.2.3.tar.gz', + ) ) + +Alias ( 'install', prog_install ) diff --git a/examples/rpm_packaging/international/main.c b/examples/rpm_packaging/international/main.c new file mode 100644 index 00000000..5940dd6c --- /dev/null +++ b/examples/rpm_packaging/international/main.c @@ -0,0 +1,4 @@ +int main( int argc, char *argv[] ) +{ + return 0; +} diff --git a/examples/rpm_packaging/simple/SConstruct b/examples/rpm_packaging/simple/SConstruct new file mode 100644 index 00000000..77c9d9b6 --- /dev/null +++ b/examples/rpm_packaging/simple/SConstruct @@ -0,0 +1,20 @@ +import os + +install_dir = os.path.join( ARGUMENTS.get('prefix', '/'), 'bin/' ) +prog_install = Install( install_dir , Program( 'main.c') ) + +Tag( prog_install, unix_attr = '(0755, root, users)' ) + +Package( projectname = 'foo', + version = '1.2.3', + type = 'rpm', + license = 'gpl', + summary = 'bla bla bla', + packageversion = 0, + x_rpm_Group = 'Application/office', + description = 'this should be reallly really long', + source_url = 'http://foo.org/foo-1.2.3.tar.gz', + source = [ prog_install ], + ) + +Alias( 'install', prog_install ) diff --git a/examples/rpm_packaging/simple/main.c b/examples/rpm_packaging/simple/main.c new file mode 100644 index 00000000..5940dd6c --- /dev/null +++ b/examples/rpm_packaging/simple/main.c @@ -0,0 +1,4 @@ +int main( int argc, char *argv[] ) +{ + return 0; +} diff --git a/examples/src_packaging/SConstruct b/examples/src_packaging/SConstruct new file mode 100644 index 00000000..1e662af1 --- /dev/null +++ b/examples/src_packaging/SConstruct @@ -0,0 +1,11 @@ +from glob import glob + +src_files = glob( 'src/*.c' ) +include_files = glob( 'src/*.h' ) + +SharedLibrary( 'foobar', src_files ) + +Package( projectname = 'libfoobar', + version = '1.2.3', + type = [ 'src_zip', 'src_targz', 'src_tarbz2' ], + source = FindSourceFiles() ) diff --git a/examples/src_packaging/src/foobar.c b/examples/src_packaging/src/foobar.c new file mode 100644 index 00000000..e69de29b diff --git a/examples/src_packaging/src/foobar.h b/examples/src_packaging/src/foobar.h new file mode 100644 index 00000000..e69de29b diff --git a/runtest.py b/runtest.py index ffaa68b4..f99f5a1b 100644 --- a/runtest.py +++ b/runtest.py @@ -165,8 +165,6 @@ opts, args = getopt.getopt(sys.argv[1:], "ab:df:hlno:P:p:qv:Xx:t", ['all', 'aegis', 'baseline=', 'builddir=', 'debug', 'file=', 'help', 'list', 'no-exec', 'noqmtest', 'output=', - 'package=', 'passed', 'python=', - 'qmtest', 'quiet', 'spe=', 'version=', 'exec=', 'time', 'verbose=', 'xml']) @@ -525,6 +523,11 @@ os.environ['SCONS_VERSION'] = version old_pythonpath = os.environ.get('PYTHONPATH') +# FIXME: the following is necessary to pull in half of the testing +# harness from $srcdir/etc. Those modules should be transfered +# to QMTest/ once we completely cut over to using that as +# the harness, in which case this manipulation of PYTHONPATH +# should be able to go away. pythonpaths = [ pythonpath_dir ] for dir in sp: diff --git a/src/engine/SCons/Action.py b/src/engine/SCons/Action.py index a010c4a7..04e68a3c 100644 --- a/src/engine/SCons/Action.py +++ b/src/engine/SCons/Action.py @@ -287,6 +287,7 @@ class _ActionAction(ActionBase): target = [target] if not SCons.Util.is_List(source): source = [source] + if exitstatfunc is _null: exitstatfunc = self.exitstatfunc if presub is _null: presub = self.presub @@ -335,6 +336,7 @@ class _ActionAction(ActionBase): os.chdir(save_cwd) if s and save_cwd: print_func('os.chdir(%s)' % repr(save_cwd), target, source, env) + return stat diff --git a/src/engine/SCons/Builder.py b/src/engine/SCons/Builder.py index c4b1d1dc..d21de467 100644 --- a/src/engine/SCons/Builder.py +++ b/src/engine/SCons/Builder.py @@ -669,6 +669,16 @@ class BuilderBase: """ self.emitter[suffix] = emitter + def push_emitter(self, emitter): + """Add a emitter to the beginning of the emitter list of this Builder. + + This creates an empty list if the emitter is None. + """ + if not self.emitter: + self.emitter = ListEmitter( [emitter] ) + else: + self.emitter.insert(0, emitter) + def add_src_builder(self, builder): """ Add a new Builder to the list of src_builders. @@ -704,7 +714,7 @@ class BuilderBase: for suf in bld.src_suffixes(env): sdict[suf] = bld return sdict - + def src_builder_sources(self, env, source, overwarn={}): source_factory = env.get_factory(self.source_factory) slist = env.arg2nodes(source, source_factory) diff --git a/src/engine/SCons/Defaults.py b/src/engine/SCons/Defaults.py index 136c3f7b..9d0ad825 100644 --- a/src/engine/SCons/Defaults.py +++ b/src/engine/SCons/Defaults.py @@ -133,7 +133,11 @@ Chmod = ActionFactory(os.chmod, lambda dest, mode: 'Chmod("%s", 0%o)' % (dest, mode)) def copy_func(dest, src): - if os.path.isfile(src): + if SCons.Util.is_List(src) and os.path.isdir(dest): + for file in src: + shutil.copy(file, dest) + return 0 + elif os.path.isfile(src): return shutil.copy(src, dest) else: return shutil.copytree(src, dest, 1) @@ -173,33 +177,6 @@ Touch = ActionFactory(touch_func, lambda file: 'Touch("%s")' % file) # Internal utility functions -def installFunc(dest, source, env): - """Install a source file or directory into a destination by copying, - (including copying permission/mode bits).""" - - if os.path.isdir(source): - if os.path.exists(dest): - if not os.path.isdir(dest): - raise SCons.Errors.UserError, "cannot overwrite non-directory `%s' with a directory `%s'" % (str(dest), str(source)) - else: - parent = os.path.split(dest)[0] - if not os.path.exists(parent): - os.makedirs(parent) - shutil.copytree(source, dest) - else: - shutil.copy2(source, dest) - st = os.stat(source) - os.chmod(dest, stat.S_IMODE(st[stat.ST_MODE]) | stat.S_IWRITE) - - return 0 - -def installStr(dest, source, env): - source = str(source) - if os.path.isdir(source): - type = 'directory' - else: - type = 'file' - return 'Install %s: "%s" as "%s"' % (type, source, dest) def _concat(prefix, list, suffix, env, f=lambda x: x, target=None, source=None): """ @@ -385,9 +362,6 @@ ConstructionEnvironment = { 'DSUFFIXES' : SCons.Tool.DSuffixes, 'ENV' : {}, 'IDLSUFFIXES' : SCons.Tool.IDLSuffixes, - 'INSTALL' : installFunc, - 'INSTALLSTR' : installStr, - '_installStr' : installStr, 'LATEXSUFFIXES' : SCons.Tool.LaTeXSuffixes, '_concat' : _concat, '_defines' : _defines, diff --git a/src/engine/SCons/Environment.py b/src/engine/SCons/Environment.py index bffe0ef2..8a7721e3 100644 --- a/src/engine/SCons/Environment.py +++ b/src/engine/SCons/Environment.py @@ -6,7 +6,7 @@ construction information to the build engine. Keyword arguments supplied when the construction Environment is created are construction variables used to initialize the -Environment +Environment """ # @@ -72,27 +72,6 @@ CalculatorArgs = {} # which seem to mess up its ability to reference SCons directly. UserError = SCons.Errors.UserError -def installFunc(target, source, env): - """Install a source file into a target using the function specified - as the INSTALL construction variable.""" - try: - install = env['INSTALL'] - except KeyError: - raise SCons.Errors.UserError('Missing INSTALL construction variable.') - return install(target[0].path, source[0].path, env) - -def installString(target, source, env): - s = env.get('INSTALLSTR', '') - if callable(s): - return s(target[0].path, source[0].path, env) - else: - return env.subst_target_source(s, 0, target, source) - -installAction = SCons.Action.Action(installFunc, installString) - -InstallBuilder = SCons.Builder.Builder(action=installAction, - name='InstallBuilder') - def alias_builder(env, target, source): pass @@ -370,7 +349,7 @@ class SubstitutionEnvironment: nodes.append(v) else: nodes.append(v) - + return nodes def gvars(self): @@ -717,6 +696,17 @@ class SubstitutionEnvironment: self[key] = t return self +# Used by the FindSourceFiles() method, below. +# Stuck here for support of pre-2.2 Python versions. +def build_source(ss, result): + for s in ss: + if isinstance(s, SCons.Node.FS.Dir): + build_source(s.all_children(), result) + elif s.has_builder(): + build_source(s.sources, result) + elif isinstance(s.disambiguate(), SCons.Node.FS.File): + result.append(s) + class Base(SubstitutionEnvironment): """Base class for "real" construction Environments. These are the primary objects used to communicate dependency and construction @@ -895,7 +885,7 @@ class Base(SubstitutionEnvironment): self._memo['_gsm'] = result return result - + def get_scanner(self, skey): """Find the appropriate scanner given a key (usually a file suffix). """ @@ -1006,7 +996,7 @@ class Base(SubstitutionEnvironment): orig = self._dict[envname][name] nv = SCons.Util.AppendPath(orig, newpath, sep) - + if not self._dict.has_key(envname): self._dict[envname] = {} @@ -1122,7 +1112,7 @@ class Base(SubstitutionEnvironment): for path in paths: dir,name = os.path.split(str(path)) - if name[:len(prefix)] == prefix and name[-len(suffix):] == suffix: + if name[:len(prefix)] == prefix and name[-len(suffix):] == suffix: return path def ParseConfig(self, command, function=None, unique=1): @@ -1261,7 +1251,7 @@ class Base(SubstitutionEnvironment): orig = self._dict[envname][name] nv = SCons.Util.PrependPath(orig, newpath, sep) - + if not self._dict.has_key(envname): self._dict[envname] = {} @@ -1593,50 +1583,6 @@ class Base(SubstitutionEnvironment): t.add_ignore(dlist) return tlist - def Install(self, dir, source): - """Install specified files in the given directory.""" - try: - dnodes = self.arg2nodes(dir, self.fs.Dir) - except TypeError: - fmt = "Target `%s' of Install() is a file, but should be a directory. Perhaps you have the Install() arguments backwards?" - raise SCons.Errors.UserError, fmt % str(dir) - try: - sources = self.arg2nodes(source, self.fs.Entry) - except TypeError: - if SCons.Util.is_List(source): - s = repr(map(str, source)) - else: - s = str(source) - fmt = "Source `%s' of Install() is neither a file nor a directory. Install() source must be one or more files or directories" - raise SCons.Errors.UserError, fmt % s - tgt = [] - for dnode in dnodes: - for src in sources: - # Prepend './' so the lookup doesn't interpret an initial - # '#' on the file name portion as meaning the Node should - # be relative to the top-level SConstruct directory. - target = dnode.Entry('.'+os.sep+src.name) - tgt.extend(InstallBuilder(self, target, src)) - return tgt - - def InstallAs(self, target, source): - """Install sources as targets.""" - sources = self.arg2nodes(source, self.fs.Entry) - targets = self.arg2nodes(target, self.fs.Entry) - if len(sources) != len(targets): - if not SCons.Util.is_List(target): - target = [target] - if not SCons.Util.is_List(source): - source = [source] - t = repr(map(str, target)) - s = repr(map(str, source)) - fmt = "Target (%s) and source (%s) lists of InstallAs() must be the same length." - raise SCons.Errors.UserError, fmt % (t, s) - result = [] - for src, tgt in map(lambda x, y: (x, y), sources, targets): - result.extend(InstallBuilder(self, tgt, src)) - return result - def Literal(self, string): return SCons.Subst.Literal(string) @@ -1681,7 +1627,7 @@ class Base(SubstitutionEnvironment): SCons.SConsign.File(name, dbm_module) def SideEffect(self, side_effect, target): - """Tell scons that side_effects are built as side + """Tell scons that side_effects are built as side effects of building targets.""" side_effects = self.arg2nodes(side_effect, self.fs.Entry) targets = self.arg2nodes(target, self.fs.Entry) @@ -1753,6 +1699,39 @@ class Base(SubstitutionEnvironment): """ return SCons.Node.Python.Value(value, built_value) + def FindSourceFiles(self, node='.'): + """ returns a list of all source files. + """ + node = self.arg2nodes(node, self.fs.Entry)[0] + + sources = [] + # Uncomment this and get rid of the global definition when we + # drop support for pre-2.2 Python versions. + #def build_source(ss, result): + # for s in ss: + # if isinstance(s, SCons.Node.FS.Dir): + # build_source(s.all_children(), result) + # elif s.has_builder(): + # build_source(s.sources, result) + # elif isinstance(s.disambiguate(), SCons.Node.FS.File): + # result.append(s) + build_source(node.all_children(), sources) + + # now strip the build_node from the sources by calling the srcnode + # function + def get_final_srcnode(file): + srcnode = file.srcnode() + while srcnode != file.srcnode(): + srcnode = file.srcnode() + return srcnode + + # get the final srcnode for all nodes, this means stripping any + # attached build node. + map( get_final_srcnode, sources ) + + # remove duplicates + return list(set(sources)) + class OverrideEnvironment(Base): """A proxy that overrides variables in a wrapped construction environment by returning values from an overrides dictionary in diff --git a/src/engine/SCons/EnvironmentTests.py b/src/engine/SCons/EnvironmentTests.py index d4e6bc58..20c1eac8 100644 --- a/src/engine/SCons/EnvironmentTests.py +++ b/src/engine/SCons/EnvironmentTests.py @@ -2204,16 +2204,16 @@ f5: \ exc_caught = None try: env.Tool('does_not_exist') - except SCons.Errors.UserError: + except SCons.Errors.EnvironmentError: exc_caught = 1 - assert exc_caught, "did not catch expected UserError" + assert exc_caught, "did not catch expected EnvironmentError" exc_caught = None try: env.Tool('$NONE') - except SCons.Errors.UserError: + except SCons.Errors.EnvironmentError: exc_caught = 1 - assert exc_caught, "did not catch expected UserError" + assert exc_caught, "did not catch expected EnvironmentError" # Use a non-existent toolpath directory just to make sure we # can call Tool() with the keyword argument. @@ -2821,78 +2821,6 @@ def generate(env): assert i.__class__.__name__ == 'Dir', i.__class__.__name__ assert i.path == 'dir2' - def test_Install(self): - """Test the Install method""" - env = self.TestEnvironment(FOO='iii', BAR='jjj') - - tgt = env.Install('export', [ 'build/foo1', 'build/foo2' ]) - paths = map(str, tgt) - paths.sort() - expect = map(os.path.normpath, [ 'export/foo1', 'export/foo2' ]) - assert paths == expect, paths - for tnode in tgt: - assert tnode.builder == InstallBuilder - - tgt = env.Install('$FOO', [ 'build/${BAR}1', 'build/${BAR}2' ]) - paths = map(str, tgt) - paths.sort() - expect = map(os.path.normpath, [ 'iii/jjj1', 'iii/jjj2' ]) - assert paths == expect, paths - for tnode in tgt: - assert tnode.builder == InstallBuilder - - tgt = env.Install('export', 'build') - paths = map(str, tgt) - paths.sort() - expect = [os.path.join('export', 'build')] - assert paths == expect, paths - for tnode in tgt: - assert tnode.builder == InstallBuilder - - tgt = env.Install('export', ['build', 'build/foo1']) - paths = map(str, tgt) - paths.sort() - expect = [ - os.path.join('export', 'build'), - os.path.join('export', 'foo1'), - ] - assert paths == expect, paths - for tnode in tgt: - assert tnode.builder == InstallBuilder - - tgt = env.Install('export', 'subdir/#file') - assert str(tgt[0]) == os.path.normpath('export/#file'), str(tgt[0]) - - env.File('export/foo1') - - exc_caught = None - try: - tgt = env.Install('export/foo1', 'build/foo1') - except SCons.Errors.UserError, e: - exc_caught = 1 - assert exc_caught, "UserError should be thrown reversing the order of Install() targets." - expect = "Target `export/foo1' of Install() is a file, but should be a directory. Perhaps you have the Install() arguments backwards?" - assert str(e) == expect, e - - def test_InstallAs(self): - """Test the InstallAs method""" - env = self.TestEnvironment(FOO='iii', BAR='jjj') - - tgt = env.InstallAs(target=string.split('foo1 foo2'), - source=string.split('bar1 bar2')) - assert len(tgt) == 2, len(tgt) - paths = map(lambda x: str(x.sources[0]), tgt) - paths.sort() - expect = map(os.path.normpath, [ 'bar1', 'bar2' ]) - assert paths == expect, paths - for tnode in tgt: - assert tnode.builder == InstallBuilder - - tgt = env.InstallAs(target='${FOO}.t', source='${BAR}.s')[0] - assert tgt.path == 'iii.t' - assert tgt.sources[0].path == 'jjj.s' - assert tgt.builder == InstallBuilder - def test_Literal(self): """Test the Literal() method""" env = self.TestEnvironment(FOO='fff', BAR='bbb') diff --git a/src/engine/SCons/Errors.py b/src/engine/SCons/Errors.py index 3a3306e9..3ee7ff42 100644 --- a/src/engine/SCons/Errors.py +++ b/src/engine/SCons/Errors.py @@ -48,6 +48,9 @@ class UserError(Exception): class StopError(Exception): pass +class EnvironmentError(Exception): + pass + class ExplicitExit(Exception): def __init__(self, node=None, status=None, *args): self.node = node diff --git a/src/engine/SCons/Node/FS.py b/src/engine/SCons/Node/FS.py index 152a3897..ad21a4db 100644 --- a/src/engine/SCons/Node/FS.py +++ b/src/engine/SCons/Node/FS.py @@ -488,7 +488,7 @@ class EntryProxy(SCons.Util.Proxy): def __get_dir(self): return EntryProxy(self.get().dir) - + dictSpecialAttrs = { "base" : __get_base_path, "posix" : __get_posix_path, "windows" : __get_windows_path, @@ -543,7 +543,7 @@ class Base(SCons.Node.Node): def __init__(self, name, directory, fs): """Initialize a generic Node.FS.Base object. - + Call the superclass initialization, take care of setting up our relative and absolute paths, identify our parent directory, and indicate that this node should use @@ -842,7 +842,7 @@ class Entry(Base): def get_contents(self): """Fetch the contents of the entry. - + Since this should return the real contents from the file system, we check to see into what sort of subclass we should morph this Entry.""" @@ -898,7 +898,7 @@ class LocalFS: if SCons.Memoize.use_memoizer: __metaclass__ = SCons.Memoize.Memoized_Metaclass - + # This class implements an abstraction layer for operations involving # a local file system. Essentially, this wraps any function in # the os, os.path or shutil modules that we use to actually go do @@ -1009,7 +1009,7 @@ class FS(LocalFS): self.Top.path = '.' self.Top.tpath = '.' self._cwd = self.Top - + def set_SConstruct_dir(self, dir): self.SConstruct_dir = dir @@ -1091,7 +1091,7 @@ class FS(LocalFS): last_orig = path_orig.pop() # strip last element last_norm = path_norm.pop() # strip last element - + # Lookup the directory for orig, norm in map(None, path_orig, path_norm): try: @@ -1133,7 +1133,7 @@ class FS(LocalFS): # disk where we just created a File node, and vice versa. result.diskcheck_match() - directory.entries[last_norm] = result + directory.entries[last_norm] = result directory.add_wkid(result) else: e.must_be_same(fsclass) @@ -1211,7 +1211,7 @@ class FS(LocalFS): directory = self.Dir(directory) name, directory = self._transformPath(name, directory) return self._doLookup(klass, name, directory, create) - + def File(self, name, directory = None, create = 1): """Lookup or create a File node with the specified name. If the name is a relative path (begins with ./, ../, or a file name), @@ -1223,7 +1223,7 @@ class FS(LocalFS): specified path. """ return self.Entry(name, directory, create, File) - + def Dir(self, name, directory = None, create = 1): """Lookup or create a Dir node with the specified name. If the name is a relative path (begins with ./, ../, or a file name), @@ -1235,11 +1235,11 @@ class FS(LocalFS): specified path. """ return self.Entry(name, directory, create, Dir) - + def BuildDir(self, build_dir, src_dir, duplicate=1): """Link the supplied build directory to the source directory for purposes of building files.""" - + if not isinstance(src_dir, SCons.Node.Node): src_dir = self.Dir(src_dir) if not isinstance(build_dir, SCons.Node.Node): @@ -1377,7 +1377,7 @@ class Dir(Base): pass if duplicate != None: node.duplicate=duplicate - + def __resetDuplicate(self, node): if node != self: node.duplicate = node.get_dir().duplicate @@ -1388,7 +1388,8 @@ class Dir(Base): def Dir(self, name): """Create a directory node named 'name' relative to this directory.""" - return self.fs.Dir(name, self) + dir = self.fs.Dir(name, self) + return dir def File(self, name): """Create a file node named 'name' relative to this directory.""" @@ -1484,7 +1485,7 @@ class Dir(Base): path_elems = ['..'] * (len(self.path_elements) - i) \ + map(lambda n: n.name, other.path_elements[i:]) - + result = string.join(path_elems, os.sep) memo_dict[other] = result @@ -2154,7 +2155,7 @@ class File(Base): def _rmv_existing(self): self.clear_memoized_values() Unlink(self, [], None) - + def prepare(self): """Prepare for this file to be created.""" SCons.Node.Node.prepare(self) diff --git a/src/engine/SCons/Options/OptionsTests.py b/src/engine/SCons/Options/OptionsTests.py index 1d9b851e..95bd1cdf 100644 --- a/src/engine/SCons/Options/OptionsTests.py +++ b/src/engine/SCons/Options/OptionsTests.py @@ -472,8 +472,8 @@ B: b - alpha test """Test generating custom format help text""" opts = SCons.Options.Options() - def my_format(env, opt, help, default, actual): - return '%s %s %s %s\n' % (opt, default, actual, help) + def my_format(env, opt, help, default, actual, aliases): + return '%s %s %s %s %s\n' % (opt, default, actual, help, aliases) opts.FormatOptionHelpText = my_format @@ -499,18 +499,18 @@ B: b - alpha test opts.Update(env, {}) expect = """\ -ANSWER 42 54 THE answer to THE question -B 42 54 b - alpha test -A 42 54 a - alpha test +ANSWER 42 54 THE answer to THE question ['ANSWER'] +B 42 54 b - alpha test ['B'] +A 42 54 a - alpha test ['A'] """ text = opts.GenerateHelpText(env) assert text == expect, text expectAlpha = """\ -A 42 54 a - alpha test -ANSWER 42 54 THE answer to THE question -B 42 54 b - alpha test +A 42 54 a - alpha test ['A'] +ANSWER 42 54 THE answer to THE question ['ANSWER'] +B 42 54 b - alpha test ['B'] """ text = opts.GenerateHelpText(env, sort=cmp) assert text == expectAlpha, text diff --git a/src/engine/SCons/Options/PathOption.py b/src/engine/SCons/Options/PathOption.py index 683ede05..995eeb98 100644 --- a/src/engine/SCons/Options/PathOption.py +++ b/src/engine/SCons/Options/PathOption.py @@ -128,7 +128,12 @@ class _PathOptionClass: """ if validator is None: validator = self.PathExists - return (key, '%s ( /path/to/%s )' % (help, key), default, - validator, None) + + if SCons.Util.is_List(key) or SCons.Util.is_Tuple(key): + return (key, '%s ( /path/to/%s )' % (help, key[0]), default, + validator, None) + else: + return (key, '%s ( /path/to/%s )' % (help, key), default, + validator, None) PathOption = _PathOptionClass() diff --git a/src/engine/SCons/Options/__init__.py b/src/engine/SCons/Options/__init__.py index 66c21435..ebd60521 100644 --- a/src/engine/SCons/Options/__init__.py +++ b/src/engine/SCons/Options/__init__.py @@ -44,31 +44,47 @@ from PathOption import PathOption # okay class Options: + instance=None + """ Holds all the options, updates the environment with the variables, and renders the help text. """ - def __init__(self, files=None, args={}): + def __init__(self, files=None, args={}, is_global=1): """ files - [optional] List of option configuration files to load - (backward compatibility) If a single string is passed it is + (backward compatibility) If a single string is passed it is automatically placed in a file list """ - self.options = [] self.args = args self.files = None if SCons.Util.is_String(files): - self.files = [ files ] + self.files = [ files ] elif files: - self.files = files + self.files = files + + # create the singleton instance + if is_global: + self=Options.instance + + if not Options.instance: + Options.instance=self def _do_add(self, key, help="", default=None, validator=None, converter=None): class Option: pass option = Option() - option.key = key + + # if we get a list or a tuple, we take the first element as the + # option key and store the remaining in aliases. + if SCons.Util.is_List(key) or SCons.Util.is_Tuple(key): + option.key = key[0] + option.aliases = key[1:] + else: + option.key = key + option.aliases = [ key ] option.help = help option.default = default option.validator = validator @@ -111,7 +127,7 @@ class Options: Each list element is a tuple/list of arguments to be passed on to the underlying method for adding options. - + Example: opt.AddOptions( ('debug', '', 0), @@ -147,11 +163,14 @@ class Options: # finally set the values specified on the command line if args is None: args = self.args - values.update(args) + + for arg, value in args.items(): + for option in self.options: + if arg in option.aliases + [ option.key ]: + values[option.key]=value # put the variables in the environment: - # (don't copy over variables that are not declared - # as options) + # (don't copy over variables that are not declared as options) for option in self.options: try: env[option.key] = values[option.key] @@ -204,7 +223,7 @@ class Options: # Convert stuff that has a repr() that # cannot be evaluated into a string value = SCons.Util.to_String(value) - + defaultVal = env.subst(SCons.Util.to_String(option.default)) if option.converter: defaultVal = option.converter(defaultVal) @@ -238,12 +257,19 @@ class Options: actual = env.subst('${%s}' % opt.key) else: actual = None - return self.FormatOptionHelpText(env, opt.key, opt.help, opt.default, actual) + return self.FormatOptionHelpText(env, opt.key, opt.help, opt.default, actual, opt.aliases) lines = filter(None, map(format, options)) return string.join(lines, '') - format = '\n%s: %s\n default: %s\n actual: %s\n' + format = '\n%s: %s\n default: %s\n actual: %s\n' + format_ = '\n%s: %s\n default: %s\n actual: %s\n aliases: %s\n' + + def FormatOptionHelpText(self, env, key, help, default, actual, aliases=[]): + # Don't display the key name itself as an alias. + aliases = filter(lambda a, k=key: a != k, aliases) + if len(aliases)==0: + return self.format % (key, help, default, actual) + else: + return self.format_ % (key, help, default, actual, aliases) - def FormatOptionHelpText(self, env, key, help, default, actual): - return self.format % (key, help, default, actual) diff --git a/src/engine/SCons/Script/__init__.py b/src/engine/SCons/Script/__init__.py index 9288b859..8204c65f 100644 --- a/src/engine/SCons/Script/__init__.py +++ b/src/engine/SCons/Script/__init__.py @@ -313,6 +313,9 @@ GlobalDefaultEnvironmentFunctions = [ 'Split', 'TargetSignatures', 'Value', + 'Tag', + 'FindInstalledFiles', + 'FindSourceFiles', ] GlobalDefaultBuilders = [ @@ -340,6 +343,7 @@ GlobalDefaultBuilders = [ 'Tar', 'TypeLibrary', 'Zip', + 'Package', ] for name in GlobalDefaultEnvironmentFunctions + GlobalDefaultBuilders: diff --git a/src/engine/SCons/Tool/ToolTests.py b/src/engine/SCons/Tool/ToolTests.py index 52c032fe..b9230f14 100644 --- a/src/engine/SCons/Tool/ToolTests.py +++ b/src/engine/SCons/Tool/ToolTests.py @@ -67,7 +67,7 @@ class ToolTestCase(unittest.TestCase): try: p = SCons.Tool.Tool('_does_not_exist_') - except SCons.Errors.UserError: + except SCons.Errors.EnvironmentError: pass else: raise diff --git a/src/engine/SCons/Tool/__init__.py b/src/engine/SCons/Tool/__init__.py index dea77fd9..bf3df563 100644 --- a/src/engine/SCons/Tool/__init__.py +++ b/src/engine/SCons/Tool/__init__.py @@ -15,7 +15,7 @@ tool definition. # # __COPYRIGHT__ -# +# # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including @@ -88,8 +88,11 @@ class Tool: module = self._tool_module() self.generate = module.generate self.exists = module.exists + if hasattr(module, 'options'): + self.options = module.options def _tool_module(self): + # TODO: Interchange zipimport with normal initilization for better error reporting oldpythonpath = sys.path sys.path = self.toolpath + sys.path @@ -102,6 +105,8 @@ class Tool: if file: file.close() except ImportError, e: + if str(e)!="No module named %s"%self.name: + raise SCons.Errors.EnvironmentError, e try: import zipimport except ImportError: @@ -130,6 +135,8 @@ class Tool: file.close() return module except ImportError, e: + if e!="No module named %s"%self.name: + raise SCons.Errors.EnvironmentError, e try: import zipimport importer = zipimport.zipimporter( sys.modules['SCons.Tool'].__path__[0] ) @@ -138,10 +145,10 @@ class Tool: return module except ImportError, e: m = "No tool named '%s': %s" % (self.name, e) - raise SCons.Errors.UserError, m + raise SCons.Errors.EnvironmentError, m except ImportError, e: m = "No tool named '%s': %s" % (self.name, e) - raise SCons.Errors.UserError, m + raise SCons.Errors.EnvironmentError, m def __call__(self, env, *args, **kw): if self.init_kw is not None: @@ -154,6 +161,16 @@ class Tool: else: kw = self.init_kw env.Append(TOOLS = [ self.name ]) + if hasattr(self, 'options'): + from SCons.Options import Options + if not env.has_key('options'): + from SCons.Script import ARGUMENTS + env['options']=Options(args=ARGUMENTS) + opts=env['options'] + + self.options(opts) + opts.Update(env) + apply(self.generate, ( env, ) + args, kw) def __str__(self): @@ -341,7 +358,7 @@ def FindAllTools(tools, env): def ToolExists(tool, env=env): return Tool(tool).exists(env) return filter (ToolExists, tools) - + def tool_list(platform, env): # XXX this logic about what tool to prefer on which platform @@ -415,11 +432,11 @@ def tool_list(platform, env): ars = ['ar', 'mslib'] c_compiler = FindTool(c_compilers, env) or c_compilers[0] - + # XXX this logic about what tool provides what should somehow be # moved into the tool files themselves. if c_compiler and c_compiler == 'mingw': - # MinGW contains a linker, C compiler, C++ compiler, + # MinGW contains a linker, C compiler, C++ compiler, # Fortran compiler, archiver and assembler: cxx_compiler = None linker = None @@ -439,6 +456,7 @@ def tool_list(platform, env): other_tools = FindAllTools(['BitKeeper', 'CVS', 'dmd', + 'install', 'filesystem', 'dvipdf', 'dvips', 'gs', 'jar', 'javac', 'javah', 'latex', 'lex', @@ -449,11 +467,11 @@ def tool_list(platform, env): # 'Subversion', 'swig', 'tar', 'tex', - 'yacc', 'zip'], + 'yacc', 'zip', 'rpm', 'wix'], env) tools = ([linker, c_compiler, cxx_compiler, fortran_compiler, assembler, ar] + other_tools) - + return filter(lambda x: x, tools) diff --git a/src/engine/SCons/Tool/bcc32.py b/src/engine/SCons/Tool/bcc32.py index 86ca0764..517ea29e 100644 --- a/src/engine/SCons/Tool/bcc32.py +++ b/src/engine/SCons/Tool/bcc32.py @@ -42,12 +42,7 @@ def findIt(program, env): borwin = env.WhereIs(program) or SCons.Util.WhereIs(program) if borwin: dir = os.path.dirname(borwin) - path = env['ENV'].get('PATH', []) - if not path: - path = [] - if SCons.Util.is_String(path): - path = string.split(path, os.pathsep) - env['ENV']['PATH'] = string.join([dir]+path, os.pathsep) + env.PrependENVPath('PATH', dir) return borwin def generate(env): diff --git a/src/engine/SCons/Tool/cc.py b/src/engine/SCons/Tool/cc.py index d1a287ab..bccd8218 100644 --- a/src/engine/SCons/Tool/cc.py +++ b/src/engine/SCons/Tool/cc.py @@ -73,6 +73,18 @@ def generate(env): shared_obj.add_action(suffix, SCons.Defaults.ShCAction) static_obj.add_emitter(suffix, SCons.Defaults.StaticObjectEmitter) shared_obj.add_emitter(suffix, SCons.Defaults.SharedObjectEmitter) +#<<<<<<< .working +# +# env['_CCCOMCOM'] = '$CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS' +# # It's a hack to test for darwin here, but the alternative of creating +# # an applecc.py to contain this seems overkill. Maybe someday the Apple +# # platform will require more setup and this logic will be moved. +# env['FRAMEWORKS'] = SCons.Util.CLVar('') +# env['FRAMEWORKPATH'] = SCons.Util.CLVar('') +# if env['PLATFORM'] == 'darwin': +# env['_CCCOMCOM'] = env['_CCCOMCOM'] + ' $_FRAMEWORKPATH' +#======= +#>>>>>>> .merge-right.r1907 add_common_cc_variables(env) diff --git a/src/engine/SCons/Tool/filesystem.py b/src/engine/SCons/Tool/filesystem.py new file mode 100644 index 00000000..5e631fdc --- /dev/null +++ b/src/engine/SCons/Tool/filesystem.py @@ -0,0 +1,89 @@ +"""SCons.Tool.filesystem + +Tool-specific initialization for the filesystem tools. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. +""" + +# +# __COPYRIGHT__ +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# +import SCons +from SCons.Tool.install import copyFunc + +copyToBuilder, copyAsBuilder = None, None + +def copyto_emitter(target, source, env): + """ changes the path of the source to be under the target (which + are assumed to be directories. + """ + n_target = [] + + for t in target: + n_target = n_target + map( lambda s, t=t: t.File( str( s ) ), source ) + + return (n_target, source) + +def copy_action_func(target, source, env): + assert( len(target) == len(source) ), "\ntarget: %s\nsource: %s" %(map(str, target),map(str, source)) + + for t, s in zip(target, source): + if copyFunc(t.get_path(), s.get_path(), env): + return 1 + + return 0 + +def copy_action_str(target, source, env): + return env.subst_target_source(env['COPYSTR'], 0, target, source) + +copy_action = SCons.Action.Action( copy_action_func, copy_action_str ) + +def generate(env): + try: + env['BUILDERS']['CopyTo'] + env['BUILDERS']['CopyAs'] + except KeyError, e: + global copyToBuilder + if copyToBuilder is None: + copyToBuilder = SCons.Builder.Builder( + action = copy_action, + target_factory = env.fs.Dir, + source_factory = env.fs.Entry, + multi = 1, + emitter = [ copyto_emitter, ] ) + + global copyAsBuilder + if copyAsBuilder is None: + copyAsBuilder = SCons.Builder.Builder( + action = copy_action, + target_factory = env.fs.Entry, + source_factory = env.fs.Entry ) + + env['BUILDERS']['CopyTo'] = copyToBuilder + env['BUILDERS']['CopyAs'] = copyAsBuilder + + env['COPYSTR'] = 'Copy file(s): "$SOURCES" to "$TARGETS"' + +def exists(env): + return 1 diff --git a/src/engine/SCons/Tool/install.py b/src/engine/SCons/Tool/install.py new file mode 100644 index 00000000..5083c9f8 --- /dev/null +++ b/src/engine/SCons/Tool/install.py @@ -0,0 +1,221 @@ +"""SCons.Tool.install + +Tool-specific initialization for the install tool. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. +""" + +# +# __COPYRIGHT__ +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# +import SCons.Action +import shutil, os, stat +from SCons.Util import make_path_relative + +# +# We keep track of *all* installed files. +_INSTALLED_FILES = [] + +# +# Functions doing the actual work of the Install Builder. +# +def copyFunc(dest, source, env): + """Install a source file or directory into a destination by copying, + (including copying permission/mode bits).""" + + if os.path.isdir(source): + if os.path.exists(dest): + if not os.path.isdir(dest): + raise SCons.Errors.UserError, "cannot overwrite non-directory `%s' with a directory `%s'" % (str(dest), str(source)) + else: + parent = os.path.split(dest)[0] + if not os.path.exists(parent): + os.makedirs(parent) + shutil.copytree(source, dest) + else: + shutil.copy2(source, dest) + st = os.stat(source) + os.chmod(dest, stat.S_IMODE(st[stat.ST_MODE]) | stat.S_IWRITE) + + return 0 + +def installFunc(target, source, env): + """Install a source file into a target using the function specified + as the INSTALL construction variable.""" + try: + install = env['INSTALL'] + except KeyError: + raise SCons.Errors.UserError('Missing INSTALL construction variable.') + + assert( len(target)==len(source) ) + for t,s in zip(target,source): + if install(t.get_path(),s.get_path(),env): + return 1 + + return 0 + +def stringFunc(target, source, env): + installstr = env.get('INSTALLSTR') + if installstr: + return env.subst_target_source(installstr, 0, target, source) + target = str(target[0]) + source = str(source[0]) + if os.path.isdir(source): + type = 'directory' + else: + type = 'file' + return 'Install %s: "%s" as "%s"' % (type, source, target) + +# +# Emitter functions +# +def add_targets_to_INSTALLED_FILES(target, source, env): + """ an emitter that adds all target files to the list stored in the + _INSTALLED_FILES global variable. This way all installed files of one + scons call will be collected. + """ + global _INSTALLED_FILES + files = _INSTALLED_FILES + #files.extend( [ x for x in target if not x in files ] ) + for x in target: + if not x in files: + files.append(x) + return (target, source) + +class DESTDIR_factory: + """ a node factory, where all files will be relative to the dir supplied + in the constructor. + """ + def __init__(self, env, dir): + self.env = env + self.dir = env.arg2nodes( dir, env.fs.Dir )[0] + + def Entry(self, name): + name = make_path_relative(name) + return self.dir.Entry(name) + + def Dir(self, name): + name = make_path_relative(name) + return self.dir.Dir(name) + +# +# The Builder Definition +# +install_action = SCons.Action.Action(installFunc, stringFunc) +installas_action = SCons.Action.Action(installFunc, stringFunc) + +InstallBuilder, InstallAsBuilder = None, None +BaseInstallBuilder = None + +added = None + +def generate(env): + + from SCons.Script import AddOption, GetOption + global added + if not added: + added = 1 + AddOption('--install-sandbox', + dest='install_sandbox', + type="string", + action="store", + help='A directory under which all installed files will be placed.') + + try: + env['BUILDERS']['Install'] + env['BUILDERS']['InstallAs'] + + except KeyError, e: + install_sandbox = GetOption('install_sandbox') + if install_sandbox: + target_factory = DESTDIR_factory(env, install_sandbox) + else: + target_factory = env.fs + + global BaseInstallBuilder + if BaseInstallBuilder is None: + BaseInstallBuilder = SCons.Builder.Builder( + action = install_action, + target_factory = target_factory.Entry, + source_factory = env.fs.Entry, + multi = 1, + emitter = [ add_targets_to_INSTALLED_FILES, ], + name = 'InstallBuilder') + + global InstallBuilder + if InstallBuilder is None: + def InstallBuilderWrapper(env, target, source, dir=None, target_factory=target_factory): + if target and dir: + raise SCons.Errors.UserError, "Both target and dir defined for Install(), only one may be defined." + if not dir: + dir=target + try: + dnodes = env.arg2nodes(dir, target_factory.Dir) + except TypeError: + raise SCons.Errors.UserError, "Target `%s' of Install() is a file, but should be a directory. Perhaps you have the Install() arguments backwards?" % str(dir) + sources = env.arg2nodes(source, env.fs.Entry) + tgt = [] + for dnode in dnodes: + for src in sources: + # Prepend './' so the lookup doesn't interpret an initial + # '#' on the file name portion as meaning the Node should + # be relative to the top-level SConstruct directory. + target = env.fs.Entry('.'+os.sep+src.name, dnode) + tgt.extend(BaseInstallBuilder(env, target, src)) + return tgt + + InstallBuilder = InstallBuilderWrapper + + global InstallAsBuilder + if InstallAsBuilder is None: + def InstallAsBuilderWrapper(env, target, source): + result = [] + for src, tgt in map(lambda x, y: (x, y), source, target): + result.extend(BaseInstallBuilder(env, tgt, src)) + return result + + InstallAsBuilder = InstallAsBuilderWrapper + + env['BUILDERS']['Install'] = InstallBuilder + env['BUILDERS']['InstallAs'] = InstallAsBuilder + + # We'd like to initialize this doing something like the following, + # but there isn't yet support for a ${SOURCE.type} expansion that + # will print "file" or "directory" depending on what's being + # installed. For now we punt by not initializing it, and letting + # the stringFunc() that we put in the action fall back to the + # hand-crafted default string if it's not set. + # + #try: + # env['INSTALLSTR'] + #except KeyError: + # env['INSTALLSTR'] = 'Install ${SOURCE.type}: "$SOURCES" as "$TARGETS"' + + try: + env['INSTALL'] + except KeyError: + env['INSTALL'] = copyFunc + +def exists(env): + return 1 diff --git a/src/engine/SCons/Tool/ipkg.py b/src/engine/SCons/Tool/ipkg.py new file mode 100644 index 00000000..3f6ca23b --- /dev/null +++ b/src/engine/SCons/Tool/ipkg.py @@ -0,0 +1,61 @@ +"""SCons.Tool.ipkg + +Tool-specific initialization for ipkg. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +The ipkg tool calls the ipkg-build. Its only argument should be the +packages fake_root. +""" + +# +# __COPYRIGHT__ +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +import SCons.Builder +from os import popen +from os.path import dirname + +def generate(env): + """Add Builders and construction variables for ipkg to an Environment.""" + try: + bld = env['BUILDERS']['Ipkg'] + except KeyError: + bld = SCons.Builder.Builder( action = '$IPKGCOM', + suffix = '$IPKGSUFFIX', + source_scanner = None, + target_scanner = None) + env['BUILDERS']['Ipkg'] = bld + + env['IPKG'] = 'ipkg-build' + env['IPKGCOM'] = '$IPKG $IPKGFLAGS ${SOURCE}' + env['IPKGUSER'] = popen('id -un').read().strip() + env['IPKGGROUP'] = popen('id -gn').read().strip() + env['IPKGFLAGS'] = SCons.Util.CLVar('-o $IPKGUSER -g $IPKGGROUP') + env['IPKGSUFFIX'] = '.ipk' + +def exists(env): + return env.Detect('ipkg-build') diff --git a/src/engine/SCons/Tool/link.py b/src/engine/SCons/Tool/link.py index b9fb71d7..be1a81aa 100644 --- a/src/engine/SCons/Tool/link.py +++ b/src/engine/SCons/Tool/link.py @@ -48,7 +48,7 @@ def generate(env): """Add Builders and construction variables for gnulink to an Environment.""" SCons.Tool.createSharedLibBuilder(env) SCons.Tool.createProgBuilder(env) - + env['SHLINK'] = '$LINK' env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -shared') env['SHLINKCOM'] = '$SHLINK -o $TARGET $SHLINKFLAGS $SOURCES $_LIBDIRFLAGS $_LIBFLAGS' diff --git a/src/engine/SCons/Tool/linkloc.py b/src/engine/SCons/Tool/linkloc.py index f7c2c5a3..f5256c96 100644 --- a/src/engine/SCons/Tool/linkloc.py +++ b/src/engine/SCons/Tool/linkloc.py @@ -96,8 +96,8 @@ def generate(env): msvs_version = env.get('MSVS_VERSION') include_path, lib_path, exe_path = get_msvc_paths(env, version = msvs_version) - env['ENV']['LIB'] = lib_path - env['ENV']['PATH'] = exe_path + env['ENV']['LIB'] = lib_path + env.PrependENVPath('PATH', exe_path) addPharLapPaths(env) diff --git a/src/engine/SCons/Tool/mingw.py b/src/engine/SCons/Tool/mingw.py index cc7f5845..359c40ff 100644 --- a/src/engine/SCons/Tool/mingw.py +++ b/src/engine/SCons/Tool/mingw.py @@ -107,15 +107,7 @@ def generate(env): mingw = find(env) if mingw: dir = os.path.dirname(mingw) - - # The mingw bin directory must be added to the path: - path = env['ENV'].get('PATH', []) - if not path: - path = [] - if SCons.Util.is_String(path): - path = string.split(path, os.pathsep) - - env['ENV']['PATH'] = string.join([dir] + path, os.pathsep) + env.PrependENVPath('PATH', dir ) # Most of mingw is the same as gcc and friends... diff --git a/src/engine/SCons/Tool/packaging.xml b/src/engine/SCons/Tool/packaging.xml new file mode 100644 index 00000000..e2d0fb47 --- /dev/null +++ b/src/engine/SCons/Tool/packaging.xml @@ -0,0 +1,73 @@ + + + +A framework for building binary and source packages. + + + + + +Builds a Binary Package of the given source files. + + +env.Package(source = FindInstalledFiles()) + + + + + + +The Java archive tool. + + + + + +The directory to which the Java archive tool should change +(using the + +option). + + + + + +The command line used to call the Java archive tool. + + + + + +The string displayed when the Java archive tool +is called +If this is not set, then &cv-JARCOM; (the command line) is displayed. + + +env = Environment(JARCOMSTR = "JARchiving $SOURCES into $TARGET") + + + + + + +General options passed to the Java archive tool. +By default this is set to + +to create the necessary +jar +file. + + + + + +The suffix for Java archives: +.jar +by default. + + diff --git a/src/engine/SCons/Tool/packaging/__init__.py b/src/engine/SCons/Tool/packaging/__init__.py new file mode 100644 index 00000000..c5f40b7c --- /dev/null +++ b/src/engine/SCons/Tool/packaging/__init__.py @@ -0,0 +1,332 @@ +"""SCons.Tool.Packaging + +SCons Packaging Tool. +""" + +# +# __COPYRIGHT__ +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +import SCons.Environment +from SCons.Options import * +from SCons.Errors import * +from SCons.Util import is_List, make_path_relative + +import os, imp +import SCons.Defaults + +__all__ = [ 'src_targz', 'src_tarbz2', 'src_zip', 'tarbz2', 'targz', 'zip', 'rpm', 'msi', 'ipk' ] + +# +# Utility and Builder function +# +def Tag(env, target, source, *more_tags, **kw_tags): + """ Tag a file with the given arguments, just sets the accordingly named + attribute on the file object. + + TODO: FIXME + """ + if not target: + target=source + first_tag=None + else: + first_tag=source + + if first_tag: + kw_tags[first_tag[0]] = '' + + if len(kw_tags) == 0 and len(more_tags) == 0: + raise UserError, "No tags given." + + # XXX: sanity checks + for x in more_tags: + kw_tags[x] = '' + + if not SCons.Util.is_List(target): + target=[target] + else: + # hmm, sometimes the target list, is a list of a list + # make sure it is flattened prior to processing. + # TODO: perhaps some bug ?!? + target=env.Flatten(target) + + for t in target: + for (k,v) in kw_tags.items(): + # all file tags have to start with PACKAGING_, so we can later + # differentiate between "normal" object attributes and the + # packaging attributes. As the user should not be bothered with + # that, the prefix will be added here if missing. + #if not k.startswith('PACKAGING_'): + if k[:10] != 'PACKAGING_': + k='PACKAGING_'+k + setattr(t, k, v) + +def Package(env, target=None, source=None, **kw): + """ Entry point for the package tool. + """ + # first check some arguments + if not source: + source = env.FindInstalledFiles() + + if len(source)==0: + raise UserError, "No source for Package() given" + + # has the option for this Tool been set? + try: kw['PACKAGETYPE']=env['PACKAGETYPE'] + except KeyError: pass + + if not kw.has_key('PACKAGETYPE') or kw['PACKAGETYPE']==None: + if env['BUILDERS'].has_key('Tar'): + kw['PACKAGETYPE']='targz' + elif env['BUILDERS'].has_key('Zip'): + kw['PACKAGETYPE']='zip' + else: + raise UserError, "No type for Package() given" + PACKAGETYPE=kw['PACKAGETYPE'] + if not is_List(PACKAGETYPE): + #PACKAGETYPE=PACKAGETYPE.split(',') + PACKAGETYPE=string.split(PACKAGETYPE, ',') + + # now load the needed packagers. + def load_packager(type): + try: + file,path,desc=imp.find_module(type, __path__) + return imp.load_module(type, file, path, desc) + except ImportError, e: + raise EnvironmentError("packager %s not available: %s"%(type,str(e))) + + packagers=map(load_packager, PACKAGETYPE) + + # now try to setup the default_target and the default PACKAGEROOT + # arguments. + try: + # fill up the target list with a default target name until the PACKAGETYPE + # list is of the same size as the target list. + if target==None or target==[]: + target=["%(NAME)s-%(VERSION)s"%kw] + + size_diff=len(PACKAGETYPE)-len(target) + if size_diff>0: + target.extend([target]*size_diff) + + if not kw.has_key('PACKAGEROOT'): + kw['PACKAGEROOT']="%(NAME)s-%(VERSION)s"%kw + + except KeyError, e: + raise SCons.Errors.UserError( "Missing PackageTag '%s'"%e.args[0] ) + + # setup the source files + source=env.arg2nodes(source, env.fs.Entry) + + # call the packager to setup the dependencies. + targets=[] + try: + for packager in packagers: + t=apply(packager.package, [env,target,source], kw) + targets.extend(t) + + except KeyError, e: + raise SCons.Errors.UserError( "Missing PackageTag '%s' for %s packager"\ + % (e.args[0],packager.__name__) ) + except TypeError, e: + # this exception means that a needed argument for the packager is + # missing. As our packagers get their "tags" as named function + # arguments we need to find out which one is missing. + from inspect import getargspec + args,varargs,varkw,defaults=getargspec(packager.package) + if defaults!=None: + args=args[:-len(defaults)] # throw away arguments with default values + map(args.remove, 'env target source'.split()) + # now remove any args for which we have a value in kw. + #args=[x for x in args if not kw.has_key(x)] + args=filter(lambda x, kw=kw: not kw.has_key(x), args) + + if len(args)==0: + raise # must be a different error, so reraise + elif len(args)==1: + raise SCons.Errors.UserError( "Missing PackageTag '%s' for %s packager"\ + % (args[0],packager.__name__) ) + else: + raise SCons.Errors.UserError( "Missing PackageTags '%s' for %s packager"\ + % (", ".join(args),packager.__name__) ) + + target=env.arg2nodes(target, env.fs.Entry) + targets.extend(env.Alias( 'package', targets )) + return targets + +def FindSourceFiles(env, target=None, source=None ): + """ returns a list of all children of the target nodes, which have no + children. This selects all leaves of the DAG that gets build by SCons for + handling dependencies. + """ + if target==None: target = '.' + + nodes = env.arg2nodes(target, env.fs.Entry) + + sources = [] + def build_source(ss): + for s in ss: + if s.__class__==SCons.Node.FS.Dir: + build_source(s.all_children()) + elif not s.has_builder() and s.__class__==SCons.Node.FS.File: + sources.append(s) + else: + build_source(s.sources) + + for node in nodes: + build_source(node.all_children()) + + # now strip the build_node from the sources by calling the srcnode + # function + def get_final_srcnode(file): + srcnode = file.srcnode() + while srcnode != file.srcnode(): + srcnode = file.srcnode() + return srcnode + + # get the final srcnode for all nodes, this means stripping any + # attached build node. + map( get_final_srcnode, sources ) + + # remove duplicates + return list(set(sources)) + +def FindInstalledFiles(env, source=[], target=[]): + """ returns the list of all targets of the Install and InstallAs Builder. + """ + from SCons.Tool import install + return install._INSTALLED_FILES + +# +# SCons tool initialization functions +# +def generate(env): + try: + env['BUILDERS']['Package'] + env['BUILDERS']['Tag'] + env['BUILDERS']['FindSourceFiles'] + env['BUILDERS']['FindInstalledFiles'] + except KeyError: + env['BUILDERS']['Package'] = Package + env['BUILDERS']['Tag'] = Tag + env['BUILDERS']['FindSourceFiles'] = FindSourceFiles + env['BUILDERS']['FindInstalledFiles'] = FindInstalledFiles + +def exists(env): + return 1 + +def options(opts): + opts.AddOptions( + EnumOption( [ 'PACKAGETYPE', '--package-type' ], + 'the type of package to create.', + None, allowed_values=map( str, __all__ ), + ignorecase=2 + ) + ) + +def copy_attr(f1, f2): + """ copies the special packaging file attributes from f1 to f2. + """ + #pattrs = [x for x in dir(f1) if not hasattr(f2, x) and\ + # x.startswith('PACKAGING_')] + copyit = lambda x, f2=f2: not hasattr(f2, x) and x[:10] == 'PACKAGING_' + pattrs = filter(copyit, dir(f1)) + for attr in pattrs: + setattr(f2, attr, getattr(f1, attr)) +# +# Emitter functions which are reused by the various packagers +# +def packageroot_emitter(pkg_root, honor_install_location=1): + """ creates the packageroot emitter. + + The package root emitter uses the CopyAs builder to copy all source files + to the directory given in pkg_root. + + If honor_install_location is set and the copied source file has an + PACKAGING_INSTALL_LOCATION attribute, the PACKAGING_INSTALL_LOCATION is + used as the new name of the source file under pkg_root. + + The source file will not be copied if it is already under the the pkg_root + directory. + + All attributes of the source file will be copied to the new file. + """ + def package_root_emitter(target, source, env, pkg_root=pkg_root, honor_install_location=honor_install_location): + pkgroot = pkg_root + # make sure the packageroot is a Dir object. + if SCons.Util.is_String(pkgroot): pkgroot=env.Dir(pkgroot) + + def copy_file_to_pkg_root(file, env=env, pkgroot=pkgroot, honor_install_location=honor_install_location): + if file.is_under(pkgroot): + return file + else: + if hasattr(file, 'PACKAGING_INSTALL_LOCATION') and\ + honor_install_location: + new_name=make_path_relative(file.PACKAGING_INSTALL_LOCATION) + else: + new_name=make_path_relative(file.get_path()) + + new_file=pkgroot.File(new_name) + new_file=env.CopyAs(new_file, file)[0] + + copy_attr(file, new_file) + + return new_file + return (target, map(copy_file_to_pkg_root, source)) + return package_root_emitter + +from SCons.Warnings import warn, Warning + +def stripinstall_emitter(): + """ create the a emitter which: + * strips of the Install Builder of the source target, and stores the + install location as the "PACKAGING_INSTALL_LOCATION" of the given source + File object. This effectively avoids having to execute the Install + Action while storing the needed install location. + * warns about files that are mangled by this emitter which have no + Install Builder. + """ + def strip_install_emitter(target, source, env): + def has_no_install_location(file): + return not (file.has_builder() and\ + hasattr(file.builder, 'name') and\ + (file.builder.name=="InstallBuilder" or\ + file.builder.name=="InstallAsBuilder")) + + if len(filter(has_no_install_location, source)): + warn(Warning, "there are file to package which have no\ + InstallBuilder attached, this might lead to irreproducible packages") + + n_source=[] + for s in source: + if has_no_install_location(s): + n_source.append(s) + else: + for ss in s.sources: + n_source.append(ss) + copy_attr(s, ss) + setattr(ss, 'PACKAGING_INSTALL_LOCATION', s.get_path()) + + return (target, n_source) + return strip_install_emitter diff --git a/src/engine/SCons/Tool/packaging/__init__.xml b/src/engine/SCons/Tool/packaging/__init__.xml new file mode 100644 index 00000000..8e7f652a --- /dev/null +++ b/src/engine/SCons/Tool/packaging/__init__.xml @@ -0,0 +1,522 @@ + + + +TODO + + + + +Builds software distribution packages. Packages consist of files to install +and packaging information. The former may be specified with the source +parameter and may be left out, in which case the &-bFindInstalledFiles; +function will collect all files that have an &-bInstall; or +&-bInstallAs; Builder attached. The target, if not specified will be deduced +from additional information given to this Builder. + +The packaging information is specified with the help of construction Variables +documented below. This information is called a tag to stress that some of them +can also be attached to files with the &-bTag; Builder.The mandatory ones will +complain if they were not specified. They vary depending on chosen target +packager. + +The target packager may be selected with the "PACKAGETYPE" command line option +or with the &-tPACKAGETYPE; construction variable. Currently there are six +packagers available: + + * msi - Microsoft Installer + * rpm - Redhat Package Manger + * ipkg - Itsy Package Management System + * tarbz, tarbz and zip + +An updated list is always available under the "package_type" option when +running "scons --help" on a project that has packaging activated. + + +env = Environment(tools=['default', 'packaging']) +env.Install('/bin/', 'my_program') +env.Package( NAME = 'foo', + VERSION = '1.2.3', + PACKAGEVERSION = 0, + PACKAGETYPE = 'rpm', + LICENSE = 'gpl', + SUMMARY = 'balalalalal', + DESCRIPTION = 'this should be really really long', + X_RPM_GROUP = 'Application/fu', + SOURCE_URL = 'http://foo.org/foo-1.2.3.tar.gz' + ) + + + + +A long description of what the project is about. + + + + + +TODO + + + + + + +A short summary of what the project is about. + + + + + +The shorthand of the license this project is under (gpl, lpgl, bsd etc.). + + + + + +Specfies the name of the project to package. + + + + + +The version of the project, given as a string. + + + + + +The version of the package, if only changes in the package were done. Currently +only used by the rpm packager. + + + + + +Selects the package type to build. Currently those are available: + + * msi - Microsoft Installer + * rpm - Redhat Package Manger + * ipkg - Itsy Package Management System + * tarbz2, targz and zip - tarball and zip packager + * src_tarbz2, src_targz and src_zip - source tarbarll and zip packager + +This may be overridden with the "package_type" command line option. + + + + + +TODO + + + + + +TODO + + + + + +TODO + + + + + +defines the directory where all files in resulting archive will be placed if +applicable. The default value is "$NAME-$VERSION". + + + + + +Short name of the license your package is under. Example: gpl, lgpl, bsd ... +See http://www.opensource.org/licenses/alphabetical + + + + + +TODO + + + + + +TODO + + + + + +TODO + + + + + +TODO + + + + + +TODO + + + + + +TODO + + + + + +TODO + + + + + +TODO + + + + + +internal, but overridable + + + + + +internal, but overridable + + + + + +internal, but overridable + + + + + +internal, but overridable + + + + + +TODO + + + + + +TODO + + + + + +TODO + + + + + +TODO + + + + + +TODO + + + + + +TODO + + + + + +TODO + + + + + +TODO + + + + + +TODO + + + + + +TODO + + + + + +TODO + + + + + +TODO + + + + + +TODO + + + + + +TODO + + + + + +TODO + + + + + +TODO + + + + + +TODO + + + + + +internal, but overridable + + + + + +TODO + + + + + +The text of the software license in rtf format. Carriage return chars will be +replaced with the rtf equivalent \\par. + + + + + +TODO + + + + + +TODO + + + + + +TODO + + + + + +TODO + + + + + +TODO + + + + + +TODO + + + + + +default is "$SUMMARY\n$DESCRIPTION" + + + + + +TODO + + + + + +TODO + + + + + +TODO + + + + + +TODO + + + + + + +Leaves hints for the Package() Builder on how specific should be packaged. +All those tags are optional. + +Tag( Library( 'lib.c' ), unix-attr="0644" ) # makes sure the built library will + # be installed with 0644 file + # access mode +Tag( 'file2.txt', doc ) # marks file2.txt to be a documentation file + + + + +internal, but overridable, TODO + + + + + +TODO + + + + + +TODO + + + + + +TODO + + + + + +TODO + + + + + +TODO + + + + + +TODO + + + + + +TODO + + + + + +TODO + + + + + +TODO + + + + + +TODO + + + + + +TODO + + + + + +TODO + + + + + +TODO + + + + + +TODO + + + + + +TODO + + + + + + +A convenience function which returns all leafs of the build tree. + + + +Returns all files "build" by the install builder. + + + + diff --git a/src/engine/SCons/Tool/packaging/ipk.py b/src/engine/SCons/Tool/packaging/ipk.py new file mode 100644 index 00000000..267fe3c6 --- /dev/null +++ b/src/engine/SCons/Tool/packaging/ipk.py @@ -0,0 +1,179 @@ +"""SCons.Tool.Packaging.ipk +""" + +# +# __COPYRIGHT__ +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +import SCons.Builder +import SCons.Node.FS +import os + +from SCons.Tool.packaging import stripinstall_emitter, packageroot_emitter + +def package(env, target, source, PACKAGEROOT, NAME, VERSION, DESCRIPTION, + SUMMARY, X_IPK_PRIORITY, X_IPK_SECTION, SOURCE_URL, + X_IPK_MAINTAINER, X_IPK_DEPENDS, **kw): + """ this function prepares the packageroot directory for packaging with the + ipkg builder. + """ + SCons.Tool.Tool('ipkg').generate(env) + + # setup the Ipkg builder + bld = env['BUILDERS']['Ipkg'] + bld.push_emitter(packageroot_emitter(PACKAGEROOT)) + bld.push_emitter(stripinstall_emitter()) + + # This should be overridable from the construction environment, + # which it is by using ARCHITECTURE=. + # Guessing based on what os.uname() returns at least allows it + # to work for both i386 and x86_64 Linux systems. + archmap = { + 'i686' : 'i386', + 'i586' : 'i386', + 'i486' : 'i386', + } + + buildarchitecture = os.uname()[4] + buildarchitecture = archmap.get(buildarchitecture, buildarchitecture) + + if kw.has_key('ARCHITECTURE'): + buildarchitecture = kw['ARCHITECTURE'] + + # setup the kw to contain the mandatory arguments to this fucntion. + # do this before calling any builder or setup function + loc=locals() + del loc['kw'] + kw.update(loc) + del kw['source'], kw['target'], kw['env'] + + # generate the specfile + specfile = gen_ipk_dir(PACKAGEROOT, source, env, kw) + + # override the default target. + if str(target[0])=="%s-%s"%(NAME, VERSION): + target=[ "%s_%s_%s.ipk"%(NAME, VERSION, buildarchitecture) ] + + # now apply the Ipkg builder + return apply(bld, [env, target, specfile], kw) + +def gen_ipk_dir(proot, source, env, kw): + # make sure the packageroot is a Dir object. + if SCons.Util.is_String(proot): proot=env.Dir(proot) + + # create the specfile builder + s_bld=SCons.Builder.Builder( + action = build_specfiles, + emitter = [stripinstall_emitter(), packageroot_emitter(proot)]) + + # create the specfile targets + spec_target=[] + control=proot.Dir('CONTROL') + spec_target.append(control.File('control')) + spec_target.append(control.File('conffiles')) + spec_target.append(control.File('postrm')) + spec_target.append(control.File('prerm')) + spec_target.append(control.File('postinst')) + spec_target.append(control.File('preinst')) + + # apply the builder to the specfile targets + apply(s_bld, [env, spec_target, source], kw) + + # the packageroot directory does now contain the specfiles. + return proot + +def build_specfiles(source, target, env): + """ filter the targets for the needed files and use the variables in env + to create the specfile. + """ + # + # At first we care for the CONTROL/control file, which is the main file for ipk. + # + # For this we need to open multiple files in random order, so we store into + # a dict so they can be easily accessed. + # + # + opened_files={} + def open_file(needle, haystack): + try: + return opened_files[needle] + except KeyError: + file=filter(lambda x: x.get_path().rfind(needle)!=-1, haystack)[0] + opened_files[needle]=open(file.abspath, 'w') + return opened_files[needle] + + control_file=open_file('control', target) + + if not env.has_key('X_IPK_DESCRIPTION'): + env['X_IPK_DESCRIPTION']="%s\n %s"%(env['SUMMARY'], + env['DESCRIPTION'].replace('\n', '\n ')) + + + content = """ +Package: $NAME +Version: $VERSION +Priority: $X_IPK_PRIORITY +Section: $X_IPK_SECTION +Source: $SOURCE_URL +Architecture: $ARCHITECTURE +Maintainer: $X_IPK_MAINTAINER +Depends: $X_IPK_DEPENDS +Description: $X_IPK_DESCRIPTION +""" + + control_file.write(env.subst(content)) + + # + # now handle the various other files, which purpose it is to set post-, + # pre-scripts and mark files as config files. + # + # We do so by filtering the source files for files which are marked with + # the "config" tag and afterwards we do the same for x_ipk_postrm, + # x_ipk_prerm, x_ipk_postinst and x_ipk_preinst tags. + # + # The first one will write the name of the file into the file + # CONTROL/configfiles, the latter add the content of the x_ipk_* variable + # into the same named file. + # + for f in [x for x in source if 'PACKAGING_CONFIG' in dir(x)]: + config=open_file('conffiles') + config.write(f.PACKAGING_INSTALL_LOCATION) + config.write('\n') + + for str in 'POSTRM PRERM POSTINST PREINST'.split(): + name="PACKAGING_X_IPK_%s"%str + for f in [x for x in source if name in dir(x)]: + file=open_file(name) + file.write(env[str]) + + # + # close all opened files + for f in opened_files.values(): + f.close() + + # call a user specified function + if env.has_key('CHANGE_SPECFILE'): + content += env['CHANGE_SPECFILE'](target) + + return 0 diff --git a/src/engine/SCons/Tool/packaging/msi.py b/src/engine/SCons/Tool/packaging/msi.py new file mode 100644 index 00000000..7fc7892a --- /dev/null +++ b/src/engine/SCons/Tool/packaging/msi.py @@ -0,0 +1,519 @@ +"""SCons.Tool.packaging.msi + +The msi packager. +""" + +# +# __COPYRIGHT__ +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +import os +import SCons +from SCons.Action import Action +from SCons.Builder import Builder + +from xml.dom.minidom import * +from xml.sax.saxutils import escape + +from SCons.Tool.packaging import stripinstall_emitter + +# +# Utility functions +# +def convert_to_id(s, id_set): + """ Some parts of .wxs need an Id attribute (for example: The File and + Directory directives. The charset is limited to A-Z, a-z, digits, + underscores, periods. Each Id must begin with a letter or with a + underscore. Google for "CNDL0015" for information about this. + + Requirements: + * the string created must only contain chars from the target charset. + * the string created must have a minimal editing distance from the + original string. + * the string created must be unique for the whole .wxs file. + + Observation: + * There are 62 chars in the charset. + + Idea: + * filter out forbidden characters. Check for a collision with the help + of the id_set. Add the number of the number of the collision at the + end of the created string. Furthermore care for a correct start of + the string. + """ + charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYabcdefghijklmnopqrstuvwxyz0123456789_.' + if s[0] in '0123456789.': + s += '_'+s + id = filter( lambda c : c in charset, s ) + + # did we already generate an id for this file? + try: + return id_set[id][s] + except KeyError: + # no we did not so initialize with the id + if not id_set.has_key(id): id_set[id] = { s : id } + # there is a collision, generate an id which is unique by appending + # the collision number + else: id_set[id][s] = id + str(len(id_set[id])) + + return id_set[id][s] + +def is_dos_short_file_name(file): + """ examine if the given file is in the 8.3 form. + """ + fname, ext = os.path.splitext(file) + proper_ext = len(ext) == 0 or (2 <= len(ext) <= 4) # the ext contains the dot + proper_fname = file.isupper() and len(fname) <= 8 + + return proper_ext and proper_fname + +def gen_dos_short_file_name(file, filename_set): + """ see http://support.microsoft.com/default.aspx?scid=kb;en-us;Q142982 + + These are no complete 8.3 dos short names. The ~ char is missing and + replaced with one character from the filename. WiX warns about such + filenames, since a collision might occur. Google for "CNDL1014" for + more information. + """ + # guard this to not confuse the generation + if is_dos_short_file_name(file): + return file + + fname, ext = os.path.splitext(file) # ext contains the dot + + # first try if it suffices to convert to upper + file = file.upper() + if is_dos_short_file_name(file): + return file + + # strip forbidden characters. + forbidden = '."/[]:;=, ' + fname = filter( lambda c : c not in forbidden, fname ) + + # check if we already generated a filename with the same number: + # thisis1.txt, thisis2.txt etc. + duplicate, num = not None, 1 + while duplicate: + shortname = "%s%s" % (fname[:8-len(str(num))].upper(),\ + str(num)) + if len(ext) >= 2: + shortname = "%s%s" % (shortname, ext[:4].upper()) + + duplicate, num = shortname in filename_set, num+1 + + assert( is_dos_short_file_name(shortname) ), 'shortname is %s, longname is %s' % (shortname, file) + filename_set.append(shortname) + return shortname + +def create_feature_dict(files): + """ X_MSI_FEATURE and doc FileTag's can be used to collect files in a + hierarchy. This function collects the files into this hierarchy. + """ + dict = {} + + def add_to_dict( feature, file ): + if not SCons.Util.is_List( feature ): + feature = [ feature ] + + for f in feature: + if not dict.has_key( f ): + dict[ f ] = [ file ] + else: + dict[ f ].append( file ) + + for file in files: + if hasattr( file, 'PACKAGING_X_MSI_FEATURE' ): + add_to_dict(file.PACKAGING_X_MSI_FEATURE, file) + elif hasattr( file, 'PACKAGING_DOC' ): + add_to_dict( 'PACKAGING_DOC', file ) + else: + add_to_dict( 'default', file ) + + return dict + +def generate_guids(root): + """ generates globally unique identifiers for parts of the xml which need + them. + + Component tags have a special requirement. Their UUID is only allowed to + change if the list of their contained resources has changed. This allows + for clean removal and proper updates. + + To handle this requirement, the uuid is generated with an md5 hashing the + whole subtree of a xml node. + """ + from md5 import md5 + + # specify which tags need a guid and in which attribute this should be stored. + needs_id = { 'Product' : 'Id', + 'Package' : 'Id', + 'Component' : 'Guid', + } + + # find all XMl nodes matching the key, retrieve their attribute, hash their + # subtree, convert hash to string and add as a attribute to the xml node. + for (key,value) in needs_id.items(): + node_list = root.getElementsByTagName(key) + attribute = value + for node in node_list: + hash = md5(node.toxml()).hexdigest() + hash_str = '%s-%s-%s-%s-%s' % ( hash[:8], hash[8:12], hash[12:16], hash[16:20], hash[20:] ) + node.attributes[attribute] = hash_str + + + +def string_wxsfile(target, source, env): + return "building WiX file %s"%( target[0].path ) + +def build_wxsfile(target, source, env): + """ compiles a .wxs file from the keywords given in env['msi_spec'] and + by analyzing the tree of source nodes and their tags. + """ + file = open(target[0].abspath, 'w') + + try: + # Create a document with the Wix root tag + doc = Document() + root = doc.createElement( 'Wix' ) + root.attributes['xmlns']='http://schemas.microsoft.com/wix/2003/01/wi' + doc.appendChild( root ) + + filename_set = [] # this is to circumvent duplicates in the shortnames + id_set = {} # this is to circumvent duplicates in the ids + + # Create the content + build_wxsfile_header_section(root, env) + build_wxsfile_file_section(root, source, env['NAME'], env['VERSION'], env['VENDOR'], filename_set, id_set) + generate_guids(root) + build_wxsfile_features_section(root, source, env['NAME'], env['VERSION'], env['SUMMARY'], id_set) + build_wxsfile_default_gui(root) + build_license_file(target[0].get_dir(), env) + + # write the xml to a file + file.write( doc.toprettyxml() ) + + # call a user specified function + if env.has_key('CHANGE_SPECFILE'): + env['CHANGE_SPECFILE'](target, source) + + except KeyError, e: + raise SCons.Errors.UserError( '"%s" package field for MSI is missing.' % e.args[0] ) + +# +# setup function +# +def create_default_directory_layout(root, NAME, VERSION, vendor, filename_set): + """ Create the wix default target directory layout and return the innermost + directory. + + We assume that the XML tree delivered in the root argument already contains + the Product tag. + + Everything is put under the PFiles directory property defined by WiX. + After that a directory with the 'vendor' tag is placed and then a + directory with the name of the project and its VERSION. This leads to the + following TARGET Directory Layout: + C:\\\\ + Example: C:\Programme\Company\Product-1.2\ + """ + doc = Document() + d1 = doc.createElement( 'Directory' ) + d1.attributes['Id'] = 'TARGETDIR' + d1.attributes['Name'] = 'SourceDir' + + d2 = doc.createElement( 'Directory' ) + d2.attributes['Id'] = 'ProgramFilesFolder' + d2.attributes['Name'] = 'PFiles' + + d3 = doc.createElement( 'Directory' ) + d3.attributes['Id'] = 'vendor_folder' + d3.attributes['Name'] = escape( gen_dos_short_file_name( vendor, filename_set ) ) + d3.attributes['LongName'] = escape( vendor ) + + d4 = doc.createElement( 'Directory' ) + project_folder = "%s-%s" % ( NAME, VERSION ) + d4.attributes['Id'] = 'MY_DEFAULT_FOLDER' + d4.attributes['Name'] = escape( gen_dos_short_file_name( project_folder, filename_set ) ) + d4.attributes['LongName'] = escape( project_folder ) + + d1.childNodes.append( d2 ) + d2.childNodes.append( d3 ) + d3.childNodes.append( d4 ) + + root.getElementsByTagName('Product')[0].childNodes.append( d1 ) + + return d4 + +# +# mandatory and optional file tags +# +def build_wxsfile_file_section(root, files, NAME, VERSION, vendor, filename_set, id_set): + """ builds the Component sections of the wxs file with their included files. + + Files need to be specified in 8.3 format and in the long name format, long + filenames will be converted automatically. + + Features are specficied with the 'X_MSI_FEATURE' or 'DOC' FileTag. + """ + root = create_default_directory_layout( root, NAME, VERSION, vendor, filename_set ) + components = create_feature_dict( files ) + factory = Document() + + def get_directory( node, dir ): + """ returns the node under the given node representing the directory. + + Returns the component node if dir is None or empty. + """ + if dir == '' or not dir: + return node + + Directory = node + dir_parts = dir.split(os.path.sep) + + # to make sure that our directory ids are unique, the parent folders are + # consecutively added to upper_dir + upper_dir = '' + + # walk down the xml tree finding parts of the directory + dir_parts = filter( lambda d: d != '', dir_parts ) + for d in dir_parts[:]: + already_created = filter( lambda c: c.nodeName == 'Directory' and c.attributes['LongName'].value == escape(d), Directory.childNodes ) + + if already_created != []: + Directory = already_created[0] + dir_parts.remove(d) + upper_dir += d + else: + break + + for d in dir_parts: + nDirectory = factory.createElement( 'Directory' ) + nDirectory.attributes['LongName'] = escape( d ) + nDirectory.attributes['Name'] = escape( gen_dos_short_file_name( d, filename_set ) ) + upper_dir += d + nDirectory.attributes['Id'] = convert_to_id( upper_dir, id_set ) + + Directory.childNodes.append( nDirectory ) + Directory = nDirectory + + return Directory + + for file in files: + drive, path = os.path.splitdrive( file.PACKAGING_INSTALL_LOCATION ) + filename = os.path.basename( path ) + dirname = os.path.dirname( path ) + + h = { + # tagname : default value + 'PACKAGING_X_MSI_VITAL' : 'yes', + 'PACKAGING_X_MSI_FILEID' : convert_to_id(filename, id_set), + 'PACKAGING_X_MSI_LONGNAME' : filename, + 'PACKAGING_X_MSI_SHORTNAME' : gen_dos_short_file_name(filename, filename_set), + 'PACKAGING_X_MSI_SOURCE' : file.get_path(), + } + + # fill in the default tags given above. + for k,v in [ (k, v) for (k,v) in h.items() if not hasattr(file, k) ]: + setattr( file, k, v ) + + File = factory.createElement( 'File' ) + File.attributes['LongName'] = escape( file.PACKAGING_X_MSI_LONGNAME ) + File.attributes['Name'] = escape( file.PACKAGING_X_MSI_SHORTNAME ) + File.attributes['Source'] = escape( file.PACKAGING_X_MSI_SOURCE ) + File.attributes['Id'] = escape( file.PACKAGING_X_MSI_FILEID ) + File.attributes['Vital'] = escape( file.PACKAGING_X_MSI_VITAL ) + + # create the Tag under which this file should appear + Component = factory.createElement('Component') + Component.attributes['DiskId'] = '1' + Component.attributes['Id'] = convert_to_id( filename, id_set ) + + # hang the component node under the root node and the file node + # under the component node. + Directory = get_directory( root, dirname ) + Directory.childNodes.append( Component ) + Component.childNodes.append( File ) + +# +# additional functions +# +def build_wxsfile_features_section(root, files, NAME, VERSION, SUMMARY, id_set): + """ This function creates the tag based on the supplied xml tree. + + This is achieved by finding all s and adding them to a default target. + + It should be called after the tree has been built completly. We assume + that a MY_DEFAULT_FOLDER Property is defined in the wxs file tree. + + Furthermore a top-level with the name and VERSION of the software will be created. + + An PACKAGING_X_MSI_FEATURE can either be a string, where the feature + DESCRIPTION will be the same as its title or a Tuple, where the first + part will be its title and the second its DESCRIPTION. + """ + factory = Document() + Feature = factory.createElement('Feature') + Feature.attributes['Id'] = 'complete' + Feature.attributes['ConfigurableDirectory'] = 'MY_DEFAULT_FOLDER' + Feature.attributes['Level'] = '1' + Feature.attributes['Title'] = escape( '%s %s' % (NAME, VERSION) ) + Feature.attributes['Description'] = escape( SUMMARY ) + Feature.attributes['Display'] = 'expand' + + for (feature, files) in create_feature_dict(files).items(): + SubFeature = factory.createElement('Feature') + SubFeature.attributes['Level'] = '1' + + if SCons.Util.is_Tuple(feature): + SubFeature.attributes['Id'] = convert_to_id( feature[0], id_set ) + SubFeature.attributes['Title'] = escape(feature[0]) + SubFeature.attributes['Description'] = escape(feature[1]) + else: + SubFeature.attributes['Id'] = convert_to_id( feature, id_set ) + if feature=='default': + SubFeature.attributes['Description'] = 'Main Part' + SubFeature.attributes['Title'] = 'Main Part' + elif feature=='PACKAGING_DOC': + SubFeature.attributes['Description'] = 'Documentation' + SubFeature.attributes['Title'] = 'Documentation' + else: + SubFeature.attributes['Description'] = escape(feature) + SubFeature.attributes['Title'] = escape(feature) + + # build the componentrefs. As one of the design decision is that every + # file is also a component we walk the list of files and create a + # reference. + for f in files: + ComponentRef = factory.createElement('ComponentRef') + ComponentRef.attributes['Id'] = convert_to_id( os.path.basename(f.get_path()), id_set ) + SubFeature.childNodes.append(ComponentRef) + + Feature.childNodes.append(SubFeature) + + root.getElementsByTagName('Product')[0].childNodes.append(Feature) + +def build_wxsfile_default_gui(root): + """ this function adds a default GUI to the wxs file + """ + factory = Document() + Product = root.getElementsByTagName('Product')[0] + + UIRef = factory.createElement('UIRef') + UIRef.attributes['Id'] = 'WixUI_Mondo' + Product.childNodes.append(UIRef) + + UIRef = factory.createElement('UIRef') + UIRef.attributes['Id'] = 'WixUI_ErrorProgressText' + Product.childNodes.append(UIRef) + +def build_license_file(directory, spec): + """ creates a License.rtf file with the content of "X_MSI_LICENSE_TEXT" + in the given directory + """ + name, text = '', '' + + try: + name = spec['LICENSE'] + text = spec['X_MSI_LICENSE_TEXT'] + except KeyError: + pass # ignore this as X_MSI_LICENSE_TEXT is optional + + if name!='' or text!='': + file = open( os.path.join(directory.get_path(), 'License.rtf'), 'w' ) + file.write('{\\rtf') + if text!='': + file.write(text.replace('\n', '\\par ')) + else: + file.write(name+'\\par\\par') + file.write('}') + file.close() + +# +# mandatory and optional package tags +# +def build_wxsfile_header_section(root, spec): + """ Adds the xml file node which define the package meta-data. + """ + # Create the needed DOM nodes and add them at the correct position in the tree. + factory = Document() + Product = factory.createElement( 'Product' ) + Package = factory.createElement( 'Package' ) + + root.childNodes.append( Product ) + Product.childNodes.append( Package ) + + # set "mandatory" default values + if not spec.has_key('X_MSI_LANGUAGE'): + spec['X_MSI_LANGUAGE'] = '1033' # select english + + # mandatory sections, will throw a KeyError if the tag is not available + Product.attributes['Name'] = escape( spec['NAME'] ) + Product.attributes['Version'] = escape( spec['VERSION'] ) + Product.attributes['Manufacturer'] = escape( spec['vendor'] ) + Product.attributes['Language'] = escape( spec['X_MSI_LANGUAGE'] ) + Package.attributes['Description'] = escape( spec['SUMMARY'] ) + + # now the optional tags, for which we avoid the KeyErrror exception + if spec.has_key( 'DESCRIPTION' ): + Package.attributes['Comments'] = escape( spec['DESCRIPTION'] ) + + if spec.has_key( 'X_MSI_UPGRADE_CODE' ): + Package.attributes['X_MSI_UPGRADE_CODE'] = escape( spec['X_MSI_UPGRADE_CODE'] ) + + # We hardcode the media tag as our current model cannot handle it. + Media = factory.createElement('Media') + Media.attributes['Id'] = '1' + Media.attributes['Cabinet'] = 'default.cab' + Media.attributes['EmbedCab'] = 'yes' + root.getElementsByTagName('Product')[0].childNodes.append(Media) + +# this builder is the entry-point for .wxs file compiler. +wxs_builder = Builder( + action = Action( build_wxsfile, string_wxsfile ), + emitter = stripinstall_emitter(), + suffix = '.wxs' ) + +def package(env, target, source, PACKAGEROOT, NAME, VERSION, + DESCRIPTION, SUMMARY, **kw): + # make sure that the Wix Builder is in the environment + SCons.Tool.Tool('wix').generate(env) + + # get put the keywords for the specfile compiler. These are the arguments + # given to the package function and all optional ones stored in kw, minus + # the the source, target and env one. + loc = locals() + del loc['kw'] + kw.update(loc) + del kw['source'], kw['target'], kw['env'] + + # put the arguments into the env and call the specfile builder. + env['msi_spec'] = kw + specfile = apply( wxs_builder, [env, target, source], kw ) + + # now call the WiX Tool with the built specfile added as a source. + msifile = env.WiX(target, specfile) + + # return the target and source tuple. + return (msifile, source+[specfile]) + diff --git a/src/engine/SCons/Tool/packaging/packager.py b/src/engine/SCons/Tool/packaging/packager.py new file mode 100644 index 00000000..7aca2b6d --- /dev/null +++ b/src/engine/SCons/Tool/packaging/packager.py @@ -0,0 +1,218 @@ +"""SCons.Tool.Packaging.packager +""" + +# +# __COPYRIGHT__ +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +import os +import SCons.Defaults +from SCons.Util import strip_abs_path + +class Packager: + """ abstract superclass of all packagers. + + Defines the minimal set of function which need to be implemented in order + to create a new packager. + """ + def __init__(self): + self.specfile_suffix = '.spec' + + def create_builder(self, env, kw): + raise Exception( "%s does not implement create_builder()" % (self.__class__.__name_) ) + + def add_targets(self, kw): + """ In the absence of a target this method creates a default one of + the spec given in the kw argument. + """ + if not kw.has_key('target'): + NAME, VERSION = kw['projectname'], kw['version'] + kw['target'] = [ "%s-%s"%(NAME,VERSION) ] + + return kw + + def strip_install_emitter(self, target, source, env): + """ this emitter assert that all files in source have an InstallBuilder + attached. We take their sources and copy over the file tags, so the + the builder this emitter is attached to is independent of the *installed* + files. + """ + tag_factories = [ LocationTagFactory() ] + + def has_no_install_location(file): + return not ( file.has_builder() and (file.builder.name == 'InstallBuilder' or file.builder.name == 'InstallAsBuilder') ) + + # check if all source file belong into this package. + files = filter( has_no_install_location, source ) + if len( files ) != 0: + raise SCons.Errors.UserError( "There are files which have no Install() Builder attached and are therefore not packageable\n%s" % map( lambda x: x.get_path(), files ) ) + + # All source files have an InstallBuilder attached and we don't want our + # package to be dependent on the *installed* files but only on the + # files that will be installed. Therefore we only care for sources of + # the files in the source list. + n_source = [] + + for s in source: + n_s = s.sources[0] + n_s.set_tags( s.get_tags(tag_factories) ) + n_source.append( n_s ) + + return ( target, n_source ) + + +class BinaryPackager(Packager): + """ abstract superclass for all packagers creating a binary package. + + Binary packagers are seperated from source packager by their requirement to + create a specfile or manifest file. This file contains the contents of the + binary packager together with some information about specific files. + + This superclass provides two needed facilities: + * its specfile_emitter function sets up the correct list of source file + and warns about files with no InstallBuilder attached. + """ + def create_specfile_targets(self, kw): + """ returns the specfile target name(s). + + This function is called by specfile_emitter to find out the specfiles + target name. + """ + p, v = kw['NAME'], kw['VERSION'] + return '%s-%s' % ( p, v ) + + def specfile_emitter(self, target, source, env): + """ adds the to build specfile to the source list. + """ + # create a specfile action that is executed for building the specfile + specfile_action = SCons.Action.Action( self.build_specfile, + self.string_specfile, + varlist=[ 'SPEC' ] ) + + # create a specfile Builder with the right sources attached. + specfile_builder = SCons.Builder.Builder( action = self.build_specfile, + suffix = self.specfile_suffix ) + + specfile = apply( specfile_builder, [ env ], { + 'target' : self.create_specfile_targets(env), + 'source' : source } ) + + specfile.extend( source ) + return ( target, specfile ) + + def string_specfile(self, target, source, env): + return "building specfile %s"%(target[0].abspath) + + def build_specfile(self, target, source, env): + """ this function is called to build the specfile of name "target" + from the source list and the settings in "env" + """ + raise Exception( 'class does not implement build_specfile()' ) + +class SourcePackager(Packager): + """ abstract superclass for all packagers which generate a source package. + + They are seperated from other packager by the their package_root attribute. + Since before a source package is created with the help of a Tar or Zip + builder their content needs to be moved to a package_root. For example the + project foo with VERSION 1.2.3, will get its files placed in foo-1.2.3/. + """ + def create_package_root(self,kw): + """ creates the package_r oot for a given specification dict. + """ + try: + return kw['package_root'] + except KeyError: + NAME, VERSION = kw['projectname'], kw['version'] + return "%s-%s"%(NAME,VERSION) + + def package_root_emitter(self, pkg_root, honor_install_location=1): + def package_root_emitter(target, source, env): + """ This emitter copies the sources to the src_package_root directory: + * if a source has an install_location, not its original name is + used but the one specified in the 'install_location' tag. + * else its original name is used. + * if the source file is already in the src_package_root directory, + nothing will be done. + """ + new_source = [] + for s in source: + if os.path.dirname(s.get_path()).rfind(pkg_root) != -1: + new_source.append(s) + else: + tags = s.get_tags() + new_s = None + + if tags.has_key( 'install_location' ) and honor_install_location: + my_target = strip_abs_path(tags['install_location']) + else: + my_target = strip_abs_path(s.get_path()) + + new_s = env.CopyAs( os.path.join( pkg_root, my_target ), s )[0] + + # store the tags of our original file in the new file. + new_s.set_tags( s.get_tags() ) + new_source.append( new_s ) + + return (target, new_source) + + return package_root_emitter + +class TagFactory: + """An instance of this class has the responsibility to generate additional + tags for a SCons.Node.FS.File instance. + + Subclasses have to be callable. This class definition is informally + describing the interface. + """ + + def __call__(self, file, current_tag_dict): + """ This call has to return additional tags in the form of a dict. + """ + pass + + def attach_additional_info(self, info=None): + pass + +class LocationTagFactory(TagFactory): + """ This class creates the "location" tag, which describes the install + location of a given file. + + This is done by analyzing the builder of a given file for a InstallBuilder, + from this builder the install location is deduced. + """ + + def __call__(self, file, current_tag_dict): + if current_tag_dict.has_key('install_location'): + return {} + + if file.has_builder() and\ + (file.builder.name == "InstallBuilder" or\ + file.builder.name == "InstallAsBuilder") and\ + file.has_explicit_builder(): + return { 'install_location' : file.get_path() } + else: + return {} + + diff --git a/src/engine/SCons/Tool/packaging/rpm.py b/src/engine/SCons/Tool/packaging/rpm.py new file mode 100644 index 00000000..49b8ff85 --- /dev/null +++ b/src/engine/SCons/Tool/packaging/rpm.py @@ -0,0 +1,354 @@ +"""SCons.Tool.Packaging.rpm + +The rpm packager. +""" + +# +# __COPYRIGHT__ +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +import os +import string + +import SCons.Builder + +from SCons.Tool.packaging import stripinstall_emitter, packageroot_emitter, src_targz + +def package(env, target, source, PACKAGEROOT, NAME, VERSION, + PACKAGEVERSION, DESCRIPTION, SUMMARY, X_RPM_GROUP, LICENSE, + **kw): + # initialize the rpm tool + SCons.Tool.Tool('rpm').generate(env) + + # create the neccesary builder + bld = env['BUILDERS']['Rpm'] + env['RPMFLAGS'] = SCons.Util.CLVar('-ta') + + bld.push_emitter(targz_emitter) + bld.push_emitter(specfile_emitter) + bld.push_emitter(stripinstall_emitter()) + + # override the default target, with the rpm specific ones. + if str(target[0])=="%s-%s"%(NAME, VERSION): + # This should be overridable from the construction environment, + # which it is by using ARCHITECTURE=. + # Guessing based on what os.uname() returns at least allows it + # to work for both i386 and x86_64 Linux systems. + archmap = { + 'i686' : 'i386', + 'i586' : 'i386', + 'i486' : 'i386', + } + + buildarchitecture = os.uname()[4] + buildarchitecture = archmap.get(buildarchitecture, buildarchitecture) + + if kw.has_key('ARCHITECTURE'): + buildarchitecture = kw['ARCHITECTURE'] + + srcrpm = '%s-%s-%s.src.rpm' % (NAME, VERSION, PACKAGEVERSION) + binrpm = string.replace(srcrpm, 'src', buildarchitecture) + + target = [ srcrpm, binrpm ] + + # get the correct arguments into the kw hash + loc=locals() + del loc['kw'] + kw.update(loc) + del kw['source'], kw['target'], kw['env'] + + # if no "SOURCE_URL" tag is given add a default one. + if not kw.has_key('SOURCE_URL'): + kw['SOURCE_URL']=(str(target[0])+".tar.gz").replace('.rpm', '') + + # now call the rpm builder to actually build the packet. + return apply(bld, [env, target, source], kw) + + +def targz_emitter(target, source, env): + """ Puts all source files into a tar.gz file. """ + # the rpm tool depends on a source package, until this is chagned + # this hack needs to be here that tries to pack all sources in. + sources = env.FindSourceFiles() + + # filter out the target we are building the source list for. + #sources = [s for s in sources if not (s in target)] + sources = filter(lambda s, t=target: not (s in t), sources) + + # find the .spec file for rpm and add it since it is not necessarily found + # by the FindSourceFiles function. + #sources.extend( [s for s in source if str(s).rfind('.spec')!=-1] ) + spec_file = lambda s: string.rfind(str(s), '.spec') != -1 + sources.extend( filter(spec_file, source) ) + + # as the source contains the url of the source package this rpm package + # is built from, we extract the target name + #tarball = (str(target[0])+".tar.gz").replace('.rpm', '') + tarball = string.replace(str(target[0])+".tar.gz", '.rpm', '') + try: + #tarball = env['SOURCE_URL'].split('/')[-1] + tarball = string.split(env['SOURCE_URL'], '/')[-1] + except KeyError, e: + raise SCons.Errors.UserError( "Missing PackageTag '%s' for RPM packager" % e.args[0] ) + + tarball = src_targz.package(env, source=sources, target=tarball, + PACKAGEROOT=env['PACKAGEROOT'], ) + + return (target, tarball) + +def specfile_emitter(target, source, env): + specfile = "%s-%s" % (env['NAME'], env['VERSION']) + + bld = SCons.Builder.Builder(action = build_specfile, + suffix = '.spec', + target_factory = SCons.Node.FS.File) + + source.extend(bld(env, specfile, source)) + + return (target,source) + +def build_specfile(target, source, env): + """ Builds a RPM specfile from a dictionary with string metadata and + by analyzing a tree of nodes. + """ + file = open(target[0].abspath, 'w') + str = "" + + try: + file.write( build_specfile_header(env) ) + file.write( build_specfile_sections(env) ) + file.write( build_specfile_filesection(env, source) ) + file.close() + + # call a user specified function + if env.has_key('CHANGE_SPECFILE'): + env['CHANGE_SPECFILE'](target, source) + + except KeyError, e: + raise SCons.Errors.UserError( '"%s" package field for RPM is missing.' % e.args[0] ) + + +# +# mandatory and optional package tag section +# +def build_specfile_sections(spec): + """ Builds the sections of a rpm specfile. + """ + str = "" + + mandatory_sections = { + 'DESCRIPTION' : '\n%%description\n%s\n\n', } + + str = str + SimpleTagCompiler(mandatory_sections).compile( spec ) + + optional_sections = { + 'DESCRIPTION_' : '%%description -l %s\n%s\n\n', + 'CHANGELOG' : '%%changelog\n%s\n\n', + 'X_RPM_PREINSTALL' : '%%pre\n%s\n\n', + 'X_RPM_POSTINSTALL' : '%%post\n%s\n\n', + 'X_RPM_PREUNINSTALL' : '%%preun\n%s\n\n', + 'X_RPM_POSTUNINSTALL' : '%%postun\n%s\n\n', + 'X_RPM_VERIFY' : '%%verify\n%s\n\n', + + # These are for internal use but could possibly be overriden + 'X_RPM_PREP' : '%%prep\n%s\n\n', + 'X_RPM_BUILD' : '%%build\n%s\n\n', + 'X_RPM_INSTALL' : '%%install\n%s\n\n', + 'X_RPM_CLEAN' : '%%clean\n%s\n\n', + } + + # Default prep, build, install and clean rules + # TODO: optimize those build steps, to not compile the project a second time + if not spec.has_key('X_RPM_PREP'): + spec['X_RPM_PREP'] = 'rm -rf "$RPM_BUILD_ROOT"' + '\n%setup -q' + + if not spec.has_key('X_RPM_BUILD'): + spec['X_RPM_BUILD'] = 'mkdir "$RPM_BUILD_ROOT"' + + if not spec.has_key('X_RPM_INSTALL'): + spec['X_RPM_INSTALL'] = 'scons --install-sandbox="$RPM_BUILD_ROOT" "$RPM_BUILD_ROOT"' + + if not spec.has_key('X_RPM_CLEAN'): + spec['X_RPM_CLEAN'] = 'rm -rf "$RPM_BUILD_ROOT"' + + str = str + SimpleTagCompiler(optional_sections, mandatory=0).compile( spec ) + + return str + +def build_specfile_header(spec): + """ Builds all section but the %file of a rpm specfile + """ + str = "" + + # first the mandatory sections + mandatory_header_fields = { + 'NAME' : '%%define name %s\nName: %%{name}\n', + 'VERSION' : '%%define version %s\nVersion: %%{version}\n', + 'PACKAGEVERSION' : '%%define release %s\nRelease: %%{release}\n', + 'X_RPM_GROUP' : 'Group: %s\n', + 'SUMMARY' : 'Summary: %s\n', + 'LICENSE' : 'License: %s\n', } + + str = str + SimpleTagCompiler(mandatory_header_fields).compile( spec ) + + # now the optional tags + optional_header_fields = { + 'VENDOR' : 'Vendor: %s\n', + 'X_RPM_URL' : 'Url: %s\n', + 'SOURCE_URL' : 'Source: %s\n', + 'SUMMARY_' : 'Summary(%s): %s\n', + 'X_RPM_DISTRIBUTION' : 'Distribution: %s\n', + 'X_RPM_ICON' : 'Icon: %s\n', + 'X_RPM_PACKAGER' : 'Packager: %s\n', + 'X_RPM_GROUP_' : 'Group(%s): %s\n', + + 'X_RPM_REQUIRES' : 'Requires: %s\n', + 'X_RPM_PROVIDES' : 'Provides: %s\n', + 'X_RPM_CONFLICTS' : 'Conflicts: %s\n', + 'X_RPM_BUILDREQUIRES' : 'BuildRequires: %s\n', + + 'X_RPM_SERIAL' : 'Serial: %s\n', + 'X_RPM_EPOCH' : 'Epoch: %s\n', + 'X_RPM_AUTOREQPROV' : 'AutoReqProv: %s\n', + 'X_RPM_EXCLUDEARCH' : 'ExcludeArch: %s\n', + 'X_RPM_EXCLUSIVEARCH' : 'ExclusiveArch: %s\n', + 'X_RPM_PREFIX' : 'Prefix: %s\n', + 'X_RPM_CONFLICTS' : 'Conflicts: %s\n', + + # internal use + 'X_RPM_BUILDROOT' : 'BuildRoot: %s\n', } + + # fill in default values: + # Adding a BuildRequires renders the .rpm unbuildable under System, which + # are not managed by rpm, since the database to resolve this dependency is + # missing (take Gentoo as an example) +# if not s.has_key('x_rpm_BuildRequires'): +# s['x_rpm_BuildRequires'] = 'scons' + + if not spec.has_key('X_RPM_BUILDROOT'): + spec['X_RPM_BUILDROOT'] = '%{_tmppath}/%{name}-%{version}-%{release}' + + str = str + SimpleTagCompiler(optional_header_fields, mandatory=0).compile( spec ) + return str + +# +# mandatory and optional file tags +# +def build_specfile_filesection(spec, files): + """ builds the %file section of the specfile + """ + str = '%files\n' + + if not spec.has_key('X_RPM_DEFATTR'): + spec['X_RPM_DEFATTR'] = '(-,root,root)' + + str = str + '%%defattr %s\n' % spec['X_RPM_DEFATTR'] + + supported_tags = { + 'PACKAGING_CONFIG' : '%%config %s', + 'PACKAGING_CONFIG_NOREPLACE' : '%%config(noreplace) %s', + 'PACKAGING_DOC' : '%%doc %s', + 'PACKAGING_UNIX_ATTR' : '%%attr %s', + 'PACKAGING_LANG_' : '%%lang(%s) %s', + 'PACKAGING_X_RPM_VERIFY' : '%%verify %s', + 'PACKAGING_X_RPM_DIR' : '%%dir %s', + 'PACKAGING_X_RPM_DOCDIR' : '%%docdir %s', + 'PACKAGING_X_RPM_GHOST' : '%%ghost %s', } + + for file in files: + # build the tagset + tags = {} + for k in supported_tags.keys(): + try: + tags[k]=getattr(file, k) + except AttributeError: + pass + + # compile the tagset + str = str + SimpleTagCompiler(supported_tags, mandatory=0).compile( tags ) + + str = str + ' ' + str = str + file.PACKAGING_INSTALL_LOCATION + str = str + '\n\n' + + return str + +class SimpleTagCompiler: + """ This class is a simple string substition utility: + the replacement specfication is stored in the tagset dictionary, something + like: + { "abc" : "cdef %s ", + "abc_" : "cdef %s %s" } + + the compile function gets a value dictionary, which may look like: + { "abc" : "ghij", + "abc_gh" : "ij" } + + The resulting string will be: + "cdef ghij cdef gh ij" + """ + def __init__(self, tagset, mandatory=1): + self.tagset = tagset + self.mandatory = mandatory + + def compile(self, values): + """ compiles the tagset and returns a str containing the result + """ + def is_international(tag): + #return tag.endswith('_') + return tag[-1:] == '_' + + def get_country_code(tag): + return tag[-2:] + + def strip_country_code(tag): + return tag[:-2] + + replacements = self.tagset.items() + + str = "" + #domestic = [ (k,v) for k,v in replacements if not is_international(k) ] + domestic = filter(lambda t, i=is_international: not i(t[0]), replacements) + for key, replacement in domestic: + try: + str = str + replacement % values[key] + except KeyError, e: + if self.mandatory: + raise e + + #international = [ (k,v) for k,v in replacements if is_international(k) ] + international = filter(lambda t, i=is_international: i(t[0]), replacements) + for key, replacement in international: + try: + #int_values_for_key = [ (get_country_code(k),v) for k,v in values.items() if strip_country_code(k) == key ] + x = filter(lambda t,key=key,s=strip_country_code: s(t[0]) == key, values.items()) + int_values_for_key = map(lambda t,g=get_country_code: (g(t[0]),t[1]), x) + for v in int_values_for_key: + str = str + replacement % v + except KeyError, e: + if self.mandatory: + raise e + + return str + diff --git a/src/engine/SCons/Tool/packaging/src_tarbz2.py b/src/engine/SCons/Tool/packaging/src_tarbz2.py new file mode 100644 index 00000000..7d876e48 --- /dev/null +++ b/src/engine/SCons/Tool/packaging/src_tarbz2.py @@ -0,0 +1,37 @@ +"""SCons.Tool.Packaging.tarbz2 + +The tarbz2 SRC packager. +""" + +# +# __COPYRIGHT__ +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +from SCons.Tool.packaging import packageroot_emitter + +def package(env, target, source, PACKAGEROOT, **kw): + bld = env['BUILDERS']['Tar'] + bld.set_suffix('.tar.bz2') + bld.push_emitter(packageroot_emitter(PACKAGEROOT, honor_install_location=0)) + return bld(env, target, source, TARFLAGS='-jc') diff --git a/src/engine/SCons/Tool/packaging/src_targz.py b/src/engine/SCons/Tool/packaging/src_targz.py new file mode 100644 index 00000000..d84976e3 --- /dev/null +++ b/src/engine/SCons/Tool/packaging/src_targz.py @@ -0,0 +1,37 @@ +"""SCons.Tool.Packaging.targz + +The targz SRC packager. +""" + +# +# __COPYRIGHT__ +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +from SCons.Tool.packaging import packageroot_emitter + +def package(env, target, source, PACKAGEROOT, **kw): + bld = env['BUILDERS']['Tar'] + bld.set_suffix('.tar.gz') + bld.push_emitter(packageroot_emitter(PACKAGEROOT, honor_install_location=0)) + return bld(env, target, source, TARFLAGS='-zc') diff --git a/src/engine/SCons/Tool/packaging/src_zip.py b/src/engine/SCons/Tool/packaging/src_zip.py new file mode 100644 index 00000000..d60fe85b --- /dev/null +++ b/src/engine/SCons/Tool/packaging/src_zip.py @@ -0,0 +1,37 @@ +"""SCons.Tool.Packaging.zip + +The zip SRC packager. +""" + +# +# __COPYRIGHT__ +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +from SCons.Tool.packaging import packageroot_emitter + +def package(env, target, source, PACKAGEROOT, **kw): + bld = env['BUILDERS']['Zip'] + bld.set_suffix('.zip') + bld.push_emitter(packageroot_emitter(PACKAGEROOT, honor_install_location=0)) + return bld(env, target, source) diff --git a/src/engine/SCons/Tool/packaging/tarbz2.py b/src/engine/SCons/Tool/packaging/tarbz2.py new file mode 100644 index 00000000..71278964 --- /dev/null +++ b/src/engine/SCons/Tool/packaging/tarbz2.py @@ -0,0 +1,38 @@ +"""SCons.Tool.Packaging.tarbz2 + +The tarbz2 SRC packager. +""" + +# +# __COPYRIGHT__ +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +from SCons.Tool.packaging import stripinstall_emitter, packageroot_emitter + +def package(env, target, source, PACKAGEROOT, **kw): + bld = env['BUILDERS']['Tar'] + bld.set_suffix('.tar.gz') + bld.push_emitter(packageroot_emitter(PACKAGEROOT)) + bld.push_emitter(stripinstall_emitter()) + return bld(env, target, source, TARFLAGS='-jc') diff --git a/src/engine/SCons/Tool/packaging/targz.py b/src/engine/SCons/Tool/packaging/targz.py new file mode 100644 index 00000000..798e570f --- /dev/null +++ b/src/engine/SCons/Tool/packaging/targz.py @@ -0,0 +1,38 @@ +"""SCons.Tool.Packaging.targz + +The targz SRC packager. +""" + +# +# __COPYRIGHT__ +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +from SCons.Tool.packaging import stripinstall_emitter, packageroot_emitter + +def package(env, target, source, PACKAGEROOT, **kw): + bld = env['BUILDERS']['Tar'] + bld.set_suffix('.tar.gz') + bld.push_emitter(packageroot_emitter(PACKAGEROOT)) + bld.push_emitter(stripinstall_emitter()) + return bld(env, target, source, TARFLAGS='-zc') diff --git a/src/engine/SCons/Tool/packaging/zip.py b/src/engine/SCons/Tool/packaging/zip.py new file mode 100644 index 00000000..7663a4aa --- /dev/null +++ b/src/engine/SCons/Tool/packaging/zip.py @@ -0,0 +1,38 @@ +"""SCons.Tool.Packaging.zip + +The zip SRC packager. +""" + +# +# __COPYRIGHT__ +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +from SCons.Tool.packaging import stripinstall_emitter, packageroot_emitter + +def package(env, target, source, PACKAGEROOT, **kw): + bld = env['BUILDERS']['Zip'] + bld.set_suffix('.zip') + bld.push_emitter(packageroot_emitter(PACKAGEROOT)) + bld.push_emitter(stripinstall_emitter()) + return bld(env, target, source) diff --git a/src/engine/SCons/Tool/rpm.py b/src/engine/SCons/Tool/rpm.py new file mode 100644 index 00000000..2fc4b58a --- /dev/null +++ b/src/engine/SCons/Tool/rpm.py @@ -0,0 +1,126 @@ +"""SCons.Tool.rpm + +Tool-specific initialization for rpm. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +The rpm tool calls the rpmbuild command. The first and only argument should a +tar.gz consisting of the source file and a specfile. +""" + +# +# __COPYRIGHT__ +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +import os +import re +import shutil +import popen2 + +import SCons.Builder +import SCons.Node.FS +import SCons.Util +import SCons.Action +import SCons.Defaults + +def get_cmd(source, env): + tar_file_with_included_specfile = source + if SCons.Util.is_List(source): + tar_file_with_included_specfile = source[0] + return "%s %s %s"%(env['RPM'], env['RPMFLAGS'], + tar_file_with_included_specfile.abspath ) + +def build_rpm(target, source, env): + # create a temporary rpm build root. + tmpdir = os.path.join( os.path.dirname( target[0].abspath ), 'rpmtemp' ) + if os.path.exists(tmpdir): + shutil.rmtree(tmpdir) + + # now create the mandatory rpm directory structure. + for d in ['RPMS', 'SRPMS', 'SPECS', 'BUILD']: + os.makedirs( os.path.join( tmpdir, d ) ) + + # set the topdir as an rpmflag. + env.Prepend( RPMFLAGS = '--define \'_topdir %s\'' % tmpdir ) + + # now call rpmbuild to create the rpm package. + handle = popen2.Popen3( get_cmd(source, env), capturestderr=1 ) + output = handle.fromchild.read() + #output += handle.childerr.read() + output = output + handle.childerr.read() + status = handle.wait() + + if status: + raise SCons.Errors.BuildError( node=target[0], + errstr=output, + filename=str(target[0]) ) + else: + # XXX: assume that LC_ALL=c is set while running rpmbuild + output_files = re.compile( 'Wrote: (.*)' ).findall( output ) + + for output, input in zip( output_files, target ): + rpm_output = os.path.basename(output) + expected = os.path.basename(input.get_path()) + + assert expected == rpm_output, "got %s but expected %s" % (rpm_output, expected) + shutil.copy( output, input.abspath ) + + + # cleanup before leaving. + shutil.rmtree(tmpdir) + + return status + +def string_rpm(target, source, env): + try: + return env['RPMCOMSTR'] + except KeyError: + return get_cmd(source, env) + +rpmAction = SCons.Action.Action(build_rpm, string_rpm) + +RpmBuilder = SCons.Builder.Builder(action = SCons.Action.Action('$RPMCOM', '$RPMCOMSTR'), + source_factory = SCons.Node.FS.Entry, + source_scanner = SCons.Defaults.DirScanner, + suffix = '$RPMSUFFIX') + + + +def generate(env): + """Add Builders and construction variables for rpm to an Environment.""" + try: + bld = env['BUILDERS']['Rpm'] + except KeyError: + bld = RpmBuilder + env['BUILDERS']['Rpm'] = bld + + env.SetDefault(RPM = 'LC_ALL=c rpmbuild') + env.SetDefault(RPMFLAGS = SCons.Util.CLVar('-ta')) + env.SetDefault(RPMCOM = rpmAction) + env.SetDefault(RPMSUFFIX = '.rpm') + +def exists(env): + return env.Detect('rpmbuild') diff --git a/src/engine/SCons/Tool/wix.py b/src/engine/SCons/Tool/wix.py new file mode 100644 index 00000000..b133947b --- /dev/null +++ b/src/engine/SCons/Tool/wix.py @@ -0,0 +1,91 @@ +"""SCons.Tool.wix + +Tool-specific initialization for wix, the Windows Installer XML Tool. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. +""" + +# +# __COPYRIGHT__ +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +import SCons.Builder +import SCons.Action +import os +import string + +def generate(env): + """Add Builders and construction variables for WiX to an Environment.""" + if not exists(env): + return + + env['WIXCANDLEFLAGS'] = ['-nologo'] + env['WIXCANDLEINCLUDE'] = [] + env['WIXCANDLECOM'] = '$WIXCANDLE $WIXCANDLEFLAGS -I $WIXCANDLEINCLUDE -o ${TARGET} ${SOURCE}' + + env['WIXLIGHTFLAGS'].append( '-nologo' ) + env['WIXLIGHTCOM'] = "$WIXLIGHT $WIXLIGHTFLAGS -out ${TARGET} ${SOURCES}" + + object_builder = SCons.Builder.Builder( + action = '$WIXCANDLECOM', + suffix = '.wxiobj', + src_suffix = '.wxs') + + linker_builder = SCons.Builder.Builder( + action = '$WIXLIGHTCOM', + src_suffix = '.wxiobj', + src_builder = object_builder) + + env['BUILDERS']['WiX'] = linker_builder + +def exists(env): + env['WIXCANDLE'] = 'candle.exe' + env['WIXLIGHT'] = 'light.exe' + + # try to find the candle.exe and light.exe tools and + # add the install directory to light libpath. + #for path in os.environ['PATH'].split(os.pathsep): + for path in string.split(os.environ['PATH'], os.pathsep): + # workaround for some weird python win32 bug. + if path[0] == '"' and path[-1:]=='"': + path = path[1:-1] + + # normalize the path + path = os.path.normpath(path) + + # search for the tools in the PATH environment variable + try: + if env['WIXCANDLE'] in os.listdir(path) and\ + env['WIXLIGHT'] in os.listdir(path): + env.PrependENVPath('PATH', path) + env['WIXLIGHTFLAGS'] = [ os.path.join( path, 'wixui.wixlib' ), + '-loc', + os.path.join( path, 'WixUI_en-us.wxl' ) ] + return 1 + except OSError: + pass # ignore this, could be a stale PATH entry. + + return None diff --git a/src/engine/SCons/Util.py b/src/engine/SCons/Util.py index a5ea859f..22aca088 100644 --- a/src/engine/SCons/Util.py +++ b/src/engine/SCons/Util.py @@ -951,6 +951,23 @@ class Unbuffered: def __getattr__(self, attr): return getattr(self.file, attr) +def make_path_relative(path): + """ makes an absolute path name to a relative pathname. + """ + if os.path.isabs(path): + drive_s,path = os.path.splitdrive(path) + + import re + if not drive_s: + path=re.compile("/*(.*)").findall(path)[0] + else: + path=path[1:] + + assert( not os.path.isabs( path ) ), path + return path + + + # The original idea for AddMethod() and RenameFunction() come from the # following post to the ActiveState Python Cookbook: # @@ -1025,4 +1042,6 @@ def RenameFunction(function, name): name, func_defaults) + + del __revision__ diff --git a/test/Copy.py b/test/Copy.py index b318e58d..4da725e8 100644 --- a/test/Copy.py +++ b/test/Copy.py @@ -54,6 +54,10 @@ env.Command('bar.out', 'bar.in', [Cat, env = Environment(OUTPUT = 'f7.out', INPUT = 'f7.in') env.Command('f8.out', 'f8.in', [Copy('$OUTPUT', '$INPUT'), Cat]) env.Command('f9.out', 'f9.in', [Cat, Copy('${TARGET}-Copy', '$SOURCE')]) + +env.CopyTo( 'd4', 'f10.in' ) +env.CopyAs( 'd4/f11.out', 'f11.in') +env.CopyAs( 'd4/f12.out', 'd5/f12.in') """) test.write('f1.in', "f1.in\n") @@ -70,6 +74,10 @@ test.subdir('d6.out') test.write('f7.in', "f7.in\n") test.write('f8.in', "f8.in\n") test.write('f9.in', "f9.in\n") +test.write('f10.in', "f10.in\n") +test.write('f11.in', "f11.in\n") +test.subdir('d5') +test.write(['d5', 'f12.in'], "f12.in\n") expect = test.wrap_stdout(read_str = """\ Copy("f1.out", "f1.in") @@ -81,6 +89,9 @@ cat(["bar.out"], ["bar.in"]) Copy("f4.out", "f4.in") Copy("d5.out", "d5.in") Copy("d6.out", "f6.in") +Copy file(s): "f10.in" to "d4/f10.in" +Copy file(s): "f11.in" to "d4/f11.out" +Copy file(s): "d5/f12.in" to "d4/f12.out" Copy("f7.out", "f7.in") cat(["f8.out"], ["f8.in"]) cat(["f9.out"], ["f9.in"]) @@ -98,6 +109,9 @@ test.must_not_exist('f7.out') test.must_not_exist('f8.out') test.must_not_exist('f9.out') test.must_not_exist('f9.out-Copy') +test.must_not_exist('d4/f10.in') +test.must_not_exist('d4/f11.out') +test.must_not_exist('d4/f12.out') test.run() @@ -111,5 +125,8 @@ test.must_match('f7.out', "f7.in\n") test.must_match('f8.out', "f8.in\n") test.must_match('f9.out', "f9.in\n") test.must_match('f9.out-Copy', "f9.in\n") +test.must_match('d4/f10.in', 'f10.in\n') +test.must_match('d4/f11.out', 'f11.in\n') +test.must_match('d4/f12.out', 'f12.in\n') test.pass_test() diff --git a/test/Install/Install.py b/test/Install/Install.py index 3c1dc49f..f7f8e26e 100644 --- a/test/Install/Install.py +++ b/test/Install/Install.py @@ -91,7 +91,7 @@ test.write(['work', 'sub', 'f4.in'], "sub/f4.in\n") test.write(f5_txt, "f5.txt\n") test.write(f6_txt, "f6.txt\n") -test.run(chdir = 'work', arguments = '.') +test.run(chdir = 'work', arguments = '--debug=stacktrace .') test.fail_test(test.read(f1_out) != "f1.in\n") test.fail_test(test.read(f2_out) != "f2.in\n") diff --git a/test/Install/directories.py b/test/Install/directories.py index 300ed4dc..3005859a 100644 --- a/test/Install/directories.py +++ b/test/Install/directories.py @@ -74,14 +74,14 @@ arguments = [ test.workpath('outside', 'd4'), ] -expect = test.wrap_stdout(""" +expect = test.wrap_stdout("""\ Install directory: "dir1" as "%s" Install directory: "dir2" as "%s" Install directory: "dir3" as "%s" Install directory: "dir4" as "%s" """ % tuple(arguments)) -test.run(chdir = 'work', arguments = arguments) +test.run(chdir = 'work', arguments = arguments, stdout = expect) test.must_match(test.workpath('outside', 'dir1', 'f2'), "work/dir1/f2\n") test.must_match(test.workpath('outside', 'dir1', 'sub', 'f3'), "work/dir1/sub/f3\n") diff --git a/test/Install/option--install-sandbox.py b/test/Install/option--install-sandbox.py new file mode 100644 index 00000000..4cf9310f --- /dev/null +++ b/test/Install/option--install-sandbox.py @@ -0,0 +1,73 @@ +#!/usr/bin/env python +# +# __COPYRIGHT__ +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +""" +Test the --install-sandbox commandline option for Install() and InstallAs(). +""" + +import os.path +import sys +import TestSCons + +test = TestSCons.TestSCons() + +test.subdir('install', 'subdir') +target = 'destination' +destdir = test.workpath( target ) +_SUBDIR_file3_out = os.path.join('$SUBDIR', 'file3.out') +_SUBDIR_file3_in = os.path.join('$SUBDIR', 'file3.in') + +target_file2_out = os.path.join(target, 'file2.out') +subdir_file3_in = os.path.join('subdir', 'file3.in') +target_subdir_file3_out = os.path.join(target, 'subdir', 'file3.out') +file1_out = target+os.path.join( target, destdir, 'file1.out' ) + +# +test.write('SConstruct', r""" +env = Environment(SUBDIR='subdir') +env.Install(r'%(destdir)s', 'file1.out') +env.InstallAs(['file2.out', r'%(_SUBDIR_file3_out)s'], + ['file2.in', r'%(_SUBDIR_file3_in)s']) +""" % locals()) + +test.write('file1.out', "file1.out\n") +test.write('file2.in', "file2.in\n") +test.write(['subdir', 'file3.in'], "subdir/file3.in\n") + +expect = test.wrap_stdout("""\ +Install file: "file2.in" as "%(target_file2_out)s" +Install file: "%(subdir_file3_in)s" as "%(target_subdir_file3_out)s" +Install file: "file1.out" as "%(file1_out)s" +""" % locals()) + +test.run(arguments = '--install-sandbox=%s' % destdir, stdout=expect) + +test.must_match(file1_out, "file1.out\n") +test.must_match('destination/file2.out', "file2.in\n") +test.must_match('destination/subdir/file3.out', "subdir/file3.in\n") + +# +test.pass_test() diff --git a/test/Java/JAVAH.py b/test/Java/JAVAH.py index 15adf34f..ecd3737f 100644 --- a/test/Java/JAVAH.py +++ b/test/Java/JAVAH.py @@ -122,7 +122,7 @@ os.system(string.join(sys.argv[1:], " ")) """ % string.replace(test.workpath('wrapper.out'), '\\', '\\\\')) test.write('SConstruct', """ -foo = Environment(tools = ['javac', 'javah'], +foo = Environment(tools = ['javac', 'javah', 'install'], JAVAC = r'%(where_javac)s', JAVAH = r'%(where_javah)s') javah = foo.Dictionary('JAVAH') diff --git a/test/option-j.py b/test/option-j.py index 8f4c1900..bc36f08f 100644 --- a/test/option-j.py +++ b/test/option-j.py @@ -66,6 +66,7 @@ foo you test.write('SConstruct', """ MyBuild = Builder(action = r'%(_python_)s build.py $TARGETS') env = Environment(BUILDERS = { 'MyBuild' : MyBuild }) +env.Tool('install') env.MyBuild(target = 'f1', source = 'f1.in') env.MyBuild(target = 'f2', source = 'f2.in') @@ -162,6 +163,7 @@ os.environ['PYTHONPATH'] = save_pythonpath test.write('SConstruct', """ MyBuild = Builder(action = r'%(_python_)s build.py $TARGETS') env = Environment(BUILDERS = { 'MyBuild' : MyBuild }) +env.Tool('install') env.MyBuild(target = 'f1', source = 'f1.in') env.MyBuild(target = 'f2', source = 'f2.in') @@ -190,6 +192,7 @@ test.fail_test(not (start2 < finish1)) test.write('SConstruct', """ MyBuild = Builder(action = r'%(_python_)s build.py $TARGETS') env = Environment(BUILDERS = { 'MyBuild' : MyBuild }) +env.Tool('install') env.MyBuild(target = 'f1', source = 'f1.in') env.MyBuild(target = 'f2', source = 'f2.in') diff --git a/test/option-n.py b/test/option-n.py index 2e7694b7..d1f87f02 100644 --- a/test/option-n.py +++ b/test/option-n.py @@ -63,6 +63,7 @@ file.close() test.write('SConstruct', """ MyBuild = Builder(action = r'%(_python_)s build.py $TARGETS') env = Environment(BUILDERS = { 'MyBuild' : MyBuild }) +env.Tool('install') env.MyBuild(target = 'f1.out', source = 'f1.in') env.MyBuild(target = 'f2.out', source = 'f2.in') env.Install('install', 'f3.in') diff --git a/test/option/profile.py b/test/option/profile.py index 7adf9337..d0c0ffc8 100644 --- a/test/option/profile.py +++ b/test/option/profile.py @@ -47,7 +47,6 @@ scons_prof = test.workpath('scons.prof') test.run(arguments = "--profile=%s -h" % scons_prof) test.fail_test(string.find(test.stdout(), 'usage: scons [OPTION]') == -1) -test.fail_test(string.find(test.stdout(), 'usage: scons [OPTION]') == -1) try: save_stdout = sys.stdout diff --git a/test/packaging/guess-package-name.py b/test/packaging/guess-package-name.py new file mode 100644 index 00000000..5b4cd670 --- /dev/null +++ b/test/packaging/guess-package-name.py @@ -0,0 +1,106 @@ +#!/usr/bin/env python +# +# __COPYRIGHT__ +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +""" +This tests the feature of guessing the package name from the given metadata +projectname and version. + +Also overriding this default package name is tested + +Furthermore that targz is the default packager is tested. +""" + +import os +import TestSCons + +python = TestSCons.python +test = TestSCons.TestSCons() +tar = test.detect('TAR', 'tar') + +if not tar: + test.skip_test('tar not found; skipping test\n') + +# +# TEST: default package name creation. +# +test.subdir('src') + +test.write( [ 'src', 'main.c' ], r""" +int main( int argc, char* argv[] ) +{ + return 0; +} +""") + +test.write('SConstruct', """ +env=Environment(tools=['default', 'packaging']) +env.Program( 'src/main.c' ) +env.Package( NAME = 'libfoo', + VERSION = '1.2.3', + PACKAGETYPE = 'zip', + source = [ 'src/main.c', 'SConstruct' ] ) +""") + +test.run(options="--debug=stacktrace", stderr = None) + +test.must_exist( 'libfoo-1.2.3.zip' ) + +# +# TEST: overriding default package name. +# + +test.write('SConstruct', """ +env=Environment(tools=['default', 'packaging']) +env.Program( 'src/main.c' ) +env.Package( NAME = 'libfoo', + VERSION = '1.2.3', + PACKAGETYPE = 'src_targz', + target = 'src.tar.gz', + source = [ 'src/main.c', 'SConstruct' ] ) +""") + +test.run(stderr = None) + +test.must_exist( 'src.tar.gz' ) + +# +# TEST: default package name creation with overriden packager. +# + +test.write('SConstruct', """ +env=Environment(tools=['default', 'packaging']) +env.Program( 'src/main.c' ) +env.Package( NAME = 'libfoo', + VERSION = '1.2.3', + PACKAGETYPE = 'src_tarbz2', + source = [ 'src/main.c', 'SConstruct' ] ) +""") + +test.run(stderr = None) + +test.must_exist( 'libfoo-1.2.3.tar.bz2' ) + +test.pass_test() diff --git a/test/packaging/ipkg.py b/test/packaging/ipkg.py new file mode 100644 index 00000000..62ce628e --- /dev/null +++ b/test/packaging/ipkg.py @@ -0,0 +1,129 @@ +#!/usr/bin/env python +# +# __COPYRIGHT__ +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +""" +Test the ability to call the ipkg tool trough SCons. + +TODO: make a test to assert that the clean action removes ALL intermediate files +""" + +import os +import TestSCons + +python = TestSCons.python +test = TestSCons.TestSCons() +ipkg = test.Environment().WhereIs('ipkg-build') + +if not ipkg: + test.skip_test("ipkg-build not found, skipping test\n") + +test.write( 'main.c', r""" +int main(int argc, char *argv[]) +{ + return 0; +} +""") + +test.write( 'foo.conf', '' ) + +test.write( 'SConstruct', r""" +env=Environment(tools=['default', 'packaging']) +prog = env.Install( 'bin/', Program( 'main.c') ) +conf = env.Install( 'etc/', File( 'foo.conf' ) ) +env.Tag( conf, 'CONF', 'MEHR', 'UND MEHR' ) +env.Package( PACKAGETYPE = 'ipk', + source = env.FindInstalledFiles(), + NAME = 'foo', + VERSION = '0.0', + SUMMARY = 'foo is the ever-present example program -- it does everything', + DESCRIPTION = '''foo is not a real package. This is simply an example that you +may modify if you wish. +. +When you modify this example, be sure to change the Package, Version, +Maintainer, Depends, and Description fields.''', + + SOURCE_URL = 'http://gnu.org/foo-0.0.tar.gz', + X_IPK_SECTION = 'extras', + X_IPK_PRIORITY = 'optional', + ARCHITECTURE = 'arm', + X_IPK_MAINTAINER = 'Familiar User ', + X_IPK_DEPENDS = 'libc6, grep', ) +""") + +expected="""scons: Reading SConscript files ... +scons: done reading SConscript files. +scons: Building targets ... +gcc -o main.o -c main.c +gcc -o main main.o +Copy file(s): "main" to "foo-0.0/bin/main" +Copy file(s): "foo.conf" to "foo-0.0/etc/foo.conf" +build_specfiles(["foo-0.0/CONTROL/control", "foo-0.0/CONTROL/conffiles", "foo-0.0/CONTROL/postrm", "foo-0.0/CONTROL/prerm", "foo-0.0/CONTROL/postinst", "foo-0.0/CONTROL/preinst"], ["foo-0.0/bin/main", "foo-0.0/etc/foo.conf"]) +ipkg-build -o %s -g %s foo-0.0 +Packaged contents of foo-0.0 into %s/foo_0.0_arm.ipk +scons: done building targets. +"""%(os.popen('id -un').read().strip(), os.popen('id -gn').read().strip(), test.workpath()) + +test.run(arguments="--debug=stacktrace foo_0.0_arm.ipk", stdout=expected) +test.must_exist( 'foo-0.0/CONTROL/control' ) +test.must_exist( 'foo_0.0_arm.ipk' ) + +test.subdir( 'foo-0.0' ) +test.subdir( [ 'foo-0.0', 'CONTROL' ] ) + +test.write( [ 'foo-0.0', 'CONTROL', 'control' ], r""" +Package: foo +Priority: optional +Section: extras +Source: http://gnu.org/foo-0.0.tar.gz +Version: 0.0 +Architecture: arm +Maintainer: Familiar User +Depends: libc6, grep +Description: foo is the ever-present example program -- it does everything + foo is not a real package. This is simply an example that you + may modify if you wish. + . + When you modify this example, be sure to change the Package, Version, + Maintainer, Depends, and Description fields. +""") + +test.write( 'main.c', r""" +int main(int argc, char *argv[]) +{ + return 0; +} +""") + +test.write('SConstruct', """ +env = Environment( tools = [ 'default', 'ipkg' ] ) +prog = env.Install( 'foo-0.0/bin/' , env.Program( 'main.c') ) +env.Ipkg( [ env.Dir( 'foo-0.0' ), prog ] ) +""") + +test.run(arguments='', stderr = None) +test.must_exist( 'foo_0.0_arm.ipk' ) + +test.pass_test() diff --git a/test/packaging/msi/file-placement.py b/test/packaging/msi/file-placement.py new file mode 100644 index 00000000..08b0ba65 --- /dev/null +++ b/test/packaging/msi/file-placement.py @@ -0,0 +1,172 @@ +#!/usr/bin/env python +# +# __COPYRIGHT__ +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +""" +Test the msi packagers ability to put files into distinct directories. +""" + +import TestSCons + +python = TestSCons.python + +test = TestSCons.TestSCons() + +try: + from xml.dom.minidom import * +except ImportError: + test.skip_test('Canoot import xml.dom.minidom skipping test\n') + +wix = test.Environment().WhereIs('candle') + +if wix: + # + # Test the default directory layout + # + test.write( 'file1.exe', "file1" ) + + test.write('SConstruct', """ +env = Environment(tools=['default', 'packaging']) +f1 = env.Install( '/bin/' , 'file1.exe' ) + +env.Package( NAME = 'foo', + VERSION = '1.2', + PACKAGETYPE = 'msi', + SUMMARY = 'balalalalal', + DESCRIPTION = 'this should be reallly really long', + VENDOR = 'Nanosoft_2000', + source = [ f1 ], + ) +""") + + test.run(arguments='', stderr = None) + + dom = parse( test.workpath( 'foo-1.2.wxs' ) ) + dirs = dom.getElementsByTagName( 'Directory' ) + + test.fail_test( not dirs[0].attributes['Name'].value == 'SourceDir' ) + test.fail_test( not dirs[1].attributes['Name'].value == 'PFiles' ) + test.fail_test( not dirs[2].attributes['Name'].value == 'NANOSOF1' ) + test.fail_test( not dirs[3].attributes['Name'].value == 'FOO-1.2' ) + + # + # Try to put 7 files into 5 distinct directories of varying depth and overlapping count + # + test.write( 'file1.exe', "file1" ) + test.write( 'file2.exe', "file2" ) + test.write( 'file3.dll', "file3" ) + test.write( 'file4.dll', "file4" ) + test.write( 'file5.class', "file5" ) + test.write( 'file6.class', "file6" ) + test.write( 'file7.class', "file7" ) + + test.write('SConstruct', """ +env = Environment(tools=['default', 'packaging']) +f1 = env.Install( '/bin/' , 'file1.exe' ) +f2 = env.Install( '/bin/' , 'file2.exe' ) +f3 = env.Install( '/lib/' , 'file3.dll' ) +f4 = env.Install( '/lib/' , 'file4.dll' ) +f5 = env.Install( '/java/edu/teco/' , 'file5.class' ) +f6 = env.Install( '/java/teco/' , 'file6.class' ) +f7 = env.Install( '/java/tec/' , 'file7.class' ) + +env.Package( NAME = 'foo', + VERSION = '1.2', + PACKAGETYPE = 'msi', + SUMMARY = 'balalalalal', + DESCRIPTION = 'this should be reallly really long', + VENDOR = 'Nanosoft_2000', + LICENSE = 'afl', + source = [ f1, f2, f3, f4, f5, f6, f7 ], + ) +""") + + test.run(arguments='', stderr = None) + + dom = parse( test.workpath( 'foo-1.2.wxs' ) ) + files = dom.getElementsByTagName( 'File' ) + + test.fail_test( not files[0].parentNode.parentNode.attributes['LongName'].value == 'bin' ) + test.fail_test( not files[1].parentNode.parentNode.attributes['LongName'].value == 'bin' ) + test.fail_test( not files[2].parentNode.parentNode.attributes['LongName'].value == 'lib' ) + test.fail_test( not files[3].parentNode.parentNode.attributes['LongName'].value == 'lib' ) + + test.fail_test( not files[4].parentNode.parentNode.attributes['LongName'].value == 'teco' ) + test.fail_test( not files[4].parentNode.parentNode.parentNode.attributes['LongName'].value == 'edu' ) + test.fail_test( not files[4].parentNode.parentNode.parentNode.parentNode.attributes['LongName'].value == 'java' ) + + test.fail_test( not files[5].parentNode.parentNode.attributes['LongName'].value == 'teco' ) + test.fail_test( not files[5].parentNode.parentNode.parentNode.attributes['LongName'].value == 'java' ) + + test.fail_test( not files[6].parentNode.parentNode.attributes['LongName'].value == 'tec' ) + test.fail_test( not files[6].parentNode.parentNode.parentNode.attributes['LongName'].value == 'java' ) + + # + # Test distinct directories put into distinct features + # + test.write( 'file1.exe', "file1" ) + test.write( 'file2.exe', "file2" ) + test.write( 'file3.dll', "file3" ) + test.write( 'file3-.dll', "file3" ) + + test.write('SConstruct', """ +env = Environment(tools=['default', 'packaging']) +f1 = env.Install( '/bin/' , 'file1.exe' ) +f2 = env.Install( '/bin/' , 'file2.exe' ) +f3 = env.Install( '/lib/' , 'file3.dll' ) +f4 = env.Install( '/lib/' , 'file3-.dll' ) # generate a collision in the ids + +env.Tag( [f1, f2, f4], X_MSI_FEATURE = 'Core Part' ) +env.Tag( f3, X_MSI_FEATURE = 'Java Part' ) + +env.Package( NAME = 'foo', + VERSION = '1.2', + PACKAGETYPE = 'msi', + SUMMARY = 'balalalalal', + DESCRIPTION = 'this should be reallly really long', + VENDOR = 'Nanosoft_2000', + LICENSE = 'afl', + source = [ f1, f2, f3, f4 ], + ) +""") + + test.run(arguments='', stderr = None) + + dom = parse( test.workpath( 'foo-1.2.wxs' ) ) + features = dom.getElementsByTagName( 'Feature' ) + + test.fail_test( not features[1].attributes['Title'].value == 'Core Part' ) + componentrefs = features[1].getElementsByTagName( 'ComponentRef' ) + test.fail_test( not componentrefs[0].attributes['Id'].value == 'file1.exe' ) + test.fail_test( not componentrefs[1].attributes['Id'].value == 'file2.exe' ) + test.fail_test( not componentrefs[2].attributes['Id'].value == 'file3.dll1' ) + + test.fail_test( not features[2].attributes['Title'].value == 'Java Part' ) + componentrefs = features[2].getElementsByTagName( 'ComponentRef' ) + test.fail_test( not componentrefs[0].attributes['Id'].value == 'file3.dll' ) + +else: + test.no_result() + diff --git a/test/packaging/msi/package.py b/test/packaging/msi/package.py new file mode 100644 index 00000000..24bd26d9 --- /dev/null +++ b/test/packaging/msi/package.py @@ -0,0 +1,138 @@ +#!/usr/bin/env python +# +# __COPYRIGHT__ +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +""" +Test the ability to create a simple msi package. +""" + +import os +import TestSCons + +python = TestSCons.python + +test = TestSCons.TestSCons() + +try: + from xml.dom.minidom import * +except ImportError: + test.skip_test('Canoot import xml.dom.minidom skipping test\n') + +wix = test.Environment().WhereIs('candle') + +if wix: + # + # build with minimal tag set and test for the given package meta-data + # + test.write( 'file1.exe', "file1" ) + test.write( 'file2.exe', "file2" ) + + test.write('SConstruct', """ +import os + +env = Environment(tools=['default', 'packaging']) + +f1 = env.Install( '/usr/' , 'file1.exe' ) +f2 = env.Install( '/usr/' , 'file2.exe' ) + +env.Package( NAME = 'foo', + VERSION = '1.2', + PACKAGETYPE = 'msi', + SUMMARY = 'balalalalal', + DESCRIPTION = 'this should be reallly really long', + VENDOR = 'Nanosoft_2000', + source = [ f1, f2 ], + ) + +env.Alias( 'install', [ f1, f2 ] ) +""") + + test.run(arguments='', stderr = None) + + test.must_exist( 'foo-1.2.wxs' ) + test.must_exist( 'foo-1.2.msi' ) + + dom = parse( test.workpath( 'foo-1.2.wxs' ) ) + Product = dom.getElementsByTagName( 'Product' )[0] + Package = dom.getElementsByTagName( 'Package' )[0] + + test.fail_test( not Product.attributes['Manufacturer'].value == 'Nanosoft_2000' ) + test.fail_test( not Product.attributes['Version'].value == '1.2' ) + test.fail_test( not Product.attributes['Name'].value == 'foo' ) + + test.fail_test( not Package.attributes['Description'].value == 'balalalalal' ) + test.fail_test( not Package.attributes['Comments'].value == 'this should be reallly really long' ) + + # + # build with file tags resulting in multiple components in the msi installer + # + test.write( 'file1.exe', "file1" ) + test.write( 'file2.exe', "file2" ) + test.write( 'file3.html', "file3" ) + test.write( 'file4.dll', "file4" ) + test.write( 'file5.dll', "file5" ) + + test.write('SConstruct', """ +import os +env = Environment(tools=['default', 'packaging']) +f1 = env.Install( '/usr/' , 'file1.exe' ) +f2 = env.Install( '/usr/' , 'file2.exe' ) +f3 = env.Install( '/usr/' , 'file3.html' ) +f4 = env.Install( '/usr/' , 'file4.dll' ) +f5 = env.Install( '/usr/' , 'file5.dll' ) + +env.Tag( f1, X_MSI_FEATURE = 'Java Part' ) +env.Tag( f2, X_MSI_FEATURE = 'Java Part' ) +env.Tag( f3, 'DOC' ) +env.Tag( f4, X_MSI_FEATURE = 'default' ) +env.Tag( f5, X_MSI_FEATURE = ('Another Feature', 'with a long description') ) + +env.Package( NAME = 'foo', + VERSION = '1.2', + PACKAGETYPE = 'msi', + SUMMARY = 'balalalalal', + DESCRIPTION = 'this should be reallly really long', + VENDOR = 'Nanosoft_tx2000', + source = [ f1, f2, f3, f4, f5 ], + ) + +env.Alias( 'install', [ f1, f2, f3, f4, f5 ] ) +""") + + test.run(arguments='', stderr = None) + + test.must_exist( 'foo-1.2.wxs' ) + test.must_exist( 'foo-1.2.msi' ) + + dom = parse( test.workpath( 'foo-1.2.wxs' ) ) + elements = dom.getElementsByTagName( 'Feature' ) + test.fail_test( not elements[1].attributes['Title'].value == 'Main Part' ) + test.fail_test( not elements[2].attributes['Title'].value == 'Documentation' ) + test.fail_test( not elements[3].attributes['Title'].value == 'Another Feature' ) + test.fail_test( not elements[3].attributes['Description'].value == 'with a long description' ) + test.fail_test( not elements[4].attributes['Title'].value == 'Java Part' ) + +else: + test.no_result() diff --git a/test/packaging/option--package-type.py b/test/packaging/option--package-type.py new file mode 100644 index 00000000..68a075c6 --- /dev/null +++ b/test/packaging/option--package-type.py @@ -0,0 +1,78 @@ +#!/usr/bin/env python +# +# __COPYRIGHT__ +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +""" +Test the --package-type option. +""" + +import TestSCons + +machine = TestSCons.machine +_python_ = TestSCons._python_ + +test = TestSCons.TestSCons() + +scons = test.program + +rpm = test.Environment().WhereIs('rpm') + +if not rpm: + test.skip_test('rpm not found, skipping test\n') + +test.subdir('src') + +test.write( 'main', '' ) + +test.write('SConstruct', """ +# -*- coding: iso-8859-15 -*- +env=Environment(tools=['default', 'packaging']) +env.Prepend(RPM = 'TAR_OPTIONS=--wildcards ') +prog=env.Install( '/bin', 'main' ) +env.Package( NAME = 'foo', + VERSION = '1.2.3', + LICENSE = 'gpl', + SUMMARY = 'hello', + PACKAGEVERSION = 0, + X_RPM_GROUP = 'Application/office', + X_RPM_INSTALL = r'%(_python_)s %(scons)s --install-sandbox="$RPM_BUILD_ROOT" "$RPM_BUILD_ROOT"', + DESCRIPTION = 'this should be really long', + source = [ prog ], + SOURCE_URL = 'http://foo.org/foo-1.2.3.tar.gz' + ) +""" % locals()) + +test.run(arguments='package PACKAGETYPE=rpm', stderr = None) + +src_rpm = 'foo-1.2.3-0.src.rpm' +machine_rpm = 'foo-1.2.3-0.%s.rpm' % machine + +test.must_exist( src_rpm ) +test.must_exist( machine_rpm ) + +test.must_not_exist( 'bin/main.c' ) +test.must_not_exist( '/bin/main.c' ) + +test.pass_test() diff --git a/test/packaging/place-files-in-subdirectory.py b/test/packaging/place-files-in-subdirectory.py new file mode 100644 index 00000000..d9758a18 --- /dev/null +++ b/test/packaging/place-files-in-subdirectory.py @@ -0,0 +1,108 @@ +#!/usr/bin/env python +# +# __COPYRIGHT__ +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +""" +Test the requirement to place files in a given subdirectory before archiving. +""" + +import os +import TestSCons + +python = TestSCons.python + +test = TestSCons.TestSCons() + +tar = test.detect('TAR', 'tar') + +if not tar: + test.skipt_test('tar not found, skipping test\n') + +# +# TEST: subdir creation and file copying +# +test.subdir('src') + +test.write('src/main.c', '') + +test.write('SConstruct', """ +env = Environment(tools=['default', 'packaging']) +env.Package( NAME = 'libfoo', + PACKAGEROOT = 'libfoo', + PACKAGETYPE = 'src_zip', + VERSION = '1.2.3', + source = [ 'src/main.c', 'SConstruct' ] ) +""") + +test.run(arguments='libfoo-1.2.3.zip', stderr = None) + +test.must_exist( 'libfoo' ) +test.must_exist( 'libfoo/SConstruct' ) +test.must_exist( 'libfoo/src/main.c' ) + +# +# TEST: subdir guessing and file copying. +# +test.subdir('src') + +test.write('src/main.c', '') + +test.write('SConstruct', """ +env = Environment(tools=['default', 'packaging']) +env.Package( NAME = 'libfoo', + VERSION = '1.2.3', + PACKAGETYPE = 'src_zip', + TARGET = 'src.zip', + source = [ 'src/main.c', 'SConstruct' ] ) +""") + +test.run(stderr = None) + +test.must_exist( 'libfoo-1.2.3' ) +test.must_exist( 'libfoo-1.2.3/SConstruct' ) +test.must_exist( 'libfoo-1.2.3/src/main.c' ) + +# +# TEST: unpacking without the buildir. +# +test.subdir('src') +test.subdir('temp') + +test.write('src/main.c', '') + +test.write('SConstruct', """ +env = Environment(tools=['default', 'packaging']) +env.Package( NAME = 'libfoo', + VERSION = '1.2.3', + PACKAGETYPE = 'src_targz', + source = [ 'src/main.c', 'SConstruct' ] ) +""") + +test.run(stderr = None) + +str = os.popen( 'tar -tzf %s'%test.workpath('libfoo-1.2.3.tar.gz') ).read() +test.fail_test( str != "libfoo-1.2.3/src/main.c\nlibfoo-1.2.3/SConstruct\n" ) + +test.pass_test() diff --git a/test/packaging/rpm/cleanup.py b/test/packaging/rpm/cleanup.py new file mode 100644 index 00000000..26bf79ba --- /dev/null +++ b/test/packaging/rpm/cleanup.py @@ -0,0 +1,100 @@ +#!/usr/bin/env python +# +# __COPYRIGHT__ +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +""" +Assert that files created by the RPM packager will be removed by 'scons -c'. +""" + +import TestSCons + +machine = TestSCons.machine +_python_ = TestSCons._python_ + +test = TestSCons.TestSCons() + +scons = test.program + +# TODO: skip this test, since only the intermediate directory needs to be +# removed. + +rpm = test.Environment().WhereIs('rpm') + +if not rpm: + test.skip_test('rpm not found, skipping test\n') + +test.subdir('src') + +test.write( [ 'src', 'main.c' ], r""" +int main( int argc, char* argv[] ) +{ + return 0; +} +""") + +test.write('SConstruct', """ +env=Environment(tools=['default', 'packaging']) + +env.Prepend(RPM = 'TAR_OPTIONS=--wildcards ') + +prog = env.Install( '/bin/' , Program( 'src/main.c') ) + +env.Package( NAME = 'foo', + VERSION = '1.2.3', + PACKAGEVERSION = 0, + PACKAGETYPE = 'rpm', + LICENSE = 'gpl', + SUMMARY = 'balalalalal', + X_RPM_GROUP = 'Application/fu', + X_RPM_INSTALL = r'%(_python_)s %(scons)s --install-sandbox="$RPM_BUILD_ROOT" "$RPM_BUILD_ROOT"', + DESCRIPTION = 'this should be really really long', + source = [ prog ], + SOURCE_URL = 'http://foo.org/foo-1.2.3.tar.gz' + ) + +env.Alias( 'install', prog ) +""" % locals()) + +# first run: build the package +# second run: test if the intermediate files have been cleaned +test.run( arguments='' ) +test.run( arguments='-c' ) + +src_rpm = 'foo-1.2.3-0.src.rpm' +machine_rpm = 'foo-1.2.3-0.%s.rpm' % machine + +test.must_not_exist( machine_rpm ) +test.must_not_exist( src_rpm ) +test.must_not_exist( 'foo-1.2.3.tar.gz' ) +test.must_not_exist( 'foo-1.2.3.spec' ) +test.must_not_exist( 'foo-1.2.3/foo-1.2.3.spec' ) +test.must_not_exist( 'foo-1.2.3/SConstruct' ) +test.must_not_exist( 'foo-1.2.3/src/main.c' ) +# We don't remove the directories themselves. Yet. +#test.must_not_exist( 'foo-1.2.3' ) +#test.must_not_exist( 'foo-1.2.3/src' ) +test.must_not_exist( 'bin/main' ) + +test.pass_test() diff --git a/test/packaging/rpm/internationalization.py b/test/packaging/rpm/internationalization.py new file mode 100644 index 00000000..66c22917 --- /dev/null +++ b/test/packaging/rpm/internationalization.py @@ -0,0 +1,180 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# __COPYRIGHT__ +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +""" +Test the ability to handle internationalized package and file meta-data. + +These are x-rpm-Group, description, summary and the lang_xx file tag. +""" + +import os + +import TestSCons + +machine = TestSCons.machine +_python_ = TestSCons._python_ + +test = TestSCons.TestSCons() + +scons = test.program + +rpm = test.Environment().WhereIs('rpm') + +if not rpm: + test.skip_test('rpm not found, skipping test\n') + +# +# test INTERNATIONAL PACKAGE META-DATA +# +test.write( [ 'main.c' ], r""" +int main( int argc, char* argv[] ) +{ + return 0; +} +""") + +test.write('SConstruct', """ +# -*- coding: utf-8 -*- +import os + +env = Environment(tools=['default', 'packaging']) + +env.Prepend(RPM = 'TAR_OPTIONS=--wildcards ') + +prog = env.Install( '/bin', Program( 'main.c' ) ) + +env.Package( NAME = 'foo', + VERSION = '1.2.3', + PACKAGETYPE = 'rpm', + LICENSE = 'gpl', + SUMMARY = 'hello', + SUMMARY_de = 'hallo', + SUMMARY_fr = 'bonjour', + PACKAGEVERSION = 0, + X_RPM_GROUP = 'Application/office', + X_RPM_GROUP_de = 'Applikation/büro', + X_RPM_GROUP_fr = 'Application/bureau', + X_RPM_INSTALL = r'%(_python_)s %(scons)s --install-sandbox="$RPM_BUILD_ROOT" "$RPM_BUILD_ROOT"', + DESCRIPTION = 'this should be really long', + DESCRIPTION_de = 'das sollte wirklich lang sein', + DESCRIPTION_fr = 'ceci devrait être vraiment long', + source = [ prog ], + SOURCE_URL = 'http://foo.org/foo-1.2.3.tar.gz' + ) + +env.Alias ( 'install', prog ) +""" % locals()) + +test.run(arguments='', stderr = None) + +src_rpm = 'foo-1.2.3-0.src.rpm' +machine_rpm = 'foo-1.2.3-0.%s.rpm' % machine + +test.must_exist( src_rpm ) +test.must_exist( machine_rpm ) + +test.must_not_exist( 'bin/main' ) + +cmd = 'rpm -qp --queryformat \'%%{GROUP}-%%{SUMMARY}-%%{DESCRIPTION}\' %s' + +os.environ['LC_ALL'] = 'de_DE.utf8' +os.environ['LANGUAGE'] = 'de' +out = os.popen( cmd % test.workpath(machine_rpm) ).read() +test.fail_test( out != 'Applikation/büro-hallo-das sollte wirklich lang sein' ) + +os.environ['LC_ALL'] = 'fr_FR.utf8' +os.environ['LANGUAGE'] = 'fr' +out = os.popen( cmd % test.workpath(machine_rpm) ).read() +test.fail_test( out != 'Application/bureau-bonjour-ceci devrait être vraiment long' ) + +os.environ['LANGUAGE'] = 'en' +out = os.popen( cmd % test.workpath(machine_rpm) ).read() +test.fail_test( out != 'Application/office-hello-this should be really long' ) + +os.environ['LC_ALL'] = 'ae' +out = os.popen( cmd % test.workpath(machine_rpm) ).read() +test.fail_test( out != 'Application/office-hello-this should be really long' ) + +# +# test INTERNATIONAL PACKAGE TAGS +# + +test.write( [ 'main.c' ], r""" +int main( int argc, char* argv[] ) +{ + return 0; +} +""") + +test.write( ['man.de'], '' ) +test.write( ['man.en'], '' ) +test.write( ['man.fr'], '' ) + +test.write('SConstruct', """ +# -*- coding: utf-8 -*- +import os + +env = Environment(tools=['default', 'packaging']) +prog = env.Install( '/bin', Program( 'main.c' ) ) + +man_pages = Flatten( [ + env.Install( '/usr/share/man/de', 'man.de' ), + env.Install( '/usr/share/man/en', 'man.en' ), + env.Install( '/usr/share/man/fr', 'man.fr' ) +] ) + +env.Tag( man_pages, 'LANG_DE', 'DOC') + +env.Package( NAME = 'foo', + VERSION = '1.2.3', + PACKAGETYPE = 'rpm', + LICENSE = 'gpl', + SUMMARY = 'hello', + SUMMARY_de = 'hallo', + SUMMARY_fr = 'bonjour', + PACKAGEVERSION = 0, + X_RPM_GROUP = 'Application/office', + X_RPM_GROUP_de = 'Applikation/büro', + X_RPM_GROUP_fr = 'Application/bureau', + X_RPM_INSTALL = r'%(_python_)s %(scons)s --install-sandbox="$RPM_BUILD_ROOT" "$RPM_BUILD_ROOT"', + DESCRIPTION = 'this should be really long', + DESCRIPTION_de = 'das sollte wirklich lang sein', + DESCRIPTION_fr = 'ceci devrait être vraiment long', + source = [ prog, man_pages ], + SOURCE_URL = 'http://foo.org/foo-1.2.3.tar.gz', + ) + +env.Alias ( 'install', [ prog, man_pages ] ) +""" % locals()) + + +test.run(arguments='--install-sandbox=blubb install', stderr = None) + +test.must_exist( src_rpm ) +test.must_exist( machine_rpm ) + +test.pass_test() diff --git a/test/packaging/rpm/package.py b/test/packaging/rpm/package.py new file mode 100644 index 00000000..a5f9f0fc --- /dev/null +++ b/test/packaging/rpm/package.py @@ -0,0 +1,91 @@ +#!/usr/bin/env python +# +# __COPYRIGHT__ +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +""" +Test the ability to create a really simple rpm package. +""" + +import os +import TestSCons + +machine = TestSCons.machine +_python_ = TestSCons._python_ + +test = TestSCons.TestSCons() + +scons = test.program + +rpm = test.Environment().WhereIs('rpm') + +if not rpm: + test.skip_test('rpm not found, skipping test\n') + +test.subdir('src') + +test.write( [ 'src', 'main.c' ], r""" +int main( int argc, char* argv[] ) +{ + return 0; +} +""") + +test.write('SConstruct', """ +import os + +env=Environment(tools=['default', 'packaging']) + +env.Prepend(RPM = 'TAR_OPTIONS=--wildcards ') + +prog = env.Install( '/bin/' , Program( 'src/main.c') ) + +env.Package( NAME = 'foo', + VERSION = '1.2.3', + PACKAGEVERSION = 0, + PACKAGETYPE = 'rpm', + LICENSE = 'gpl', + SUMMARY = 'balalalalal', + X_RPM_GROUP = 'Application/fu', + X_RPM_INSTALL = r'%(_python_)s %(scons)s --debug=tree --install-sandbox="$RPM_BUILD_ROOT" "$RPM_BUILD_ROOT"', + DESCRIPTION = 'this should be really really long', + source = [ prog ], + SOURCE_URL = 'http://foo.org/foo-1.2.3.tar.gz' + ) + +env.Alias( 'install', prog ) +""" % locals()) + +test.run(arguments='', stderr = None) + +src_rpm = 'foo-1.2.3-0.src.rpm' +machine_rpm = 'foo-1.2.3-0.%s.rpm' % machine + +test.must_exist( machine_rpm ) +test.must_exist( src_rpm ) +test.must_not_exist( 'bin/main' ) +test.fail_test( not os.popen('rpm -qpl %s' % machine_rpm).read()=='/bin/main\n') +test.fail_test( not os.popen('rpm -qpl %s' % src_rpm).read()=='foo-1.2.3.spec\nfoo-1.2.3.tar.gz\n') + +test.pass_test() diff --git a/test/packaging/rpm/tagging.py b/test/packaging/rpm/tagging.py new file mode 100644 index 00000000..198799a1 --- /dev/null +++ b/test/packaging/rpm/tagging.py @@ -0,0 +1,97 @@ +#!/usr/bin/env python +# +# __COPYRIGHT__ +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +""" +Test the ability to add file tags +""" + +import os +import string + +import TestSCons + +machine = TestSCons.machine +_python_ = TestSCons._python_ + +test = TestSCons.TestSCons() + +scons = test.program + +rpm = test.Environment().WhereIs('rpm') + +if not rpm: + test.skip_test('rpm not found, skipping test\n') + +# +# Test adding an attr tag to the built program. +# +test.subdir('src') + +test.write( [ 'src', 'main.c' ], r""" +int main( int argc, char* argv[] ) +{ +return 0; +} +""") + +test.write('SConstruct', """ +import os + +env = Environment(tools=['default', 'packaging']) +env.Prepend(RPM = 'TAR_OPTIONS=--wildcards ') +install_dir= os.path.join( ARGUMENTS.get('prefix', '/'), 'bin/' ) +prog_install = env.Install( install_dir , Program( 'src/main.c' ) ) +env.Tag( prog_install, UNIX_ATTR = '(0755, root, users)' ) +env.Alias( 'install', prog_install ) + +env.Package( NAME = 'foo', + VERSION = '1.2.3', + PACKAGETYPE = 'rpm', + LICENSE = 'gpl', + SUMMARY = 'balalalalal', + PACKAGEVERSION = 0, + X_RPM_GROUP = 'Applicatio/fu', + X_RPM_INSTALL = r'%(_python_)s %(scons)s --install-sandbox="$RPM_BUILD_ROOT" "$RPM_BUILD_ROOT"', + DESCRIPTION = 'this should be really really long', + source = [ prog_install ], + SOURCE_URL = 'http://foo.org/foo-1.2.3.tar.gz' + ) +""" % locals()) + +test.run(arguments='', stderr = None) + +src_rpm = 'foo-1.2.3-0.src.rpm' +machine_rpm = 'foo-1.2.3-0.%s.rpm' % machine + +test.must_exist( machine_rpm ) +test.must_exist( src_rpm ) +test.fail_test( not os.popen('rpm -qpl %s' % machine_rpm).read()=='/bin/main\n') +test.fail_test( not os.popen('rpm -qpl %s' % src_rpm).read()=='foo-1.2.3.spec\nfoo-1.2.3.tar.gz\n') + +expect = '(0755, root, users) /bin/main' +test.fail_test(string.find(test.read('foo-1.2.3.spec'), expect) == -1) + +test.pass_test() diff --git a/test/packaging/sandbox-test.py b/test/packaging/sandbox-test.py new file mode 100644 index 00000000..f82940bf --- /dev/null +++ b/test/packaging/sandbox-test.py @@ -0,0 +1,73 @@ +#!/usr/bin/env python +# +# __COPYRIGHT__ +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +""" +Test a simple project +""" + +import TestSCons + +python = TestSCons.python + +test = TestSCons.TestSCons() + +tar = test.detect('TAR', 'tar') + +if not tar: + test.skip_test('tar not found, skipping test\n') + +test.subdir('src') + +test.write([ 'src', 'foobar.h' ], '') +test.write([ 'src', 'foobar.c' ], '') + +test.write('SConstruct', """ +from glob import glob + +src_files = glob( 'src/*.c' ) +include_files = glob( 'src/*.h' ) + +SharedLibrary( 'foobar', src_files ) + +env = Environment(tools=['default', 'packaging']) + +env.Package( NAME = 'libfoobar', + VERSION = '1.2.3', + PACKAGETYPE = 'targz', + source = src_files + include_files ) + +env.Package( NAME = 'libfoobar', + VERSION = '1.2.3', + PACKAGETYPE = 'zip', + source = src_files + include_files ) +""") + +test.run(stderr=None) + +test.must_exist( 'libfoobar-1.2.3.tar.gz' ) +test.must_exist( 'libfoobar-1.2.3.zip' ) + +test.pass_test() diff --git a/test/packaging/strip-install-dir.py b/test/packaging/strip-install-dir.py new file mode 100644 index 00000000..65b6a615 --- /dev/null +++ b/test/packaging/strip-install-dir.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python +# +# __COPYRIGHT__ +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +""" +Test stripping the InstallBuilder of the Package source file. +""" + +import TestSCons + +python = TestSCons.python + +test = TestSCons.TestSCons() + +test.write( 'main.c', '' ) +test.write('SConstruct', """ +prog = Install( '/bin', 'main.c' ) +env=Environment(tools=['default', 'packaging']) +env.Package( NAME = 'foo', + VERSION = '1.2.3', + source = [ prog ], + ) +""") + +expected = """scons: Reading SConscript files ... +scons: done reading SConscript files. +scons: Building targets ... +Copy file(s): "main.c" to "foo-1.2.3/bin/main.c" +tar -zc -f foo-1.2.3.tar.gz foo-1.2.3/bin/main.c +scons: done building targets. +""" + +test.run(arguments='', stderr = None, stdout=expected) + +test.must_not_exist( 'bin/main.c' ) +test.must_not_exist( '/bin/main.c' ) + +test.pass_test() diff --git a/test/packaging/tar/bz2.py b/test/packaging/tar/bz2.py new file mode 100644 index 00000000..938ac11a --- /dev/null +++ b/test/packaging/tar/bz2.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python +# +# __COPYRIGHT__ +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +""" +This tests the SRC bz2 packager, which does the following: + - create a tar package from the specified files +""" + +import os +import TestSCons + +python = TestSCons.python + +test = TestSCons.TestSCons() + +tar = test.detect('TAR', 'tar') + +if tar: + test.subdir('src') + + test.write( [ 'src', 'main.c' ], r""" +int main( int argc, char* argv[] ) +{ + return 0; +} + """) + + test.write('SConstruct', """ +Program( 'src/main.c' ) +env=Environment(tools=['default', 'packaging']) +env.Package( PACKAGETYPE = 'src_tarbz2', + target = 'src.tar.bz2', + PACKAGEROOT = 'test', + source = [ 'src/main.c', 'SConstruct' ] ) +""") + + test.run(arguments='', stderr = None) + + test.must_exist( 'src.tar.bz2' ) + +test.pass_test() diff --git a/test/packaging/tar/gz.py b/test/packaging/tar/gz.py new file mode 100644 index 00000000..26ce60d6 --- /dev/null +++ b/test/packaging/tar/gz.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python +# +# __COPYRIGHT__ +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +""" +This tests the SRC 'targz' packager, which does the following: + - create a targz package containing the specified files. +""" + +import os +import TestSCons + +python = TestSCons.python + +test = TestSCons.TestSCons() + +tar = test.detect('TAR', 'tar') + +if tar: + test.subdir('src') + + test.write( [ 'src', 'main.c' ], r""" +int main( int argc, char* argv[] ) +{ + return 0; +} + """) + + test.write('SConstruct', """ +Program( 'src/main.c' ) +env=Environment(tools=['default', 'packaging']) +env.Package( PACKAGETYPE = 'src_targz', + target = 'src.tar.gz', + PACKAGEROOT = 'test', + source = [ 'src/main.c', 'SConstruct' ] ) +""") + + test.run(arguments='', stderr = None) + + test.must_exist( 'src.tar.gz' ) + +test.pass_test() diff --git a/test/packaging/use-builddir.py b/test/packaging/use-builddir.py new file mode 100644 index 00000000..50a569a5 --- /dev/null +++ b/test/packaging/use-builddir.py @@ -0,0 +1,93 @@ +#!/usr/bin/env python +# +# __COPYRIGHT__ +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +""" +Test the ability to use the archiver in combination with builddir. +""" + +import os +import TestSCons + +python = TestSCons.python + +test = TestSCons.TestSCons() + +tar = test.detect('TAR', 'tar') + +if not tar: + test.skip_test('tar not found, skipping test\n') + +# +# TEST: builddir usage. +# +test.subdir('src') +test.subdir('build') + +test.write('src/main.c', '') + +test.write('SConstruct', """ +BuildDir('build', 'src') +env=Environment(tools=['default', 'packaging']) +env.Package( NAME = 'libfoo', + PACKAGEROOT = 'build/libfoo', + VERSION = '1.2.3', + PACKAGETYPE = 'src_zip', + target = 'build/libfoo-1.2.3.zip', + source = [ 'src/main.c', 'SConstruct' ] ) +""") + +test.run(stderr = None) + +test.must_exist( 'build/libfoo-1.2.3.zip' ) + +# TEST: builddir not placed in archive +# XXX: BuildDir should be stripped. +# +test.subdir('src') +test.subdir('build') +test.subdir('temp') + +test.write('src/main.c', '') + +test.write('SConstruct', """ +BuildDir('build', 'src') +env=Environment(tools=['default', 'packaging']) +env.Package( NAME = 'libfoo', + VERSION = '1.2.3', + PAKCAGETYPE = 'src_targz', + source = [ 'src/main.c', 'SConstruct' ] ) +""") + +test.run(stderr = None) + +test.must_exist( 'libfoo-1.2.3.tar.gz' ) + +os.popen( 'tar -C temp -xzf %s'%test.workpath('libfoo-1.2.3.tar.gz') ) + +test.must_exist( 'temp/libfoo-1.2.3/src/main.c' ) +test.must_exist( 'temp/libfoo-1.2.3/SConstruct' ) + +test.pass_test() diff --git a/test/packaging/zip.py b/test/packaging/zip.py new file mode 100644 index 00000000..a2406e6f --- /dev/null +++ b/test/packaging/zip.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python +# +# __COPYRIGHT__ +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +""" +This tests the SRC zip packager, which does the following: + - create a zip package from the specified files +""" + +import TestSCons + +python = TestSCons.python + +test = TestSCons.TestSCons() + +zip = test.detect('ZIP', 'zip') + +if not zip: + test.skip_test('zip not found, skipping test\n') + +test.subdir('src') + +test.write( [ 'src', 'main.c' ], r""" +int main( int argc, char* argv[] ) +{ + return 0; +} +""") + +test.write('SConstruct', """ +Program( 'src/main.c' ) +env=Environment(tools=['default', 'packaging']) +env.Package( PACKAGETYPE = 'src_zip', + target = 'src.zip', + PACKAGEROOT = 'test', + source = [ 'src/main.c', 'SConstruct' ] ) +""") + +test.run(arguments='', stderr = None) + +test.must_exist( 'src.zip' ) + +test.pass_test()