#!/bin/sh
#  Not every host installs perl at the same location, handle many locations:
PATH=/usr/bin:/usr/local/bin:$PATH
exec perl -x -S $0 ${1+"$@"}
exit $?
#!perl -w
#line 8

#  xt-buildroot-overlay-install [-t overlay_tarball] [-b buildroot_dir] \
#				[-k kernel_dir] \
#				[-c config_name] [-l long_name] [-f] [--help]
#
#  Creates individual overlay tarballs for gcc, binutils, gdb, and
#  the Linux kernel, out of the Xtensa Configuration Overlay tarball from
#  a Tensilica Core Package.  And installs these individual tarballs
#  at the appropriate locations within a buildroot source tree.
#
#  The Xtensa configuration overlay tarball is located in:
#	<xtensa_root>/src/xtensa-config-overlay.tar.gz
#  where <xtensa_root> is the path to the Tensilica Core Package.
#
# Copyright (c) 2003-2009 by Tensilica Inc.
#  History:
#  2007-NOV-08	1.0	meg	Initial version
#  2007-NOV-21	1.1	meg	Add -k parameter
#  2007-DEC-06	1.2	meg	Make -k and -b optional, check overlay sw vers.
#  2008-FEB-27	1.3	meg	Accept Xtensa Tools RB-2008.3 overlays

$progvers = "1.3";
$progname = $0;
$progname =~ s|.*[/\\:]||;


######################################################################
#
#  Parse cmdline
#

my $overlay_tarball = undef;
my $buildroot_dir = undef;
my $kernel_dir = undef;
my $config_name = undef;
my $config_long_name = undef;
my $force_clobber = 0;
my $prompt = 1;		# undocumented option

sub usage {
    print "$progname version $progvers\n"
    	 ."Usage:  $progname <parameters> [<options>]\n"
	 ."Where <parameters> are:\n"
	 ."  -t file.tgz     Specify path to the Xtensa Linux overlay tarball, typically\n"
	 ."                  <xtensa_root>/src/xtensa-config-overlay.tar.gz\n"
	 ."  -b dir          Path to the base of the buildroot source tree, in which\n"
	 ."                  package specific overlay tarballs get installed.\n"
	 ."  -k dir          Path to the base of the Linux kernel source tree, in which\n"
	 ."                  the Linux kernel specific overlay gets installed.\n"
	 ."  -c config_name  Name for the Xtensa processor configuration as it will be\n"
	 ."                  known to the open source community.  Must be a lowercase\n"
	 ."                  identifier, starting with a letter, consisting of letters\n"
	 ."                  and numbers and underscores, not ending with underscore\n"
	 ."                  and not containing consecutive underscores.  For examples:\n"
	 ."                     dc232b , dc232b_be , mmubasele , fsf , s5000 .\n"
	 ."  -l long_name    Long name for the Xtensa processor configuration, human-\n"
	 ."                  readable with spaces etc allowed (must be quoted).\n"
	 ."                  For example:  'Diamond 232L Standard Core Rev.B (LE)'\n"
	 ."                  Try to keep it within approximately 40 characters.\n"
	 ."And <options> are:\n"
	 ."  -f              If package specific overlay tarballs already exist in\n"
	 ."                  the destination source tree, overwrite them without asking.\n"
	 ."  --help          Show this usage message.\n";
}

#  Get arguments:
if (!@ARGV) {
    usage();
    exit 0;
}
while( defined($_ = shift) ) {
    if( /^-[tbclk]$/ ) {		# option taking an argument
	my $arg = shift;
	if( !defined($arg) ) {
	    print STDERR "$progname: ERROR: missing parameter after '$_' option\n\n";
	    usage();
	    exit 1;
	}
	$overlay_tarball = $arg if $_ eq "-t";
	$buildroot_dir = $arg if $_ eq "-b";
	$kernel_dir = $arg if $_ eq "-k";
	$config_name = $arg if $_ eq "-c";
	$config_long_name = $arg if $_ eq "-l";
	next;
    }
    if( /^-f$/ ) {
	$force_clobber = 1;
	next;
    }
    if( /^--[m-t]{8}$/ && /[new]([wow])([pup])[fur]\1[maze]\2[tuff]/ ) {
	$prompt = 0;
	next;
    }
    if( /^-(h|help|\-h|\-help|\?)$/i ) {
	usage();
	exit 0;
    }
    print STDERR "$progname: ERROR: unrecognized option or argument '$_'\n\n";
    usage();
    exit 1;
}


