#!/usr/bin/perl
#
# Copyright (c) 2003-2014 Hypertriton, Inc.
# 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 Cwd;
#use Errno qw(EEXIST);
$COOKIE = ".mkconcurrent_$$";
@DIRS = ();
$BUILD = '';
@MKFILES = (
'Makefile\.(prog|proj|in)',
'\.mk$',
'\.inc$',
'\.m4$',
'^README$',
'^mkdep$',
'^install-includes.sh$',
'^install-sh$',
'^config\.(guess|sub)$',
'^configure$',
'^configure\.in$',
'^ltconfig$',
'^ltmain\.sh$',
'^manlinks\.pl$',
'^hstrip\.pl$',
'^cmpfiles\.pl$',
'^cleanfiles\.pl$',
'^gen-includes\.pl$',
'^gen-includelinks\.pl$',
'^gen-declspecs\.pl$',
'^install-manpages\.sh$',
);
my %V = ();
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 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 mkconcurrent.pl (BSDbuild)
# for concurrent building.
#
SRC=$SRC
BUILD=$BUILD
BUILDREL=$dir
EOF
my @deps = ();
my @objs = ();
my @shobjs = ();
my %catman;
my %psman;
my $libtool = 1;
my $shared = 0;
my $static = 1;
my $module = 1;
my $isProg = 0;
my $isLib = 0;
foreach $_ (@lines) {
my @srcs = ();
if (/^\s*PROG\s*=/) { $isProg = 1; }
if (/^\s*LIB\s*=/) { $isLib = 1; }
if (/^\s*USE_LIBTOOL\s*=\s*No\s*$/) { $libtool = 0; }
if (/^\s*LIB_SHARED\s*=\s*Yes\s*$/) { $shared = 1; }
if (/^\s*LIB_STATIC\s*=\s*No\s*$/) { $static = 0; }
if (/^\s*LIB_MODULE\s*=\s*Yes\s*$/) { $module = 1; }
if (/^\s*(SRCS|MAN\d|MOS)\s*=\s*(.+)$/) {
my $type = $1;
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/;
push @shobjs, $shobj;
} else {
$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;
}
# SYNC with build.{prog,lib}.mk
if ($src =~ /\.[cly]$/) { # C/Lex/Yacc
if ($isLib && $libtool) {
push @deps,
"$shobj: $SRC/$ndir/$src";
push @deps, << 'EOF';
${LIBTOOL} --mode=compile ${CC} ${LIBTOOLFLAGS} ${CFLAGS} ${CPPFLAGS} -c $<
EOF
} else {
push @deps,
"$obj: $SRC/$ndir/$src";
push @deps, << 'EOF',
${CC} ${CFLAGS} ${CPPFLAGS} -c $<
EOF
}
} elsif ($src =~ /\.cc$/) { # C++
if ($isLib && $libtool) {
push @deps,
"$shobj: $SRC/$ndir/$src";
push @deps, << 'EOF';
${LIBTOOL} --mode=compile ${CC} ${LIBTOOLFLAGS} ${CXXFLAGS} ${CPPFLAGS} -c $<
EOF
} else {
push @deps,
"$obj: $SRC/$ndir/$src";
push @deps, << 'EOF',
${CC} ${CXXFLAGS} ${CPPFLAGS} -c $<
EOF
}
} elsif ($src =~ /\.m$/) { # C+Objective-C
if ($isLib && $libtool) {
push @deps,
"$shobj: $SRC/$ndir/$src";
push @deps, << 'EOF';
${LIBTOOL} --mode=compile ${OBJC} ${LIBTOOLFLAGS} ${CFLAGS} ${OBJCFLAGS} ${CPPFLAGS} -c $<
EOF
} else {
push @deps,
"$obj: $SRC/$ndir/$src";
push @deps, << 'EOF',
${CC} ${CFLAGS} ${OBJCFLAGS} ${CPPFLAGS} -c $<
EOF
}
} elsif ($type =~ /MAN(\d)/) {
# Nroff -> ASCII
# -> Sync with build.man.mk.
push @deps,
"$obj.cat$1: $SRC/$ndir/$src";
push @deps, << 'EOF';
@echo "${NROFF} -Tascii -mandoc $< > $@"
@(cat $< | \
sed 's,\$$SYSCONFDIR,${SYSCONFDIR},' | \
sed 's,\$$PREFIX,${PREFIX},' | \
sed 's,\$$DATADIR,${DATADIR},' | \
${NROFF} -Tascii -mandoc > $@) || (rm -f $@; true)
EOF
# Nroff -> PostScript
# -> Sync with build.man.mk.
push @deps,
"$obj.ps$1: $SRC/$ndir/$src";
push @deps, << 'EOF';
@echo "${NROFF} -Tps -mandoc $< > $@"
@(cat $< | \
sed 's,\$$SYSCONFDIR,${SYSCONFDIR},' | \
sed 's,\$$PREFIX,${PREFIX},' | \
sed 's,\$$DATADIR,${DATADIR},' | \
${NROFF} -Tps -mandoc > $@) || (rm -f $@; true)
EOF
} elsif ($type =~ /MOS/) {
# Portable object -> machine object
# -> Sync with build.po.mk.
push @deps, "$obj: $SRC/$ndir/$src";
push @deps, << 'EOF';
@if [ "${ENABLE_NLS}" = "yes" -a "${HAVE_GETTEXT}" = "yes" ]; then \
echo "${MSGFMT} -o $@ $<"; \
${MSGFMT} -o $@ $<; \
else \
echo "skipping $@ (no gettext)"; \
fi
EOF
}
}
}
if (/^\s*(SRCS|MAN\d|XCF|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";
}
}
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 (0);
}
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");
}