2 # Construct file to build scons during development.
3 # (Kind of ironic that we're using the classic Perl Cons
4 # to build its Python child...)
8 # Copyright (c) 2001, 2002 Steven Knight
10 # Permission is hereby granted, free of charge, to any person obtaining
11 # a copy of this software and associated documentation files (the
12 # "Software"), to deal in the Software without restriction, including
13 # without limitation the rights to use, copy, modify, merge, publish,
14 # distribute, sublicense, and/or sell copies of the Software, and to
15 # permit persons to whom the Software is furnished to do so, subject to
16 # the following conditions:
18 # The above copyright notice and this permission notice shall be included
19 # in all copies or substantial portions of the Software.
21 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
22 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
23 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
25 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
27 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 Using Cons to build the SCons packages is no longer supported,
33 and this Construct file is deprecated.
35 Use SCons to build its own packages instead.
37 See the README file for details.
47 # An internal "whereis" routine to figure out if we have a
48 # given program available. Put it in the "cons::" package
49 # so subsidiary Conscript files can get at it easily, too.
54 foreach my $dir (split(/$Config{path_sep}/, $ENV{PATH})) {
55 $f = File::Spec->catfile($dir, $file);
56 return $f if -x $f && ! -d $f;
63 open(F, "<$file") || die "cannot open $file: $!";
66 return wantarray ? @lines : join('', @lines);
70 # We let the presence or absence of various utilities determine
71 # whether or not we bother to build certain pieces of things.
72 # This will allow people to still do SCons work even if they
73 # don't have Aegis or RPM installed, for example.
75 $aegis = cons::whereis('aegis');
76 $aesub = cons::whereis('aesub');
77 $rpm = cons::whereis('rpm');
78 $dh_builddeb = cons::whereis('dh_builddeb');
79 $fakeroot = cons::whereis('fakeroot');
81 # My installation on Red Hat doesn't like any debhelper version
82 # beyond 2, so let's use 2 as the default on any non-Debian build.
83 $DH_COMPAT = (-f "/etc/debian_version") ? 3 : 2;
86 # Now grab the information that we "build" into the files (using sed).
88 chomp($date = $ARG{date});
90 ($sec,$min,$hour,$mday,$mon,$year) = localtime(time);
92 $date = sprintf("%4d/%02d/%02d %02d:%02d:%02d",
93 $year, $mon, $mday, $hour, $min, $sec);
96 $developer = $ARG{developer} || $ENV{USERNAME} || $ENV{LOGNAME} || $ENV{USER};
98 $revision = $ARG{version};
99 chomp($revision = `$aesub '\$version' 2>/dev/null`) if $aesub && ! $revision;
100 $revision = '0.05' if ! $revision;
102 @arr = split(/\./, $revision);
103 @arr = ($arr[0], map {length($_) == 1 ? "0$_" : $_} @arr[1 .. $#arr]);
104 $revision = join('.', @arr);
106 # Here's how we'd turn the calculated $revision into our package $version.
107 # This makes it difficult to coordinate with other files (debian/changelog
108 # and rpm/scons.spec) that hard-code the version number, so just go with
110 #pop @arr if $#arr >= 2;
111 #map {s/^[CD]//, s/^0*(\d\d)$/$1/} @arr;
112 #$version = join('.', @arr);
115 $change = $ARG{change};
116 chomp($change = `$aesub '\$change' 2>/dev/null`) if $aesub && ! $change;
118 chomp($python_ver = `python -c "import sys; print sys.version[0:3]"`);
120 chomp($platform = `python -c "from distutils.util import get_platform; print get_platform()"`);
122 if ($platform eq "win32") {
125 $archsuffix = "tar.gz"
129 $test1_dir = File::Spec->catfile(cwd, "build", "test1");
130 $test2_dir = File::Spec->catfile(cwd, "build", "test2");
132 $lib_project = File::Spec->catfile("lib", "$project");
134 # Originally, we were going to package the build engine in a
135 # private SCons library that contained the version number, so
136 # we could easily have multiple side-by-side versions of SCons
137 # installed. Keep this around in case we ever want to go back
138 # to that scheme. Note that this also requires changes to
139 # runtest.py and src/setup.py.
140 #$lib_project = File::Spec->catfile("lib", "$project-$version");
142 $test1_lib_dir = File::Spec->catfile($test1_dir, $lib_project);
144 $test2_lib_dir = File::Spec->catfile($test2_dir,
146 "python${python_ver}",
149 $unpack_dir = File::Spec->catfile(cwd, "build", "unpack");
151 $env = new cons( ENV => {
152 AEGIS_PROJECT => $ENV{AEGIS_PROJECT},
156 TEST1_LIB_DIR => $test1_lib_dir,
157 TEST2_LIB_DIR => $test2_lib_dir,
160 DEVELOPER => $developer,
161 REVISION => $revision,
165 # Use %(-%) around the date so date
166 # changes don't cause rebuilds.
167 SEDFLAGS => " %( -e 's+__DATE__+%DATE+' %)" .
168 " -e 's+__DEVELOPER__+%DEVELOPER+'" .
169 " -e 's+__FILE__+%<+'" .
170 " -e 's+__REVISION__+%REVISION+'" .
171 " -e 's+__VERSION__+%VERSION+'",
172 SEDCOM => "%SED %SEDFLAGS %< > %>",
176 # Define SCons packages.
178 # In the original, more complicated packaging scheme, we were going
179 # to have separate packages for:
181 # python-scons only the build engine
182 # scons-script only the script
183 # scons the script plus the build engine
185 # We're now only delivering a single "scons" package, but this is still
186 # "built" as two sub-packages (the build engine and the script), so
187 # the definitions remain here, even though we're not using them for
192 'pkg' => "python-$project",
193 'src_subdir' => 'engine',
194 'inst_subdir' => File::Spec->catfile("lib",
197 'prefix' => $test2_dir,
199 'debian_deps' => [ qw(debian/rules debian/control
200 debian/changelog debian/copyright
201 debian/python-scons.postinst
202 debian/python-scons.prerm) ],
204 'files' => [ qw(LICENSE.txt README.txt setup.cfg setup.py) ],
206 'LICENSE.txt' => '../LICENSE.txt',
211 # The original packaging scheme would have have required us to push
212 # the Python version number into the package name (python1.5-scons,
213 # python2.0-scons, etc.), which would have required a definition
214 # like the following. Leave this here in case we ever decide to do
215 # this in the future, but note that this would require some modification
216 # to src/engine/setup.py before it would really work.
219 # 'pkg' => "python2-$project",
220 # 'src_subdir' => 'engine',
221 # 'inst_subdir' => File::Spec->catfile("lib",
224 # 'prefix' => $test2_dir,
226 # 'debian_deps' => [ qw(debian/rules debian/control
227 # debian/changelog debian/copyright
228 # debian/python2-scons.postinst
229 # debian/python2-scons.prerm) ],
231 # 'files' => [ qw(LICENSE.txt README.txt setup.cfg setup.py) ],
233 # 'LICENSE.txt' => '../LICENSE.txt',
238 'pkg' => "$project-script",
239 'src_subdir' => 'script',
240 'inst_subdir' => 'bin',
241 'prefix' => $test2_dir,
243 'debian_deps' => [ qw(debian/rules debian/control
244 debian/changelog debian/copyright
245 debian/python-scons.postinst
246 debian/python-scons.prerm) ],
248 'files' => [ qw(LICENSE.txt README.txt setup.cfg setup.py) ],
250 'LICENSE.txt' => '../LICENSE.txt',
251 'scons' => 'scons.py',
257 'inst_subdir' => undef,
258 'prefix' => $test1_dir,
260 'debian_deps' => [ qw(debian/rules debian/control
261 debian/changelog debian/copyright
262 debian/scons.postinst
263 debian/scons.prerm) ],
265 'files' => [ qw(CHANGES.txt LICENSE.txt
266 README.txt RELEASE.txt
267 os_spawnv_fix.diff scons.1
268 script/scons.bat setup.cfg setup.py) ],
270 'scons.1' => '../doc/man/scons.1',
273 'subpkgs' => [ $python_scons, $scons_script ],
274 'subinst_dirs' => { "python-$project" => $lib_project,
275 "$project-script" => 'bin',
283 # Initialize variables with the right directories for this package.
285 my $pkg = $p->{'pkg'};
288 $src = File::Spec->catfile($src, $p->{'src_subdir'}) if $p->{'src_subdir'};
290 my $build = File::Spec->catfile('build', $pkg);
292 my $prefix = $p->{'prefix'};
293 my $install = $prefix;
294 if ($p->{'inst_subdir'}) {
295 $install = File::Spec->catfile($install, $p->{'inst_subdir'});
299 # Read up the list of source files from our MANIFEST.in.
300 # This list should *not* include LICENSE.txt, MANIFEST,
301 # README.txt, or setup.py. Make a copy of the list for the
304 my @src_files = cons::read_file("$src/MANIFEST.in");
306 my @dst_files = map(File::Spec->catfile($install, $_), @src_files);
308 if ($p->{'subpkgs'}) {
310 # This package includes some sub-packages. Read up their
311 # MANIFEST.in files, and add them to our source and destination
312 # file lists, modifying them as appropriate to add the
315 foreach $sp (@{$p->{'subpkgs'}}) {
316 my $ssubdir = $sp->{'src_subdir'};
317 my $isubdir = $p->{'subinst_dirs'}->{$sp->{'pkg'}};
318 my $manifest = File::Spec->catfile($src, $ssubdir, 'MANIFEST.in');
319 my @f = cons::read_file($manifest);
321 push(@src_files, map(File::Spec->catfile($sp->{'src_subdir'}, $_),
323 push(@dst_files, map(File::Spec->catfile($install, $isubdir, $_),
326 foreach $k (keys %{$sp->{'filemap'}}) {
327 my $f = $sp->{'filemap'}->{$k};
328 next if ! defined $f;
329 $k = File::Spec->catfile($sp->{'src_subdir'}, $k);
330 $p->{'filemap'}->{$k} = File::Spec->catfile($sp->{'src_subdir'},
337 # Now that we have the "normal" source files, add those files
338 # that are standard for each distribution. Note that we don't
339 # add these to dst_files, because they don't get installed.
340 # And we still have the MANIFEST to add.
342 push(@src_files, @{$p->{'files'}});
345 # Now run everything in src_file through the sed command we
346 # concocted to expand __FILE__, __VERSION__, etc.
348 foreach $b (@src_files) {
349 my $s = $p->{'filemap'}->{$b} || $b;
350 $env->Command("$build/$b", "$src/$s", "%SEDCOM");
354 # NOW, finally, we can create the MANIFEST, which we do
355 # by having Perl spit out the contents of the @src_files
356 # array we've carefully created. After we've added
357 # MANIFEST itself to the array, of course.
359 push(@src_files, "MANIFEST");
360 $env->Command("$build/MANIFEST", "$src/MANIFEST.in",
361 qq([perl] open(F, ">%>"); print F join("\\n", sort qw(@src_files)), "\\n"; close(F)));
364 # Use the Python distutils to generate the packages.
366 my $archive = "$build/dist/$pkg-$version.$archsuffix";
368 push(@src_deps, $archive);
370 my @build_targets = (
371 "$build/dist/$pkg-$version.$platform.$archsuffix",
373 "$build/dist/$pkg-$version.win32.exe",
375 my @install_targets = @build_targets;
377 # We can get away with calling setup.py using a directory path
378 # like this because we put a preamble in it that will chdir()
379 # to the directory in which setup.py exists.
380 my @bdist_dirs = ("$build/build/lib", "$build/build/scripts");
381 my $commands = qq(rm -rf @bdist_dirs && python $build/setup.py bdist
382 python $build/setup.py sdist
383 python $build/setup.py bdist_wininst);
387 $topdir = "$cwd/$build/build/bdist.$platform/rpm";
389 $BUILDdir = "$topdir/BUILD/$pkg-$version";
390 $RPMSdir = "$topdir/RPMS/noarch";
391 $SOURCESdir = "$topdir/SOURCES";
392 $SPECSdir = "$topdir/SPECS";
393 $SRPMSdir = "$topdir/SRPMS";
395 $specfile = "$SPECSdir/$pkg-$version-1.spec";
396 $sourcefile = "$SOURCESdir/$pkg-$version.$archsuffix";
397 $rpm = "$RPMSdir/$pkg-$version-1.noarch.rpm";
398 $src_rpm = "$SRPMSdir/$pkg-$version-1.src.rpm";
400 # We'd like to use the following here:
402 # $env->InstallAs($specfile, "rpm/$pkg.spec");
403 # $env->InstallAs($sourcefile, $archive);
405 # but it looks like InstallAs doesn't propogate the
406 # signatures correctly, which means that the RPM file
407 # wouldn't always get rebuilt when it should. Work
410 $env->Command($specfile, "rpm/$pkg.spec",
411 "[perl] File::Copy::copy('%<', '%>')");
412 $env->Command($sourcefile, $archive,
413 "[perl] File::Copy::copy('%<', '%>')");
415 if (! -d $BUILDdir) {
416 $cmd = "mkdir -p $BUILDdir; ";
418 my @targets = ( $rpm, $src_rpm );
419 $env->Command(\@targets, $specfile,
420 "${cmd}rpm --define '_topdir $topdir' -ba %<");
421 $env->Depends(\@targets, $sourcefile);
423 push(@install_targets, @targets);
426 @build_src_files = map("$build/$_", @src_files);
428 if ($dh_builddeb && $fakeroot) {
429 # Debian builds directly into build/dist, so we don't
430 # need to add the .debs to the install_targets.
431 my $deb = "build/dist/${pkg}_$version-1_all.deb";
432 $env->Command($deb, @build_src_files, qq(
433 fakeroot make -f debian/rules VERSION=%VERSION DH_COMPAT=$DH_COMPAT ENVOKED_BY_CONSTRUCT=1 binary-$pkg
434 env DH_COMPAT=$DH_COMPAT dh_clean));
435 $env->Depends($deb, @{$p->{'debian_deps'}});
439 # Now set up creation and installation of the packages.
441 $env->Command([@build_targets], @build_src_files, $commands);
443 $env->Install("build/dist", @install_targets);
446 # Unpack the archive created by the distutils into build/unpack.
448 my @unpack_files = map("$unpack_dir/$pkg-$version/$_", @src_files);
450 # We'd like to replace the last three lines with the following:
452 # tar zxf %< -C $unpack_dir
454 # but that gives heartburn to Cygwin's tar, so work around it
455 # with separate zcat-tar-rm commands.
456 Command $env [@unpack_files], $archive, qq(
457 rm -rf $unpack_dir/$pkg-$version
459 tar xf .temp -C $unpack_dir
464 # Run setup.py in the unpacked subdirectory to "install" everything
465 # into our build/test subdirectory. Auxiliary modules that we need
466 # (TestCmd.py, TestSCons.py, unittest.py) will be copied in by
467 # etc/Conscript. The runtest.py script will set PYTHONPATH so that
468 # the tests only look under build/test. This makes sure that our
469 # tests pass with what we really packaged, not because of something
470 # hanging around in the development directory.
472 # We can get away with calling setup.py using a directory path
473 # like this because we put a preamble in it that will chdir()
474 # to the directory in which setup.py exists.
475 Command $env [@dst_files], @unpack_files, qq(
477 python $unpack_dir/$pkg-$version/setup.py install --prefix=$prefix
482 # Arrange for supporting packages to be installed in the test directories.
486 Build "etc/Conscript";
491 Link 'build/doc' => 'doc';
493 Build 'build/doc/Conscript';
497 # If we're running in the actual Aegis project, pack up a complete
498 # source archive from the project files and files in the change,
499 # so we can share it with helpful developers who don't use Aegis.
501 # First, lie and say that we've seen any files removed by this
502 # change, so they don't get added to the source files list
503 # that goes into the archive.
507 foreach (`aegis -list -unf -c $change cf 2>/dev/null`) {
508 $seen{"$1\n"}++ if /^(?:source|test) remove(?:\s.*)+\s(\S+)$/;
511 eval '@src_files = grep(! $seen{$_}++,
512 `aegis -list -terse pf 2>/dev/null`,
513 `aegis -list -terse cf 2>/dev/null`)';
515 @src_files = grep($_ !~ /(\.aeignore|\.consign)$/, @src_files);
520 foreach $file (@src_files) {
521 $env->Command("build/$project-src/$file",
524 chmod --reference=%< %>)
528 $env->Command("build/dist/$project-src-$version.tar.gz",
530 map("build/$project-src/$_", @src_files),
532 rm -rf build/$project-src-$version
533 cp -rp build/$project-src build/$project-src-$version
534 find build/$project-src-$version -name .consign -exec rm {} \\;
535 tar zcf %> -C build $project-src-$version