#!/bin/env perl
use FindBin;
use lib $FindBin::Bin;
use strict;
use warnings FATAL => 'all';
use IO::Handle;
use File::Basename;
use File::Temp;
use List::MoreUtils qw(uniq);
use Data::Dumper qw(Dumper);
use _kxLab;
#
# Generate $(HARDWARE).pkglist file for current directory
#
# usage:
# $0 format topdir toolchain hardware [flavour]
#
# where:
# 'format' - the output format: list or tree
# 'topdir' - is a absolute path to the top directory of checked out branch
# 'toolchain' - is a TOOLCHAIN name
# 'hardware' - is a HARDWARE variant
# 'flavour' - is a FLAVOUR variant
#
# global variables
my ($build_system);
my ($format, $topdir, $toolchain, $hardware, $flavour);
my ($target_build_dir, $srclist_file);
my ($system_version, $distro_name, $distro_version, $url);
my @tarballs = ();
sub usage
{
print <<EOF;
Usage: $0 format topdir toolchain hardware [flavour]
Where:
format - the output format: list or tree
topdir - is a absolute path to the top of checked out branch;
toolchain - is a TOOLCHAIN name;
hardware - is a HARDWARE variant;
flavour - is a FLAVOUR variant.
EOF
exit;
}
#
# Getting information from build-system/constants.mk
#
sub system_version
{
my $build_system = shift;
my $version;
open( FILE, "< $build_system/constants.mk" );
while( <FILE> )
{
if( /^SYSTEM_VERSION(.+= +)(.+)/ )
{
$version = $2;
}
}
close( FILE );
return $version;
}
sub distro_name
{
my $build_system = shift;
my $name;
open( FILE, "< $build_system/constants.mk" );
while( <FILE> )
{
if( /^DISTRO_NAME(.+= +)(.+)/ )
{
$name = $2;
}
}
close( FILE );
return $name;
}
sub distro_version
{
my $build_system = shift;
my $name;
open( FILE, "< $build_system/constants.mk" );
while( <FILE> )
{
if( /^DISTRO_VERSION(.+= +)(.+)/ )
{
$name = $2;
}
}
close( FILE );
return $name;
}
sub bug_url
{
my $build_system = shift;
my $url;
open( FILE, "< $build_system/constants.mk" );
while( <FILE> )
{
if( /^BUG_URL(.+= +)(.+)/ )
{
$url = $2;
}
}
close( FILE );
return $url;
}
#
# value_from_makefile( $makefile, $variable_name, $stop_line ):
# ------------------------------------------------------------
# This function returns the value of variable defined in the
# Makefile on global level before od REQUIRES declaration.
#
# The optional $stop_line argument presents the start of the
# line which terminates the analized part of Makefile.
#
sub value_from_makefile
{
my $makefile = shift;
my $vname = shift;
my $stop_line = shift;
my $value = "";
if( !defined $stop_line || $stop_line eq '' )
{
$stop_line = "__END_OF_REQUIRES__";
}
else
{
$stop_line = "^" . $stop_line;
}
my $cdir = dirname( $makefile );
my $shell_output = <<`SHELL`;
cd $cdir
head -n `cat Makefile | grep -n "$stop_line" | cut -f 1 -d ':'` Makefile | \
make TOOLCHAIN=$toolchain HARDWARE=$hardware FLAVOUR=$flavour -f - -p __build_requires__ 2>/dev/null | grep "$vname"
exit 0
SHELL
if( $shell_output =~ m/^$vname(.+= +)(.+)/gm )
{
$value = $2;
}
return $value;
}
#
# Getting information from Makefile:
#
sub pkg_group
{
my $makefile = shift;
my $group;
open( FILE, '<', $makefile );
while( <FILE> )
{
if( /^PKG_GROUP(.+= +)(.+)/ )
{
$group = $2;
}
}
close( FILE );
return $group;
}
sub pkg_name
{
my $makefile = shift;
my $name = "";
open( FILE, '<', $makefile );
while( <FILE> )
{
if( /^[A-Z_0-9]*_PKG_NAME(.+= +)(.+)/ )
{
$name = $2;
}
}
close( FILE );
return $name;
}
sub pkg_version
{
my $makefile = shift;
my $version;
my $stop_line;
open( FILE, '<', $makefile );
while( <FILE> )
{
if( /^([A-Z_0-9]*_PKG_VERSION)(.+= +)(.+)/ )
{
$stop_line = $1;
$version = $3;
}
}
close( FILE );
# check if version is present as a reference to some variable:
if( $version =~ m/^\$\((.+)\)/ )
{
my $vname = $1;
# get value of referenced variable
$version = value_from_makefile( $makefile, $vname, $stop_line );
}
return $version;
}
#
# Getting information from .requires and .dist files:
#
sub built_gcc_libs
{
my $makefile = shift;
my $stop_line;
my $vname = $hardware;
my $libs;
$vname =~ tr/a-z-/A-Z_/;
$vname .= "_USE_BUILT_GCC_LIBS";
$libs = value_from_makefile( $makefile, $vname, $stop_line );
return $libs;
}
sub get_root
{
my $requires = $target_build_dir . "/.requires";
my $root = "";
if( -f $requires )
{
open( FILE, '<', $requires ) or
_kxLab::error( "$0: Could not open $requires file: $!" );
while( <FILE> )
{
if( /^# ROOT(=)(.+)/ )
{
$root = $2;
}
}
close FILE;
}
return $root;
}
sub get_deps
{
my $dir = shift;
my $requires = $topdir . "/" . $dir . "/" . $target_build_dir . "/.requires";
my @deps = ();
if( -f $requires )
{
open( FILE, '<', $requires ) or
_kxLab::error( "$0: Could not open $requires file: $!" );
while( <FILE> )
{
if( /all(: +)(.+)/ )
{
@deps = split( ' ', $2 );
}
}
close FILE;
}
return @deps;
}
sub get_tarball
{
my $dir = shift;
my $package = "";
my $proddir = $topdir . "/dist/products/" . $toolchain . "/" . $hardware;
my $dist = $topdir . "/" . $dir . "/" . $target_build_dir . "/.dist";
my $patern;
if( $flavour eq "" ) {
$patern = "^products/" . $toolchain . "/" . $hardware . "/(.+\.t?z)";
} else {
$patern = "^products/" . $toolchain . "/" . $hardware . "/" . $flavour . "/(.+\.t?z)";
}
if( -f $dist )
{
open( FILE, '<', $dist ) or
_kxLab::error( "$0: Could not open $dist file: $!" );
foreach my $line ( <FILE> )
{
if( $line =~ /$patern/ )
{
# Existing packages only:
if( -f $proddir . "/" . $1 ) { $package = $1; }
}
}
close FILE;
}
return $package;
}
sub get_tarballs
{
my $dir = shift;
my @deps = get_deps( $dir );
foreach ( @deps )
{
my $tarball = get_tarball( $_ );
if( defined $tarball && $tarball ne "" )
{
push @tarballs, $tarball;
}
get_tarballs( $_ );
}
}
sub print_result
{
my $format = shift;
my $out_string = "";
if( $format eq "list" ) {
$out_string = sprintf( "####### Packages Install List: done\n" );
} else {
$out_string = sprintf( "####### Required Packages Tree: done\n" );
}
print $out_string;
}
#
# Parse the command line options
#
# Get the rest arguments of the command line
$format = shift;
$topdir = shift;
$toolchain = shift;
$hardware = shift;
$flavour = shift;
$format =~ tr/A-Z/a-z/;
my $cmd = "";
my $srclist_suffix = "";
my $makefile = "Makefile";
if( ! defined $format or $format eq "" ) { usage; }
if( ! defined $topdir or $topdir eq "" ) { usage; }
if( ! defined $toolchain or $toolchain eq "" ) { usage; }
if( ! defined $hardware or $hardware eq "" ) { usage; }
if( ! defined $flavour or $flavour eq "" ) { $flavour = ""; }
_kxLab::error( "$0: $topdir is not a directory" ) if( ! -d $topdir );
_kxLab::error( "$0: Makefile missing: $makefile" ) if ( ! -f $makefile );
$build_system = $topdir . "/build-system";
$system_version = system_version( $build_system );
$distro_name = distro_name( $build_system );
$distro_version = distro_version( $build_system );
$url = bug_url( $build_system );
if( $flavour eq "" ) {
$target_build_dir = "." . $toolchain . "/" . $hardware;
} else {
$target_build_dir = "." . $toolchain . "/" . $hardware . "/" . $flavour;
}
if( $format eq "list" ) {
$srclist_suffix = ".srclist";
} else {
$srclist_suffix = ".srctree";
}
if( $flavour eq "" ) {
$srclist_file = $target_build_dir . "/." . $hardware . $srclist_suffix;
} else {
$srclist_file = $target_build_dir . "/." . $hardware . "-" . $flavour . $srclist_suffix;
}
# open the output file:
open( SRCLIST_FILE, '>', $srclist_file ) or
_kxLab::error( "$0: Could not open $srclist_file file: $!" );
my $exclude = "";
if( pkg_name( $makefile ) ne "init-devices" ) {
$exclude = " -e init-devices";
if( built_gcc_libs( $makefile ) eq "yes" && pkg_name( $makefile ) ne "gcc-runtime" ) {
$exclude .= ",gcc-runtime";
}
}
my $tree_format = " -t bin";
my $root = get_root();
my $rpkg = get_tarball( $root );
get_tarballs( $root );
@tarballs = uniq( @tarballs );
my $tmpdir = $build_system . "/var/tmp";
my $srcdir = $topdir . "/dist/products/" . $toolchain . "/" . $hardware;
my $head = $hardware;
if( defined $rpkg && $rpkg ne "" ) {
$head = pkg_group( $makefile ) . "/" . pkg_name( $makefile ) . "-" . pkg_version( $makefile );
print SRCLIST_FILE $rpkg . "\n";
}
foreach ( @tarballs ) {
print SRCLIST_FILE $_ . "\n";
}
if( $format eq "list" ) {
$cmd = "TMP=" . $tmpdir . " " . $build_system . "/sbin/make-pkglist" .
" -w " . $hardware . " -H " . $head . $exclude .
" -i pkg -o list -s " . $srcdir .
" -F " . $srclist_file . " " . $target_build_dir . "/";
} else {
$cmd = "TMP=" . $tmpdir . " " . $build_system . "/sbin/make-pkglist" .
" -w " . $hardware . " -H " . $head . $exclude .
" -i pkg" . $tree_format . " -o json -m -s " . $srcdir .
" -F " . $srclist_file . " " . $target_build_dir . "/";
}
# close output file:
close SRCLIST_FILE;
_kxLab::system( "$cmd" );
#
# Copy Packages List to the TARGET_BUILD_DIR (for release compatibility):
#
if( $format eq "list" && $head ne $hardware ) {
$cmd = "cp -a " . $target_build_dir . "/" . $head . ".pkglist " . $target_build_dir . "/" . $hardware . ".pkglist";
_kxLab::system( "$cmd" );
}
print "#######\n";
print_result( $format );
print "#######\n";