#!/usr/bin/perl use Cwd; use File::Spec; use File::stat; our $top_dir = (File::Spec->splitpath(File::Spec->rel2abs($0)))[1]; our $opts_file = "${top_dir}opts.pl"; is_file_newer("${top_dir}configure.pl", $opts_file) and die "Build options out of date. Please run configure.pl first.\n"; require $opts_file; my $targets_script = defined($ARGV[0]) ? $ARGV[0] : 'targets.pl'; unless (-f $targets_script) { print "build.pl: target script '$targets_script' does not exist.\n"; exit 1; } do $targets_script; 1; # is_file_newer($file, $than) # Checks if $file is newer than $than. sub is_file_newer { (-f $_[1]) or return 1; my $file = stat($_[0]); my $than = stat($_[1]); return $file->mtime >= $than->mtime; } # needs_building($file, $than) # Checks if targets or build options changed in addition to checking # whether the object file needs an update. sub needs_building { return is_file_newer($_[0], $_[1]) || is_file_newer('targets.pl', $_[1]) || is_file_newer($opts_file, $_[1]); } sub get_asm_cmd { return "jwasm $jwasm_args -zt1"; } # get_cxx_cmd(\@includes, \@defines) sub get_cxx_cmd { my @cc_opts = ('g++', '-c'); # OWORLD currently miscompiles at -O2 on gcc 4.3.3. #push (@cc_opts, $no_asm ? "-O2" : "-O1"); defined($debug) and $debug and push (@cc_opts, "-g"); push (@cc_opts, map { "-D$_" } @{$_[1]}); push (@cc_opts, map { "-I$_" } @{$_[0]}); return "@cc_opts"; } sub get_wrc_cmd { return $platform =~ /^linux/ ? 'wrc' : 'windres'; } sub link_exe { my ($exe, $obj_files, $libs, $lib_dirs) = @_; defined($exe) or return 1; # No exe targets here my $flag = 0; foreach my $i (@$obj_files) { unless (-f $i) { print "build.pl: missing file '$i' for linking.\n"; exit 1; } # check modification times to see if we have to relink if (-f $exe) { is_file_newer($i, $exe) and $flag = 1; } else { $flag = 1; } } if ($flag) { my $linker = $platform =~ /^linux/ ? 'wineg++' : 'g++'; my @linker_opts = ("-g -mno-cygwin"); $debug or push(@linker_opts, "-mwindows"); my $cmd = "$linker " . join(' ', @linker_opts) . ' ' . "@$obj_files " . join(' ', map { "-l$_" } @$libs) . ' ' . join(' ', map { "-L$_" } @$lib_dirs) . ' ' . "-o $exe"; print $cmd,"\n"; if (system $cmd) { print "build.pl: couldn't create executable '$exe'\n"; exit 1; } } return 1; } # include_targets(@target_files) # # Usage: Use in your target files to include build stages found in # other directories. This will change directory to where your # target file is found in and execute the target script. # # Note that the new target file executed will have its own clean # scope. This forces you to redefine what is necessary to run # the building. It makes sense if you are logically grouping # various targets by their purpose. # # Returns paths to the built targets. # sub include_targets { my @built_targets; my $orig_dir = cwd; foreach my $i (@_) { unless (-f $i) { print "build.pl: target script '$i' does not exist.\n"; exit 1; } # change directory my ($dir, $inc) = (File::Spec->splitpath($i))[1,2]; ($dir) = $dir =~ /(.+?)\/?$/; unless (-d $dir && chdir $dir) { print "build.pl: unable to enter directory '$dir'.\n"; exit 1; } print "Entering '$dir'.\n"; # run script push (@built_targets, map { "$dir/$_" } do $inc); # go back to the original directory print "Leaving '$dir'.\n"; unless (chdir $orig_dir) { print "build.pl: original directory disappeared.\n"; exit 1; } } return @built_targets; } sub break_extension { my @parts = split('\.', $_[0]); my $extension = pop(@parts); my $name = join('.', @parts); return ($name, $extension); } # build_targets(\@files_to_build, \@includes, \@defines) # # Usage: Called from target script. An array of files # that are to be built is passed. # i.e. ('AM.cpp','7k.ico', 'IB.asm') # or qw(AM.cpp 7k.ico IB.asm) # # Pass the include paths and defines for the C++ compiler # as needed for the source files to be built. # sub build_targets { my $cxx_cmd; my $asm_cmd; my $wrc_cmd; my @built_objects; foreach my $i (@{$_[0]}) { my ($name, $extension) = break_extension($i); unless (defined($extension)) { print "build.pl: no extension to figure out the file type of '$i'\n"; exit 1; } my $obj = "$name.o"; if (needs_building($i, $obj)) { my $cmd; # get the command to build this type of file if ($extension eq 'cpp') { defined($cxx_cmd) or $cxx_cmd = get_cxx_cmd($_[1], $_[2]); $cmd = "$cxx_cmd $i -o $obj"; } elsif ($extension eq 'asm') { defined($asm_cmd) or $asm_cmd = get_asm_cmd(); $cmd = "$asm_cmd -Fo $name.o $i"; } elsif ($extension eq 'rc') { defined($wrc_cmd) or $wrc_cmd = get_wrc_cmd(); $cmd = "$wrc_cmd -i $i -o $name.o"; } else { print "build.pl: cannot identify how to build file type '$extension'\n"; exit 1; } print "$cmd\n"; if (system $cmd) { print "build.pl: couldn't build '$i'. Stopping.\n"; exit 1; } } push (@built_objects, $obj); } return @built_objects; }