#!perl -w
#-----------------------------------------------------------------------------
#  Project :
#  Modul   :  nds_busyb.pl
#-----------------------------------------------------------------------------
#  Copyright 2003 Texas Instruments Deutschland GmbH
#                 All rights reserved.
#
#                 This file is confidential and a trade secret of Texas
#                 Instruments Deutschland GmbH
#                 The receipt of or possession of this file does not convey
#                 any rights to reproduce or disclose its contents or to
#                 manufacture, use, or sell anything it may describe, in
#                 whole, or in part, without the specific written consent of
#                 Texas Instruments Deutschland GmbH.
#-----------------------------------------------------------------------------
#  Purpose :
#-----------------------------------------------------------------------------


use strict;
use File::Copy;
use File::Find;
use File::Spec;
use Cwd;
use Getopt::Long qw(:config pass_through);


#-------------------------------------------------------------------------------
# Variables
#-------------------------------------------------------------------------------

# misc. variables
my $hw_variant;
my $l1_variant;

my $command;

# make tool (gnumake/clearmake)
my $make;

# makefile file name
my $makeFile;

my $makeVars;
my $condat_var_variables;

# specifies whether input file format is XML (default) or make (*.mak)
my $called_with_mak_file;

# specifies whether there is a "mconst:" target in the makefile or not
my $has_target_named_mconst;

my @busyb_libs;
my @intram_libs;

my $current_dir = cwd;
# do not only store the drive in $current_drive, but rather store the path
# to the current dir (necessary to work directly on V:\<view>\...)
my $current_drive = cwd;
$current_drive =~ s|[\\/][^\\/]+$||;

# path to Nice software (covering SSA, WCP, and SDVK)
my $hw_root = $current_dir . "/../chipsetsw";
my $hw_layer1 = $hw_root . "/layer1";
my $hw_system = $hw_root . "/system";
my $hw_system_config = $hw_system . "/config";
# points to the system/deps_objs/<ABC-CONFIG> directory (initialized by
# extract_globals_from_makefile() function)
my $hw_system_objects;

# path to GPF software
my $gpf_root = $current_drive . "/gpf";
# we need to honor proper upper and lower case when using clearmake!
my $gpf_ccd = $gpf_root . "/CCD";

# BuSyB cdginc output directories (necessary for step 7)
my $cdginc_dir;
my $cdginc_tools_dir;

# BuSyB target image (.out) file name (necessary for step 9)
my $target_image;

# specifies whether output is being redirected (logged to a file) or not
my $outerr_redirected;

# specifies whether ccddata_dll.dll can be built (1) or not (0):
# this depends on the presence of cl.exe, see also determine_cl_version() and
# step__check_environment()
my $generate_ccddata_dll;

# Generated makefile names
my $abc_exports;
my $busyb_prefix;
my $condat_lib_ip_mak;
my $condat_var_mak;

# tool definitions
my $t_perl           = "perl";
my $t_updtBusybInc   = "gen_abc_exports.mak";
my $t_updtAbc        = "update_exported_makefiles.pl";
my $t_abc            = "abc.pl";
my $t_updtBusyb      = "system/update_makefile.mak";

# command line options (do not initialize $o_make! -- see step 0 below)
my $o_file;
my $o_make;
my $o_logfile = "report.txt";
my $o_shell = 0;
my $o_help = 0;

# ABC/make pass-through options
my $abc_opt;
my $make_opt;

# script exit value, being set by last step: step__compile_berlin_sw()
my $exit_value;

# Variables needed for the progress bar
my $list_of_libs_available = 0 ;
my $pid;



#-------------------------------------------------------------------------------
# Main Program
#-------------------------------------------------------------------------------

# parse command line, passing through all unknown options to make
parse_command_line();


# logging is enabled by default (unless $o_shell is set, done by option "-s")
if (!$o_shell)
{
    # attempt to redirect STDOUT+STDERR
    $outerr_redirected = redirect_output();
}
else
{
    # do not redirect output, it is being sent to shell
    $outerr_redirected = 0;
}

my $build_message = "\nStarting build of " . $o_file . ",
REDIRECT_PLACEHOLDER

Build may take a while, so please be patient!\n\n";

if ($outerr_redirected)
{
    $build_message =~ s/REDIRECT_PLACEHOLDER/output is being logged to $o_logfile,/;
    print CONSOLE_OUT $build_message;
}
else
{
    $build_message =~ s/REDIRECT_PLACEHOLDER/output is NOT being logged!/;
    print $build_message;
}


# steps 1 and 2 must always be performed
step__check_environment (1);
step__parse_input_file (2);

# all other steps depend on the input file format, "mconst:" target in the
# BuSyB makefile, or hybrid build system vs. unified build system
my @build_steps;