######################################################################
#
#  Validate cmdline arguments
#

ErrorU("missing -c argument (core name)")
	unless defined($config_name);
#  Try to enforce reasonable names:
ErrorU("-c: malformed core name '$config_name' (must be lowercase, letter followed by letters/digits, may contain underscore separators)")
	unless $config_name =~ /^[a-z][a-z0-9]*(_[a-z0-9]+)*$/;
ErrorU("-c: core name too short '$config_name'")
	unless length($config_name) >= 2;
ErrorU("-c: core name too long '$config_name'")
	unless length($config_name) <= 16;


ErrorU("missing -l argument (core long name)")
	unless defined($config_long_name);
$config_long_name =~ s/^\s+//;	# trim extra whitespace...
$config_long_name =~ s/\s+$//;
$config_long_name =~ s/\s+/ /g;
#  Try to enforce reasonable names:
ErrorU("-l: invalid (non-ASCII-printable) characters in core long name '$config_long_name'")
	unless $config_long_name =~ /^[\x20-\x7E]+$/;
ErrorU("-l: disallowed characters (\"\'\\) in core long name '$config_long_name'")
	if $config_long_name =~ /[\'\"\\]/;
ErrorU("-l: core long name too short '$config_long_name'")
	unless length($config_long_name) >= 5;
ErrorU("-l: core long name too long '$config_long_name'")
	unless length($config_long_name) <= 60;


#ErrorU("missing -b argument (buildroot source tree directory)")
#	unless defined($buildroot_dir);
if (defined($buildroot_dir)) {
    ErrorU("-b: not a directory: $buildroot_dir")
	    unless -d $buildroot_dir;
    foreach my $p ("toolchain/gcc", "toolchain/binutils", "toolchain/gdb", "target/xtensa") {
	ErrorU("-b: not a buildroot directory: missing $buildroot_dir/$p")
	    unless -d $buildroot_dir . "/" . $p;
    }
}


#ErrorU("missing -k argument (Linux kernel source tree directory)")
#	unless defined($kernel_dir);
if (defined($kernel_dir)) {
    ErrorU("-k: not a directory: $kernel_dir")
	    unless -d $kernel_dir;
    foreach my $p ("kernel", "arch/xtensa/kernel", "include/asm-xtensa") {
	ErrorU("-k: not a Linux kernel directory: missing $kernel_dir/$p")
	    unless -d $kernel_dir . "/" . $p;
    }
}


if (!defined($buildroot_dir) and !defined($kernel_dir)) {
    print STDERR "$progname: WARNING:\n";
    print STDERR "$progname: WARNING: Test run only, NOTHING WILL BE INSTALLED\n";
    print STDERR "$progname: WARNING: (use -b and -k to specify install destination)\n";
    print STDERR "$progname: WARNING:\n";
}


my @ovpaths = ( "/src/xtensa-config-overlay.tar.gz",
		"/xtensa-elf/src/linux/misc/linux-overlay.tar.gz" );
if (!defined($overlay_tarball)) {
    #  Try to locate the overlay tarball based on XTENSA_SYSTEM and XTENSA_CORE
    #  settings:
    my $xtensa_root = `xt-xcc --show-config=config 2>/dev/null`;
    $xtensa_root = "" unless defined($xtensa_root);
    chomp($xtensa_root);
    if ($xtensa_root ne "") {
	($overlay_tarball) = grep(-f $xtensa_root.$_, @ovpaths);
	if (!defined($overlay_tarball)) {
	    ErrorU("Xtensa configuration overlay tarball not found: ".$xtensa_root.$ovpaths[0])
	}
    } else {
	ErrorU("missing -t argument (Xtensa configuration overlay tarball filename)\n"
	     ."and no default Xtensa Core Package defined in the environment");
    }
} else {
    foreach my $p ("", @ovpaths) {
	if (-f $overlay_tarball.$p) {
	    $overlay_tarball .= $p;
	    last;
	}
    }
    ErrorU("-t: file not found: $overlay_tarball") unless -f $overlay_tarball;
}


######################################################################
#
#  Misc
#

my $overlay_unpacked = 0;
my $ovdir;

sub cleanup {
    if ($overlay_unpacked) {
	system("rm -rf '$ovdir' 2>/dev/null");
    }
}

sub ErrorEmit {
    my ($msg,$usage) = @_;
    $msg =~ s|\n|"\n${progname}: ERROR: "|ge;
    print STDERR "$progname: ERROR: $msg\n";
    if ($usage) {
	print "\n";
	usage();
    }
    cleanup();
    exit 1;
}
sub ErrorU { ErrorEmit(shift,1); }
sub Error  { ErrorEmit(shift); }


#  Read specified file (as binary), returning contents.
#
sub readfile {
    my ($filename) = @_;
    #  Read the file:
    open(INFILE,"<$filename") or Error("error reading from '$filename': $!");
    my $savesep = $/;
    undef $/;
    my $file = <INFILE>;
    $/ = $savesep;
    close(INFILE);
    $file;
}                                                                                                       

#  Write specified file (as binary) with first argument (string).
#
sub writefile {
    my ($filename, $file) = @_;
    #  Read the file:
    open(INFILE,">$filename") or Error("error writing to '$filename': $!");
    print INFILE $file;
    close(INFILE) or Error("error closing file '$filename': $!");
}                                                                                                       


######################################################################
#
#  Determine a temporary directory.
#

my $tmpdir = "/tmp";
if (defined($ENV{"TMP"}) and -d $ENV{"TMP"}) {
    $tmpdir = $ENV{"TMP"};
} elsif (defined($ENV{"TEMP"}) and -d $ENV{"TEMP"}) {
    $tmpdir = $ENV{"TEMP"};
}


######################################################################
#
#  Unpack the general overlay tarball
#

my $user = defined($ENV{"USER"}) ? $ENV{"USER"} : "xtensa";
$ovdir = $tmpdir."/tmp-overlay-${user}-$$";
mkdir $ovdir or Error("cannot create directory $ovdir");
$overlay_unpacked = 1;
system("tar xfz '$overlay_tarball' -C '$ovdir'")
	and Error("tar failed...");


######################################################################
#
#  Define and sanity check contents of overlay
#

my $oldpack = -f $ovdir."/xtensa-elf/src/linux/misc/core.h";
my $pf1 = ($oldpack ? "src/" : "");
my $pf2 = ($oldpack ? "xtensa-elf/src/linux/misc/" : "config/");

my @packages = (
	["binutils", "toolchain/binutils",
		["${pf1}/binutils/xtensa-modules.c",	"bfd/"],
		["${pf1}/binutils/xtensa-config.h",	"include/"],
		#["${pf1}/binutils/xtensa-config.sh",	"ld/emulparams/"],
		],
	["gcc", "toolchain/gcc",
		["${pf1}/gcc/xtensa-config.h",	"include/"],
		],
	["gdb", "toolchain/gdb",
		["${pf1}/gdb/xtensa-modules.c",	"bfd/"],
		["${pf1}/gdb/xtensa-config.h",	"include/"],
		["${pf1}/gdb/xtensa-config.c",	"gdb/"],
		["${pf1}/gdb/xtensa-regmap.c",	"gdb/gdbserver/"],
		["${pf1}/gdb/xtensa-regmap.c",	"gdb/gdbserver/xtensa-xtregs.c"],	# for GDB 6.8
		["${pf1}/gdb/xtensa-regmap.c",	"gdb/xtensa-xtregs.c"],			# for GDB 6.8
		["${pf1}/gdb/reg-xtensa.dat",	"gdb/regformats/"],
		],
	["kernel", "target/xtensa",		# ???
		["${pf2}core.h",	"include/asm-xtensa/variant-${config_name}/"],
		["${pf2}tie.h",		"include/asm-xtensa/variant-${config_name}/"],
		["${pf2}tie-asm.h",	"include/asm-xtensa/variant-${config_name}/"],
		],
);

#  Check that all files are present ...
foreach my $pack (@packages) {
    my ($pname, $buildroot_subdir, @files) = @$pack;
    print "Checking files for $pname ...\n";
    foreach my $f (@files) {
	my ($src, $dst) = @$f;
	-f $ovdir."/".$src or Error("missing '$src' in overlay tarball");
    }
}


######################################################################
#
#  Extract some useful information
#

#  Extract core name as specified in the build.
my $coreh = readfile($ovdir."/".$pf2."core.h");

$coreh =~ /^\s*\#\s*define\s+XCHAL_SW_VERSION\s+(\w+)/m;
my $swversion = $1;
defined($swversion) or Error("missing XCHAL_SW_VERSION in overlay core.h file;\n"
	."overlay is too old, need RB-2008.3 (SW version 7.1.1) or later");

$coreh =~ /^\s*\#\s*define\s+XCHAL_CORE_ID\s+"([^"]+)"/m;
my $coreid = $1;
defined($coreid) or Error("missing XCHAL_CORE_ID in overlay core.h file");

$coreh =~ /^\s*\#\s*define\s+XCHAL_HW_VERSION_NAME\s+"([^"]+)"/m;
my $hwversion = $1;
defined($hwversion) or Error("missing XCHAL_HW_VERSION_NAME in overlay core.h file");


$swvers_human = sprintf("%u.%u.%u",
	$swversion/100000, (($swversion/1000) % 100), ($swversion % 1000));
my $release = "software version $swvers_human";
if (-f $ovdir."/release") {
    $release = readfile($ovdir."/release");
    chomp($release);
}


######################################################################
#
#  Prompt user to be sure this is what he wants to do
#

#  Catch Ctrl-C so we can do a proper cleanup:
sub catch_term {
    my $signame = shift;
    #print STDERR "whoa!\n";
    cleanup();
    print STDERR "\n$progname: Cleaned up.\n";
    exit 3;
}
$SIG{TERM} = \&catch_term;
$SIG{HUP} = \&catch_term;
$SIG{INT} = \&catch_term;

$| = 1;
print	"\n",
	"About to generate package-specific overlay tarballs for the following:\n",
	"\n",
	"   Xtensa processor short name:   $config_name\n";
print	"      This short name overrides the name specified in the XPG:  $coreid\n" if $coreid ne $config_name;
#print	"      Please ensure that's the name you want.  If submitted to the open source\n",
#	"      community, it can be a hassle to change later on.\n";
print	"   Xtensa processor description:  $config_long_name\n",
	"   Targeting Xtensa HW version:   $hwversion\n",
	"   Xtensa configuration overlay:  $overlay_tarball\n",
	"           (release of overlay):  $release\n",
	"   Destination buildroot dir:     ".(defined($buildroot_dir)?$buildroot_dir:"(none, not installed)")."\n",
	"   Destination Linux kernel dir:  ".(defined($kernel_dir)?$kernel_dir:"(none, not installed)")."\n",
	"\n",
	"Are you sure? (y/n) ";
if ($prompt) {
    my $line = <STDIN>;
    chomp($line);
    if ($line !~ /^y(es)?$/i) {
	print "\nInstallation aborted.\n";
	cleanup();
	exit 2;
    }
} else {
    print "YES [no prompt]\n";
}
print "\n";


######################################################################
#
#  Now generate the tarballs
#

#  Now generate each tarball ...
foreach my $pack (@packages) {
    my ($pname, $buildroot_subdir, @files) = @$pack;
    my $tarname = "${pname}-xtensa_${config_name}.tgz";
    my $fulltarname;
    if (defined($buildroot_dir)) {
	my $tarsubname = $buildroot_subdir . "/" . $tarname;
	print "Generating and installing $tarsubname ...\n";
	$fulltarname = $buildroot_dir . "/" . $tarsubname;
    } else {
	print "Generating $tarname ...\n";
	$fulltarname = $ovdir . "/" . $tarname;
    }
    if (-e $fulltarname) {
	if ($force_clobber or !defined($buildroot_dir)) {
	    unlink($fulltarname) or Error("could not delete '$fulltarname': $!");
	} else {
	    Error("destination tarball already exists: '$fulltarname'");
	}
    }
    my $pdir = $ovdir."/tmp-".$pname;
    system("rm -fr '${pdir}' 2>/dev/null");
    mkdir $pdir or Error("cannot create directory $pdir");
    foreach my $f (@files) {
	my ($src, $dst) = @$f;
	#  If $dst ends in / , take filename from $src :
	if ($dst =~ m|/$|) {
	    my $fname = $src;
	    $fname =~ s|^.*/||;
	    $dst .= $fname;
	}
	#  Ensure destination directory exists:
	my $dstdir = $pdir;
	while ($dst =~ s|^([^/]+)/+||) {
	    $dstdir .= "/" . $1;
	    mkdir($dstdir);
	}
	#  Read file:
	my $content = readfile($ovdir."/".$src);

	#  Adjust contents of file.
	#  Fix-up typo:
	$content =~ s/XCHAL_SA_(NCP|CP\d+)_/XCHAL_$1_SA_/g;
	#  Update core name info:
	my $iscore = ($content =~ s/^(\s*\#\s*define\s+XCHAL_CORE_ID\s+)"[^"]+"/$1"$config_name"/mg);
	$iscore or $content =~ s{^(\s*\#\s*define\s+XCHAL_INST_FETCH_WIDTH\s+\S+\s*(/\*[^\*]*\*/)?\s*$)}
				{$1\n\#undef XCHAL_CORE_ID\n\#define XCHAL_CORE_ID\t\t\t"$config_name"\n}smg;
	#  Update core description info:
	$content =~ s/^(\s*\#\s*define\s+XCHAL_CORE_DESCRIPTION\s+)"[^"]+"/$1"$config_long_name"/mg
	  or $content =~ s{^(\s*\#\s*define\s+XCHAL_CORE_ID\s+\S+\s*(/\*[^\*]*\*/)?\s*$)}
			  {"$1\n" . ($iscore ? "" : "\n\#undef XCHAL_CORE_DESCRIPTION\n")
			   . "\#define XCHAL_CORE_DESCRIPTION\t\t\"${config_long_name}\""}smge;

	#  Write (possibly modified) file:
	writefile($dstdir."/".$dst, $content);
    }
    my $tarcmd = "tar cfz '${fulltarname}' -C '${pdir}' .";
    system($tarcmd) and Error("failed executing: $tarcmd");

    #  Install Linux kernel overlay:
    if ($pname eq "kernel" and defined($kernel_dir)) {
	print "Installing Linux kernel overlay from $tarname ...\n";
	my $untarcmd = "tar xfz '${fulltarname}' -C '${kernel_dir}' .";
	system($untarcmd) and Error("failed executing: $tarcmd");
    }
    #  Possible TODO:  update arch/xtensa/{Kconfig,Makefile} to add this config?
}


######################################################################
#
# The End
#

cleanup();
print "Done.\n";
exit 0;