#!/usr/bin/perl
use FindBin;
use lib $FindBin::Bin;
use strict;
use warnings FATAL => 'all';
use IO::Handle;
use File::Basename;
use File::Temp;
use Getopt::Long;
use _kxLab;
#
# Generate .$(HARDWARE)_requires file for current directory
#
# usage:
# $0 [options] topdir toolchain flavour
#
# where:
# 'topdir' - is a absolute path to the top directory of checked out branch
# 'toolchain' - is a TOOLCHAIN name
# 'flavour' - is a HARDWARE variant
#
# global variables
my (%all_requires, $top_dir, $opt_max_depth, %requires_depend, $requires_file, %skip_dirs);
my ($toolchain, $flavour, $target_build_dir);
sub usage
{
print <<EOF;
Usage: build_requires [options] topdir toolchain flavour
Options:
--max-depth=i - where 'i' is a maximal directory depth for finding requires;
--skip-dir - directory to be skipped (such as dist or TARGET_BUILD_DIR);
topdir - is a absolute path to the top of checked out branch;
toolchain - is a TOOLCHAIN name;
flavour - is a HARDWARE variant.
EOF
exit;
}
sub requires_depend
{
my $makefile = shift;
if( ! exists $requires_depend{$makefile} )
{
print REQUIRES_DEPEND_FILE "$requires_file: $makefile\n\n";
print REQUIRES_DEPEND_FILE "$makefile:\n\n";
$requires_depend{$makefile} = "";
}
}
sub read_requires
{
my $makefile = shift;
my $flavour = shift;
# Add a dependency to the Makefile
requires_depend( $makefile );
my $cdir = dirname( $makefile );
my %requires;
#
# We read the head of Makefile until '__END_OF_REQUIRES__' keyword.
# The 'build-system/constants.mk' should be included before requires.
#
my $shell_output = <<`SHELL`;
cd $cdir
head -n `cat Makefile | grep -n "__END_OF_REQUIRES__" | cut -f 1 -d ':'` Makefile | \
make TOOLCHAIN=$toolchain FLAVOUR=$flavour -f - -p __build_requires__ 2>/dev/null | grep "REQUIRES"
exit 0
SHELL
while( $shell_output =~ m/^REQUIRES(.+= +)(.+)/gm )
{
my @n = split( " ", $2 );
foreach my $req ( @n )
{
my ($d, $f);
$d = `echo $req | cut -f 1 -d '^'`;
$d =~ s/^\s+|\s+$//;
if( $req =~ m/\^/ )
{
$f = `echo $req | cut -f 2 -d '^'`;
$f =~ s/^\s+|\s+$//;
}
else
{
$f = "";
}
if( $d eq "ALL_DIRS" )
{
my $dirname = dirname( $makefile );
opendir( DIR, "$dirname" ) or
_kxLab::error( "build_requires: Could not open directory: $dirname: $!" );
my @dirs = grep { ! /^\./ && -d "$_" && -f "$_/Makefile" } readdir( DIR );
closedir DIR;
foreach my $dir (@dirs)
{
requires_depend( "$dirname/$dir/Makefile" );
"$dirname/$dir" =~ m!$top_dir/(.+)!;
$requires{$1} = "";
}
}
else
{
# Print a nice error message if the REQUIRES statement points to a missing directory
_kxLab::error( "build_requires: REQUIRES '$d' in $makefile not found. Exit" ) if( ! -d "$top_dir/$d" );
if( -f "$top_dir/$d/Makefile" )
{
if( $f eq "" ) { $requires{$d} = ""; }
else { $requires{$d . "^" . $f} = ""; }
}
}
}
}
return %requires;
}
sub start_depend
{
my $req = shift;
print REQUIRES_FILE "$req:";
}
sub depend
{
my $req = shift;
print REQUIRES_FILE " $req";
}
sub end_depend
{
print REQUIRES_FILE "\n\n";
}
sub make_sub_requires
{
my $req = shift;
if( ! exists $all_requires{$req} )
{
$all_requires{$req} = "";
my ($d, $f);
$d = `echo $req | cut -f 1 -d '^'`;
$d =~ s/^\s+|\s+$//;
if( $req =~ m/\^/ )
{
$f = `echo $req | cut -f 2 -d '^'`;
$f =~ s/^\s+|\s+$//;
}
else
{
$f = "";
}
# Read sub requires
my $makefile = "$top_dir/$d/Makefile";
my %sub_requires = read_requires( $makefile, $f );
if( scalar(%sub_requires) )
{
my @sorted_sub_requires = sort(keys %sub_requires);
# Build dependencies for sub requires
if( $f eq "" ) { start_depend( $d ); }
else { start_depend( $d . "^" . $f ); }
foreach my $sub_req ( @sorted_sub_requires )
{
depend( $sub_req );
}
end_depend();
# Make sub sub requires
foreach my $sub_req ( @sorted_sub_requires )
{
make_sub_requires( $sub_req );
}
}
}
}
#
# Parse the command line options
#
$opt_max_depth = 10;
my @opt_skip_dirs;
GetOptions( "max-depth=i" => \$opt_max_depth, "skip-dir=s" => \@opt_skip_dirs );
%skip_dirs = map { $_ => "" } @opt_skip_dirs;
# Get the rest of the command line
my $topdir = shift;
$toolchain = shift;
$flavour = shift;
my $makefile = "Makefile";
if( ! defined $topdir or $topdir eq "" ) { usage; }
if( ! defined $toolchain or $toolchain eq "" ) { usage; }
if( ! defined $flavour or $flavour eq "" )
{
$flavour = "";
$target_build_dir = "." . $toolchain ;
}
else
{
$target_build_dir = "." . $toolchain . "/" . $flavour;
}
_kxLab::error( "build_requires: $topdir is not a directory" ) if( ! -d $topdir );
_kxLab::error( "build_requires: Makefile missing: $makefile" ) if ( ! -f $makefile );
# setup $top_build_dir
$top_dir = $topdir;
my $build_system = $top_dir . "/build-system";
#my $build_system = "build-system";
_kxLab::system( "mkdir -p $target_build_dir" );
$requires_file = $target_build_dir . "/" . ".requires";
my $requires_depend_file = $requires_file . "_depend";
# open the output files
open(REQUIRES_FILE, "> $requires_file") or
_kxLab::error( "build_requires: Could not open $requires_file file: $!" );
open(REQUIRES_DEPEND_FILE, "> $requires_depend_file") or
_kxLab::error( "build_requires: Could not open $requires_depend_file file: $!" );
# root component
my $pwd = `pwd`;
chomp $pwd;
$pwd =~ m!$top_dir(.*)!;
my $root;
if( $1 eq "" )
{
$root = "all";
}
else
{
$1 =~ m!/(.+)!;
$root = $1;
}
print REQUIRES_FILE "# ROOT=$root\n\n";
print REQUIRES_DEPEND_FILE "\n";
# read the makefile
my %requires = read_requires( "$pwd/$makefile", $flavour );
# ignore the "build-system" dependency (if any), since this dependency is implicit
delete $requires{"build-system"};
my @sorted_requires = sort(keys %requires);
# build the all: rule
start_depend( "all" );
foreach my $req ( @sorted_requires )
{
depend( $req );
}
end_depend();
# build sub dependencies
foreach my $req ( @sorted_requires )
{
make_sub_requires( $req );
}
# Finish by including tree.mk
print REQUIRES_FILE "TREEDIRS = ", join(" ", sort(keys %all_requires)), "\n\n";
if( $pwd =~ m/$build_system/ )
{
print REQUIRES_FILE "include \$(BUILDSYSTEM)/tree-bs.mk\n";
}
else
{
print REQUIRES_FILE "include \$(BUILDSYSTEM)/tree.mk\n";
}
close REQUIRES_FILE;
close REQUIRES_DEPEND_FILE;