Subversion Repositories DevTools

Rev

Rev 1129 | Rev 5649 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

########################################################################
# Copyright (C) 2007 ERG Limited, All rights reserved
#
# Module name   : jats.sh
# Module type   : Makefile system
# Compiler(s)   : n/a
# Environment(s): jats
#
# Description   : This package extends the JATS toolset at build time
#                 It provides additional directives to the JATS makefiles
#                 to simplify the directives.
#
#                 This directive does all its work at 'build' time
#                 It uses makefile.pl directives
#
# Operation     : This package adds the JATS directive ManifestFiles
#                 This is used to create linux manifest files
#
# Syntax        : ManifestFiles (<platforms>, Options+);
#                 See the function header for details
#
#......................................................................#

require 5.6.1;
use strict;
use warnings;

#
#   Globals
#
my @Manifests;                      # Manifest entries
my $Manifest_has_version = 1;       # Create Manifest_xxxx by default
my %package_dirs;                   # Package dirs discovered and used
my $pkg_subdir;                     # Alternate packaging

#-------------------------------------------------------------------------------
# Function        : BEGIN
#
# Description     : Setup directive hooks
#                   Register a function to be called just before we start to
#                   generate makefiles. This will be used to process the data
#                   collected in all the ManifestFiles directives
#
# Inputs          : None
#
# Returns         : None
#
BEGIN
{
    RegisterMakefileGenerate (\&ManifestFiles_Generate);
}