# determine if UnBuSy or HyBuSy is being used for compilation
if (defined($hw_variant))
{
    # queue HyBuSy (hybrid build system) steps
    # ($called_with_mak_file is being set in step__check_environment() and is
    # already known at this point)
    push @build_steps, \&step__update_busyb_makefile
        unless $called_with_mak_file;
    push @build_steps, \&step__busyb_create_directories;
    push @build_steps, sub
    {
        # $has_target_named_mconst may be set as late as in
        # step__update_busyb_makefile(), so it may not yet be known and we need
        # to check this at run-time
        if ($has_target_named_mconst)
        {
            # only process SAP/MSG docs if "mconst:" target exists in makefile
            step__busyb_process_interfaces (@_);
        }
        else
        {
            # if not, need to decrement $step_no (first parameter of this function)
            $_[0]--;
        }
    };
    push @build_steps, sub
    {
        # $cdginc_tools_dir may be set as late as in
        # step__update_busyb_makefile(), so it may not yet be known and we need
        # to check this at run-time
        if ($cdginc_tools_dir)
        {
            # only generate ccddata_dll.dll if SAP/MSG docs were processed
            step__generate_ccddata_dll (@_);
        }
        else
        {
            # if not, need to decrement $step_no (first parameter of this function)
            $_[0]--;
        }
    };
    push @build_steps, \&step__generate_interface_for_abc
        unless $called_with_mak_file;
    push @build_steps, \&step__update_abc_config;
    push @build_steps, \&step__extract_abc_variables
        unless $called_with_mak_file;
    push @build_steps, \&step__abc_compilation;
    push @build_steps, \&step__busyb_compilation;

}
else
{
    # queue UnBuSy (unified build system) steps
    # ($called_with_mak_file is being set in step__check_environment() and is
    # already known at this point)
#    push @build_steps, \&step__busyb_generate_l1_configdef_files;
    push @build_steps, sub { step__update_busyb_makefile ($_[0], "system/unbusy_g23m.ini"); }
        unless $called_with_mak_file;
    push @build_steps, \&step__busyb_create_directories;
    push @build_steps, \&step__busyb_generate_cfg_files;
    push @build_steps, \&step__busyb_generate_header_files;
    push @build_steps, sub
    {
        # $has_target_named_mconst may be set as late as in
        # step__update_busyb_makefile(), so it may not yet be known and we need
        # to check this at run-time
        if ($has_target_named_mconst)
        {
            # only process SAP/MSG docs if "mconst:" target exists in makefile
            step__busyb_process_interfaces (@_);
        }
        else
        {
            # if not, need to decrement $step_no (first parameter of this function)
            $_[0]--;
        }
    };
    push @build_steps, sub
    {
        # $cdginc_tools_dir may be set as late as in
        # step__update_busyb_makefile(), so it may not yet be known and we need
        # to check this at run-time
        if ($cdginc_tools_dir)
        {
            # only generate ccddata_dll.dll if SAP/MSG docs were processed
            step__generate_ccddata_dll (@_);
        }
        else
        {
            # if not, need to decrement $step_no (first parameter of this function)
            $_[0]--;
        }
    };
    push @build_steps, \&step__busyb_compilation;
}


# execution of queued steps, starting with step number 3
$exit_value = 0;
my $step_no = 3;
foreach my $step (@build_steps)
{
    &{$step} ($step_no);
    $step_no++;
}

# restore redirected STDOUT+STDERR, if necessary
restore_redirection()
    if $outerr_redirected;

exit $exit_value;


#-------------------------------------------------------------------------------
# SUBROUTINES
#-------------------------------------------------------------------------------


#-------------------------------------------------------------------------------
# parses the command line, sets global $o_* variables to all specified options,
# checks which options/parameters are passed through to make or ABC
#-------------------------------------------------------------------------------
sub parse_command_line
{
    GetOptions ("file=s"=>\$o_file,
        "log=s"=>\$o_logfile,
        "shell"=>\$o_shell,
        "make=s"=>\$o_make,
        "help|?" =>\$o_help);

    if ($o_help)
    {
        usage();
        exit 0;
    }

    # determine ABC and make/BuSyB pass-through options from @ARGV
    $abc_opt = "";
    $make_opt = "";
    foreach (@ARGV)
    {
        if (/^-x/)
        {
            # ABC pass-through option: -x...
            #FIXME: are multiple ABC options allowed?
            s/^-x//;
            $abc_opt = $_;
        }
        else
        {
            # make/BuSyB pass-through option: all else
            $make_opt .= " " . $_;
        }
    }

    # sanity checks: due to enabled 'pass_through' and 'permute' of GetOptions(),
    # some busyb.pl options may end up in other options parameters instead (e.g. if
    # options which require a parameter are specified w/o any parameter: "... -m",
    # or "... -m -l", ...)
    foreach ($o_file, $o_logfile, $o_make)
    {
        # check all options which should take a parameter if they actually contain
        # another otion and no parameter
        if (defined($_) and /^-/)
        {
            print "\nERROR: Option missing mandatory parameter!\n\n";
            usage();
            exit 1;
        }
    }
    foreach ("-f", "-l", "-m")
    {
        # check if the pass-through options to make contain on of the busyb.pl
        # options
        if ($make_opt =~ /$_/i)
        {
            print "\nERROR: Option missing mandatory parameter!\n\n";
            usage();
            exit 1;
        }
    }

    if (!$o_file)
    {
        print "\nERROR: No input/configuration file specified with \"-f file\"!\n\n";
        usage();
        exit 1;
    }
    die "\nERROR: Input/configuration file \"" . $o_file . "\" not found, aborting"
        unless -e $o_file;
    # replace backslash with slash in filename
    $o_file =~ s:\\:/:g;
}


#-------------------------------------------------------------------------------
# print short usage notes
#-------------------------------------------------------------------------------
sub usage
{
    print "\nUSAGE:
    perl busyb.pl -f XML-File [OPTIONS] [make_flags] [make_targets]

Build TI protocol stack with BuSyB/ABC, logging all output to report.txt by
default.

OPTIONS:
    -l LOGFILE  log to LOGFILE (default is report.txt)
    -s          output to current shell, no logging to report.txt
    -x\"OPT\"     pass-through options for ABC

EXAMPLES:
    perl busyb.pl -f variants/2b_gp_mf_fd_..ps.xml
    perl busyb.pl -f variants/2b_gp_mf_fd_..ps.xml clean_aci
    perl busyb.pl -f variants/2b_gp_mf_fd_..ps.xml -x\"ENVFILE=my_env.mak\"
    perl busyb.pl -f variants/2b_gp_mf_fd_..ps.xml -k
";
}


#-------------------------------------------------------------------------------
# print current step to STDOUT (usually redirected to report.txt) and
# additionally to CONSOLE_OUT
#-------------------------------------------------------------------------------
sub print_status
{
    print "\n-------------------------------------------------------------------------------";
    print "\n$_[0]\n";
    print "-------------------------------------------------------------------------------\n\n";

    print CONSOLE_OUT "$_[0]\n"
        if $outerr_redirected;
}


#-------------------------------------------------------------------------------
# print to CONSOLE_OUT for progress bar
#-------------------------------------------------------------------------------
sub print_progress
{
    print CONSOLE_OUT "$_[0]\r"
        if $outerr_redirected;

}



