Radix cross Linux Build System

Cross-platform build system is designed to build distributions of different operating systems for a set of target devices

39 Commits   2 Branches   2 Tags
#!/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 Getopt::Long;

use _kxLab;

#
# Generate .$(HARDWARE)_requires file for current directory
#
# usage:
#   $0 [options] topdir toolchain hardware
#
# where:
#      'topdir' - is a absolute path to the top directory of checked out branch
#   'toolchain' - is a TOOLCHAIN name
#    'hardware' - is a HARDWARE  name
#     'flavour' - is a HARDWARE  variant
#

# global variables
my (%all_requires, $top_dir, $opt_max_depth, %requires_depend, $requires_file, %skip_dirs);

my ($toolchain, $hardware, $flavour, $target_build_dir);


sub usage
{
  print <<EOF;

Usage: build_requires [options] topdir toolchain hardware [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;
        hardware - is a HARDWARE 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 HARDWARE=$hardware 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;
$hardware  = shift;
$flavour   = shift;

my $makefile = "Makefile";

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 = "";
  $target_build_dir = "." . $toolchain . "/" . $hardware;
}
else
{
  $target_build_dir = "." . $toolchain . "/" . $hardware . "/" . $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";

_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 );

#$requires{"build-system"} = "";
# 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 output files
close REQUIRES_FILE;
close REQUIRES_DEPEND_FILE;