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 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";