#!/usr/bin/env perl # # Copyright (c) 2003-2018 Julien Nadeau Carriere # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE # USE OF THIS SOFTWARE EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # use strict; use Cwd; #use Errno qw(EEXIST); my $SRC = ''; my $COOKIE = ".mkconcurrent_$$"; my @DIRS = (); my $BUILD = ''; my @MKFILES = ( 'Makefile\.(prog|proj|in)', '\.mk$', '\.inc$', '\.m4$', '^README$', '^mkdep$', '^install-includes.sh$', '^deinstall-includes.sh$', '^install-sh$', '^config\.(guess|sub)$', '^configure$', '^configure\.in$', '^ltconfig$', '^ltmain\.sh$', '^manlinks\.pl$', '^cmpfiles\.pl$', '^cleanfiles\.pl$', '^gen-bundle\.pl$', '^gen-declspecs\.pl$', '^gen-includes\.pl$', '^gen-includelinks\.pl$', '^gen-revision\.sh$', '^install-manpages\.sh$', ); my %V = (); my $line = ''; my %CachedRules = (); sub Debug { print STDERR @_, "\n"; } # Return a Makefile's contents, with lines expanded and variables substituted. sub ProcessedMakefile ($$) { my $path = shift; my $dir = shift; if (!open(MF, $path)) { return (); } my @lines = (); foreach $_ () { chop; if (/^(.+)\\$/) { # Expansion $line .= $1; } else { # New line if ($line) { push @lines, $line . $_; $line = ''; } else { push @lines, $_; } } } foreach $_ (@lines) { if (/^\s*#/) { next; } if (/^\t/) { next; } s/\$\{(\w+)\}/$V{$1}/g; if (/^\s*(\w+)\s*=\s*"(.+)"$/ || /^\s*(\w+)\s*=\s*(.+)$/) { $V{$1} = $2; } elsif (/^\s*(\w+)\s*\+=\s*"(.+)"$/ || /^\s*(\w+)\s*\+=\s*(.+)$/) { if (exists($V{$1}) && $V{$1} ne '') { $V{$1} .= ' '.$2; } else { $V{$1} = $2; } } if (/^\s*include\s+(.+)$/) { my $incl = $1; if ($incl =~ /Makefile\.config$/) { # Special case: configure-generated file ProcessedMakefile($BUILD.'/'.$dir.'/'.$incl, $BUILD); } else { ProcessedMakefile($dir.'/'.$incl, $dir); } } } close(MF); # if (open(FOUT, ">>processed.txt")) { # print FOUT "======================= $path (in $dir) ====================================\n"; # print FOUT join("\n", @lines), "\n"; # close(FOUT); # } return (@lines); } sub MakeRule { my ($lib, $rule) = @_; my $path = $SRC.'/mk/'.$lib; my $inside = 0; my $out = ''; if (exists($CachedRules{$lib.' '.$rule}) && $CachedRules{$lib.' '.$rule}) { return $CachedRules{$lib.' '.$rule}; } open(LIB, $path) || die "$path: $!"; foreach my $l () { chop($l); if ($l =~ /^([\w\-\.\s]+):$/) { my @items = split(' ', $1); if (grep { $_ eq $rule } @items) { $inside = 1; next; } elsif ($inside) { $inside = 0; last; } } if ($inside) { $out .= $l."\n"; } } close(LIB); $CachedRules{$lib.' '.$rule} = $out; return ($out); } sub ConvertMakefile { my ($dir, $ndir, $ent) = @_; my @lines; open(DSTMAKEFILE, ">$BUILD/$ndir/$ent") || die "dest: $BUILD/$ndir/$ent: $!"; %V = (); @lines = ProcessedMakefile($dir.'/'.$ent, $dir); unless (@lines) { return; } print DSTMAKEFILE << "EOF"; # # This file was automatically generated by a BSDBuild ./configure script # (http://bsdbuild.hypertriton.com/) for building this project outside of # $SRC. # SRC=$SRC BUILD=$BUILD BUILDREL=$dir EOF my @deps = (); my @objs = (); my @shobjs = (); my %catman; my %psman; my $libtool = 0; my $isProg = 0; my $isLib = 0; my $objExt = 'o'; my $bbLib; my $lineNo = 1; foreach $_ (@lines) { my @srcs = (); if (/^\s*PROG\s*=/) { $isProg = 1; $bbLib = 'build.prog.mk'; push @deps, "# Using (Makefile:$lineNo)"; } if (/^\s*LIB\s*=/) { $isLib = 1; $bbLib = 'build.lib.mk'; push @deps, "# Using (Makefile:$lineNo)"; } if (/^\s*USE_LIBTOOL\s*=\s*Yes\s*$/) { # XXX $libtool = 1; $objExt = 'lo'; push @deps, "# Using libtool (Makefile:$lineNo)"; } if (/^\s*(SRCS|MAN\d|MOS)\s*=\s*(.+)$/) { my $type = $1; push @deps, "# Generating " . int(split(/\s/, $2)) . " entries from $type (Makefile:$lineNo)"; foreach my $src (split(/\s/, $2)) { unless ($src) { next; } my $obj = $src; my $shobj = $src; if ($type eq 'SRCS') { if ($isLib && $libtool) { $shobj =~ s/\.(c|cc|l|y|m)$/\.lo/; } else { $shobj =~ s/\.(c|cc|l|y|m)$/\.o/; } push @shobjs, $shobj; $obj =~ s/\.(c|cc|l|y|m)$/\.o/; push @objs, $obj; } elsif ($type =~ /MAN(\d)/) { $obj =~ s/\.(\d)$//; $catman{$1} .= " $obj.cat$1"; $psman{$1} .= " $obj.ps$1"; } elsif ($type =~ /MOS/) { $src =~ s/\.mo$/\.po/g; } if ($src =~ /\.(adb|ads|asm)$/) { push @deps, "$obj: $SRC/$ndir/$src"; push @deps, MakeRule($bbLib,".$1.o"); } elsif ($src =~ /\.(c|cc|l|m|y)$/) { push @deps, "$shobj: $SRC/$ndir/$src"; push @deps, MakeRule($bbLib, ".$1.$objExt"); } elsif ($type =~ /MAN(\d)/) { push @deps, "$obj.cat$1: $SRC/$ndir/$src"; push @deps, MakeRule('build.man.mk', ".$1.cat$1"); foreach my $fmt ('ps', 'pdf', 'html') { push @deps, "$obj.$fmt$1: $SRC/$ndir/$src"; push @deps, MakeRule('build.man.mk', ".$1.$fmt"); } } elsif ($type =~ /MOS/) { push @deps, MakeRule('build.po.mk', '.po.mo'); } } } if (/^\s*(SRCS|MAN\d|TTF|POS)\s*=\s*(.+)$/) { my $type = $1; my $srclist = $2; foreach my $src (split(/\s/, $srclist)) { unless ($src) { next; } push @srcs, $src; } my $i = 0; foreach my $src (@srcs) { $srcs[$i] = "$SRC/$ndir/$srcs[$i]"; $i++; } print DSTMAKEFILE $type . '=' . join(' ', @srcs), "\n"; } else { if (/^\s*include.+\/build\.(lib|prog|po)\.mk\s*$/) { print DSTMAKEFILE "# Generated objects:\n"; if ($isLib && $libtool) { print DSTMAKEFILE "SHOBJS=@shobjs\n"; } else { print DSTMAKEFILE "OBJS=@objs\n"; } print DSTMAKEFILE "CATMAN1=$catman{1}\n"; print DSTMAKEFILE "CATMAN2=$catman{2}\n"; print DSTMAKEFILE "CATMAN3=$catman{3}\n"; print DSTMAKEFILE "CATMAN4=$catman{4}\n"; print DSTMAKEFILE "CATMAN5=$catman{5}\n"; print DSTMAKEFILE "CATMAN6=$catman{6}\n"; print DSTMAKEFILE "CATMAN7=$catman{7}\n"; print DSTMAKEFILE "CATMAN8=$catman{8}\n"; print DSTMAKEFILE "CATMAN9=$catman{9}\n"; print DSTMAKEFILE "PSMAN1=$psman{1}\n"; print DSTMAKEFILE "PSMAN2=$psman{2}\n"; print DSTMAKEFILE "PSMAN3=$psman{3}\n"; print DSTMAKEFILE "PSMAN4=$psman{4}\n"; print DSTMAKEFILE "PSMAN5=$psman{5}\n"; print DSTMAKEFILE "PSMAN6=$psman{6}\n"; print DSTMAKEFILE "PSMAN7=$psman{7}\n"; print DSTMAKEFILE "PSMAN8=$psman{8}\n"; print DSTMAKEFILE "PSMAN9=$psman{9}\n"; print DSTMAKEFILE "\n"; } print DSTMAKEFILE $_, "\n"; } $lineNo++; } if (@deps) { print DSTMAKEFILE 'CFLAGS+=-I${BUILD}', "\n"; print DSTMAKEFILE "\n", join("\n", @deps), "\n"; print DSTMAKEFILE 'include .depend'."\n"; } close(DSTMAKEFILE); # Prevent make from complaining. open(DSTDEPEND, ">$BUILD/$ndir/.depend") or die "$BUILD/$ndir/.depend: $!"; print DSTDEPEND "\n"; close(DSTDEPEND); } sub Scan { my $dir = shift; opendir(DIR, $dir) || die "$dir: $!"; ENTRY: foreach my $ent (readdir(DIR)) { if ($ent eq '.' || $ent eq '..' || $ent eq 'CVS' || $ent eq '.svn') { next ENTRY; } my $dent = join('/',$dir,$ent); my $ndir = $dir; $ndir =~ s/^\.\///; my $ndent = join('/', $BUILD,$ndir,$ent); if ((! -l $dent) && (-d $dent && ! -e "$dent/$COOKIE")) { mkdir($ndent, 0755); Scan($dent); } else { if ($ent eq 'Makefile') { ConvertMakefile($dir, $ndir, $ent); } else { foreach my $pat (@MKFILES) { if ($ent =~ $pat) { open(OLDMK, $dent) || die "$dent: $!"; open(NEWMK, ">$ndent") || die "$ndent: $!"; print NEWMK ; close(NEWMK); close(OLDMK); last; } } } } } closedir(DIR); } $SRC = $ARGV[0]; unless ($SRC) { print STDERR "Usage: $0 [source-directory-path]\n"; exit (1); } unless (-d $SRC) { print STDERR "$SRC: $!\n"; exit(1); } if (-e 'INSTALL') { print STDERR "Cannot perform concurrent build in source directory\n"; exit(1); } $BUILD = getcwd(); chdir($SRC) || die "$SRC: $!"; open(COOKIE, ">$BUILD/$COOKIE") || die "$BUILD/COOKIE: $!"; Scan('.'); close(COOKIE); END { unlink("$BUILD/$COOKIE"); }