summaryrefslogtreecommitdiff
path: root/infrastructure/makebuildenv.pl.in
diff options
context:
space:
mode:
authorChris Wilson <chris+github@qwirx.com>2016-08-30 22:44:36 +0100
committerChris Wilson <chris+github@qwirx.com>2016-08-28 21:47:18 +0100
commit0ebfa6464b9987b5bd2a084872f7511625647391 (patch)
tree8d6abc564823f68d2efe3f3f37383ab15543c4d4 /infrastructure/makebuildenv.pl.in
parentbaf19fe9e7dd689597b3320202efeb69c61838a8 (diff)
Merge makebuildenv.pl and makeparcels.pl, write monolithic Makefiles.
This is necessary to fix compilation on machines with large numbers of cores (e.g. Travis build workers) because the build system automatically attempts maximum parallelism, and the old one is broken and tries to build multiple libraries simultaneously. The new buildsystem almost entirely avoids recursive Make: only trivial Makefiles in target directories (which invoke the top-level Makefile to build the appropriate output file) and the old docs makefile are not part of the new master Makefile. Due to higher parallelism and better dependency checking, Make should be much faster now. Not all old targets are supported though. I've removed implicit dependencies hard-coded in the build system, and expressed all dependencies in modules.txt, since the implicit dependencies were confusing, obscure and unnecessary, and it was hard to express their own dependencies on each other. Test both the default target and explicitly "parcels" for release builds on Travis.
Diffstat (limited to 'infrastructure/makebuildenv.pl.in')
-rwxr-xr-xinfrastructure/makebuildenv.pl.in1032
1 files changed, 697 insertions, 335 deletions
diff --git a/infrastructure/makebuildenv.pl.in b/infrastructure/makebuildenv.pl.in
index 5f1b0618..60c6d854 100755
--- a/infrastructure/makebuildenv.pl.in
+++ b/infrastructure/makebuildenv.pl.in
@@ -14,7 +14,10 @@ $|=1;
print "Box build environment setup.\n";
-my @implicit_deps = ('lib/common');
+# Modules that everything else depends on, without being explicitly specified.
+# Deprecated because we can't specify dependencies between them in the usual way,
+# and it's totally unnecessary anyway:
+my @implicit_deps;
# work out platform variables
use lib 'infrastructure';
@@ -26,10 +29,6 @@ print "Building on '$build_os $build_os_ver' using ".
# keep copy of command line args
my $makebuildenv_args = join(' ',@ARGV);
-# do command line arguments
-my $compile_line_extra = $platform_compile_line_extra;
-my $link_line_extra = $platform_link_line_extra;
-
# make sure local files directory exists
unless(-d 'local')
{
@@ -40,9 +39,6 @@ unless(-d 'local')
# flags about the environment
my %env_flags;
-$module_dependency{"lib/common"} = ["lib/win32"];
-push @implicit_deps, "lib/win32";
-
# print "Flag: $_\n" for(keys %env_flags);
# seed autogen code
@@ -377,17 +373,156 @@ for my $mod (@modules, @implicit_deps)
closedir DIR;
}
-# Then write a makefile for each module
+my $default_cflags = '@CFLAGS@';
+my $default_cxxflags = '@CXXFLAGS@';
+$default_cflags =~ s/ -O2//g;
+$default_cxxflags =~ s/ -O2//g;
+my $debug_base_dir = 'debug';
+my $release_base_dir = 'release';
+
+my $release_flags = "-O2";
+if ($target_windows)
+{
+ $release_flags = "-O0 -g";
+}
+
+# Then write a master Makefile, and a mini-Makefile for each module
print "done\n\nGenerating Makefiles...\n";
-my %module_resources_win32;
+my $makefile_ifdef_prefix = $bsd_make ? "." : "";
+
+open MASTER_MAKEFILE, ">Makefile" or die "Makefile: $!";
+print MASTER_MAKEFILE <<__E;
+#
+# AUTOMATICALLY GENERATED FILE
+# do not edit!
+#
+#
+
+# "parcels" is defined near the end of this Makefile.
+default: parcels
+
+CC = @CC@
+CXX = @CXX@
+AR = @AR@
+RANLIB = @RANLIB@
+PERL = @PERL@
+WINDRES = @WINDRES@
+
+# Work around a mistake in QDBM (using <angled> includes for a file not in the
+# system path) by adding it to the include path with -I.
+DEFAULT_CFLAGS = \@CPPFLAGS@ $default_cflags \@CXXFLAGS_STRICT@ \\
+ $extra_platform_defines $platform_compile_line_extra \\
+ -DBOX_VERSION="\\"$product_version\\"" -Iqdbm
+DEFAULT_CXXFLAGS = \@CPPFLAGS@ $default_cxxflags \@CXXFLAGS_STRICT@ \\
+ $extra_platform_defines $platform_compile_line_extra \\
+ -DBOX_VERSION="\\"$product_version\\""
+LDFLAGS += \@LDFLAGS@ \@LDADD_RDYNAMIC@ $platform_link_line_extra
+
+RELEASE_CFLAGS = \$(DEFAULT_CFLAGS) -DBOX_RELEASE_BUILD $release_flags
+RELEASE_CXXFLAGS = \$(DEFAULT_CXXFLAGS) -DBOX_RELEASE_BUILD $release_flags
+RELEASE_OUTBASE = $release_base_dir
+# http://gcc.gnu.org/onlinedocs/libstdc++/manual/debug_mode_using.html#debug_mode.using.mode
+DEBUG_CFLAGS = \$(DEFAULT_CFLAGS) -g -O0 -D_GLIBCXX_DEBUG
+DEBUG_CXXFLAGS = \$(DEFAULT_CXXFLAGS) -g -O0 -D_GLIBCXX_DEBUG
+DEBUG_OUTBASE = $debug_base_dir
+
+__E
+
+if ($bsd_make)
+{
+ print MASTER_MAKEFILE <<__E;
+.ifdef V
+HIDE =
+_CC = \$(CC)
+_CXX = \$(CXX)
+_LINK = \$(CXX)
+_WINDRES = \$(WINDRES)
+_AR = \$(AR)
+_RANLIB = \$(RANLIB)
+_PERL = \$(PERL)
+.else
+HIDE = @
+_CC = @ echo " [CC] " \$(*F) && \$(CC)
+_CXX = @ echo " [CXX] " \$(*F) && \$(CXX)
+_LINK = @ echo " [LINK] " \$(*F) && \$(CXX)
+_WINDRES = @ echo " [WINDRES]" \$(*F) && \$(WINDRES)
+_AR = @ echo " [AR] " \$(*F) && \$(AR)
+_RANLIB = @ echo " [RANLIB] " \$(*F) && \$(RANLIB)
+_PERL = @ echo " [PERL] " \$(*F) && \$(PERL) >/dev/null
+.endif
+
+__E
+}
+else
+{
+ print MASTER_MAKEFILE <<__E;
+HIDE = \$(if \$(V),,@)
+_CC = \$(if \$(V),\$(CC), @ echo " [CC] \$<" && \$(CC))
+_CXX = \$(if \$(V),\$(CXX), @ echo " [CXX] \$<" && \$(CXX))
+_LINK = \$(if \$(V),\$(CXX), @ echo " [LINK] \$@" && \$(CXX))
+_WINDRES = \$(if \$(V),\$(WINDRES), @ echo " [WINDRES] \$<" && \$(WINDRES))
+_AR = \$(if \$(V),\$(AR), @ echo " [AR] \$@" && \$(AR))
+_RANLIB = \$(if \$(V),\$(RANLIB), @ echo " [RANLIB] \$@" && \$(RANLIB))
+_PERL = \$(if \$(V),\$(PERL), @ echo " [PERL] \$@" && \$(PERL) >/dev/null)
+
+__E
+}
+my %module_resources_win32;
+my @debug_build_targets;
+my @release_build_targets;
+my @all_clean_targets;
+my %mod_type_name;
+my %mod_end_targets;
+my %library_targets;
+
+# First, loop over all modules and quickly categorise them and generate the target filenames.
+# We need this to write correct dependency info into the makefile when modules are listed
+# out of order in modules.txt, which isn't strictly necessary but may look tidier.
for my $mod (@implicit_deps, @modules)
{
print $mod,"\n";
my ($type,$name) = split /\//,$mod;
-
+ if (not $name)
+ {
+ # External modules such as qdbm have no "type" in the directory path.
+ $name = $mod;
+ $type = 'lib';
+ }
+
+ # is target a library?
+ my $target_is_library = ($type ne 'bin' && $type ne 'test');
+
+ # make target name
+ my $end_target_file = $name;
+
+ if ($target_is_library)
+ {
+ $end_target_file .= '.a';
+ }
+ else
+ {
+ $end_target_file .= $platform_exe_ext;
+ }
+
+ $end_target_file = '_test'.$platform_exe_ext if $type eq 'test';
+
+ $mod_type_name{$mod} = [$type, $name];
+ $mod_end_targets{$mod} = $end_target_file;
+ $library_targets{$mod} = $target_is_library;
+}
+
+# Now loop over them again, generating makefile instructions etc.
+for my $mod (@implicit_deps, @modules)
+{
+ my ($type,$name) = @{$mod_type_name{$mod}};
+ my $end_target_file = $mod_end_targets{$mod};
+ my $target_is_library = $library_targets{$mod};
+
+ print $mod,": $type\n";
+
# add additional files for tests
if($type eq 'test')
{
@@ -478,9 +613,9 @@ __E
close TESTFILE;
}
- writetestfile("$mod/_t", "GLIBCXX_FORCE_NEW=1 ".
+ writetestfile("$mod/t", "GLIBCXX_FORCE_NEW=1 ".
'./_test' . $platform_exe_ext . ' "$@"', $mod);
- writetestfile("$mod/_t-gdb", "GLIBCXX_FORCE_NEW=1 ".
+ writetestfile("$mod/t-gdb", "GLIBCXX_FORCE_NEW=1 ".
'gdb ./_test' . $platform_exe_ext . ' "$@"', $mod);
}
@@ -516,124 +651,62 @@ __E
}
}
}
-
- # make include path
- my $include_paths = join(' ',map {'-I../../'.$_} @all_deps_for_module);
-
- # is target a library?
- my $target_is_library = ($type ne 'bin' && $type ne 'test');
-
- # make target name
- my $end_target = $name;
+ # get the list of library things to add -- in order of dependency
+ # so things link properly
+ my @lib_files;
+ my @dep_targets;
+ my @include_search_dirs;
- if ($target_is_library)
- {
- $end_target .= '.a';
- }
- else
+ foreach my $dep (reverse @all_deps_for_module)
{
- $end_target .= $platform_exe_ext;
+ my $dep_target = $mod_end_targets{$dep};
+ die "No output file found for $dep" unless $dep_target;
+ $dep_target = "$dep/$dep_target";
+ push @dep_targets, $dep_target;
+
+ if ($dep =~ m|^lib\/(.+)$|)
+ {
+ push @lib_files, $dep_target;
+ push @include_search_dirs, $dep;
+ }
+ elsif ($dep =~ m|^([^/]+)$|)
+ {
+ push @lib_files, $dep_target;
+ push @include_search_dirs, $dep;
+ }
}
- $end_target = '_test'.$platform_exe_ext if $type eq 'test';
+ # make include path
+ my $cpp_include_paths = join(' ',map {"-I$_"} @include_search_dirs);
+ print MASTER_MAKEFILE "${type}_${name}_includes = $cpp_include_paths\n";
# adjust for outdir
- $end_target = '$(OUTDIR)/' . $end_target;
+ my $debug_end_target = "\$(DEBUG_OUTBASE)/$mod/$end_target_file";
+ my $release_end_target = "\$(RELEASE_OUTBASE)/$mod/$end_target_file";
+ push @debug_build_targets, $debug_end_target;
+ push @release_build_targets, $release_end_target;
# start the makefile
- my $mk_name_extra = ($bsd_make)?'':'X';
- open MAKE,">$mod/Makefile".$mk_name_extra or die "Can't open Makefile for $mod\n";
- my $debug_link_extra = ($target_is_library)?'':'../../debug/lib/debug/debug.a';
-
- my $default_cxxflags = '@CXXFLAGS@';
- $default_cxxflags =~ s/ -O2//g;
-
- my $release_flags = "-O2";
- if ($target_windows)
- {
- $release_flags = "-O0 -g";
- }
-
- print MAKE <<__E;
+ open MINI_MODULE_MAKEFILE,">$mod/Makefile" or die "Can't open Makefile for $mod\n";
+ print MINI_MODULE_MAKEFILE <<__E;
#
# AUTOMATICALLY GENERATED FILE
# do not edit!
#
-#
-CXX = @CXX@
-AR = @AR@
-RANLIB = @RANLIB@
-PERL = @PERL@
-WINDRES = @WINDRES@
-
-DEFAULT_CXXFLAGS = @CPPFLAGS@ $default_cxxflags @CXXFLAGS_STRICT@ \\
- $include_paths $extra_platform_defines \\
- -DBOX_VERSION="\\"$product_version\\""
-LDFLAGS += @LDFLAGS@ @LDADD_RDYNAMIC@
-
-.ifdef RELEASE
-CXXFLAGS += \$(DEFAULT_CXXFLAGS) -DBOX_RELEASE_BUILD $release_flags
-OUTBASE = ../../release
-OUTDIR = ../../release/$mod
-DEPENDMAKEFLAGS = -D RELEASE
-VARIENT = RELEASE
-.else
-# http://gcc.gnu.org/onlinedocs/libstdc++/manual/debug_mode_using.html#debug_mode.using.mode
-CXXFLAGS += \$(DEFAULT_CXXFLAGS) -g -O0 -D_GLIBCXX_DEBUG
-OUTBASE = ../../debug
-OUTDIR = ../../debug/$mod
-DEPENDMAKEFLAGS =
-VARIENT = DEBUG
-.endif
-
-__E
-
- if ($bsd_make)
- {
- print MAKE <<__E;
-.ifdef V
-HIDE =
-_CXX = \$(CXX)
-_LINK = \$(CXX)
-_WINDRES = \$(WINDRES)
-_AR = \$(AR)
-_RANLIB = \$(RANLIB)
-_PERL = \$(PERL)
-.else
-HIDE = @
-_CXX = @ echo " [CXX] " \$(*F) && \$(CXX)
-_LINK = @ echo " [LINK] " \$(*F) && \$(CXX)
-_WINDRES = @ echo " [WINDRES]" \$(*F) && \$(WINDRES)
-_AR = @ echo " [AR] " \$(*F) && \$(AR)
-_RANLIB = @ echo " [RANLIB] " \$(*F) && \$(RANLIB)
-_PERL = @ echo " [PERL] " \$(*F) && \$(PERL) >/dev/null
-.endif
-
-__E
- }
- else
- {
- print MAKE <<__E;
-HIDE = \$(if \$(V),,@)
-_CXX = \$(if \$(V),\$(CXX), @ echo " [CXX] $mod/\$<" && \$(CXX))
-_LINK = \$(if \$(V),\$(CXX), @ echo " [LINK] $mod/\$@" && \$(CXX))
-_WINDRES = \$(if \$(V),\$(WINDRES), @ echo " [WINDRES] $mod/\$<" && \$(WINDRES))
-_AR = \$(if \$(V),\$(AR), @ echo " [AR] $mod/\$@" && \$(AR))
-_RANLIB = \$(if \$(V),\$(RANLIB), @ echo " [RANLIB] $mod/\$@" && \$(RANLIB))
-_PERL = \$(if \$(V),\$(PERL), @ echo " [PERL] $mod/\$@" && \$(PERL) >/dev/null)
-
+${makefile_ifdef_prefix}ifdef RELEASE
+TARGET = $release_base_dir/$mod/$end_target_file
+${makefile_ifdef_prefix}else
+TARGET = $debug_base_dir/$mod/$end_target_file
+${makefile_ifdef_prefix}endif
+
+.PHONY: default
+default:
+ \$(MAKE) -C ../.. \$(TARGET)
__E
- }
+ close MINI_MODULE_MAKEFILE;
- # if there is a Makefile.pre, include it now
- if(-e "$mod/Makefile.pre")
- {
- print MAKE ".include <Makefile.pre>\n\n";
- }
-
- # read directory
- opendir DIR,$mod;
+ opendir DIR, $mod;
my @items = readdir DIR;
closedir DIR;
@@ -646,10 +719,10 @@ __E
{
# Read items
my $d = "$mod/$di";
- opendir DIR,$d;
+ opendir DIR, $d;
my @i = readdir DIR;
closedir DIR;
- for(@i)
+ for (@i)
{
next if m/\A\./;
push @autogen_items,"$di/$_"
@@ -678,294 +751,583 @@ __E
}
}
- # ready for the rest of the details...
- my $make;
-
- # then... do the cpp files...
- my @obj_base;
- for my $file (@items)
+ # write the recipes for debug and release builds of each file
+ foreach my $var_prefix ('DEBUG', 'RELEASE')
{
- my $is_cpp = $file =~ m/\A(.+)\.cpp\Z/i;
- my $is_rc = $file =~ m/\A(.+)\.rc\Z/i;
- my $base = $1;
+ my $make;
- if ($target_windows)
- {
- next if not $is_cpp and not $is_rc;
- }
- else
+ # then... do the cpp files...
+ my @obj_base;
+ for my $file (@items)
{
- next if not $is_cpp;
- }
+ my $is_c = $file =~ m/\A(.+)\.c\Z/i;
+ my $is_cpp = $file =~ m/\A(.+)\.cpp\Z/i;
+ my $is_rc = $file =~ m/\A(.+)\.rc\Z/i;
+ my $base = $1;
- next if $file =~ /\A\._/; # Temp Mac OS Resource hack
+ # Don't try to compile .rc files except on Windows:
+ next if not $is_c and not $is_cpp and not ($is_rc and $target_windows);
+ next if $file =~ /\A\._/; # Temp Mac OS Resource hack
- # store for later
- push @obj_base,$base;
-
- # get the file...
- open FL,"$mod/$file";
- my $f;
- read FL,$f,-s "$mod/$file";
- close FL;
+ # store for later
+ push @obj_base, $base;
- my %dep;
+ # get the file...
+ open FL, "$mod/$file";
+ my $f;
+ read FL, $f, -s "$mod/$file";
+ close FL;
+
+ my %dep;
- while($f =~ m/\#include\s+"([^"]+?)"/g)
- {
- insert_dep($1, \%dep) if exists $hfiles{$1};
+ while($f =~ m/\#include\s+"([^"]+?)"/g)
+ {
+ insert_dep($1, \%dep) if exists $hfiles{$1};
+ }
+
+ # output filename
+ my $out_name = "\$(${var_prefix}_OUTBASE)/$mod/$base.o";
+
+ # write the line for this cpp file
+ my @dep_paths = map
+ {
+ ($hfiles{$_} eq $mod)
+ ? "$mod/$_"
+ : $hfiles{$_}."/$_"
+ }
+ keys %dep;
+
+ $make .= "$out_name: $mod/$file @dep_paths\n";
+
+ if ($is_c)
+ {
+ $make .= "\t\$(_CC) \$(${var_prefix}_CFLAGS) ".
+ "\$(${type}_${name}_includes) -DBOX_MODULE=\"\\\"$mod\\\"\" " .
+ "-c $mod/$file -o $out_name\n\n";
+ }
+ if ($is_cpp)
+ {
+ $make .= "\t\$(_CXX) \$(${var_prefix}_CXXFLAGS) ".
+ "\$(${type}_${name}_includes) -DBOX_MODULE=\"\\\"$mod\\\"\" " .
+ "-c $mod/$file -o $out_name\n\n";
+ }
+ elsif ($is_rc)
+ {
+ $make .= "\t\$(_WINDRES) $file $out_name\n\n";
+ my $res_list = $module_resources_win32{$mod};
+ $res_list ||= [];
+ push @$res_list, $base.'.o';
+ $module_resources_win32{$mod} = $res_list;
+ }
}
+
+ # need to see if the extra makefile fragments require extra object files
+ # or include any more makefiles
+ my @objs = @obj_base;
+ my @makefile_includes;
- # output filename
- my $out_name = '$(OUTDIR)/'.$base.'.o';
+ additional_objects_from_make_fragment("$mod/Makefile.extra", \@objs, \@makefile_includes);
+ additional_objects_from_make_fragment("$mod/Makefile.extra.$build_os", \@objs, \@makefile_includes);
+
+ my $prefixed_end_target = "\$(${var_prefix}_OUTBASE)/$mod/$end_target_file";
+ my $o_file_list = join(' ',map {"\$(${var_prefix}_OUTBASE)/$mod/$_.o"} sort @objs);
+ my @prefixed_lib_files = map {"\$(${var_prefix}_OUTBASE)/$_"} @lib_files;
+ my @prefixed_dep_targets = map {"\$(${var_prefix}_OUTBASE)/$_"} @dep_targets;
+
+ print MASTER_MAKEFILE "$prefixed_end_target: $o_file_list";
+ print MASTER_MAKEFILE " @prefixed_dep_targets" unless $target_is_library;
+ print MASTER_MAKEFILE "\n";
- # write the line for this cpp file
- my @dep_paths = map
- {
- ($hfiles{$_} eq $mod)
- ? $_
- : '../../'.$hfiles{$_}."/$_"
+ if ($target_windows)
+ {
+ foreach my $dep (@all_deps_for_module)
+ {
+ my $res_list = $module_resources_win32{$dep};
+ next unless $res_list;
+ $o_file_list .= ' '.join(' ',
+ map {"\$(${var_prefix}_OUTBASE)/$dep/$_"} @$res_list);
+ }
}
- keys %dep;
- $make .= $out_name.': '.join(' ',$file,@dep_paths)."\n";
-
- if ($is_cpp)
+ # stuff to make the final target...
+ if($target_is_library)
{
- $make .= "\t\$(_CXX) \$(CXXFLAGS) $compile_line_extra ".
- "-DBOX_MODULE=\"\\\"$mod\\\"\" " .
- "-c $file -o $out_name\n\n";
+ # make a library archive...
+ print MASTER_MAKEFILE "\t\$(HIDE) (rm -f $prefixed_end_target)\n";
+ print MASTER_MAKEFILE "\t\$(_AR) cq $prefixed_end_target $o_file_list\n";
+ print MASTER_MAKEFILE "\t\$(_RANLIB) $prefixed_end_target\n";
}
- elsif ($is_rc)
+ else
{
- $make .= "\t\$(_WINDRES) $file $out_name\n\n";
- my $res_list = $module_resources_win32{$mod};
- $res_list ||= [];
- push @$res_list, $base.'.o';
- $module_resources_win32{$mod} = $res_list;
+ # work out library options
+ # need to be... least used first, in absolute order they appear in the modules.txt file
+ my @libops;
+
+ sub libops_fill
+ {
+ my ($module, $libops_ref) = @_;
+
+ my $library_link_opts = $module_library_link_opts{$module};
+ if ($library_link_opts)
+ {
+ push @$libops_ref, @$library_link_opts;
+ }
+
+ my $deps = $module_dependency{$module};
+ foreach my $dep (@$deps)
+ {
+ libops_fill($dep, $libops_ref);
+ }
+ }
+
+ libops_fill($mod,\@libops);
+
+ my $lo = '';
+ my %ldone;
+ for(@libops)
+ {
+ next if exists $ldone{$_};
+ $lo .= ' '.$_;
+ $ldone{$_} = 1;
+ }
+
+ # link line...
+ print MASTER_MAKEFILE "\t\$(_LINK) \$(LDFLAGS) " .
+ "-o $prefixed_end_target $o_file_list " .
+ "@prefixed_lib_files $lo $platform_lib_files\n";
}
- }
-
- my $has_deps = ($#{$module_dependency{$mod}} >= 0);
-# ----- # always has dependencies with debug library
- $has_deps = 1;
- $has_deps = 0 if $target_is_library;
- # Dependency stuff
- my $deps_makeinfo;
- if($has_deps)
- {
- if($bsd_make)
+ # tests need to copy the test file over
+ if($type eq 'test')
{
- $deps_makeinfo = <<'__E';
-.BEGIN::
-.ifndef NODEPS
-. if $(.TARGETS) == ""
+ print MASTER_MAKEFILE <<__E;
+ \$(HIDE) cp $mod/t \$(${var_prefix}_OUTBASE)/$mod/t
+ \$(HIDE) chmod u+x \$(${var_prefix}_OUTBASE)/$mod/t
+ \$(HIDE) cp $mod/t-gdb \$(${var_prefix}_OUTBASE)/$mod/t-gdb
+ \$(HIDE) chmod u+x \$(${var_prefix}_OUTBASE)/$mod/t-gdb
__E
}
- else
+
+ print MASTER_MAKEFILE $make,"\n";
+
+ for(@makefile_includes)
{
- # gnu make
- $deps_makeinfo = <<'__E';
-.PHONY: dep_modules
-dep_modules:
-ifndef NODEPS
-ifeq ($(strip $(.TARGETS)),)
-__E
+ print MASTER_MAKEFILE "\n.include <$_>\n";
}
+}
+
+ print MASTER_MAKEFILE <<__E;
+clean_${type}_${name}:
+ rm -rf \$(DEBUG_OUTBASE)/$mod/*
+ rm -rf \$(RELEASE_OUTBASE)/$mod/*
+__E
+ push @all_clean_targets, "clean_${type}_${name}";
+
+ my $includes = "";
+
+ if(-e "$mod/Makefile.extra")
+ {
+ $includes .= ".include <$mod/Makefile.extra>\n\n";
+ }
+ if(-e "$mod/Makefile.extra.$build_os")
+ {
+ $includes .= ".include <$mod/Makefile.extra.$build_os>\n\n";
+ }
+
+ if(!$bsd_make)
+ {
+ # need to post process this into a GNU makefile
+ $includes =~ s/\A\.\s*(ifdef|else|endif|ifndef)/$1/;
+ $includes =~ s/\A\.\s*include\s+<(.+?)>/include $1/;
+ $includes =~ s/-D\s+(\w+)/$1=1/g;
+ }
+
+ print MASTER_MAKEFILE $includes;
+}
+
+my @parcels;
+my %parcel_contents;
+
+sub starts_with ($$)
+{
+ my ($string,$expected) = @_;
+ return substr($string, 0, length $expected) eq $expected;
+}
+
+sub os_matches ($)
+{
+ my ($prefix_string) = @_;
+ my @prefixes = split m'\,', $prefix_string;
+ foreach my $prefix (@prefixes)
+ {
+ return 1 if starts_with($build_os, $prefix);
+ return 1 if starts_with($ac_target_os, $prefix);
+ return 1 if starts_with("$ac_target_cpu-$ac_target_os",
+ $prefix);
+ return 1 if starts_with($ac_target, $prefix);
+ }
+ return 0;
+}
+
+my $copy_command = "cp -p";
+
+open PARCELS,"parcels.txt" or die "Can't open parcels file";
+{
+ my $cur_parcel = '';
+ while(<PARCELS>)
+ {
+ chomp; s/#.+\Z//; s/\s+\Z//; s/\s+/ /g;
+ next unless m/\S/;
- # run make for things we require
- for my $dep (@all_deps_for_module)
+ # omit bits on some platforms?
+ next if m/\AEND-OMIT/;
+ if(m/\AOMIT:(.+)/)
{
- my $dep_target = "";
- if ($dep =~ m|^lib/(.*)|)
+ if (os_matches($1))
{
- $dep_target = "\$(OUTBASE)/$dep/$1.a";
+ while(<PARCELS>)
+ {
+ last if m/\AEND-OMIT/;
+ }
}
- elsif ($dep =~ m|^.*/(.*)|)
+ next;
+ }
+
+ if (m'\AONLY:(.+)')
+ {
+ if (not os_matches($1))
{
- $dep_target = "\$(OUTBASE)/$dep/$1$platform_exe_ext";
+ while (<PARCELS>)
+ {
+ last if m'\AEND-ONLY';
+ }
}
- else
+ next;
+ }
+ next if (m'\AEND-ONLY');
+
+ if (m'\AEXCEPT:(.+)')
+ {
+ if (os_matches($1))
{
- $dep_target = "lib$dep.a";
+ while (<PARCELS>)
+ {
+ last if m'\AEND-EXCEPT';
+ }
}
-
- $deps_makeinfo .= <<EOF;
- \$(HIDE) ( \\
- cd ../../$dep; \\
- \$(MAKE) $sub_make_options -q \$(DEPENDMAKEFLAGS) -D NODEPS $dep_target \\
- || \$(MAKE) $sub_make_options \$(DEPENDMAKEFLAGS) -D NODEPS $dep_target \\
- )
-EOF
+ next;
}
-
- $deps_makeinfo .= ".\tendif\n.endif\n\n";
- }
- print MAKE $deps_makeinfo if $bsd_make;
-
- # get the list of library things to add -- in order of dependency
- # so things link properly
- my @lib_files;
- foreach my $dep (reverse @all_deps_for_module)
- {
- if ($dep =~ m|^lib\/(.+)$|)
+ next if (m'\AEND-EXCEPT');
+
+ # new parcel, or a new parcel definition?
+ if(m/\A\s+(.+)\Z/)
{
- push @lib_files, "\$(OUTBASE)/$dep/$1.a";
+ push @{$parcel_contents{$cur_parcel}},$1
}
- elsif ($dep =~ m|^([^/]+)$|)
+ else
{
- push @lib_files, "../../$dep/lib$1.a";
+ $cur_parcel = $_;
+ push @parcels,$_;
}
}
+}
+close PARCELS;
- # need to see if the extra makefile fragments require extra object files
- # or include any more makefiles
- my @objs = @obj_base;
- my @makefile_includes;
-
- additional_objects_from_make_fragment("$mod/Makefile.extra", \@objs, \@makefile_includes);
- additional_objects_from_make_fragment("$mod/Makefile.extra.$build_os", \@objs, \@makefile_includes);
+# create parcels directory
+mkdir "parcels",0755;
+mkdir "parcels/scripts",0755;
+
+print MASTER_MAKEFILE "parcels:\t",join(' ',map {"build-".$_} @parcels),"\n\n";
+
+my $runtest_script = $target_windows ? './infrastructure/mingw/runtest.sh'
+ : './runtest.pl';
+
+print MASTER_MAKEFILE <<__END_OF_FRAGMENT;
+.PHONY: test
+test: debug_tests release_tests
+
+.PHONY: debug_tests
+debug_tests:
+ $runtest_script ALL debug
+
+.PHONY: release_tests
+release_tests:
+ $runtest_script ALL release
+
+.PHONY: docs
+docs:
+ cd docs; \$(MAKE)
- my $o_file_list = join(' ',map {'$(OUTDIR)/'.$_.'.o'} sort @objs);
+__END_OF_FRAGMENT
- if ($has_deps and not $bsd_make)
+my $release_flag = BoxPlatform::make_flag('RELEASE');
+my @parcel_targets;
+
+for my $parcel (@parcels)
+{
+ my $version = BoxPlatform::parcel_root($parcel);
+ my $make_target = BoxPlatform::parcel_target($parcel);
+ my $dir = BoxPlatform::parcel_dir($parcel);
+ my @parcel_deps;
+
+ # Need to use BSD install on Solaris
+ my $install_bin = $build_os eq 'SunOS' ? '/usr/ucb/install' : 'install';
+
+ unless ($target_windows)
{
- print MAKE ".PHONY: all\n" .
- "all: dep_modules $end_target\n\n";
+ open SCRIPT,">parcels/scripts/install-$parcel" or die
+ "Can't open installer script for $parcel for writing";
+ print SCRIPT "#!/bin/sh\n\n";
}
- print MAKE $end_target,': ',$o_file_list;
- print MAKE " @lib_files" unless $target_is_library;
- print MAKE "\n";
-
- if ($target_windows)
+ for(@{$parcel_contents{$parcel}})
{
- foreach my $dep (@all_deps_for_module)
+ my @args = split /\s+/;
+
+ my ($type,$name,$dest) = @args;
+ my $optional = 0;
+ my $install = 1;
+
+ if ($type eq 'optional')
{
- my $res_list = $module_resources_win32{$dep};
- next unless $res_list;
- $o_file_list .= ' '.join(' ',
- map {'$(OUTBASE)/'.$dep."/$_"} @$res_list);
+ $optional = 1;
+ shift @args;
+ ($type,$name,$dest) = @args;
}
- }
- # stuff to make the final target...
- if($target_is_library)
- {
- # make a library archive...
- print MAKE "\t\$(HIDE) (echo -n > $end_target; rm $end_target)\n";
- print MAKE "\t\$(_AR) cq $end_target $o_file_list\n";
- print MAKE "\t\$(_RANLIB) $end_target\n";
- }
- else
- {
- # work out library options
- # need to be... least used first, in absolute order they appear in the modules.txt file
- my @libops;
-
- sub libops_fill
+ if ($type eq 'noinstall')
{
- my ($module, $libops_ref) = @_;
-
- my $library_link_opts = $module_library_link_opts{$module};
- if ($library_link_opts)
+ $install = 0;
+ shift @args;
+ ($type,$name,$dest) = @args;
+ }
+
+ if($type eq 'bin')
+ {
+ my $exeext = $platform_exe_ext;
+ print MASTER_MAKEFILE <<EOF;
+$dir/$name$exeext: release/bin/$name/$name$exeext
+ mkdir -p $dir
+ $copy_command release/bin/$name/$name$exeext $dir
+
+EOF
+ push @parcel_deps, "$dir/$name$exeext";
+ }
+ elsif ($type eq 'script')
+ {
+ # Replace any variables ($ac_target etc.) with their
+ # values.
+ $name =~ s|(\$[^/ ]+)|$1|eeg;
+ my $fullpath = $name;
+ my $filename = $name;
+ # remove path from script name
+ $filename =~ s{.*/}{};
+
+ print MASTER_MAKEFILE <<EOF;
+$dir/$filename: $fullpath
+ mkdir -p $dir
+EOF
+
+ if ($optional)
{
- push @$libops_ref, @$library_link_opts;
+ print MASTER_MAKEFILE "\ttest -r $fullpath " .
+ "&& $copy_command $fullpath $dir || true\n";
}
-
- my $deps = $module_dependency{$module};
- foreach my $dep (@$deps)
+ else
{
- libops_fill($dep, $libops_ref);
+ print MASTER_MAKEFILE "\t$copy_command $fullpath $dir\n";
}
+
+ print MASTER_MAKEFILE "\n";
+
+ push @parcel_deps, "$dir/$filename";
}
-
- libops_fill($mod,\@libops);
-
- my $lo = '';
- my %ldone;
- for(@libops)
+ elsif($type eq 'man')
{
- next if exists $ldone{$_};
- $lo .= ' '.$_;
- $ldone{$_} = 1;
+ print MASTER_MAKEFILE <<EOF;
+$dir/${name}.gz: docs/man/${name}.gz
+ mkdir -p $dir
+ $copy_command docs/man/${name}.gz $dir
+
+EOF
+ # Releases have the docs pre-made, but users
+ # may want to rebuild them for some reason.
+ my $docbook_source = "docs/docbook/${name}";
+ $docbook_source =~ s/\.[58]$/.xml/;
+ print MASTER_MAKEFILE <<EOF;
+docs/man/${name}.gz: $docbook_source docs/docbook/bb-man.xsl
+ cd docs; \$(MAKE) man/${name}.gz
+
+EOF
+ push @parcel_deps, "$dir/${name}.gz";
}
-
- # link line...
- print MAKE "\t\$(_LINK) \$(LDFLAGS) $link_line_extra " .
- "-o $end_target $o_file_list " .
- "@lib_files $lo $platform_lib_files\n";
- }
+ elsif($type eq 'html')
+ {
+ print MASTER_MAKEFILE <<EOF;
+$dir/docs/${name}.html: docs/htmlguide/man-html/${name}.html
+ mkdir -p $dir/docs
+ $copy_command docs/htmlguide/man-html/${name}.html $dir/docs
- # tests need to copy the test file over
- if($type eq 'test')
- {
- print MAKE "\tcp _t \$(OUTDIR)/t\n\tchmod u+x \$(OUTDIR)/t\n";
- print MAKE "\tcp _t-gdb \$(OUTDIR)/t-gdb\n\tchmod u+x \$(OUTDIR)/t-gdb\n";
+EOF
+ # Releases have the docs pre-made, but users
+ # may want to rebuild them for some reason.
+ my $docbook_source = "docs/docbook/${name}.xml";
+ print MASTER_MAKEFILE <<EOF;
+docs/htmlguide/man-html/${name}.html: $docbook_source docs/docbook/bb-nochunk-book.xsl
+ cd docs; \$(MAKE) htmlguide/man-html/${name}.html
+
+EOF
+ push @parcel_deps, "$dir/docs/${name}.html";
+ }
+ elsif ($type eq 'subdir')
+ {
+ push @parcel_deps, "build_${type}_${name}";
+ }
}
- # dependency line?
- print MAKE "\n";
+ print MASTER_MAKEFILE <<EOF;
+build-$parcel: $make_target
- # module dependencies for GNU make?
- print MAKE $deps_makeinfo if !$bsd_make;
-
- # print the rest of the file
- print MAKE $make,"\n";
-
- # and a clean target
- print MAKE <<EOF;
-clean:
- -rm -rf \$(OUTDIR)/*
-. ifndef SUBCLEAN
+$make_target: @parcel_deps
+ test -d $dir || mkdir $dir
EOF
- for my $dep (@all_deps_for_module)
- {
- print MAKE "\t\$(HIDE) (cd ../../$dep; \$(MAKE) \$(DEPENDMAKEFLAGS) -D SUBCLEAN clean)\n";
- }
- print MAKE ".\tendif\n";
+
+ push @parcel_targets, "build-$parcel";
- # include any extra stuff
- print MAKE "\n\n";
- if(-e "$mod/Makefile.extra")
- {
- print MAKE ".include <Makefile.extra>\n\n";
- }
- if(-e "$mod/Makefile.extra.$build_os")
+ for(@{$parcel_contents{$parcel}})
{
- print MAKE ".include <Makefile.extra.$build_os>\n\n";
+ my @args = split /\s+/;
+
+ my ($type,$name,$dest) = @args;
+
+ my $optional = 0;
+ my $install = 1;
+
+ if ($type eq 'optional')
+ {
+ $optional = 1;
+ shift @args;
+ ($type,$name,$dest) = @args;
+ }
+
+ if ($type eq 'noinstall')
+ {
+ $install = 0;
+ shift @args;
+ ($type,$name,$dest) = @args;
+ }
+
+ if ($type eq 'script')
+ {
+ # remove path from script name
+ $name =~ s{.*/}{};
+ }
+
+ if ($type eq 'html')
+ {
+ $dest = "share/doc/@PACKAGE_TARNAME@";
+ $name = "docs/$name.html";
+ }
+
+ if ($type eq 'man')
+ {
+ $name =~ /([0-9])$/;
+ $dest = "man/man$1";
+ $name =~ s/$/\.gz/;
+ }
+
+ if ($install and not $target_windows and not $type eq "subdir")
+ {
+ my $local_install_dir = $install_into_dir;
+ if (defined $dest)
+ {
+ if ($dest =~ m,^/,)
+ {
+ # Don't add $prefix if $dest is a literal path
+ $local_install_dir = $dest;
+ }
+ else
+ {
+ $local_install_dir = "@prefix@/$dest";
+ }
+ }
+ print SCRIPT "mkdir -p " .
+ "\${DESTDIR}$local_install_dir/\n";
+ print SCRIPT "$install_bin $name " .
+ "\${DESTDIR}$local_install_dir\n";
+ }
}
- for(@makefile_includes)
- {
- print MAKE ".include <$_>\n\n";
+
+ unless ($target_windows)
+ {
+ close SCRIPT;
+ chmod 0755,"parcels/scripts/install-$parcel";
}
- # and finally a target for rebuilding the build system
- print MAKE "\nbuildsystem:\n\t(cd ../..; perl ./infrastructure/makebuildenv.pl $makebuildenv_args)\n\n";
-
- close MAKE;
+ my $root = BoxPlatform::parcel_root($parcel);
- if(!$bsd_make)
+ unless ($target_windows)
{
- # need to post process this into a GNU makefile
- open MAKE,">$mod/Makefile";
- open MAKEB,"$mod/MakefileX";
+ print MAKE "\tcp parcels/scripts/install-$parcel $dir\n";
+ }
- while(<MAKEB>)
- {
- s/\A\.\s*(ifdef|else|endif|ifndef)/$1/;
- s/\A\.\s*include\s+<(.+?)>/include $1/;
- s/-D\s+(\w+)/$1=1/g;
- print MAKE;
- }
+ print MASTER_MAKEFILE "\t(cd parcels; tar cf - $root | gzip -9 - > $root.tgz )\n";
+
+ print MASTER_MAKEFILE "\n";
- close MAKEB;
- close MAKE;
- unlink "$mod/MakefileX";
+ unless ($target_windows)
+ {
+ print MASTER_MAKEFILE "install-$parcel:\n";
+ print MASTER_MAKEFILE "\t(cd $dir; ./install-$parcel)\n\n";
}
}
+print MASTER_MAKEFILE <<EOF;
+
+debug_build: @debug_build_targets
+release_build: @release_build_targets
+parcels: @parcel_targets
+
+# well-known targets that users might expect to be able to build:
+install:
+ cat local/install.msg
+
+clean: @all_clean_targets
+ cd docs; \$(MAKE) clean
+
+realclean: clean
+ find . -name 'autogen_*' -type f -exec rm -f {} \\;
+ find release debug -type f -exec rm -f {} \\;
+
+# and finally a target for rebuilding the build system:
+buildsystem:
+ perl ./infrastructure/makebuildenv.pl $makebuildenv_args
+EOF
+
+for my $parcel (@parcels)
+{
+ # need to use -f to avoid error if they don't exist (already cleaned)
+ print MASTER_MAKEFILE "\trm -rf ", BoxPlatform::parcel_dir($parcel), "\n";
+ print MASTER_MAKEFILE "\trm -f ", BoxPlatform::parcel_target($parcel), "\n";
+}
+
+open INSTALLMSG,">local/install.msg" or die "Can't open install message file for writing";
+print INSTALLMSG <<__E;
+
+Parcels need to be installed separately, and as root. Type one of the following:
+
+__E
+
+for(@parcels)
+{
+ print INSTALLMSG " $make_command install-".$_."\n";
+}
+print INSTALLMSG "\n";
+
+close INSTALLMSG;
+
+close MASTER_MAKEFILE;
+
print "\nType 'cd <module_dir>; $make_command' to build a module\n\n";
if($modules_omitted)