#-------------------------------------------------------------------------------
# Function        : ManifestFiles
#
# Description     : Create a ManifestFiles entry
#
# Inputs          : Platform             - Active Platform Selector
#                   Options              - One or more options
#                       --Name=Name             - Mandatory
#                       --Tier=Name             - Mandatory
#                       --Architecture=xxxx     - Default actitecture is the current target
#                       --Product=yyyy          - Product Family
#                       --Debian=BaseName[,--Prod,--Debug,--Arch=xxxx,--Product=yyyy]
#                       --MugPackage=Package[,--Subdir=subdir[,subdir]+]
#                       --SrcFile=xxx
#                       --SrcFileNoCopy=xxx
#                       --Comment=xxx           - Add comment to Manifst
#                       --NoManifestVersion     - Create unversioned Manifest File
#                       --PkgSubdir=xxx         - Specifies packaging subdir
#                       --ImportManifest=Package[,--Subdir=subdir] - Import Manifest from package
#
# Returns         : Nothing
#
sub ManifestFiles
{
    my( $platforms, @elements ) = @_;
    Debug2( "ManifestFiles($platforms, @elements)" );
    return if ( ! ActivePlatform($platforms) );

    my $name;
    my $tier;
    my @files;
    my $mug_dir;
    my $default_arch = $::ScmPlatform;
    my $default_prod = '';
    my $imported_manifest = 0;

    #
    #   Collect user options
    #
    foreach ( @elements )
    {
        if ( m~^--Name=(.+)~ ) {
            if ( $name )
            {
                ReportError ("ManifestFiles:--Name option is only allowed once");
                next;
            }
            $name = $1;

        } elsif ( m~^--Tier=(.+)~ ) {
            if ( $tier )
            {
                ReportError ("ManifestFiles:--Tier option is only allowed once");
                next;
            }
            $tier = $1;

        } elsif ( m~^--Comment=~ ) {
            my $cmt = $_;
            $cmt =~ s~.+=~~;
            $cmt =~ s~\s*\n\s*~\n~g;
            push @files, {'cmt' => $cmt };

        } elsif ( m~^--Debian=(.+)~ ) {
            push @files, {'file' => LocateDebianFile($1, $default_arch, $default_prod)};

        } elsif ( m~^--SrcFile=(.+)~ ) {
            push @files, {'file' => LocatePreReq($1)};
            
        } elsif ( m~^--SrcFileNoCopy=(.+)~ ) {
            push @files, {'filenocopy' => $1};
            
        } elsif ( m~^--MugPackage=(.+)~ ) {
            if ( $mug_dir )
            {
                ReportError ("ManifestFiles:--MugPackage option is only allowed once");
                next;
            }    
            $mug_dir = LocateMugDir($1);

        } elsif ( m/^--Arch(.*)=(.+)/ ) {
            $default_arch = $2;

        } elsif ( m/^--Product=(.+)/ ) {
            $default_prod = $1;

        } elsif ( m/^--NoManifestVersion/i ) {
            $Manifest_has_version = 0;

        } elsif ( m/^--PkgSubdir=(.+)/i ) {
            if ( $pkg_subdir )
            {
                ReportError ("ManifestFiles:--PkgSubdir option is only allowed once");
                next;
            }
            $pkg_subdir = $1;

        } elsif ( m/^--ImportManifest=(.+)/i ) {
            my $import_info = ImportManifest($1);
#DebugDumpData("ImportInfo", $import_info );
            push @files, {'manifest' => $import_info };

            #
            #   Fill in details unless already provided
            #
            $tier = $import_info->{'tier'} unless ( defined $tier );
            $name = $import_info->{'name'} unless ( defined $name );

            $imported_manifest = 1;

        } else {
            ReportError ("ManifestFiles: Unknown option or argument: $_");

        }
    }

    #
    #   Sanity test the user options
    #
    ReportError ("ManifestFiles: No name specified")
        unless $name;
    ReportError ("ManifestFiles: No tier specified")
        unless defined ($tier);
    ReportError ("ManifestFiles: Cannot mix --Debian/--SrcFile with --MugPackage in one directive")
        if ( $mug_dir && (@files || $imported_manifest) );
    ReportError ("ManifestFiles: Must specify files to add to Manifest")
        unless ( $mug_dir ||  @files || $imported_manifest);
    ErrorDoExit();

    #
    #   Save information for processing at the end of the parsing phase
    #   Data collected from ALL the ManifestFiles directives will be collected
    #   and processed into one Manifest file
    #
    my %data;
    $data{tier} = $tier;
    $data{name} = $name;
    $data{files} = \@files;
    $data{mugdir} = $mug_dir;
    $data{pkgsubdir} = $pkg_subdir;
#DebugDumpData("DirectiveData", \%data );

    push @Manifests, \%data;
    return;

    #-------------------------------------------------------------------------------
    # Function        : LocateDebianFile
    #
    # Description     : Locate a debian file
    #                   Internal Function
    #
    #                   Scan packages for the Debian package specified
    #                   The user provides the base name of the package
    #                   A Debian Package name has several fields
    #                   These are:
    #                       1) Base Name - Provided by the user
    #                       2) Version - Version will be wildcarded
    #                       3) Architecture - Wildcarded. Uses bin/arch directory
    #                   
    #                   Expect to find Debian Packages in the bin/PLATFORM subdir
    #
    # Inputs          : Debian base name, complete with suboptions
    #
    # Returns         : Full path of the file
    #
    sub LocateDebianFile
    {
        my ($arg, $arch, $product) = @_;
        Verbose("LocateDebianFile: Processing: $arg");

        my @type = qw( P D );
        my @debian_file_path;
    
        #
        #   Extract sub-options
        #       --Prod[uction]
        #       --Debug
        #       --Arch[itecture]=yyy
        #
        my ($base_name, @opts) = split( ',', $arg );
        foreach ( @opts )
        {
            if ( m/^--Arch(.*)=(.+)/ ) {
                $arch=$2;
            } elsif ( m/^--Product=(.+)/ ) {
                $product=$1;
            } elsif ( m/^--Prod/ ) {
                @type = 'P';
            } elsif ( m/^--Debug/ ) {
                @type = 'D';
            }
        }

        #
        #   Create a list of products
        #   ie: PRODUCT_ARCH
        #
        my @products;
        push @products, $product . '_' . $arch if ( $product );
        push @products, $arch;

        #
        #   Scan all packages for the specified debian package
        #
        foreach my $package_dir ( getPackagePaths ('--All') )
        {
            foreach my $type ( @type )
            {
                foreach my $prd ( @products )
                {
                    foreach my $joiner ( qw(/ .) )
                    {
                        my $dir = "$package_dir/bin$joiner$prd$type";
                        Verbose("ManifestFiles: Search in $dir");
                        next unless ( -d $dir );
                        my @files = glob ( "$dir/${base_name}_*.deb" );
                        next unless ( @files );
                        push @debian_file_path, @files;
                        $package_dirs{$package_dir}{used} = 1;
                    }
                }
            }
        }

        ReportError ("Required Debain package not found: $base_name") unless @debian_file_path;
        ReportError ("Multiple matching Debian Packages located: $base_name", @debian_file_path ) if ( $#debian_file_path > 0 );
        return $debian_file_path[0];
    }

    #-------------------------------------------------------------------------------
    # Function        : LocateMugDir
    #
    # Description     : Locate the directory containing the mugfiles
    #                   Internal Function
    #
    # Inputs          : Mufile package, with embedded options
    #
    # Returns         : Full path
    #
    sub LocateMugDir
    {
        my ($mug_package) = @_;

        #
        #   Locate the mugfile subdir
        #
        my $package_name = $mug_package;
        my @dirs = 'mug';
        my $mug_dir;

        #
        #   Extract sub options
        #       --Subdir=xxxx,yyyy,zzzz
        #
        if ( $package_name =~ m/(.*?),--Subdir=(.*)/ )
        {
            $package_name = $1;
            @dirs = split( ',', $2 );
        }

        my $package = GetPackageEntry( $package_name );
        unless ( $package )
        {
            ReportError ("ManifestFiles: Package not known to build: $package_name");
            return undef;
        }

        foreach my $subdir ( @dirs )
        {
            my $dir = "$package->{'ROOT'}/$subdir";
            if ( -d $dir )
            {
                Warning ("Multiple Mugfile directories located. Only the first will be used",
                         "Ignoring: $subdir" )if ( $mug_dir );
                $mug_dir = $dir;
            }
        }
        ReportError ("Mugfile directory not found in package: $package_name")
            unless $mug_dir;

        return $mug_dir;
    }

    #-------------------------------------------------------------------------------
    # Function        : ImportManifest
    #
    # Description     : Import an existing manifest
    #
    # Inputs          : Args
    #                           PackageName[,Subdir=name]
    #
    # Returns         : A hash of data to be used later
    #
    sub ImportManifest
    {
        my ($args) = @_;
        my @file_contents;
        my @file_list;

        #
        #   Locate the mugfile subdir
        #
        my $package_name = $args;
        my @dirs = 'mug';
        my $pkg_dir;
        my $pkg_root;
        my $manifest;
        my $first_tier;
        my $first_name;

        #
        #   Extract sub options
        #       --Subdir=xxxx,yyyy,zzzz
        #
        if ( $package_name =~ m/(.*?),--Subdir=(.*)/ )
        {
            $package_name = $1;
            @dirs = split( ',', $2 );
        }

        my $package = GetPackageEntry( $package_name );
        unless ( $package )
        {
            ReportError ("ManifestFiles: Package not known to build: $package_name");
            return undef;
        }

        foreach my $subdir ( @dirs )
        {
            my $dir = "$package->{'ROOT'}/$subdir";
            my $root = $package->{'ROOT'};
            if ( -d $dir )
            {
                Warning ("Multiple Package directories located. Only the first will be used",
                         "Ignoring: $subdir" )if ( $pkg_dir );
                $pkg_dir = $dir;
                $pkg_root = $root;
            }
        }
        ReportError ("Package directory not found in package: $package_name")
            unless $pkg_dir;

        #
        #   Determine Manifest File name
        #
        foreach my $file ( glob ($pkg_dir . '/Manifest*' ) )
        {
                next unless ( -f $file );
                Warning ("Multiple Manifest Files find. Only the first will be used",
                         "Using: $manifest",
                         "Ignoring: $file" ) if ( $manifest );
                $manifest = $file;
        }
        ReportError ("ImportManifest. No Manifest found: $package_name")
            unless $manifest;


        #
        #   
        #
        open (MF, '<', $manifest ) || Error ("Cannot open the Manifest file: $manifest", $!);
        while ( <MF> )
        {
            #
            #   Clean traling whitespace ( line-feed and new lines )
            #   Comment out [Version] data
            #
            s~\s+$~~;
            s~(\s*\[Version])~#$1~;
            push @file_contents, $_;

            #
            #   Part lines and determine files
            #
            next unless ( $_ );
            next if ( m~\s*#~ );
            next if ( m~\s*\[~ );
            my( $aname, $atier, $afile) = split(/\s*\,\s*/, $_);
#            print "---------- $_\n";
#            print "T: $atier, N:$aname, F:$afile\n";
            push @file_list, $afile;

            #
            #   Capture first tier and name
            #
            $first_tier = $atier unless ( defined $first_tier );
            $first_name = $aname unless ( defined $first_name );

        }
        close MF;

        #
        #   Create a hash of data that describes the manifest that has
        #   just been read in.
        #
        $package_dirs{$pkg_root}{used} = 1;
        $manifest =~ s~.*/~~;
        return { 'contents' => \@file_contents,
                  'files' => \@file_list,
                  'file_base' => $pkg_dir,
                  'manifest' => $manifest,
                  'pkg_dir' => $pkg_root,
                  'tier' => $first_tier,
                  'name' => $first_name,
                };
    }
}

#-------------------------------------------------------------------------------
# Function        : ManifestFiles_Generate
#
# Description     : Internal Function
#                   Process all the collected data and create directives
#                   for the creation of the manifest
#
#                   This function will be called, just before the Makefile
#                   is created. The function will:
#                       1) Create the Manifest File
#                       2) Package the Manifest File
#                       3) Package the manifest file contents
#
#                   using (mostly) normal makefile.pl directives.
#
# Inputs          : None
#
# Returns         : Nothing
#
sub ManifestFiles_Generate
{
    Debug ("ManifestFiles_Generate");
    Message ("Generating Manifest File");

    #
    #   Need at least one Manifest Entry
    #
    return unless ( @Manifests );
#DebugDumpData ( "Manifests", \@Manifests );

    #
    #   Determine the target packaging directory
    #   Default is .../mug
    #
    my $pkgdir = 'mug';
    if ( exists $Manifests[0]->{pkgsubdir} && defined $Manifests[0]->{pkgsubdir} )
    {
        my $subdir = $Manifests[0]->{pkgsubdir};
        $pkgdir .= '/' . $subdir;
        $pkgdir =~ s~^mug/mug~mug~;
    }

    #
    #   Create the Manifest File as we process the lists
    #   Place this in the 'lib' directory:
    #       - So that it will be deleted on clobber
    #       - So that it can be placed in a target-specific subdir
    #       - So that we can have one per makefile.pl
    #
    Error ("ManifestFiles: Needs local directory specified in build.pl") unless ( $::ScmLocal );

    my $manifest_dir = "$::ScmPlatform.LIB";
    System( "$::GBE_BIN/mkdir -p $manifest_dir" );

    my $manifest_file = $manifest_dir . '/Manifest';
    $manifest_file .= '_' . $::ScmBuildVersion if ( $Manifest_has_version );
    ToolsetGenerate( $manifest_file );
    Verbose ("ManifestFiles_Generate: File: $manifest_file");
    
    PackageFile ('*', $manifest_file, '--Subdir=' . $pkgdir, '--Strip' );

    open (MF, '>', $manifest_file ) || Error ("Cannot create the Manifest file: $manifest_file");

    print_mf ("# PackageName: $::ScmBuildPackage");
    print_mf ("# PackageVersion: $::ScmBuildVersion");
    print_mf ("# BuildDate: $::CurrentTime");
    print_mf ("#");
    print_mf ("[Version],$::ScmBuildVersion");
    print_mf ("#");

    #
    #   Process each tier in the order presented in the source file
    #
    my $last_was_comment = 0;
    foreach my $entry ( @Manifests )
    {
    #print_mf ("#");

#DebugDumpData ( "Manifest Entry", $entry );

        my $tier = $entry->{tier};
        my $name = $entry->{name};

        #
        #   Insert all the files that have been specified
        #   The user specified order is preserved
        #
        #   Entries may be either a file or a comment
        #   Comments: Merge multiple comments and create blocks
        #
        #
        my @files = @{ $entry->{files} };
        foreach my $fentry ( @files )
        {
            if ( my $cmt = $fentry->{'cmt'} )
            {
                print_mf ('') unless ( $last_was_comment ) ;
                print_mf ( map (('# ' . $_) , split ("\n", $cmt) ));
                $last_was_comment = 1;
                next;
            }

            print_mf ('#') if ( $last_was_comment );
            if ( my $file = $fentry->{'file'} )
            {
                my $base_file = StripDir( $file );
                print_mf ("$name,$tier,$base_file");
                PackageFile ('*', $file, '--Subdir=' . $pkgdir, '--Strip' );
                $last_was_comment = 0;
            }

            if ( my $file = $fentry->{'filenocopy'} )
            {
                print_mf ("$name,$tier,$file");
                $last_was_comment = 0;
            }

            if ( my $emf = $fentry->{'manifest'} )
            {
                $last_was_comment = 0;
                #
                #   Insert the entire manifest
                #   Items are:
                #        contents
                #        files
                #        file_base
                #        manifest
                #
#DebugDumpData ( "Embedded Manifest Entry", $emf );
                print_mf ( '## Included Manifest: ' .  $emf->{'manifest'}  );
                print_mf ($_) foreach  ( @{$emf->{'contents'}} );
                PackageFile ('*', $emf->{'file_base'}. '/' . $_, '--Subdir=' . $pkgdir, '--Strip' )foreach  ( @{$emf->{'files'}});;
                print_mf ( '## End Included Manifest' );
            }
        }

        #
        #   Expand out the entire MUG directory
        #   All .mug files in the MUG directory will be added to the manifest
        #   The assumption is that the MUG directory has been created by
        #   something that knows what its doing
        #
        if ( my $mugdir = $entry->{mugdir} )
        {
            foreach my $file ( glob ($mugdir . '/*.mug' ) )
            {
                next unless ( -f $file );
                my $base_file = StripDir($file);
                print_mf ("$name,$tier,$base_file");
                PackageFile ('*', $file, '--Subdir=mug', '--Strip' );
            }
        }
    }

    #
    #   Complete the creation of the Manifest File
    #
    print_mf ("#");
    print_mf ("# End of File");
    close MF;
    ErrorDoExit();

    #
    #   Sanity test of packages that did not provide a debian file
    #   Just a hint that something may have been missed
    #
    my @not_used_packages;
    foreach my $package_dir ( getPackagePaths ('--All') )
    {
        next if ( $package_dir =~ m~/manifest-tool/~ );
        unless ( exists $package_dirs{$package_dir}{used} )
        {
            push @not_used_packages, $package_dir;
        }
    }
    if ( @not_used_packages )
    {
        Warning ("Packages that did not contribute packages to the manifest:",
                  @not_used_packages );
    }

    return;

    #-------------------------------------------------------------------------------
    # Function        : print_mf
    #
    # Description     : Internal Function
    #                   Print one line to the Manifest File
    #                   Checks the length of the line being created
    #
    # Inputs          : $line
    #
    # Returns         : 
    #

    sub print_mf
    {
        foreach  ( @_ )
        {
            ReportError ( "Manifest line too long",
                    "Line: $_" ) if ( length ($_) > 79 );
            print MF $_ . "\n";
        }
    }
    
}

1;