#-------------------------------------------------------------------------------
# this function parses the XML file
# it returns 0 on failure
# it sets the following global variables
#   $hw_variant, $l1_variant, $makeVars
#-------------------------------------------------------------------------------
sub parse_file {
  my $line;
  open (IN,"<$_[0]")
    or die "ERROR: Can't open file \"$_[0]\" ($!), aborting";

  while(defined($line = <IN>))
  {
    # find HW string in ConfigDef document, e.g.
    # <property name="HW_VARIANT" value="GPRS_DSAMPLE_AMR_NW"/>
    #                                    ^^^^^^^^^^^^^^^^^^^
    if ($line =~ /name=\"HW_VARIANT\"\s*value=\"(.*)\"/ ) {
      $hw_variant = $1;
    }
    elsif ($line =~ /name=\"(L1)\"\s*value=\"(.*)\"/ ) {
      $l1_variant = $2;
      $makeVars .= " $1=$2";
    }
    # finding all other properties in order to pass them to make
    elsif ($line =~ /name=\"(\w+)\"\s*value=\"(.*)\"/ ) {
      $makeVars .= " $1=$2";
    }
  }
  close IN ;


  return 1;
}


#-------------------------------------------------------------------------------
# currently need to remove all L1 objects before compiling (dependency problems
# in ABC) when the ABC config (== $hw_variant) changes (last ABC config is stored in
# /g23m/.abc_config)
#-------------------------------------------------------------------------------
sub purge_abc_obj_files
{
    my $last_abc_config_file = $current_dir . "/.abc_config";
    my $abc_config_changed = 1;

    if (-e $last_abc_config_file)
    {
        open (IN, $last_abc_config_file)
            or die "ERROR: could not open file \"" . $last_abc_config_file . "\" ($!),";

        while (<IN>)
        {
            chomp;
            if (/ABC config = $hw_variant$/)
            {
                # still using the same ABC config as the last build
                $abc_config_changed = 0;
                print "ABC config not changed since the last build, keeping layer 1 *.obj files.\n";
            }
        }
        close IN;
    }

    if ($abc_config_changed == 1)
    {
        # udpate .abc_config with current ABC config ($hw_variant)
        open (OUT, ">$last_abc_config_file")
            or die "ERROR: could not write to file \"" . $last_abc_config_file . "\" ($!),";
        print OUT "This file is auto-generated, do not change!\nCurrent ABC config = " . $hw_variant . "\n";
        close OUT;

        # remove L1 .obj files
        print "Removing all *.obj files in \"" . $hw_layer1 . "\" and below
    (currently necessary to properly re-compile when the ABC config changed)\n\n";
        find (\&rm_file, $hw_layer1);
    }
}


#-------------------------------------------------------------------------------
# Check/Initialize some necessary env. variables:
# - %PATH must contain gpf/bin, gpf/tools/bin, chipsetsw/system (in that order)
# - %C_DIR must only contain slashes (no backslashes, semicolons)
#-------------------------------------------------------------------------------
sub init_environment
{
    my $current_drive_winformat = $current_drive;
    $current_drive_winformat =~ s:/:\\:g;

    # check if all necessary paths are in %PATH, add them if not (this removes
    # the dependency on initvars.bat)
    if (!($ENV{'PATH'} =~ m:[\\/]chipsetsw[\\/]system:))
    {
        # add \chipsetsw\system to %PATH (should be third)
        $ENV{'PATH'} = "$current_drive_winformat\\chipsetsw\\system;" . $ENV{'PATH'};
        print "%PATH   : \"$current_drive_winformat\\chipsetsw\\system\" + %PATH\n";
    }

    if (!($ENV{'PATH'} =~ m:[\\/]gpf[\\/]tools[\\/]bin:))
    {
        # add \gpf\tools\bin to %PATH (should be second)
        $ENV{'PATH'} = "$current_drive_winformat\\gpf\\tools\\bin;" . $ENV{'PATH'};
        print "%PATH   : \"$current_drive_winformat\\gpf\\tools\\bin\" + %PATH\n";
    }

    if (!($ENV{'PATH'} =~ m:[\\/]gpf[\\/]bin:))
    {
        # add \gpf\bin to %PATH (should be first)
        $ENV{'PATH'} = "$current_drive_winformat\\gpf\\bin;" . $ENV{'PATH'};
        print "%PATH   : \"$current_drive_winformat\\gpf\\bin\" + %PATH\n";
    }

    # check correct setting of environment variables for TI compiler and linker
    # PATH_CC_1_22e=C:\tools\TMS4701x_1.22e\NT
    die "\nERROR: environment variable %PATH_CC_1_22e must be set!\n"
      unless exists($ENV{'PATH_CC_1_22e'});
    $ENV{'PATH_CC_1_22e'} =~ s|\\|/|g;
    $ENV{'PATH_CC_1_22e'} =~ s|;.*||;
    print "%PATH_CC_1_22e  : \"" . $ENV{'PATH_CC_1_22e'} . "\"\n";


    # PATH_LNK_1_9902=C:\tools\vislink_1.9902
    die "\nERROR: environment variable %PATH_LNK_1_9902 must be set!\n"
      unless exists($ENV{'PATH_LNK_1_9902'});
    $ENV{'PATH_LNK_1_9902'} =~ s|\\|/|g;
    $ENV{'PATH_LNK_1_9902'} =~ s|;.*||;
    print "%PATH_LNK_1_9902  : \"" . $ENV{'PATH_LNK_1_9902'} . "\"\n";

} # init_environment

#-------------------------------------------------------------------------------
# Return "<view-name> (type: <view-type>)" or "none":
# <view-name>: CC view name/"unknown"
# <view-type>: CC view type ("dynamic/snapshot/unknown")
# Check view text mode, which must be 'unix' (aka 'transparent mode').
#-------------------------------------------------------------------------------
sub determine_view_context
{
    # check if using CC dynamic/snapshot view or no view at all
    my $view_details = `cleartool lsview -long -properties -full -cview 2>&1`;
    my $cc_view;

    if ($? == 0)
    {
        # store view name + view type (dynamic/snapshot)
        if ($view_details =~ /Tag:\s*(\w+)/)
        {
            $cc_view = $1;
        }
        else
        {
            print "\nWARNING: Could not determine current view name.\n";
            $cc_view = "unknown";
        }

        if ($view_details =~ /Properties: (\w+)/)
        {
            $cc_view .= " (type: " . $1 . ")";
        }
        else
        {
            print "WARNING: Could not determine view type (dynamic/snapshot).\n";
            $cc_view .= " (type: unknown)";
        }

        # check view text mode (_must_ be "unix", otherwise most GNU tools like
        # gnumake don't work properly!)
        if ($view_details =~ /Text mode: (\w+)/)
        {
            if ($1 ne "unix")
            {
                die "\nERROR: Wrong text mode (found \"$1\", should be \"unix\") of CC view $cc_view, aborting";
            }
        }
    }
    else
    {
        $cc_view = "none";
    }
    return $cc_view;
} # determine_view_context

