1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606 |
- # ------------------------------------------------------------------------------
- # NAME
- # Fcm::Build
- #
- # DESCRIPTION
- # This is the top level class for the FCM build system.
- #
- # COPYRIGHT
- # (C) Crown copyright Met Office. All rights reserved.
- # For further details please refer to the file COPYRIGHT.txt
- # which you should have received as part of this distribution.
- # ------------------------------------------------------------------------------
- use strict;
- use warnings;
- package Fcm::Build;
- use base qw(Fcm::ConfigSystem);
- use Carp qw{croak} ;
- use Cwd qw{cwd} ;
- use Fcm::BuildSrc ;
- use Fcm::BuildTask ;
- use Fcm::Config ;
- use Fcm::Dest ;
- use Fcm::CfgLine ;
- use Fcm::Timer qw{timestamp_command} ;
- use Fcm::Util qw{expand_tilde run_command touch_file w_report};
- use File::Basename qw{dirname} ;
- use File::Spec ;
- use List::Util qw{first} ;
- use Text::ParseWords qw{shellwords} ;
- # List of scalar property methods for this class
- my @scalar_properties = (
- 'name', # name of this build
- 'target', # targets of this build
- );
- # List of hash property methods for this class
- my @hash_properties = (
- 'srcpkg', # source packages of this build
- 'dummysrcpkg', # dummy for handling package inheritance with file extension
- );
- # List of compare_setting_X methods
- my @compare_setting_methods = (
- 'compare_setting_bld_blockdata', # program executable blockdata dependency
- 'compare_setting_bld_dep', # custom dependency setting
- 'compare_setting_bld_dep_excl', # exclude dependency setting
- 'compare_setting_bld_dep_n', # no dependency check
- 'compare_setting_bld_dep_pp', # custom PP dependency setting
- 'compare_setting_bld_dep_exe', # program executable extra dependency
- 'compare_setting_bld_exe_name', # program executable rename
- 'compare_setting_bld_pp', # PP flags
- 'compare_setting_infile_ext', # input file extension
- 'compare_setting_outfile_ext', # output file extension
- 'compare_setting_tool', # build tool settings
- );
- my $DELIMITER_LIST = $Fcm::Config::DELIMITER_LIST;
- # ------------------------------------------------------------------------------
- # SYNOPSIS
- # $obj = Fcm::Build->new;
- #
- # DESCRIPTION
- # This method constructs a new instance of the Fcm::Build class.
- # ------------------------------------------------------------------------------
- sub new {
- my $this = shift;
- my %args = @_;
- my $class = ref $this || $this;
- my $self = Fcm::ConfigSystem->new (%args);
- $self->{$_} = undef for (@scalar_properties);
- $self->{$_} = {} for (@hash_properties);
- bless $self, $class;
- # List of sub-methods for parse_cfg
- push @{ $self->cfg_methods }, (qw/target source tool dep misc/);
- # Optional prefix in configuration declaration
- $self->cfg_prefix ($self->setting (qw/CFG_LABEL BDECLARE/));
- # System type
- $self->type ('bld');
- return $self;
- }
- # ------------------------------------------------------------------------------
- # SYNOPSIS
- # $value = $obj->X;
- # $obj->X ($value);
- #
- # DESCRIPTION
- # Details of these properties are explained in @scalar_properties.
- # ------------------------------------------------------------------------------
- for my $name (@scalar_properties) {
- no strict 'refs';
- *$name = sub {
- my $self = shift;
- # Argument specified, set property to specified argument
- if (@_) {
- $self->{$name} = $_[0];
- }
- # Default value for property
- if (not defined $self->{$name}) {
- if ($name eq 'target') {
- # Reference to an array
- $self->{$name} = [];
- } elsif ($name eq 'name') {
- # Empty string
- $self->{$name} = '';
- }
- }
- return $self->{$name};
- }
- }
- # ------------------------------------------------------------------------------
- # SYNOPSIS
- # %hash = %{ $obj->X () };
- # $obj->X (\%hash);
- #
- # $value = $obj->X ($index);
- # $obj->X ($index, $value);
- #
- # DESCRIPTION
- # Details of these properties are explained in @hash_properties.
- #
- # If no argument is set, this method returns a hash containing a list of
- # objects. If an argument is set and it is a reference to a hash, the objects
- # are replaced by the the specified hash.
- #
- # If a scalar argument is specified, this method returns a reference to an
- # object, if the indexed object exists or undef if the indexed object does
- # not exist. If a second argument is set, the $index element of the hash will
- # be set to the value of the argument.
- # ------------------------------------------------------------------------------
- for my $name (@hash_properties) {
- no strict 'refs';
- *$name = sub {
- my ($self, $arg1, $arg2) = @_;
- # Ensure property is defined as a reference to a hash
- $self->{$name} = {} if not defined ($self->{$name});
- # Argument 1 can be a reference to a hash or a scalar index
- my ($index, %hash);
- if (defined $arg1) {
- if (ref ($arg1) eq 'HASH') {
- %hash = %$arg1;
- } else {
- $index = $arg1;
- }
- }
- if (defined $index) {
- # A scalar index is defined, set and/or return the value of an element
- $self->{$name}{$index} = $arg2 if defined $arg2;
- return (
- exists $self->{$name}{$index} ? $self->{$name}{$index} : undef
- );
- } else {
- # A scalar index is not defined, set and/or return the hash
- $self->{$name} = \%hash if defined $arg1;
- return $self->{$name};
- }
- }
- }
- # ------------------------------------------------------------------------------
- # SYNOPSIS
- # ($rc, $new_lines) = $self->X ($old_lines);
- #
- # DESCRIPTION
- # This method compares current settings with those in the cache, where X is
- # one of @compare_setting_methods.
- #
- # If setting has changed:
- # * For bld_blockdata, bld_dep_ext and bld_exe_name, it sets the re-generate
- # make-rule flag to true.
- # * For bld_dep_excl, in a standalone build, the method will remove the
- # dependency cache files for affected sub-packages. It returns an error if
- # the current build inherits from previous builds.
- # * For bld_pp, it updates the PP setting for affected sub-packages.
- # * For infile_ext, in a standalone build, the method will remove all the
- # sub-package cache files and trigger a re-build by removing most
- # sub-directories created by the previous build. It returns an error if the
- # current build inherits from previous builds.
- # * For outfile_ext, in a standalone build, the method will remove all the
- # sub-package dependency cache files. It returns an error if the current
- # build inherits from previous builds.
- # * For tool, it updates the "flags" files for any changed tools.
- # ------------------------------------------------------------------------------
- for my $name (@compare_setting_methods) {
- no strict 'refs';
- *$name = sub {
- my ($self, $old_lines) = @_;
- (my $prefix = uc ($name)) =~ s/^COMPARE_SETTING_//;
- my ($changed, $new_lines) =
- $self->compare_setting_in_config ($prefix, $old_lines);
- my $rc = scalar (keys %$changed);
- if ($rc and $old_lines) {
- $self->srcpkg ('')->is_updated (1);
- if ($name =~ /^compare_setting_bld_dep(?:_excl|_n|_pp)?$/) {
- # Mark affected packages as being updated
- for my $key (keys %$changed) {
- for my $pkg (values %{ $self->srcpkg }) {
- next unless $pkg->is_in_package ($key);
- $pkg->is_updated (1);
- }
- }
- } elsif ($name eq 'compare_setting_bld_pp') {
- # Mark affected packages as being updated
- for my $key (keys %$changed) {
- for my $pkg (values %{ $self->srcpkg }) {
- next unless $pkg->is_in_package ($key);
- next unless $self->srcpkg ($key)->is_type_any (
- keys %{ $self->setting ('BLD_TYPE_DEP_PP') }
- ); # Is a type requiring pre-processing
- $pkg->is_updated (1);
- }
- }
- } elsif ($name eq 'compare_setting_infile_ext') {
- # Re-set input file type if necessary
- for my $key (keys %$changed) {
- for my $pkg (values %{ $self->srcpkg }) {
- next unless $pkg->src and $pkg->ext and $key eq $pkg->ext;
- $pkg->type (undef);
- }
- }
- # Mark affected packages as being updated
- for my $pkg (values %{ $self->srcpkg }) {
- $pkg->is_updated (1);
- }
- } elsif ($name eq 'compare_setting_outfile_ext') {
- # Mark affected packages as being updated
- for my $pkg (values %{ $self->srcpkg }) {
- $pkg->is_updated (1);
- }
- } elsif ($name eq 'compare_setting_tool') {
- # Update the "flags" files for changed tools
- for my $name (sort keys %$changed) {
- my ($tool, @names) = split /__/, $name;
- my $pkg = join ('__', @names);
- my @srcpkgs = $self->srcpkg ($pkg)
- ? ($self->srcpkg ($pkg))
- : @{ $self->dummysrcpkg ($pkg)->children };
- for my $srcpkg (@srcpkgs) {
- my $file = File::Spec->catfile (
- $self->dest->flagsdir, $srcpkg->flagsbase ($tool)
- );
- &touch_file ($file) or croak $file, ': cannot update, abort';
- print $file, ': updated', "\n" if $self->verbose > 2;
- }
- }
- }
- }
- return ($rc, $new_lines);
- }
- }
- # ------------------------------------------------------------------------------
- # SYNOPSIS
- # ($rc, $new_lines) = $self->compare_setting_dependency ($old_lines, $flag);
- #
- # DESCRIPTION
- # This method uses the previous settings to determine the dependencies of
- # current source files.
- # ------------------------------------------------------------------------------
- sub compare_setting_dependency {
- my ($self, $old_lines, $flag) = @_;
- my $prefix = $flag ? 'DEP_PP' : 'DEP';
- my $method = $flag ? 'ppdep' : 'dep';
- my $rc = 0;
- my $new_lines = [];
- # Separate old lines
- my %old;
- if ($old_lines) {
- for my $line (@$old_lines) {
- next unless $line->label_starts_with ($prefix);
- $old{$line->label_from_field (1)} = $line;
- }
- }
- # Go through each source to see if the cache is up to date
- my $count = 0;
- my %mtime;
- for my $srcpkg (values %{ $self->srcpkg }) {
- next unless $srcpkg->cursrc and $srcpkg->type;
- my $key = $srcpkg->pkgname;
- my $out_of_date = $srcpkg->is_updated;
- # Check modification time of cache and source file if not out of date
- if (exists $old{$key}) {
- if (not $out_of_date) {
- $mtime{$old{$key}->src} = (stat ($old{$key}->src))[9]
- if not exists ($mtime{$old{$key}->src});
- $out_of_date = 1 if $mtime{$old{$key}->src} < $srcpkg->curmtime;
- }
- }
- else {
- $out_of_date = 1;
- }
- if ($out_of_date) {
- # Re-scan dependency
- $srcpkg->is_updated(1);
- my ($source_is_read, $dep_hash_ref) = $srcpkg->get_dep($flag);
- if ($source_is_read) {
- $count++;
- }
- $srcpkg->$method($dep_hash_ref);
- $rc = 1;
- }
- else {
- # Use cached dependency
- my ($progname, %hash) = split (
- /$Fcm::Config::DELIMITER_PATTERN/, $old{$key}->value
- );
- $srcpkg->progname ($progname) if $progname and not $flag;
- $srcpkg->$method (\%hash);
- }
- # New lines values: progname[::dependency-name::type][...]
- my @value = ((defined $srcpkg->progname ? $srcpkg->progname : ''));
- for my $name (sort keys %{ $srcpkg->$method }) {
- push @value, $name, $srcpkg->$method ($name);
- }
- push @$new_lines, Fcm::CfgLine->new (
- LABEL => $prefix . $Fcm::Config::DELIMITER . $key,
- VALUE => join ($Fcm::Config::DELIMITER, @value),
- );
- }
- print 'No. of file', ($count > 1 ? 's' : ''), ' scanned for',
- ($flag ? ' PP': ''), ' dependency: ', $count, "\n"
- if $self->verbose and $count;
- return ($rc, $new_lines);
- }
- # ------------------------------------------------------------------------------
- # SYNOPSIS
- # ($rc, $new_lines) = $self->compare_setting_srcpkg ($old_lines);
- #
- # DESCRIPTION
- # This method uses the previous settings to determine the type of current
- # source files.
- # ------------------------------------------------------------------------------
- sub compare_setting_srcpkg {
- my ($self, $old_lines) = @_;
- my $prefix = 'SRCPKG';
- # Get relevant items from old lines, stripping out $prefix
- my %old;
- if ($old_lines) {
- for my $line (@$old_lines) {
- next unless $line->label_starts_with ($prefix);
- $old{$line->label_from_field (1)} = $line;
- }
- }
- # Check for change, use previous setting if exist
- my $out_of_date = 0;
- my %mtime;
- for my $key (keys %{ $self->srcpkg }) {
- if (exists $old{$key}) {
- next unless $self->srcpkg ($key)->cursrc;
- my $type = defined $self->setting ('BLD_TYPE', $key)
- ? $self->setting ('BLD_TYPE', $key) : $old{$key}->value;
- $self->srcpkg ($key)->type ($type);
- if ($type ne $old{$key}->value) {
- $self->srcpkg ($key)->is_updated (1);
- $out_of_date = 1;
- }
- if (not $self->srcpkg ($key)->is_updated) {
- $mtime{$old{$key}->src} = (stat ($old{$key}->src))[9]
- if not exists ($mtime{$old{$key}->src});
- $self->srcpkg ($key)->is_updated (1)
- if $mtime{$old{$key}->src} < $self->srcpkg ($key)->curmtime;
- }
- } else {
- $self->srcpkg ($key)->is_updated (1);
- $out_of_date = 1;
- }
- }
- # Check for deleted keys
- for my $key (keys %old) {
- next if $self->srcpkg ($key);
- $out_of_date = 1;
- }
- # Return reference to an array of new lines
- my $new_lines = [];
- for my $key (keys %{ $self->srcpkg }) {
- push @$new_lines, Fcm::CfgLine->new (
- LABEL => $prefix . $Fcm::Config::DELIMITER . $key,
- VALUE => $self->srcpkg ($key)->type,
- );
- }
- return ($out_of_date, $new_lines);
- }
- # ------------------------------------------------------------------------------
- # SYNOPSIS
- # ($rc, $new_lines) = $self->compare_setting_target ($old_lines);
- #
- # DESCRIPTION
- # This method compare the previous target settings with current ones.
- # ------------------------------------------------------------------------------
- sub compare_setting_target {
- my ($self, $old_lines) = @_;
- my $prefix = 'TARGET';
- my $old;
- if ($old_lines) {
- for my $line (@$old_lines) {
- next unless $line->label_starts_with ($prefix);
- $old = $line->value;
- last;
- }
- }
- my $new = join (' ', sort @{ $self->target });
- return (
- (defined ($old) ? $old ne $new : 1),
- [Fcm::CfgLine->new (LABEL => $prefix, VALUE => $new)],
- );
- }
- # ------------------------------------------------------------------------------
- # SYNOPSIS
- # $rc = $self->invoke_fortran_interface_generator ();
- #
- # DESCRIPTION
- # This method invokes the Fortran interface generator for all Fortran free
- # format source files. It returns true on success.
- # ------------------------------------------------------------------------------
- sub invoke_fortran_interface_generator {
- my $self = shift;
- my $pdoneext = $self->setting (qw/OUTFILE_EXT PDONE/);
- # Set up build task to generate interface files for all selected Fortran 9x
- # sources
- my %task = ();
- SRC_FILE:
- for my $srcfile (values %{ $self->srcpkg }) {
- if (!defined($srcfile->interfacebase())) {
- next SRC_FILE;
- }
- my $target = $srcfile->interfacebase . $pdoneext;
- $task{$target} = Fcm::BuildTask->new (
- TARGET => $target,
- TARGETPATH => $self->dest->donepath,
- SRCFILE => $srcfile,
- DEPENDENCY => [$srcfile->flagsbase ('GENINTERFACE')],
- ACTIONTYPE => 'GENINTERFACE',
- );
- # Set up build tasks for each source file/package flags file for interface
- # generator tool
- for my $i (1 .. @{ $srcfile->pkgnames }) {
- my $target = $srcfile->flagsbase ('GENINTERFACE', -$i);
- my $depend = $i < @{ $srcfile->pkgnames }
- ? $srcfile->flagsbase ('GENINTERFACE', -$i - 1)
- : undef;
- $task{$target} = Fcm::BuildTask->new (
- TARGET => $target,
- TARGETPATH => $self->dest->flagspath,
- DEPENDENCY => [defined ($depend) ? $depend : ()],
- ACTIONTYPE => 'UPDATE',
- ) if not exists $task{$target};
- }
- }
- # Set up build task to update the flags file for interface generator tool
- $task{$self->srcpkg ('')->flagsbase ('GENINTERFACE')} = Fcm::BuildTask->new (
- TARGET => $self->srcpkg ('')->flagsbase ('GENINTERFACE'),
- TARGETPATH => $self->dest->flagspath,
- ACTIONTYPE => 'UPDATE',
- );
- my $count = 0;
- # Performs task
- for my $task (values %task) {
- next unless $task->actiontype eq 'GENINTERFACE';
- my $rc = $task->action (TASKLIST => \%task);
- $count++ if $rc;
- }
- print 'No. of generated Fortran interface', ($count > 1 ? 's' : ''), ': ',
- $count, "\n"
- if $self->verbose and $count;
- return 1;
- }
- # ------------------------------------------------------------------------------
- # SYNOPSIS
- # $rc = $self->invoke_make (%args);
- #
- # DESCRIPTION
- # This method invokes the make stage of the build system. It returns true on
- # success.
- #
- # ARGUMENTS
- # ARCHIVE - If set to "true", invoke the "archive" mode. Most build files and
- # directories created by this build will be archived using the
- # "tar" command. If not set, the default is not to invoke the
- # "archive" mode.
- # JOBS - Specify number of jobs that can be handled by "make". If set, the
- # value must be a natural integer. If not set, the default value is
- # 1 (i.e. run "make" in serial mode).
- # TARGETS - Specify targets to be built. If set, these targets will be built
- # instead of the ones specified in the build configuration file.
- # ------------------------------------------------------------------------------
- sub invoke_make {
- my ($self, %args) = @_;
- $args{TARGETS} ||= ['all'];
- $args{JOBS} ||= 1;
- my @command = (
- $self->setting(qw/TOOL MAKE/),
- shellwords($self->setting(qw/TOOL MAKEFLAGS/)),
- # -f Makefile
- ($self->setting(qw/TOOL MAKE_FILE/), $self->dest()->bldmakefile()),
- # -j N
- ($args{JOBS} ? ($self->setting(qw/TOOL MAKE_JOB/), $args{JOBS}) : ()),
- # -s
- ($self->verbose() >= 3 ? $self->setting(qw/TOOL MAKE_SILENT/) : ()),
- @{$args{TARGETS}}
- );
- my $old_cwd = $self->_chdir($self->dest()->rootdir());
- run_command(
- \@command, ERROR => 'warn', RC => \my($code), TIME => $self->verbose() >= 3,
- );
- $self->_chdir($old_cwd);
- my $rc = !$code;
- if ($rc && $args{ARCHIVE}) {
- $rc = $self->dest()->archive();
- }
- $rc &&= $self->dest()->create_bldrunenvsh();
- while (my ($key, $source) = each(%{$self->srcpkg()})) {
- $rc &&= defined($source->write_lib_dep_excl());
- }
- return $rc;
- }
- # ------------------------------------------------------------------------------
- # SYNOPSIS
- # $rc = $self->invoke_pre_process ();
- #
- # DESCRIPTION
- # This method invokes the pre-process stage of the build system. It
- # returns true on success.
- # ------------------------------------------------------------------------------
- sub invoke_pre_process {
- my $self = shift;
-
- # Check whether pre-processing is necessary
- my $invoke = 0;
- for (values %{ $self->srcpkg }) {
- next unless $_->get_setting ('BLD_PP');
- $invoke = 1;
- last;
- }
- return 1 unless $invoke;
- # Scan header dependency
- my $rc = $self->compare_setting (
- METHOD_LIST => ['compare_setting_dependency'],
- METHOD_ARGS => ['BLD_TYPE_DEP_PP'],
- CACHEBASE => $self->setting ('CACHE_DEP_PP'),
- );
- return $rc if not $rc;
- my %task = ();
- my $pdoneext = $self->setting (qw/OUTFILE_EXT PDONE/);
- # Set up tasks for each source file
- for my $srcfile (values %{ $self->srcpkg }) {
- if ($srcfile->is_type_all (qw/CPP INCLUDE/)) {
- # Set up a copy build task for each include file
- $task{$srcfile->base} = Fcm::BuildTask->new (
- TARGET => $srcfile->base,
- TARGETPATH => $self->dest->incpath,
- SRCFILE => $srcfile,
- DEPENDENCY => [keys %{ $srcfile->ppdep }],
- ACTIONTYPE => 'COPY',
- );
- } elsif ($srcfile->lang ('TOOL_SRC_PP')) {
- next unless $srcfile->get_setting ('BLD_PP');
- # Set up a PP build task for each source file
- my $target = $srcfile->base . $pdoneext;
- # Issue warning for duplicated tasks
- if (exists $task{$target}) {
- w_report 'WARNING: ', $target, ': unable to create task for: ',
- $srcfile->src, ': task already exists for: ',
- $task{$target}->srcfile->src;
- next;
- }
- $task{$target} = Fcm::BuildTask->new (
- TARGET => $target,
- TARGETPATH => $self->dest->donepath,
- SRCFILE => $srcfile,
- DEPENDENCY => [$srcfile->flagsbase ('PPKEYS'), keys %{ $srcfile->ppdep }],
- ACTIONTYPE => 'PP',
- );
- # Set up update ppkeys/flags build tasks for each source file/package
- my $ppkeys = $self->setting (
- 'TOOL_SRC_PP', $srcfile->lang ('TOOL_SRC_PP'), 'PPKEYS'
- );
- for my $i (1 .. @{ $srcfile->pkgnames }) {
- my $target = $srcfile->flagsbase ($ppkeys, -$i);
- my $depend = $i < @{ $srcfile->pkgnames }
- ? $srcfile->flagsbase ($ppkeys, -$i - 1)
- : undef;
- $task{$target} = Fcm::BuildTask->new (
- TARGET => $target,
- TARGETPATH => $self->dest->flagspath,
- DEPENDENCY => [defined ($depend) ? $depend : ()],
- ACTIONTYPE => 'UPDATE',
- ) if not exists $task{$target};
- }
- }
- }
- # Set up update global ppkeys build tasks
- for my $lang (keys %{ $self->setting ('TOOL_SRC_PP') }) {
- my $target = $self->srcpkg ('')->flagsbase (
- $self->setting ('TOOL_SRC_PP', $lang, 'PPKEYS')
- );
- $task{$target} = Fcm::BuildTask->new (
- TARGET => $target,
- TARGETPATH => $self->dest->flagspath,
- ACTIONTYPE => 'UPDATE',
- );
- }
- # Build all PP tasks
- my $count = 0;
- for my $task (values %task) {
- next unless $task->actiontype eq 'PP';
- my $rc = $task->action (TASKLIST => \%task);
- $task->srcfile->is_updated ($rc);
- $count++ if $rc;
- }
- print 'No. of pre-processed file', ($count > 1 ? 's' : ''), ': ', $count, "\n"
- if $self->verbose and $count;
- return 1;
- }
- # ------------------------------------------------------------------------------
- # SYNOPSIS
- # $rc = $self->invoke_scan_dependency ();
- #
- # DESCRIPTION
- # This method invokes the scan dependency stage of the build system. It
- # returns true on success.
- # ------------------------------------------------------------------------------
- sub invoke_scan_dependency {
- my $self = shift;
- # Scan/retrieve dependency
- # ----------------------------------------------------------------------------
- my $rc = $self->compare_setting (
- METHOD_LIST => ['compare_setting_dependency'],
- CACHEBASE => $self->setting ('CACHE_DEP'),
- );
- # Check whether make file is out of date
- # ----------------------------------------------------------------------------
- my $out_of_date = not -r $self->dest->bldmakefile;
- if ($rc and not $out_of_date) {
- for (qw/CACHE CACHE_DEP/) {
- my $cache_mtime = (stat (File::Spec->catfile (
- $self->dest->cachedir, $self->setting ($_),
- )))[9];
- my $mfile_mtime = (stat ($self->dest->bldmakefile))[9];
- next if not defined $cache_mtime;
- next if $cache_mtime < $mfile_mtime;
- $out_of_date = 1;
- last;
- }
- }
- if ($rc and not $out_of_date) {
- for (values %{ $self->srcpkg }) {
- next unless $_->is_updated;
- $out_of_date = 1;
- last;
- }
- }
- if ($rc and $out_of_date) {
- # Write Makefile
- # --------------------------------------------------------------------------
- # Register non-word package name
- my $unusual = 0;
- for my $key (sort keys %{ $self->srcpkg }) {
- next if $self->srcpkg ($key)->src;
- next if $key =~ /^\w*$/;
- $self->setting (
- ['FCM_PCK_OBJECTS', $key], 'FCM_PCK_OBJECTS' . $unusual++,
- );
- }
- # Write different parts in the Makefile
- my $makefile = '# Automatic Makefile' . "\n\n";
- $makefile .= 'FCM_BLD_NAME = ' . $self->name . "\n" if $self->name;
- $makefile .= 'FCM_BLD_CFG = ' . $self->cfg->actual_src . "\n";
- $makefile .= 'export FCM_VERBOSE ?= ' . $self->verbose . "\n\n";
- $makefile .= $self->dest->write_rules;
- $makefile .= $self->_write_makefile_perl5lib;
- $makefile .= $self->_write_makefile_tool;
- $makefile .= $self->_write_makefile_vpath;
- $makefile .= $self->_write_makefile_target;
- # Write rules for each source package
- # Ensure that container packages come before files - this allows $(OBJECTS)
- # and its dependent variables to expand correctly
- my @srcpkg = sort {
- if ($self->srcpkg ($a)->libbase and $self->srcpkg ($b)->libbase) {
- $b cmp $a;
- } elsif ($self->srcpkg ($a)->libbase) {
- -1;
- } elsif ($self->srcpkg ($b)->libbase) {
- 1;
- } else {
- $a cmp $b;
- }
- } keys %{ $self->srcpkg };
- for (@srcpkg) {
- $makefile .= $self->srcpkg ($_)->write_rules if $self->srcpkg ($_)->rules;
- }
- $makefile .= '# EOF' . "\n";
- # Update Makefile
- open OUT, '>', $self->dest->bldmakefile
- or croak $self->dest->bldmakefile, ': cannot open (', $!, '), abort';
- print OUT $makefile;
- close OUT
- or croak $self->dest->bldmakefile, ': cannot close (', $!, '), abort';
- print $self->dest->bldmakefile, ': updated', "\n" if $self->verbose;
- # Check for duplicated targets
- # --------------------------------------------------------------------------
- # Get list of types that cannot have duplicated targets
- my @no_duplicated_target_types = split (
- /$DELIMITER_LIST/,
- $self->setting ('BLD_TYPE_NO_DUPLICATED_TARGET'),
- );
- my %targets;
- for my $name (sort keys %{ $self->srcpkg }) {
- next unless $self->srcpkg ($name)->rules;
- for my $key (sort keys %{ $self->srcpkg ($name)->rules }) {
- if (exists $targets{$key}) {
- # Duplicated target: warning for most file types
- my $status = 'WARNING';
- # Duplicated target: error for the following file types
- if (@no_duplicated_target_types and
- $self-srcpkg ($name)->is_type_any (@no_duplicated_target_types) and
- $targets{$key}->is_type_any (@no_duplicated_target_types)) {
- $status = 'ERROR';
- $rc = 0;
- }
- # Report the warning/error
- w_report $status, ': ', $key, ': duplicated targets for building:';
- w_report ' ', $targets{$key}->src;
- w_report ' ', $self->srcpkg ($name)->src;
- } else {
- $targets{$key} = $self->srcpkg ($name);
- }
- }
- }
- }
- return $rc;
- }
- # ------------------------------------------------------------------------------
- # SYNOPSIS
- # $rc = $self->invoke_setup_build ();
- #
- # DESCRIPTION
- # This method invokes the setup_build stage of the build system. It returns
- # true on success.
- # ------------------------------------------------------------------------------
- sub invoke_setup_build {
- my $self = shift;
- my $rc = 1;
- # Extract archived sub-directories if necessary
- $rc = $self->dest->dearchive if $rc;
- # Compare cache
- $rc = $self->compare_setting (METHOD_LIST => [
- 'compare_setting_target', # targets
- 'compare_setting_srcpkg', # source package type
- @compare_setting_methods,
- ]) if $rc;
- # Set up runtime dependency scan patterns
- my %dep_pattern = %{ $self->setting ('BLD_DEP_PATTERN') };
- for my $key (keys %dep_pattern) {
- my $pattern = $dep_pattern{$key};
- while ($pattern =~ /##([\w:]+)##/g) {
- my $match = $1;
- my $val = $self->setting (split (/$Fcm::Config::DELIMITER/, $match));
- last unless defined $val;
- $val =~ s/\./\\./;
- $pattern =~ s/##$match##/$val/;
- }
- $self->setting (['BLD_DEP_PATTERN', $key], $pattern)
- unless $pattern eq $dep_pattern{$key};
- }
- return $rc;
- }
- # ------------------------------------------------------------------------------
- # SYNOPSIS
- # $rc = $self->invoke_system (%args);
- #
- # DESCRIPTION
- # This method invokes the build system. It returns true on success. See also
- # the header for invoke_make for further information on arguments.
- #
- # ARGUMENTS
- # STAGE - If set, it should be an integer number or a recognised keyword or
- # abbreviation. If set, the build is performed up to the named stage.
- # If not set, the default is to perform all stages of the build.
- # Allowed values are:
- # 1, setup or s
- # 2, pre_process or pp
- # 3, generate_dependency or gd
- # 4, generate_interface or gi
- # 5, all, a, make or m
- # ------------------------------------------------------------------------------
- sub invoke_system {
- my $self = shift;
- my %args = @_;
- # Parse arguments
- # ----------------------------------------------------------------------------
- # Default: run all 5 stages
- my $stage = (exists $args{STAGE} and $args{STAGE}) ? $args{STAGE} : 5;
- # Resolve named stages
- if ($stage !~ /^\d$/) {
- my %stagenames = (
- 'S(?:ETUP)?' => 1,
- 'P(?:RE)?_?P(?:ROCESS)?' => 2,
- 'G(?:ENERATE)?_?D(?:ENPENDENCY)?' => 3,
- 'G(?:ENERATE)?_?I(?:NTERFACE)?' => 4,
- '(?:A(?:LL)|M(?:AKE)?)' => 5,
- );
- # Does it match a recognised stage?
- for my $name (keys %stagenames) {
- next unless $stage =~ /$name/i;
- $stage = $stagenames{$name};
- last;
- }
- # Specified stage name not recognised, default to 5
- if ($stage !~ /^\d$/) {
- w_report 'WARNING: ', $stage, ': invalid build stage, default to 5.';
- $stage = 5;
- }
- }
- # Run the method associated with each stage
- # ----------------------------------------------------------------------------
- my $rc = 1;
- my @stages = (
- ['Setup build' , 'invoke_setup_build'],
- ['Pre-process' , 'invoke_pre_process'],
- ['Scan dependency' , 'invoke_scan_dependency'],
- ['Generate Fortran interface', 'invoke_fortran_interface_generator'],
- ['Make' , 'invoke_make'],
- );
- for my $i (1 .. 5) {
- last if (not $rc) or $i > $stage;
- my ($name, $method) = @{ $stages[$i - 1] };
- $rc = $self->invoke_stage ($name, $method, %args) if $rc and $stage >= $i;
- }
- return $rc;
- }
- # ------------------------------------------------------------------------------
- # SYNOPSIS
- # $rc = $self->parse_cfg_dep (\@cfg_lines);
- #
- # DESCRIPTION
- # This method parses the dependency settings in the @cfg_lines.
- # ------------------------------------------------------------------------------
- sub parse_cfg_dep {
- my ($self, $cfg_lines) = @_;
- my $rc = 1;
- # EXCL_DEP, EXE_DEP and BLOCKDATA declarations
- # ----------------------------------------------------------------------------
- for my $name (qw/BLD_BLOCKDATA BLD_DEP BLD_DEP_EXCL BLD_DEP_EXE/) {
- for my $line (grep {$_->slabel_starts_with_cfg ($name)} @$cfg_lines) {
- # Separate label into a list, delimited by double-colon, remove 1st field
- my @flds = $line->slabel_fields;
- shift @flds;
- if ($name =~ /^(?:BLD_DEP|BLD_DEP_EXCL|BLD_DEP_PP)$/) {
- # BLD_DEP_*: label fields may contain sub-package
- my $pk = @flds ? join ('__', @flds) : '';
- # Check whether sub-package is valid
- if ($pk and not ($self->srcpkg ($pk) or $self->dummysrcpkg ($pk))) {
- $line->error ($line->label . ': invalid sub-package in declaration.');
- $rc = 0;
- next;
- }
- # Setting is stored in an array reference
- $self->setting ([$name, $pk], [])
- if not defined $self->setting ($name, $pk);
- # Add current declaration to the array if necessary
- my $list = $self->setting ($name, $pk);
- my $value = $name eq 'BLD_DEP_EXCL' ? uc ($line->value) : $line->value;
- push @$list, $value if not grep {$_ eq $value} @$list;
- } else {
- # EXE_DEP and BLOCKDATA: label field may be an executable target
- my $target = @flds ? $flds[0] : '';
- # The value contains a list of objects and/or sub-package names
- my @deps = split /\s+/, $line->value;
- if (not @deps) {
- if ($name eq 'BLD_BLOCKDATA') {
- # The objects containing a BLOCKDATA program unit must be declared
- $line->error ($line->label . ': value not set.');
- $rc = 0;
- next;
- } else {
- # If $value is a null string, target(s) depends on all objects
- push @deps, '';
- }
- }
- for my $dep (@deps) {
- $dep =~ s/$Fcm::Config::DELIMITER_PATTERN/__/g;
- }
- $self->setting ([$name, $target], join (' ', sort @deps));
- }
- $line->parsed (1);
- }
- }
- return $rc;
- }
- # ------------------------------------------------------------------------------
- # SYNOPSIS
- # $rc = $self->parse_cfg_dest (\@cfg_lines);
- #
- # DESCRIPTION
- # This method parses the build destination settings in the @cfg_lines.
- # ------------------------------------------------------------------------------
- sub parse_cfg_dest {
- my ($self, $cfg_lines) = @_;
- my $rc = $self->SUPER::parse_cfg_dest ($cfg_lines);
- # Set up search paths
- for my $name (@Fcm::Dest::paths) {
- (my $label = uc ($name)) =~ s/PATH//;
- $self->setting (['PATH', $label], $self->dest->$name);
- }
- return $rc;
- }
- # ------------------------------------------------------------------------------
- # SYNOPSIS
- # $rc = $self->parse_cfg_misc (\@cfg_lines);
- #
- # DESCRIPTION
- # This method parses misc build settings in the @cfg_lines.
- # ------------------------------------------------------------------------------
- sub parse_cfg_misc {
- my ($self, $cfg_lines_ref) = @_;
- my $rc = 1;
- my %item_of = (
- BLD_DEP_N => [\&_parse_cfg_misc_dep_n , 1 ], # boolean
- BLD_EXE_NAME => [\&_parse_cfg_misc_exe_name ],
- BLD_LIB => [\&_parse_cfg_misc_dep_n ],
- BLD_PP => [\&_parse_cfg_misc_dep_n , 1 ], # boolean
- BLD_TYPE => [\&_parse_cfg_misc_dep_n ],
- INFILE_EXT => [\&_parse_cfg_misc_file_ext, 0, 1], # uc($value)
- OUTFILE_EXT => [\&_parse_cfg_misc_file_ext, 1, 0], # uc($ns)
- );
- while (my ($key, $item) = each(%item_of)) {
- my ($handler, @extra_arguments) = @{$item};
- for my $line (@{$cfg_lines_ref}) {
- if ($line->slabel_starts_with_cfg($key)) {
- if ($handler->($self, $key, $line, @extra_arguments)) {
- $line->parsed(1);
- }
- else {
- $rc = 0;
- }
- }
- }
- }
- return $rc;
- }
- # ------------------------------------------------------------------------------
- # parse_cfg_misc: handler of BLD_EXE_NAME or similar.
- sub _parse_cfg_misc_exe_name {
- my ($self, $key, $line) = @_;
- my ($prefix, $name, @fields) = $line->slabel_fields();
- if (!$name || @fields) {
- $line->error(sprintf('%s: expects a single label name field.', $key));
- return 0;
- }
- $self->setting([$key, $name], $line->value());
- return 1;
- }
- # ------------------------------------------------------------------------------
- # parse_cfg_misc: handler of BLD_DEP_N or similar.
- sub _parse_cfg_misc_dep_n {
- my ($self, $key, $line, $value_is_boolean) = @_;
- my ($prefix, @fields) = $line->slabel_fields();
- my $ns = @fields ? join(q{__}, @fields) : q{};
- if ($ns && !$self->srcpkg($ns) && !$self->dummysrcpkg($ns)) {
- $line->error($line->label() . ': invalid sub-package in declaration.');
- return 0;
- }
- my @srcpkgs
- = $self->dummysrcpkg($ns) ? @{$self->dummysrcpkg($ns)->children()}
- : $self->srcpkg($ns)
- ;
- my $value = $value_is_boolean ? $line->bvalue() : $line->value();
- for my $srcpkg (@srcpkgs) {
- $self->setting([$key, $srcpkg->pkgname()], $value);
- }
- return 1;
- }
- # ------------------------------------------------------------------------------
- # parse_cfg_misc: handler of INFILE_EXT/OUTFILE_EXT or similar.
- sub _parse_cfg_misc_file_ext {
- my ($self, $key, $line, $ns_in_uc, $value_in_uc) = @_;
- my ($prefix, $ns) = $line->slabel_fields();
- my $value = $value_in_uc ? uc($line->value()) : $line->value();
- $self->setting([$key, ($ns_in_uc ? uc($ns) : $ns)], $value);
- return 1;
- }
- # ------------------------------------------------------------------------------
- # SYNOPSIS
- # $rc = $self->parse_cfg_source (\@cfg_lines);
- #
- # DESCRIPTION
- # This method parses the source package settings in the @cfg_lines.
- # ------------------------------------------------------------------------------
- sub parse_cfg_source {
- my ($self, $cfg_lines) = @_;
- my $rc = 1;
- my %src = ();
- # Automatic source directory search?
- # ----------------------------------------------------------------------------
- my $search = 1;
- for my $line (grep {$_->slabel_starts_with_cfg ('SEARCH_SRC')} @$cfg_lines) {
- $search = $line->bvalue;
- $line->parsed (1);
- }
- # Search src/ sub-directory if necessary
- %src = %{ $self->dest->get_source_files } if $search;
- # SRC declarations
- # ----------------------------------------------------------------------------
- for my $line (grep {$_->slabel_starts_with_cfg ('FILE')} @$cfg_lines) {
- # Expand ~ notation and path relative to srcdir of destination
- my $value = $line->value;
- $value = File::Spec->rel2abs (&expand_tilde ($value), $self->dest->srcdir);
- if (not -r $value) {
- $line->error ($value . ': source does not exist or is not readable.');
- next;
- }
- # Package name
- my @names = $line->slabel_fields;
- shift @names;
- # If package name not set, determine using the path if possible
- if (not @names) {
- my $package = $self->dest->get_pkgname_of_path ($value);
- @names = @$package if defined $package;
- }
- if (not @names) {
- $line->error ($self->cfglabel ('FILE') .
- ': package not specified/cannot be determined.');
- next;
- }
- $src{join ('__', @names)} = $value;
- $line->parsed (1);
- }
- # For directories, get non-recursive file listing, and add to %src
- # ----------------------------------------------------------------------------
- for my $key (keys %src) {
- next unless -d $src{$key};
- opendir DIR, $src{$key} or die $src{$key}, ': cannot read directory';
- while (my $base = readdir 'DIR') {
- next if $base =~ /^\./;
- my $file = File::Spec->catfile ($src{$key}, $base);
- next unless -f $file and -r $file;
- my $name = join ('__', ($key, $base));
- $src{$name} = $file unless exists $src{$name};
- }
- closedir DIR;
- delete $src{$key};
- }
- # Set up source packages
- # ----------------------------------------------------------------------------
- my %pkg = ();
- for my $name (keys %src) {
- $pkg{$name} = Fcm::BuildSrc->new (PKGNAME => $name, SRC => $src{$name});
- }
- # INHERIT::SRC declarations
- # ----------------------------------------------------------------------------
- my %can_inherit = ();
- for my $line (
- grep {$_->slabel_starts_with_cfg(qw/INHERIT FILE/)} @{$cfg_lines}
- ) {
- my ($key1, $key2, @ns) = $line->slabel_fields();
- $can_inherit{join('__', @ns)} = $line->bvalue();
- $line->parsed(1);
- }
- # Inherit packages, if it is OK to do so
- for my $inherited_build (reverse(@{$self->inherit()})) {
- SRCPKG:
- while (my ($key, $srcpkg) = each(%{$inherited_build->srcpkg()})) {
- if (exists($pkg{$key}) || !$srcpkg->src()) {
- next SRCPKG;
- }
- my $known_key = first {exists($can_inherit{$_})} @{$srcpkg->pkgnames()};
- if (defined($known_key) && !$can_inherit{$known_key}) {
- next SRCPKG;
- }
- $pkg{$key} = $srcpkg;
- }
- }
- # Get list of intermediate "packages"
- # ----------------------------------------------------------------------------
- for my $name (keys %pkg) {
- # Name of current package
- my @names = split /__/, $name;
- my $cur = $name;
- while ($cur) {
- # Name of parent package
- pop @names;
- my $parent = @names ? join ('__', @names) : '';
- # If parent package does not exist, create it
- $pkg{$parent} = Fcm::BuildSrc->new (PKGNAME => $parent)
- unless exists $pkg{$parent};
- # Current package is a child of the parent package
- push @{ $pkg{$parent}->children }, $pkg{$cur}
- unless grep {$_->pkgname eq $cur} @{ $pkg{$parent}->children };
- # Go up a package
- $cur = $parent;
- }
- }
- $self->srcpkg (\%pkg);
- # Dummy: e.g. "foo/bar/baz.egg" belongs to the "foo/bar/baz" dummy.
- # ----------------------------------------------------------------------------
- for my $name (keys %pkg) {
- (my $dname = $name) =~ s/\.\w+$//;
- next if $dname eq $name;
- next if $self->srcpkg ($dname);
- $self->dummysrcpkg ($dname, Fcm::BuildSrc->new (PKGNAME => $dname))
- unless $self->dummysrcpkg ($dname);
- push @{ $self->dummysrcpkg ($dname)->children }, $pkg{$name};
- }
- # Make sure a package is defined
- # ----------------------------------------------------------------------------
- if (not %{$self->srcpkg}) {
- w_report 'ERROR: ', $self->cfg->actual_src, ': no source file to build.';
- $rc = 0;
- }
- return $rc;
- }
- # ------------------------------------------------------------------------------
- # SYNOPSIS
- # $rc = $self->parse_cfg_target (\@cfg_lines);
- #
- # DESCRIPTION
- # This method parses the target settings in the @cfg_lines.
- # ------------------------------------------------------------------------------
- sub parse_cfg_target {
- my ($self, $cfg_lines) = @_;
- # NAME declaraions
- # ----------------------------------------------------------------------------
- for my $line (grep {$_->slabel_starts_with_cfg ('NAME')} @$cfg_lines) {
- $self->name ($line->value);
- $line->parsed (1);
- }
- # TARGET declarations
- # ----------------------------------------------------------------------------
- for my $line (grep {$_->slabel_starts_with_cfg ('TARGET')} @$cfg_lines) {
- # Value is a space delimited list
- push @{ $self->target }, split (/\s+/, $line->value);
- $line->parsed (1);
- }
- # INHERIT::TARGET declarations
- # ----------------------------------------------------------------------------
- # By default, do not inherit target
- my $inherit_flag = 0;
- for (grep {$_->slabel_starts_with_cfg (qw/INHERIT TARGET/)} @$cfg_lines) {
- $inherit_flag = $_->bvalue;
- $_->parsed (1);
- }
- # Inherit targets from inherited build, if $inherit_flag is set to true
- # ----------------------------------------------------------------------------
- if ($inherit_flag) {
- for my $use (reverse @{ $self->inherit }) {
- unshift @{ $self->target }, @{ $use->target };
- }
- }
- return 1;
- }
- # ------------------------------------------------------------------------------
- # SYNOPSIS
- # $rc = $self->parse_cfg_tool (\@cfg_lines);
- #
- # DESCRIPTION
- # This method parses the tool settings in the @cfg_lines.
- # ------------------------------------------------------------------------------
- sub parse_cfg_tool {
- my ($self, $cfg_lines) = @_;
- my $rc = 1;
- my %tools = %{ $self->setting ('TOOL') };
- my @package_tools = split(/$DELIMITER_LIST/, $self->setting('TOOL_PACKAGE'));
- # TOOL declaration
- # ----------------------------------------------------------------------------
- for my $line (grep {$_->slabel_starts_with_cfg ('TOOL')} @$cfg_lines) {
- # Separate label into a list, delimited by double-colon, remove TOOL
- my @flds = $line->slabel_fields;
- shift @flds;
- # Check that there is a field after TOOL
- if (not @flds) {
- $line->error ('TOOL: not followed by a valid label.');
- $rc = 0;
- next;
- }
- # The first field is the tool iteself, identified in uppercase
- $flds[0] = uc ($flds[0]);
- # Check that the tool is recognised
- if (not exists $tools{$flds[0]}) {
- $line->error ($flds[0] . ': not a valid TOOL.');
- $rc = 0;
- next;
- }
- # Check sub-package declaration
- if (@flds > 1 and not grep {$_ eq $flds[0]} @package_tools) {
- $line->error ($flds[0] . ': sub-package not accepted with this TOOL.');
- $rc = 0;
- next;
- }
- # Name of declared package
- my $pk = join ('__', @flds[1 .. $#flds]);
- # Check whether package exists
- if (not ($self->srcpkg ($pk) or $self->dummysrcpkg ($pk))) {
- $line->error ($line->label . ': invalid sub-package in declaration.');
- $rc = 0;
- next;
- }
- $self->setting (['TOOL', join ('__', @flds)], $line->value);
- $line->parsed (1);
- }
- return $rc;
- }
- # ------------------------------------------------------------------------------
- # SYNOPSIS
- # $string = $self->_write_makefile_perl5lib ();
- #
- # DESCRIPTION
- # This method returns a makefile $string for defining $PERL5LIB.
- # ------------------------------------------------------------------------------
- sub _write_makefile_perl5lib {
- my $self = shift;
- my $classpath = File::Spec->catfile (split (/::/, ref ($self))) . '.pm';
- my $libdir = dirname (dirname ($INC{$classpath}));
- my @libpath = split (/:/, (exists $ENV{PERL5LIB} ? $ENV{PERL5LIB} : ''));
- my $string = ((grep {$_ eq $libdir} @libpath)
- ? ''
- : 'export PERL5LIB := ' . $libdir .
- (exists $ENV{PERL5LIB} ? ':$(PERL5LIB)' : '') . "\n\n");
- return $string;
- }
- # ------------------------------------------------------------------------------
- # SYNOPSIS
- # $string = $self->_write_makefile_target ();
- #
- # DESCRIPTION
- # This method returns a makefile $string for defining the default targets.
- # ------------------------------------------------------------------------------
- sub _write_makefile_target {
- my $self = shift;
- # Targets of the build
- # ----------------------------------------------------------------------------
- my @targets = @{ $self->target };
- if (not @targets) {
- # Build targets not specified by user, default to building all main programs
- my @programs = ();
- # Get all main programs from all packages
- for my $pkg (values %{ $self->srcpkg }) {
- push @programs, $pkg->exebase if $pkg->exebase;
- }
- @programs = sort (@programs);
- if (@programs) {
- # Build main programs, if there are any
- @targets = @programs;
- } else {
- # No main program in source tree, build the default library
- @targets = ($self->srcpkg ('')->libbase);
- }
- }
- my $return = 'FCM_BLD_TARGETS = ' . join (' ', @targets) . "\n\n";
- # Default targets
- $return .= '.PHONY : all' . "\n\n";
- $return .= 'all : $(FCM_BLD_TARGETS)' . "\n\n";
- # Targets for copy dummy
- $return .= sprintf("%s:\n\ttouch \$@\n\n", $self->setting(qw/BLD_CPDUMMY/));
- return $return;
- }
- # ------------------------------------------------------------------------------
- # SYNOPSIS
- # $string = $self->_write_makefile_tool ();
- #
- # DESCRIPTION
- # This method returns a makefile $string for defining the build tools.
- # ------------------------------------------------------------------------------
- sub _write_makefile_tool {
- my $self = shift;
- # List of build tools
- my $tool = $self->setting ('TOOL');
- # List of tools local to FCM, (will not be exported)
- my %localtool = map {($_, 1)} split ( # map into a hash table
- /$DELIMITER_LIST/, $self->setting ('TOOL_LOCAL'),
- );
- # Export required tools
- my $count = 0;
- my $return = '';
- for my $name (sort keys %$tool) {
- # Ignore local tools
- next if exists $localtool{(split (/__/, $name))[0]};
- if ($name =~ /^\w+$/) {
- # Tools with normal name, just export it as an environment variable
- $return .= 'export ' . $name . ' = ' . $tool->{$name} . "\n";
- } else {
- # Tools with unusual characters, export using a label/value pair
- $return .= 'export FCM_UNUSUAL_TOOL_LABEL' . $count . ' = ' . $name . "\n";
- $return .= 'export FCM_UNUSUAL_TOOL_VALUE' . $count . ' = ' .
- $tool->{$name} . "\n";
- $count++;
- }
- }
- $return .= "\n";
- return $return;
- }
- # ------------------------------------------------------------------------------
- # SYNOPSIS
- # $string = $self->_write_makefile_vpath ();
- #
- # DESCRIPTION
- # This method returns a makefile $string for defining vpath directives.
- # ------------------------------------------------------------------------------
- sub _write_makefile_vpath {
- my $self = shift();
- my $FMT = 'vpath %%%s $(FCM_%sPATH)';
- my %SETTING_OF = %{$self->setting('BLD_VPATH')};
- my %EXT_OF = %{$self->setting('OUTFILE_EXT')};
- # Note: each setting can be either an empty string or a comma-separated list
- # of output file extension keys.
- join(
- "\n",
- (
- map
- {
- my $key = $_;
- my @types = split(qr{$DELIMITER_LIST}msx, $SETTING_OF{$key});
- @types ? (map {sprintf($FMT, $EXT_OF{$_}, $key)} sort @types)
- : sprintf($FMT, q{}, $key)
- ;
- }
- sort keys(%SETTING_OF)
- ),
- ) . "\n\n";
- }
- # Wraps chdir. Returns the old working directory.
- sub _chdir {
- my ($self, $path) = @_;
- if ($self->verbose() >= 3) {
- printf("cd %s\n", $path);
- }
- my $old_cwd = cwd();
- chdir($path) || croak(sprintf("%s: cannot change directory ($!)\n", $path));
- $old_cwd;
- }
- # ------------------------------------------------------------------------------
- 1;
- __END__
|