#-------------------------------------------------------------------------------
# Check for gnumake/clearmake availability, returns gnumake/clearmake depending
# on view context ($dynamic_view) and user choice ($o_make)
#-------------------------------------------------------------------------------
sub determine_make_tool
{
    my $cmake;
    my $gmake;
    my $use_make;

    # checking for clearmake/gnumake availability
    my ($rc) = int (system("gnumake -ver > NUL 2>&1") / 256);
    
    if($rc == 0){
      $gmake = "gnumake";
    }

    # choosing the "right" make (gnumake for all kind of views)
    if (defined($gmake)) {
      $use_make = $gmake;
    }
    else {
      die "\nERROR: No appropriate gnumake tool found, aborting";
    }

    # checking for user supplied make
    if (defined($o_make) && ($o_make =~ /clearmake/i) && defined($cmake)) {
      die "\nERROR: Clearmake make tool no longer supported, aborting";
    }
    elsif (defined($o_make) && ($o_make =~ /gnumake/i) && defined($gmake)) {
      $use_make = $gmake;
    }
    elsif (defined($o_make)) {
      die "\nERROR: Specified make tool \"$o_make\" not found or not supported, aborting";
    }

    return $use_make;
}


#-------------------------------------------------------------------------------
# Check which GCC variant (Cygwin/MingW) and which version is installed,
# return GCC version and set %GCC env. variable to run this GCC variant in ABC.
#-------------------------------------------------------------------------------
sub determine_gcc_version
{
    my $gcc_ver = `gcc --version 2>&1`;
    if ($? == 0)
    {
        # only keep the first line of GCC version string
        $gcc_ver =~ s/\n.*$//s;

        # Nice GCC variant: Cygwin
        $ENV{'GCC'} = "\@gcc";
    }
    else
    {
        $gcc_ver = `cpp --version 2>&1`;
        if ($? == 0)
        {
            # only keep the first line of GCC version string
            $gcc_ver =~ s/\n.*$//s;

            # Berlin GCC variant: MingW (defines WIN32 by default which is
            # interpreted by Nice SW as 'simulation build')
            $ENV{'GCC'} = "\@cpp -UWIN32";
        }
        else
        {
            die "\nERROR: GCC not found (gcc.exe/cpp.exe not installed or not in the path), aborting";
        }
    }

    return $gcc_ver;
}


#-------------------------------------------------------------------------------
# Check if cl.exe (shipped with MS Visual Studio/VC++) is installed in the path,
# return version string and indicate whether ccddata_dll.dll can be built
# ($generate_ccddata_dll = 1) or not (= 0)
#-------------------------------------------------------------------------------
sub determine_cl_version
{
    my $cl_ver = `cl.exe 2>&1`;
    if ($? == 0)
    {
        # only keep the first line of version string
        chomp $cl_ver;
        $cl_ver =~ s/\n.*$//s;

        # indicate that ccddata_dll.dll should be built
        $generate_ccddata_dll = 1;
    }
    else
    {
        $cl_ver = "not found, unable to generate ccddata_dll.dll";

        # indicate that ccddata_dll.dll should not be built
        $generate_ccddata_dll = 0;
    }

    return $cl_ver;
}


#-------------------------------------------------------------------------------
# Check if Java is installed (in the path), return version string of Java.
#-------------------------------------------------------------------------------
sub determine_java_version
{
    my $java_ver = `java -version 2>&1`;
    if ($? == 0)
    {
        # only keep the first line of java version string and remove "java", if present
        $java_ver =~ s/\n.*$//s;
        $java_ver =~ s/\s*java\s*//;
    }
    else
    {
        die "\nERROR: Java not found (not installed or not in the path), aborting";
    }

    return $java_ver;
}


#-------------------------------------------------------------------------------
# Extract some globals from a given makefile
# 1) $cdginc_dir
# 2) $cdginc_tools_dir
# 3) $target_image
# 4) $has_target_named_mconst
# 5) @busyb_libs
#-------------------------------------------------------------------------------
sub extract_globals_from_makefile
{
    my ($makeFile) = @_;

    my $grab_libs = 0;
    my $grab_irlibs = 0;
    my $grab_irplacements = 0;
    my $lib_no = 0;

    open (IN, $makeFile)
        or die "ERROR: could not open file \"" . $makeFile . "\" ($!),";

    while (<IN>)
    {
        if (/.*(__out__\/.+\/cdginc)\/mconst\.cdg/)
        {
            # grep cdginc directory from generated makefile
            $cdginc_dir = $1;
        }
        elsif (/.*(__out__\/.+\/cdginc[^ \/]+)/)
        {
            # grep cdginc_tools dir from generated makefile (take anything which
            # starts with "cdginc" but w/o blank or slash directly afterwards)
            $cdginc_tools_dir = $1;
        }
        elsif (/(__out__\/.+\.out):/)
        {
            # grep target image (.out) name (w/ complete path) from generated
            # makefile for step 9
            $target_image = $1;

            # start grabbing libs (from next line on)
            $grab_libs = 1;
        }
        elsif ($grab_libs and /\s+(\S+)\s*([\\]*)/)
        {
            # disable lib-grabbing if last lib (i.e. no "\" at end of line)
            if ($2 eq "")
            {
                $grab_libs = 0;
            }
            # skip any make variables
            next if ($1 =~ /\$\(/);
            # skip the linker command file (*.lcf)
            next if ($1 =~ /.*\.lcf/);
            # skip all libs stored in chipsetsw VOB
            next if ($1 =~ /chipsetsw\//);

            # store lib in @busyb_libs
            push @busyb_libs, $1;
        }
        elsif (/__out__.+\.lcf:\s*(\w*)\s*\\/)
        {
            # start grabbing intram libs
            $grab_irlibs = 1;
        }
        elsif ($grab_irlibs and /\s+(\S+)\s*([\\]*)/)
        {
            # disable lib-grabbing if last lib (i.e. "\" at end of line),
            # start grabbing ir-placements instead
            if ($2 eq "")
            {
                $grab_irlibs = 0;
                $grab_irplacements = 1;
            }

            # skip any make variables
            next if ($1 =~ /\$\(/);
            # skip linker command file template
            next if ($1 =~ /\.template/);

            # store lib in @intram_libs
            push @intram_libs, $1;
        }
        elsif ($grab_irplacements and /\$\(BSB_ECHO\)\s+(\(.*\))\s*>/)
        {
            # grab intram lib placements
            # skip any make variables (e.g. $SSA_INT_RAM_PLACEMENT)
            next if ($1 =~ /\$\(/);

            # need to store each placement with the correct lib
            $intram_libs[$lib_no] .= " $1";
            $lib_no++;
        }
        elsif ($grab_irplacements and /make_cmd\.pl/)
        {
            # stop grabbing intram lib placements
            $grab_irplacements = 0;
        }
        elsif (/^mconst:/)
        {
            $has_target_named_mconst = 1;
        }
        elsif (/chipsetsw\/system\/deps_objs\/(\w+)\// and !defined($hw_system_objects))
        {
            # grep Nice deps_objs dir (different for each ABC config)
            $hw_system_objects = $1;
        }
    }
    close IN;
} # extract_globals_from_makefile


sub step__check_environment
{
    print_status ($_[0] . ". Checking environment");

    # create the makefile filename
    my $dir = (File::Spec->splitpath($o_file))[1];  #FIXME: remove this, we don't seem to need it anymore
    $makeFile = (File::Spec->splitpath($o_file))[2];
    $makeFile =~ s/(.*)\..*/$1.mak/;

    # generate *.abc_exports.mak filename
    $abc_exports = $makeFile;
    $abc_exports =~ s/\.mak$//;
    $abc_exports = $abc_exports . ".abc_exports.mak";

    # initialize necessary env. variables (%PATH, %C_DIR)
    init_environment();

    # get view name, view type (dynamic/snapshot/none), and check text mode for
    # dynamic views (must be 'unix')
    my $view = determine_view_context();
    print "CC View : $view\n";

    # check for clearmake & gnumake, determine if user specified make exists
    $make = determine_make_tool ($view);
    print "Make    : using \"$make\" for the build\n";

    # check installed GCC variant (gcc.exe/cpp.exe), set %GCC env. variable for ABC
    my $gcc_version = determine_gcc_version();
    print "GCC     : " . $gcc_version . ", using \"" . $ENV{'GCC'} . "\" for ABC\n";

    # check installed cl.exe version (part of MS Visual Studio/VC++)
    my $cl_version = determine_cl_version();
    print "cl.exe  : $cl_version\n";


    # determine input file format: XML (default), or make (*.mak)
    $called_with_mak_file = ($o_file =~ /.*\.mak/);
    if ($called_with_mak_file)
    {
        print "Input   : Makefile format (will skip several steps during build)\n";

        # set global variables $cdginc_dir, $target_image, and
        # $has_target_named_mconst, which are needed to determine further steps
        extract_globals_from_makefile ($makeFile);
    }
    else
    {
        print "Input   : XML format\n";

        # extract_globals_from_makefile() is only possible after step 2, where the
        # makefile is being created
    }


    # only check if Java is installed, if input is XML (i.e. we need to generate
    # everything ourselves) _or_ if input is MAK and we have a target "mconst:"
    my $java_version;
    if ((not ($called_with_mak_file)) or
        (($called_with_mak_file) and ($has_target_named_mconst)))
    {
        $java_version = determine_java_version();
    }
    else
    {
        $java_version = "not necessary";
    }
    print "Java    : " . $java_version . "\n";


    # set environment variable %BUSYB_PREFIX, which is being used in ABCs master.mak
    # (is necessary for supporting old Berlin build system in parallel)
    $busyb_prefix = $makeFile;
    $busyb_prefix =~ s/\.mak$/\./;
    $ENV{'BUSYB_PREFIX'} = $busyb_prefix;
    print "Prefix  : BUSYB_PREFIX=" . $busyb_prefix . "\n";
}


sub step__parse_input_file
{
    print_status ($_[0] . ". Parsing input file ($o_file)");

    if (!parse_file ($o_file))
    {
        die "ERROR: Either HW_VARIANT or L1 property missing in file \"$o_file\", aborting";
    }

    if (defined($l1_variant))
    {
        print "L1: " . $l1_variant . "\n";
    }
    else
    {
        print "L1 : Not defined\n"
    }
    if (defined($hw_variant))
    {
        print "HW variant: " . $hw_variant . "\n";
    }
    else
    {
        print "HW variant : Not defined - Compiling with Unified BuSyB\n"
    }

}


#-------------------------------------------------------------------------------
# update BuSyB makefile, if necessary; takes an additional second parameter
# to specify the BuSyB .ini file
#-------------------------------------------------------------------------------
sub step__update_busyb_makefile
{
    print_status ($_[0] . ". Updating BuSyB makefile ($makeFile)");

    $command = "$make -f $t_updtBusyb MFILE=$makeFile XMLFILE=$o_file ABC_EXPORTS_MAKEFILE_NAME=$abc_exports";
    if (defined ($_[1]))
    {
        $command .= " BUSYB_INI_FILE=$_[1]";
    }
    print "$command\n\n";

    system($command) == 0
        or die "ERROR: Can't create makefile \"$makeFile\" ($!), aborting";
    print "\n";

    # set global variables $cdginc_dir, $target_image, and $has_target_named_mconst,
    # which are needed to determine further steps (in case $called_with_mak_file
    # is not true, see step__check_environment())
    extract_globals_from_makefile ($makeFile);

    # generate clearmake build option specification (<makefile>.options),
    # regardless if using clearmake or gnumake in order to have this file
    # available in case one first wants to use gnumake and switches later on
    # to clearmake
    open (BOS, ">$makeFile.options")
        or die "ERROR: could not write to file \"" . $makeFile . ".options\" ($!),";
    print BOS "# clearmake build option specification (BOS) file

# this clearmake-specific rule causes clearmake to treat the logfile as a
# view-private file and not as a derived object (which causes several problems
# during the initial and subsequent builds with clearmake)
.NO_DO_FOR_SIBLING: " . $o_logfile . "\n";
    close BOS;
}


sub step__busyb_create_directories
{
    print_status ($_[0] . ". Creating directories ($make)");

    # create all the directories using the BUSYB makefile

    $command = "$make -f $makeFile MAKEDEPEND=0 allOutDirs";
    print "$command\n\n";

    system($command) == 0
        or die "ERROR: Can't create directories ($!), aborting";
    print "\n";
}


sub step__busyb_process_interfaces
{
    print_status ($_[0] . ". Processing SAP/MSG interfaces ($make)");

    # process SAP/MSG documents using the BUSYB makefile

    $command = "$make -f $makeFile MAKEDEPEND=0 mconst";
    print "$command\n\n";

    system($command) == 0
        or die "ERROR: Can't process SAP/MSG documents ($!), aborting";
    print "\n";
}


#-------------------------------------------------------------------------------
# generate ccddata_dll.dll (requires cl.exe, see determine_cl_version() above)
#-------------------------------------------------------------------------------
sub step__generate_ccddata_dll
{
    if ($generate_ccddata_dll)
    {
        print_status ($_[0] . ". Generating ccddata_dll.dll ($make)");

        chdir $gpf_ccd
            or die "ERROR: Can't cd to \"$gpf_ccd\" ($!), aborting";

        #FIXME: PROJECT=gprs may always be used...? Check with Henning.
        my $out_dir = $cdginc_tools_dir;
        $out_dir =~ s:/cdginc.+::;

        $command = "$make -f ccddata.mk TARGET=win32 PROJECT=gprs \
            GPF=$gpf_root \
            CCDDATA_LIB=$current_dir/$out_dir/bin/ccddata_dll.dll \
            CDGINCDIR=$current_dir/$cdginc_tools_dir \
            OBJDIR=$current_dir/$cdginc_tools_dir";
        print "$command\n\n";

        system($command) == 0
            or print "WARNING: Error while executing ccddata.mk ($!), continuing build.\n";
        print "\n";

        chdir $current_dir
            or die "ERROR: Can't cd to \"$current_dir\" ($!), aborting";
    }
    else
    {
        print_status ($_[0] . ". Skipping ccddata_dll.dll generation (cl.exe not found)");
    }
}


#-------------------------------------------------------------------------------
# generate makefiles <variant>_condat_lib_ip.mak and <variant>_condat_var.mak for ABC
#-------------------------------------------------------------------------------
sub step__generate_interface_for_abc
{
    print_status ($_[0] . ". Generate interface for ABC (<variant>_condat_*.mak)");

    $condat_lib_ip_mak = $busyb_prefix . "condat_lib_ip.mak";
    $condat_var_mak = $busyb_prefix . "condat_var.mak";

    print "cdginc output dir: \"" . $cdginc_dir . "\"\n\n";

    # Berlin libs
    my $berlin_libs;
    foreach my $lib (@busyb_libs)
    {
        $berlin_libs .= " \$(CONDAT_BASE_DIR)/../$lib";
    }

    # Berlin intram libs + their placements
    my $berlin_bss_libs;
    my $berlin_const_libs;
    foreach my $place (@intram_libs)
    {
        next if ($place =~ /chipsetsw\//);
        if ($place =~ /BSS_LIBS/)
        {
            (my $bss = $place) =~ s/CONST_LIBS\s+\(.*\)//;
            $bss =~ s/\(+BSS_LIBS\s+(\([^)]+\))/$1/;
            $berlin_bss_libs .= " \$(CONDAT_BASE_DIR)/../$bss"
                if ($bss ne "");
        }
        if ($place =~ /CONST_LIBS/)
        {
            (my $const = $place) =~ s/BSS_LIBS\s+\(\.\w+\)\s+//;
            $const =~ s/\(+CONST_LIBS\s+(\([^)]+\))\)*/$1/;
            $berlin_const_libs .= " \$(CONDAT_BASE_DIR)/../$const"
                if ($const ne "");
        }
    }

    # generate <variant>.condat_lib_ip.mak
    print "Generating condat/" . $condat_lib_ip_mak . "\n";
    open (CONDAT_LIB_IP, ">condat/" . $condat_lib_ip_mak)
        or die "ERROR: could not open file \"condat/" . $condat_lib_ip_mak . "\" for writing ($!),";
    print CONDAT_LIB_IP "# This file is auto-generated, do not change!
ifndef CONDAT_LIB_IP_MAK # to avoid multiple condat_lib_ip.mak (import + include)
	export CONDAT_LIB_IP_MAK := 1

ifndef GPF_DIR
	export GPF_DIR := \$(CONDAT_BASE_DIR)/../../gpf
	export CONDAT_DIR := \$(CONDAT_BASE_DIR)
endif

    L23_NAMES := \$(NCMPMODE)\$(NMMI)\$(NPMODE)\$(NPKTIO)\$(NSRVC)\$(NSTD)\$(NTK)\$(NWAP)\$(NGAME)\$(NEMS)\$(NMMS)\$(NHZONE)\$(NUNIC)\$(NCHIMMI)\$(NETXT)\$(NPDU)\$(NPS)\$(NEM)\$(NDP)\$(NCOMPTRC)\$(NEOTD)\$(NTTY)\$(NCPHS)\$(NBT)

export CONDAT_INCLUDES := \$(CONDAT_BASE_DIR)/com/include \$(CONDAT_BASE_DIR)/ms/src/l1 \$(GPF_DIR)/frame/cust_os \$(CONDAT_BASE_DIR)/com/src/comframe/configps \$(GPF_DIR)/inc

export CONDAT_LIBS :=" . $berlin_libs . "

export CONDAT_BSS_LIBS :=" . $berlin_bss_libs . "
export CONDAT_CONST_LIBS :=" . $berlin_const_libs . "

# extracted from " . $makeFile . "
export ICDG=\$(CONDAT_BASE_DIR)/../" . $cdginc_dir . "
export SRCACI=\$(CONDAT_BASE_DIR)/ms/src/aci
export SRCACIDTIMNG=\$(CONDAT_BASE_DIR)/ms/src/aci_dti_mng
export SRCACIA=\$(CONDAT_BASE_DIR)/ms/src/acia
export SRCKSD=\$(CONDAT_BASE_DIR)/ms/src/ksd

endif # ifndef CONDAT_LIB_IP_MAK\n";
    close CONDAT_LIB_IP;

    # generate <variant>.condat_var.mak
    print "Generating condat/" . $condat_var_mak . "\n";

    # put all feature flags in <variant>.condat_var.mak, except for
    # L1, CHIPSET, BOARD, COPY_DOCS, BUILD_UTILITIES, CMP_MODE, MAKE_DEPENDENCIES,
    # since these are not relevant for ABC
    $condat_var_variables = $makeVars;
    $condat_var_variables =~ s/ /\nexport /g;
    $condat_var_variables =~ s/\nexport L1=/\n#export L1=/;
    $condat_var_variables =~ s/\nexport CHIPSET=/\n#export CHIPSET=/;
    $condat_var_variables =~ s/\nexport BOARD=/\n#export BOARD=/;
    $condat_var_variables =~ s/\nexport COPY_DOCS=/\n#export COPY_DOCS=/;
    $condat_var_variables =~ s/\nexport BUILD_UTILITIES=/\n#export BUILD_UTILITIES=/;
    $condat_var_variables =~ s/\nexport CMP_MODE=/\n#export CMP_MODE=/;
    $condat_var_variables =~ s/\nexport MAKE_DEPENDENCIES=/\n#export MAKE_DEPENDENCIES=/;

    open (CONDAT_VAR, ">condat/" . $condat_var_mak)
        or die "ERROR: could not open file \"condat/" . $condat_var_mak . "\" for writing ($!),";
    print CONDAT_VAR "# This file is auto-generated, do not change!
ifndef CONDAT_VAR_MAK # to avoid multiple condat_var.mak (import + include)
	export CONDAT_VAR_MAK := 1

# matching " . $o_file . ":
# all feature flags, except for L1, CHIPSET, BOARD, COPY_DOCS, BUILD_UTILITIES, CMP_MODE, MAKE_DEPENDENCIES
" . $condat_var_variables . "

endif # ifndef CONDAT_VAR_MAK\n";
    close CONDAT_VAR;
} # generate_interface_for_abc


sub step__update_abc_config
{
    print_status ($_[0] . ". Updating ABC config ($t_updtAbc)");

    chdir $hw_system
        or die "ERROR: Can't cd to \"$hw_system\" ($!), aborting";

    $command = "$t_perl $t_updtAbc $hw_variant";
    print "$command\n\n";

    system($command) == 0
        or die "ERROR: Execution of \"$t_updtAbc\" failed ($!), aborting";

    chdir $current_dir
        or die "ERROR: Can't cd to \"$current_dir\" ($!), aborting";
}


#-------------------------------------------------------------------------------
# generate include makefile for SSA filenames
#-------------------------------------------------------------------------------

sub step__extract_abc_variables
{
    print_status ($_[0] . ". Extracting ABC variables for Berlin SW ($abc_exports)");

    my $command;

    $command = "$make -f $t_updtBusybInc ABC_EXPORTS_MAKEFILE_NAME=$abc_exports $makeVars";
    print "$command\n\n";

    system($command) == 0
        or die "ERROR: Execution of \"" . $t_updtBusybInc . "\" failed ($!),\n";
    print " \n";

    if (! -e $abc_exports) {
      die "ERROR: File \"$abc_exports\" has not been generated\n"
    }
} # extracting_abc_variables



#-------------------------------------------------------------------------------
# compiling nice sw using abc
#-------------------------------------------------------------------------------
sub step__abc_compilation
{
    print_status ($_[0] . ". Compiling Nice SW ($t_abc)");

    my $command;

    # remove some .obj files in the \chipsetsw VOB due to ABC dependency errors
    # (temporary solution!)
    ##purge_abc_obj_files();
    print "\n";

    # compile Nice software w/ ABC
    chdir $hw_system
        or die "ERROR: Can't cd to \"" . $hw_system . "\" ($!),";

    $command = "$t_perl $t_abc $hw_variant";
    if ($abc_opt)
    {
        $command .= " -x\"$abc_opt\"";
    }
    print "$command\n\n";

    my $res = system($command);

    if ($res != 0)
    {
        print_status ("ERROR: BUILD FAILED!");
        exit 1;
    }

    chdir $current_dir
        or die "ERROR: Can't cd to \"" . $current_dir . "\" ($!),";
} # compiling_nice_sw



#-------------------------------------------------------------------------------
# finally run make w/ the generated makefile from BuSyB
#-------------------------------------------------------------------------------

sub step__busyb_compilation
{
    print_status ($_[0] . ". Running BuSyB makefile ($make)");

    # Start progress bar display mechanism
    start_progress_bar_display();

    my $command;

    $command = "$make -f $makeFile$make_opt";
    print "$command\n\n";

    if ((system($command) == 0) and (-e $target_image))
    {
        print_status ("Build succeeded");
        $exit_value = 0;
    }
    else
    {
        print_status ("ERROR: BUILD FAILED!");
        $exit_value = 1;
    }
    # Kill child process if it exists
    if ($list_of_libs_available == 1) {kill 2, $pid; }
}



sub step__busyb_generate_l1_configdef_files
{
    print_status ($_[0] . ". Generating Layer1 ConfigDef files");

    chdir ("$hw_layer1\\tools")
        or die "ERROR: Can't cd to \"" . $hw_system . "\" ($!),";

    $command = "perl -w $hw_system\\Scripts\\L1Config.pl";
    print "$command\n\n";

    system($command) == 0
        or die "ERROR: Execution of \"" . $command . "\" failed ($!),";

    chdir $current_dir
        or die "ERROR: Can't cd to \"" . $current_dir . "\" ($!),";
}


sub step__busyb_generate_cfg_files
{
    print_status ($_[0] . ". Generating .cfg files ($make)");

    $command = "$make -f $makeFile cfg_files";
    print "$command\n\n";

    system($command) == 0
        or die "ERROR: Execution of \"" . $command . "\" failed ($!),";

}


sub step__busyb_generate_header_files
{
    print_status ($_[0] . ". Generating header files ($make)");

    $command = "$make -f $makeFile header_files";
    print "$command\n\n";

    system($command) == 0
        or die "ERROR: Execution of \"" . $command . "\" failed ($!),";

}


sub step__init_icl470_environment
{
    print_status ($_[0] . ". Init icl470 environment");

    print "%TEMPORARY_FILE_DIR: \"" . $hw_system . "/deps_objs/" . $hw_system_objects . "\"\n\n";

    $ENV{'TEMPORARY_FILE_DIR'} = $hw_system . "/deps_objs/" . $hw_system_objects;
}

#-------------------------------------------------------------------------------
# this function is used by find() (File::Find) above and removes all found
# *.obj files (NOTE: some are checked-in, so unlink() will fail)
#-------------------------------------------------------------------------------
sub rm_file
{
    unlink ($_)
        if /.*\.obj$/;
}


#-------------------------------------------------------------------------------
# redirect STDOUT+STDERR to $o_logfile, return 1 if redirection successful or
# 0 if redirection failed
#-------------------------------------------------------------------------------
sub redirect_output
{
    my $redirected = 1;

    open (CONSOLE_OUT, ">&STDOUT");
    open (CONSOLE_ERR, ">&STDERR");

    open (STDOUT, '>', $o_logfile)
        or $redirected = 0;
    open (STDERR, ">&STDOUT")
        or $redirected = 0;

    if ($redirected)
    {
        # make output unbuffered
        select (STDERR); $| = 1;
        select (STDOUT); $| = 1;

        # install signal handler function for die()
        $SIG{__DIE__} = \&die_handler
            or print CONSOLE_ERR "WARNING: Could not install die() signal handler,
console output may be corrupted when exiting with an error!\n";
    }
    else
    {
        # redirection failed: use old STDOUT+STDERR
        restore_redirection();

        print "WARNING: Could not redirect STDOUT+STDERR to " . $o_logfile . ",
not logging output!\n";
    }

    return $redirected;
}


#-------------------------------------------------------------------------------
# close logging to $o_logfile and restore old STDOUT+STDERR
# (pointing to console)
#-------------------------------------------------------------------------------
sub restore_redirection
{
    # restore redirected STDOUT+STDERR
    close (STDOUT);
    close (STDERR);

    open (STDOUT, ">&CONSOLE_OUT");
    open (STDERR, ">&CONSOLE_ERR");
}


#-------------------------------------------------------------------------------
# print die() error message, to also log it to $o_logfile and then restore
# the STDOUT+STDERR redirection
#-------------------------------------------------------------------------------
sub die_handler
{
    print STDERR $_[0];

    restore_redirection();
}

#-------------------------------------------------------------------------------
# Get number of libraries that have to be generated and display a 'progress bar'
# during the build according to the number of rebuilt libraries
#-------------------------------------------------------------------------------

sub start_progress_bar_display
{
    # Get libraries list
    my @libraries = () ;
    my %libDates ;
    my $total_lib = 0;
    my $line;

    open (IN, "<$makeFile")
        or die "ERROR: could not open file \"" . $makeFile . "\" ($!),";

    while(defined($line = <IN>))
    {
        # Check if variable BSB_TARGETS_ALL exists in the makefile
        if ( ( $line =~ /^BSB_TARGETS_ALL\s*=\s*([^\s]*)\s*(\\*)/ ) ||
             ( ( $line =~ /\s*([^\s]*)\s*(\\*)/ ) && ( $list_of_libs_available == 1 ) ) )
        {
            my $cur_lib = $1 ;
            my $end = $2 ;
            $list_of_libs_available = 1 ;

            if ( $cur_lib =~ m/\.lib$/ )
            {
                push( @libraries, $cur_lib ) ;
            }

            if ( $end eq "" ) {last;}
            next ;
        }
    }
    $total_lib = $#libraries + 1;

    # Retrieve current time (this allows to make time dif with future generated libraries)
    my $mytime = time;

    # If the list of libraries is available, create the process which take care about the progress
    # and start display progress
    if ($list_of_libs_available == 1)
    {
        # Create the process to display the progress bar
        defined($pid = fork)
            or die "Cannot fork: $!";

        # If child process
        unless ($pid)
        {
            # Flush Output buffer
            select CONSOLE_OUT;
            $| = 1;

            # The child process is stopped when the build is finished
            while (1)
            {
                # For the algorithm, we guess that the list of libraries in the array are ordered
                # in the same way that BuSyB compile these one. This means if the lib at index n
                # has been rebuilt, that means 100*n/total_lib have been rebuilt (where
                # total_libs is the number of libs that contribute to the image file)
                my $i = 0;
                foreach (reverse( @libraries ))
                {
                    my $mlib = "$_" ;
                    if (-e $mlib) # Check if the library exist
                    {
                        if ( ( stat( $mlib ) )[ 10 ] > $mytime ) # Check if the file has been rebuilt
                        {
                            last;
                        }
                        else
                        {
                            $i++;
                        }
                    }
                    else {$i++;} # If the library does not exist, we continue with the
                                 #previous library in the array
                }
                my $nb_generated_libs = $total_lib - $i;

                # Search for the number of files already generated
                my $progress;

                $progress = sprintf ("%.02d", (100 * $nb_generated_libs)/$total_lib);

                print_progress("Progress : " . $progress . "%");

                sleep 10;
            }
        }
    }
}

