--- a/imgtools/imaker/src/imaker.pl Fri Jun 18 13:49:03 2010 +0300
+++ b/imgtools/imaker/src/imaker.pl Thu Jun 24 10:35:05 2010 +0300
@@ -11,157 +11,2183 @@
#
# Contributors:
#
-# Description: iMaker main Perl script
+# Description: iMaker main Perl script & common routines
#
#
-$(error >>>MAKECMDGOALS=$(MAKECMDGOALS)<<<)
+$(error |MAKE=$(MAKE)|MAKE_VERSION=$(MAKE_VERSION)|SHELL=$(SHELL)|MAKECMDGOALS=$(MAKECMDGOALS)|)
#
#!perl
+#line 24
+
+use subs qw(CORE::GLOBAL::die);
use strict;
use warnings;
-use Getopt::Long qw(:config pass_through no_auto_abbrev);
+use Cwd;
+use Digest::MD5 qw(md5_hex);
+use File::Basename;
+use File::Copy;
+use File::Find;
+use File::Path;
+use File::Spec;
+use File::Temp qw(tempfile);
+use POSIX qw(strftime);
+use Text::ParseWords;
+use Time::Local;
-my $error = "";
-my $perlver;
-my $start;
+sub InitMkglobals();
+sub PrintEnv($);
+sub Max(@);
+sub Min(@);
+sub Trim($;$);
+sub Quote($);
+sub Unquote($);
+sub Int2Hex($;$);
+sub Byte2Str($@);
+sub Str2Byte($);
+sub Str2Xml($);
+sub Ascii2Uni($);
+sub Uni2Ascii($);
+sub GetTimestamp();
+sub Sec2Min($);
+sub Wcard2Restr($);
+sub Wcard2Regex($);
+sub ParseCmdWords($);
+sub DPrint($@);
+sub Echo($$$);
+sub PathConv($;$$$);
+sub ParseFiles($);
+sub GlobFiles($;$);
+sub GetBasename($);
+sub GetDirname($);
+sub GetAbsDirname($;$$$);
+sub GetAbsFname($;$$$);
+sub GetRelFname($;$$);
+sub GetWriteFname($);
+sub GetFreeDrive(;$);
+sub SubstDrive($$);
+sub UnsubstDrive($);
+sub Search($$$$$$\@\$);
+sub Find($$$$$\$);
+sub ChangeDir($);
+sub DeleteDir($;$);
+sub FindDir($$$$);
+sub MakeDir($);
+sub MakeChangeDir($);
+sub SetWorkdir($);
+sub OpenFile(*$$;$);
+sub Test($);
+sub CutFile($$$$$);
+sub Copy($$;$);
+sub CopyIby($$);
+sub DeleteFile($;$);
+sub FindFile($$$$);
+sub HeadFile($$$);
+sub TailFile($$$);
+sub TypeFile($;$);
+sub ReadFile($$);
+sub WriteFile($$$;$$);
+sub UnzipFile($$);
+sub Zip($$$$@);
+sub Move($$);
+sub Touch($@);
+sub SetLogfile($);
+sub RunSystemCmd($;$$$);
+sub ParseSystemCmd($$$$$);
+sub GenExclfile($$$$$);
+sub GenIbyfile($$$);
+sub GenObyfile($$$$@);
+sub GenMakefile($$$$$);
+sub GenWidgetConf($$$$);
+sub AddImageHeader($$$$$);
+sub Sleep($);
+sub FindSOSFiles($$$$);
+sub CheckTool(@);
+sub OpCacheInstall($$$);
+sub SisInstall($$$$$$$$);
+sub GetIPar(;$);
+sub PEval($);
+sub PeekICmd($);
+sub SkipICmd();
+sub GetICmd();
+sub EndICmd();
+sub SplitStep($);
+sub RunStep($);
+sub RunIExtCmd($);
+sub GetConfmkList(;$);
+sub GetFeatvarIncdir($);
+sub SetVerbose($;$);
+sub CloseLog();
+sub RunIMakerCmd($$$$$@);
+sub RunMakeCmd($$);
+sub HandleCmdArg($);
+sub HandleExtCmdArg($);
+sub MenuRuncmd($);
+sub Menu($);
+sub Install($$$);
+
+use constant READBUFSIZE => 2097152; # 2 MB
+use constant STARTSTR => '>>>[START]=========8<==========8<==========8<==========8<==========8<==========';
+use constant ENDSTR => '==========>8==========>8==========>8==========>8==========>8===========[END]<<<';
+
+# device[VARID]==... !!
+#
+use constant BOOTBINARYSTATEMENT => qr/^\s*bootbinary\s*(?:=+|\s)\s*(?:"(.+?)"|(\S+))/i;
+
+use constant FILESPECSTATEMENT =>
+ qr/^\s*(?:data|device|dll|extension|file|primary|secondary|variant)\S*?\s*(?:=+|\s)\s*(?:"(.+?)"|(\S+))\s+(?:"(.+?)"|(\S+))(\s+.+?)?\s*$/i;
+
+our ($gArgv, $gCmdcnt, @gCmdoutbuf, %gConfmkList, $gEpocdrive, $gEpocroot, $gError, $gErrwarn, $gEvalerr,
+ %gExportvar, $gFiltercmd, @gFindresult, $gICmd, @gIcmd, $gImakerext, $gImgtype, $gKeepgoing, @gLogbuf,
+ $gLogfile, %gLogfiles, $gMakecmd, @gMakeinfo, $gOutfilter, $gParamcnt, $gPrintcmd, @gReport, $gStartmk,
+ $gStarttime, $gStep, @gStepDur, %gStepIcmd, %gSubstdrv, $gTgterr, %gTool, $gVerbose, $gWinOS, $gWorkdir,
+ $gWorkdrive, @iVar);
+
+
+###############################################################################
+#
+
+sub InitMkglobals()
+{
+ $gCmdcnt = 0;
+ @gCmdoutbuf = ();
+ $gFiltercmd = qr/\S/;
+ @gFindresult = ();
+ $gICmd = "";
+ @gIcmd = ();
+ $gImgtype = "";
+ $gOutfilter = "";
+ $gParamcnt = 0;
+ $gPrintcmd = 0;
+ $gStep = "";
+ @gStepDur = ();
+ %gStepIcmd = ();
+ @iVar = (); # General purpose variable to be used from $(call peval,...)
+}
BEGIN {
- ($start, $perlver) = (time(), sprintf("%vd", $^V));
+ ($gArgv, $gEvalerr, $gStarttime, $gWinOS) = (scalar(@ARGV), 0, time(), $^O =~ /MSWin/i);
+ $_ = "default input and pattern-searching space";
+ eval("use Archive::Zip qw(:ERROR_CODES)");
+ eval("use constant AZ_OK => -1") if $@;
+ eval("use Archive::Zip::Tree");
+ if ($gWinOS) { eval("
+ use Win32API::File qw(:DDD_);
+ use Win32::File;
+ use constant WIN32_FILE_HIDDEN => Win32::File::HIDDEN");
+ } else { eval("
+ use constant DDD_REMOVE_DEFINITION => -1;
+ use constant WIN32_FILE_HIDDEN => -1");
+ }
+}
+
+INIT {
+ $gWorkdir = Cwd::cwd();
+ $gWorkdrive = ($gWorkdir =~ /^([a-z]:)/i ? uc($1) : "");
+ $ENV{EPOCROOT} = ($gWinOS ? "\\" : "$gWorkdir/") if !$ENV{EPOCROOT};
+ $ENV{IMAKER_CMDARG} = "" if !defined($ENV{IMAKER_CMDARG});
+ $ENV{IMAKER_CYGWIN} = 0 if !$ENV{IMAKER_CYGWIN};
+
+ InitMkglobals();
+ %gConfmkList = ();
+ $gEpocdrive = ($ENV{EPOCROOT} =~ /^([a-z]:)/i ? uc($1) : $gWorkdrive);
+ ($gEpocroot = GetAbsDirname($ENV{EPOCROOT})) =~ s/\/+$//;
+ $gError = 0;
+ $gErrwarn = 0;
+ %gExportvar = (); $gExportvar{""} = 0;
+ $gKeepgoing = 0;
+ @gLogbuf = ();
+ $gLogfile = "";
+ %gLogfiles = ();
+ $gMakecmd = "";
+ @gMakeinfo = ("?", "?", "?");
+ @gReport = ();
+ $gStartmk = 0;
+ %gSubstdrv = ();
+ $gTgterr = 0;
+ %gTool = (); map{ $gTool{$_} => $_ } ("cpp", "elf2e32", "interpretsis", "opcache", "unzip");
+ $gVerbose = 1;
+
select(STDERR); $|++;
select(STDOUT); $|++;
- if (!@ARGV) {
- warn("Warning: iMaker is running under Cygwin!\n")
+
+ # Overload die
+ *CORE::GLOBAL::die = sub {
+ $gError = 1 if !$gEvalerr;
+ return if (PeekICmd("iferror") && !$gEvalerr);
+ CORE::die(@_) if ($gEvalerr || !$gKeepgoing);
+ $gErrwarn = 1;
+ warn(@_);
+ };
+
+ # Handler for __DIE__ signal
+ $SIG{__DIE__} = sub {
+ return if $gEvalerr;
+ $gErrwarn = 1;
+ warn(@_);
+ exit(1);
+ };
+
+ # Handler for __WARN__ signal
+ $SIG{__WARN__} = sub {
+ if (($gEvalerr != 1) && ($gKeepgoing < 3) && ($_[0] ne "\n")) {
+ select(STDERR);
+ my $msg = ($gStep ? "($gStep): " : "") . $_[0];
+ if ($gErrwarn && ($gKeepgoing < 2)) {
+ DPrint(0, "*** Error: $msg") }
+ else { DPrint(127, "Warning: $msg") }
+ select(STDOUT);
+ }
+ $gErrwarn = 0;
+ };
+
+ if (!$gArgv) {
+ warn("iMaker is running under Cygwin!\n")
if (!$ENV{IMAKER_CYGWIN} && $^O =~ /cygwin/i);
- warn("Warning: iMaker uses Perl version $perlver! Recommended versions are 5.6.1 and 5.8.8.\n")
- if ($perlver !~ /^5\.(6\.1|8\.8)$/);
+ my $perlver = sprintf("%vd", $^V);
+ warn("iMaker uses Perl version $perlver! Recommended versions are 5.6.1, 5.8.x and 5.10.x.\n")
+ if ($perlver !~ /^5\.(?:6\.1|(?:8|10)\.\d+)$/);
}
- unshift(@INC, defined($ENV{IMAKER_DIR}) ? $ENV{IMAKER_DIR} : ($0 =~ /^(.*)[\/\\]/ ? $1 : "."));
}
-use imaker;
-
###############################################################################
# Main program
{
- if (!@ARGV) {
- $ENV{CONFIGROOT} = imaker::GetAbsDirname($ENV{CONFIGROOT});
- $ENV{ITOOL_DIR} = imaker::GetAbsDirname($ENV{ITOOL_DIR}, 0, 1);
- $ENV{IMAKER_DIR} = imaker::GetAbsDirname($ENV{IMAKER_DIR}, 0, 1);
- $ENV{PATH} = join(";", grep(!/[\\\/]cygwin[\\\/]/i, split(/;+/, $ENV{PATH})))
- if $imaker::gWinOS && !$ENV{IMAKER_CYGWIN};
+ if ($gArgv) {
+ my $iopt = shift(@ARGV);
+ print(map("$_\n", GetFeatvarIncdir("@ARGV"))), exit(0) if ($iopt eq "--incdir");
+ print(map("$_\n", @ARGV)), exit(0) if ($iopt eq "--splitarg");
+ die("Unknown internal imaker.pl option: `$iopt'.\n");
+ }
+
+ delete($ENV{MAKE}) if $gWinOS;
+ map { delete($ENV{$_}) } qw(MAKECMDGOALS MAKEFILES MAKEFLAGS MAKELEVEL MAKE_VERSION);
+
+ $ENV{CONFIGROOT} = GetAbsDirname($ENV{CONFIGROOT} || "$gEpocroot/epoc32/rom/config");
+ $ENV{ITOOL_DIR} = GetAbsDirname($ENV{ITOOL_DIR} || "$gEpocroot/epoc32/tools/rom");
+ $ENV{IMAKER_DIR} = GetAbsDirname($ENV{IMAKER_DIR});
+
+ $ENV{IMAKER_EXPORTMK} = "";
+ $ENV{IMAKER_MAKE} = ($gWinOS ? "$ENV{IMAKER_DIR}/mingw_make.exe" : $ENV{MAKE} || "make") if !$ENV{IMAKER_MAKE};
+ $ENV{IMAKER_MAKESHELL} = ($ENV{COMSPEC} || "cmd.exe") if (!$ENV{IMAKER_MAKESHELL} && $gWinOS);
+ $ENV{IMAKER_MKCONF} = $ENV{CONFIGROOT} . ',image_conf_(.+?)\.mk$,_(?:ncp)?\d+\.mk$,1' if !$ENV{IMAKER_MKCONF};
+
+ my $pathsep = ($gWinOS ? ";" : ":");
+ $ENV{PATH} = join(";", grep(!/[\\\/]cygwin[\\\/]/i, split(/;+/, $ENV{PATH}))) if (!$ENV{IMAKER_CYGWIN} && $gWinOS);
+ ($ENV{PATH} = Trim($ENV{PATH})) =~ s/"$/";/ if $gWinOS; # http://savannah.gnu.org/bugs/index.php?25412
+ $ENV{PATH} = PathConv("$ENV{ITOOL_DIR}", $gWinOS) . $pathsep . PathConv("$gEpocroot/epoc32/tools", $gWinOS) .
+ $pathsep . ($gWinOS ? PathConv("$gEpocroot/epoc32/gcc/bin", 1) . ";" : "") . $ENV{PATH};
+
+ $ENV{PERL5LIB} = $ENV{IMAKER_DIR} . ($ENV{PERL5LIB} ? "$pathsep$ENV{PERL5LIB}" : "");
+
+ die($@) if !defined($gImakerext = do("$ENV{IMAKER_DIR}/imaker_extension.pm")) && $@;
+
+ my ($version, $verfile) = ("", "$ENV{IMAKER_DIR}/imaker_version.mk");
+ open(FILE, "<$verfile") and map { $version = $1 if /^\s*IMAKER_VERSION\s*[+:?]?=\s*(.*?)\s*$/ } <FILE>;
+ close(FILE);
+ if ($version) { DPrint(1, "$version\n") }
+ else { warn("Can't read iMaker version from `$verfile'.\n") }
+
+ if ($ENV{IMAKER_CMDARG} =~ /^\s*--?(install|clean)=?(.*?)\s*$/i) {
+ Install(lc($1) eq "clean", "$ENV{IMAKER_DIR}/../group/bld.inf", $2);
+ exit(0);
+ }
+
+ $gMakecmd = "$ENV{IMAKER_MAKE} -R --no-print-directory" .
+ ($ENV{IMAKER_MAKESHELL} ? " SHELL=\"$ENV{IMAKER_MAKESHELL}\"" : "");
+ my $cmdout = qx($gMakecmd -f "$0" 2>&1);
+ ($cmdout = (defined($cmdout) ? $cmdout : "")) =~ s/\n+$//;
+ die("Can't run Make properly: `$cmdout'\n")
+ if ($cmdout !~ /\|MAKE=(.*?)\|MAKE_VERSION=(.*?)\|SHELL=(.*?)\|/);
+ @gMakeinfo = ($1, $2, $3);
+ warn(($gMakeinfo[1] eq "" ? "Can't resolve Make version" : "iMaker uses Make version $gMakeinfo[1]") .
+ ", recommended version is 3.81.\n") if ($gMakeinfo[1] !~ /^\s*3\.81/);
+
+ RunIMakerCmd("$gMakecmd TIMESTAMP=" . GetTimestamp() .
+ " -I \"$ENV{CONFIGROOT}\" -f \"$ENV{IMAKER_DIR}/imaker.mk\"", $ENV{IMAKER_CMDARG}, "", 0, 0, ());
+}
+
+
+###############################################################################
+#
+
+sub PrintEnv($)
+{
+ return if !@gMakeinfo;
+ DPrint(shift(), "=" x 79 . "\n" .
+ "User : " . (getlogin() || "?") . "@" . ($ENV{HOSTNAME} || $ENV{COMPUTERNAME} || "?") . " on $^O\n" .
+ "Time : " . localtime() . "\n" .
+ "Current dir : `$gWorkdir'\n" .
+ "iMaker tool : `$ENV{IMAKER_TOOL}' -> `$0'\n" .
+ "Cmdline args: `$ENV{IMAKER_CMDARG}'\n" .
+ "Perl : `$^X' version " . sprintf("%vd\n", $^V) .
+ "PERL5LIB : `$ENV{PERL5LIB}'\n" .
+ "PERL5OPT : `" . (defined($ENV{PERL5OPT}) ? "$ENV{PERL5OPT}'\n" : "'\n") .
+ "Make : `$gMakeinfo[0]' version $gMakeinfo[1]\n" .
+ "Make shell : `$gMakeinfo[2]'\n" .
+ "EPOCROOT : `$ENV{EPOCROOT}'\n" .
+ "CONFIGROOT : `$ENV{CONFIGROOT}'\n" .
+ "PATH : `$ENV{PATH}'\n");
+ @gMakeinfo = ();
+}
+
+sub Max(@)
+{
+ my $max = (shift() || 0);
+ map { $max = $_ if $_ > $max } @_;
+ return($max);
+}
+
+sub Min(@)
+{
+ my $min = (shift() || 0);
+ map { $min = $_ if $_ < $min } @_;
+ return($min);
+}
+
+sub Trim($;$)
+{
+ (my $str = shift()) =~ s/^\s+|\s+$//g;
+ $str =~ s/\s+(?=\s)//g if shift();
+ return($str);
+}
+
+sub Quote($)
+{
+ local $_ = shift();
+ return("") if !defined();
+ s/\\( |n|t)/\\\\$1/g;
+ return($_);
+}
+
+sub Unquote($)
+{
+ local $_ = shift();
+ return("") if !defined();
+ s/(?<!\\)(?<=\\n)\s+(\\n)?//g;
+ s/(?<!\\)\s+(?=\\n)//g;
+ s/(?<!\\)\\ / /g;
+ s/(?<!\\)\\n/\n/g;
+ s/(?<!\\)\\t/\t/g;
+ s/\\\\( |n|t)/\\$1/g;
+ s/\x00//g;
+ return($_);
+}
+
+sub Int2Hex($;$)
+{
+ my ($int, $len) = @_;
+ return((defined($len) ? $len : ($len = ($int < 4294967296 ? 8 : 16))) < 9 ? sprintf("%0${len}X", $int) :
+ sprintf("%0" . ($len - 8) . "X%08X", int($int / 4294967296), $int % 4294967296)); # 4294967296 = 4 G
+}
+
+sub Byte2Str($@)
+{
+ my ($base, @byte) = @_;
+ return(join("", map(($_ % 16 ? "" : sprintf("%04X:", $base + $_)) . sprintf(" %02X", $byte[$_]) .
+ (!(($_ + 1) % 16) || ($_ == (@byte - 1)) ? "\n" : ""), (0 .. (@byte - 1)))));
+}
+
+sub Str2Byte($)
+{
+ my ($str, $ind, @byte) = (shift(), 0, ());
+ $str =~ s/,$/, /;
+ map {
+ $ind++;
+ s/^\s+|\s+$//g;
+ if (/^\d+$/ && $_ < 256) {
+ push(@byte, $_);
+ } elsif (/^0x[0-9A-F]+$/i && hex() < 256) {
+ push(@byte, hex());
+ } else {
+ die("Invalid $ind. byte: `$_'.\n");
+ return;
+ }
+ } split(/,/, $str);
+ return(@byte);
+}
+
+sub Str2Xml($)
+{
+ my $str = shift();
+ $str =~ s/(.)/{'"'=>'"', '&'=>'&', "'"=>''', '<'=>'<', '>'=>'>'}->{$1} || $1/ge;
+ return($str);
+}
+
+sub Ascii2Uni($)
+{
+ (local $_ = shift()) =~ s/(?<!\r)\n/\r\n/g; # Use CR+LF newlines
+ s/(.)/$1\x00/gs;
+ return("\xFF\xFE$_");
+}
+
+sub Uni2Ascii($)
+{
+ (local $_ = shift()) =~ s/(.)\x00/$1/gs;
+ s/\r\n/\n/g;
+ return(substr($_, 2));
+}
+
+sub GetTimestamp()
+{
+ my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday) = localtime();
+ return(sprintf("%04d%02d%02d%02d%02d%02d%02d",
+ $year + 1900, $mon + 1, $mday, $hour, $min, $sec, int(($yday + ($year == 109 ? 3 : -3)) / 7) + 1));
+}
+
+sub Sec2Min($)
+{
+ my $sec = shift();
+ return(sprintf("%02d:%02d", $sec / 60, $sec % 60));
+}
+
+sub Wcard2Restr($)
+{
+ (my $wcard = shift()) =~ s/(.)/{"*"=>".*", "?"=>"."}->{$1} || "\Q$1\E"/ge;
+ return($wcard);
+}
+
+sub Wcard2Regex($)
+{
+ my $restr = Wcard2Restr(shift());
+ return(qr/$restr/i);
+}
+
+sub ParseCmdWords($)
+{
+ my $line = Trim(shift());
+ $line =~ s/\\/\\\\/g if $gWinOS;
+ return(Text::ParseWords::parse_line('\s+', 0, $line));
+}
+
+
+###############################################################################
+#
+
+sub DPrint($@)
+{
+ my ($verbose, @outlist) = @_;
+ map { tr/\x00\x1F/#/ } @outlist;
+ print(@outlist) if !$verbose || ($verbose & $gVerbose);
+ push(@gLogbuf, @outlist) if ($verbose < 32) || ($verbose & $gVerbose);
+ return if ($gLogfile eq "" || !@gLogbuf);
+ print(LOG @gLogbuf);
+ @gLogbuf = ();
+}
- my ($version, $verfile) = ("", "$ENV{IMAKER_DIR}/imaker_version.mk");
- open(FILE, "<$verfile") and map { $version = $1 if /^\s*IMAKER_VERSION\s*[+:?]?=\s*(.*?)\s*$/ } <FILE>;
- close(FILE);
- $version and print("$version\n") or
- warn("Can't read iMaker version from `$verfile'.\n");
+sub Echo($$$)
+{
+ return if SkipICmd();
+ my ($verbose, $str) = (shift(), shift());
+ DPrint($verbose, shift() ? "$str\n" : Unquote($str));
+}
+
+
+###############################################################################
+# File operations
+
+sub PathConv($;$$$)
+{
+ my $path = shift();
+ if (shift()) { $path =~ tr-\/-\\- }
+ else { $path =~ tr-\\-\/- }
+ return($path) if (!$gWinOS || $path =~ /^(?:\/\/|\\\\)/);
+ my $drive = shift();
+ return(ucfirst(($path =~ /^[a-z]:/i ? "" : ($_[0] ? $_[0] : $gWorkdrive)) . $path))
+ if !$drive;
+ $drive = $gWorkdrive if !($drive = shift());
+ $path =~ s/^$drive//i;
+ return($path);
+}
+
+sub ParseFiles($)
+{
+ my ($file, @files) = (" " . shift() . " ", ());
+ push(@files, defined($1) ? $1 : (defined($2) ? $2 : ())) while ($file =~ /\s(?:"\s*"|"+(.+?)"+|((\\\s|\S)+))(?=\s)/g);
+ return(@files);
+}
+
+sub GlobFiles($;$)
+{
+ return(@gFindresult) if (my $file = shift()) =~ /^__find__$/i;
+ return(map(/[\*\?]/ ? sort({lc($a) cmp lc($b)} grep(!/[\/\\]\.\.?$/,
+ glob(scalar(s/\*/\{\.\*\,\*\}/g, /\s/) ? "\"$_\"" : $_))) : $_, (shift() ? $file : ParseFiles($file))));
+}
+
+sub GetBasename($)
+{
+ return((File::Basename::fileparse(shift()))[0]);
+}
+
+sub GetDirname($)
+{
+ (my $dir = shift()) =~ s/^>>?(?!>)//;
+ return((File::Basename::fileparse($dir))[1]);
+}
+
+sub GetAbsDirname($;$$$)
+{
+ (my $dir = shift()) =~ s/^>>?(?!>)//;
+ $dir = "." if ($dir eq "");
+ my $absdir = "";
+ eval { local $gEvalerr = 1; $absdir = Cwd::abs_path($dir) };
+ return(PathConv($absdir || File::Spec->rel2abs($dir,
+ $dir !~ /^$gWorkdrive/i && $dir =~ /^([a-z]:)/i ? "$1/" : ""), shift(), shift(), shift()));
+}
+
+sub GetAbsFname($;$$$)
+{
+ my $file = shift();
+ return($file) if ($file eq "" || $file =~ /STD(IN|OUT|ERR)$/);
+ my $append = ($file =~ s/^>>(?!>)// ? ">>" : "");
+ return($append . PathConv(File::Spec->catpath("", GetAbsDirname(GetDirname($file)), GetBasename($file)), shift(), shift(), shift()));
+}
+
+sub GetRelFname($;$$)
+{
+ my ($file, $base) = (shift(), shift());
+ my $append = ($file =~ s/^>>(?!>)// ? ">>" : "");
+ ($file = PathConv(File::Spec->abs2rel($file, GetAbsDirname(defined($base) && ($base ne "") ? $base : ".")),
+ shift(), 1, "[a-z]:")) =~ s/^[\/\\]+//;
+ return("$append$file");
+}
+
+sub GetWriteFname($)
+{
+ (my $file = shift()) =~ s/^>?/>/;
+ return($file);
+}
+
+sub GetFreeDrive(;$)
+{
+ my $drives = Win32API::File::GetLogicalDrives();
+ for my $drive ("F".."Z", "A".."E") {
+ return("$drive:") if !($drives & (2 ** (ord($drive) - ord("A"))));
+ }
+ return("") if shift();
+ die("GetFreeDrive: No free drive available.\n");
+}
+
+sub SubstDrive($$)
+{
+ my ($drive, $path) = (uc(shift()), GetAbsDirname(shift()));
+ DPrint(16, "SubstDrive: `$drive' => `$path'\n");
+ $gSubstdrv{$drive} = 1, return if !(Win32API::File::GetLogicalDrives() & (2 ** (ord($drive) - ord("A")))) &&
+ Win32API::File::DefineDosDevice(0, $drive, $path);
+ die("Can't substitute `$drive' => `$path'\n");
+}
+
+sub UnsubstDrive($)
+{
+ return if (my $drive = uc(shift())) eq "";
+ DPrint(16, "UnsubstDrive: `$drive'\n");
+ delete($gSubstdrv{$drive}), return if Win32API::File::DefineDosDevice(DDD_REMOVE_DEFINITION, $drive, []) &&
+ !(Win32API::File::GetLogicalDrives() & (2 ** (ord($drive) - ord("A"))));
+ warn("Can't remove substituted drive `$drive'\n");
+}
- my $cmdarg = " " . imaker::HandleCmdArg($ENV{IMAKER_CMDARG}) . " ";
- my $makecmd = "$ENV{IMAKER_MAKE} -R --no-print-directory" .
- ($ENV{IMAKER_MAKESHELL} ? " SHELL=\"$ENV{IMAKER_MAKESHELL}\"" : "");
- my $cmdout = qx($makecmd -f $0 $cmdarg 2>&1);
- my $targets = ($cmdout =~ />>>MAKECMDGOALS=(.*?)<<</ ? $1 : undef);
+sub Search($$$$$$\@\$)
+{
+ my ($dir, $basere, $inclre, $exclre, $subdir, $finddir, $files, $total) = @_;
+ my @dir = my @file = ();
+
+ opendir(SDIR, $dir) or warn("Can't open directory `$dir'.\n");
+ while (local $_ = readdir(SDIR)) {
+ next if ($_ eq ".") || ($_ eq "..");
+ push(@dir, $_) if ((my $isdir = !(my $isfile = -f($_ = "$dir/$_")) && -d()) && $subdir);
+ next if ($finddir ? $isfile : $isdir);
+ ++$$total;
+ (my $fname = $_) =~ s/$basere//;
+ push(@file, $_) if ($fname =~ /$inclre/) && ($fname !~ /$exclre/) &&
+ (($finddir != 2) || !@{[glob((/\s/ ? "\"$_\"" : $_) . "/{[^.],.[^.],.??*,*}")]});
+ }
+ closedir(SDIR);
+ push(@$files, sort({lc($a) cmp lc($b)} @file));
+
+ foreach (sort({lc($a) cmp lc($b)} @dir)) {
+ Search($_, $basere, $inclre, $exclre, 1, $finddir, @$files, $$total);
+ }
+}
+
+sub Find($$$$$\$)
+{
+ my ($dur, $dir, $inclpat, $exclpat, $subdir, $finddir, $total) = (time(), @_);
+ ($dir, $$total) = (GetAbsDirname($dir), 0);
+ my ($inclre, $exclre, @files) = ("", "", ());
+ if ($inclpat =~ /^\//) {
+ $inclre = eval("qr$inclpat");
+ $inclpat = "";
+ } else {
+ $inclre = join("|", map(Wcard2Restr($_), split(/\s+/, $inclpat)));
+ $inclre = qr/\/(?:$inclre)$/i;
+ }
+ if ($exclpat =~ /^\//) {
+ $exclre = eval("qr$exclpat");
+ $exclpat = "";
+ } else {
+ $exclre = join("|", map(Wcard2Restr($_), split(/\s+/, $exclpat)));
+ $exclre = qr/\/(?:$exclre)$/i;
+ }
+ DPrint(16, "Find" . ($finddir == 2 ? "EmptyDir" : ($finddir ? "Dir" : "File")) . ": Directory `$dir'" .
+ ($subdir ? " and subdirectories" : "") . ", pattern `" . ($inclpat ne "" ? "$inclpat' $inclre" : "$inclre'") .
+ ($exclre eq qr/\/(?:)$/i ? "" : " excluding `" . ($exclpat ne "" ? "$exclpat' $exclre" : "$exclre'")));
+ foreach (GlobFiles($dir, 1)) {
+ Search($_, qr/^$_/i, $inclre, $exclre, $subdir, $finddir, @files, $$total) if -d();
+ }
+ DPrint(16, ", found " . @files . "/$$total " . ($finddir ? "directories" : "files") .
+ ", duration: " . Sec2Min(time() - $dur) . "\n");
+ return(@files);
+}
+
+sub ChangeDir($)
+{
+ if ((my $dir = GetAbsDirname(shift())) ne GetAbsDirname(".")) {
+ DPrint(16, "ChangeDir: `$dir'\n");
+ chdir($dir) or die("Can't change to directory `$dir'.\n");
+ }
+}
+
+sub DeleteDir($;$)
+{
+ return if !-d(my $dir = GetAbsDirname(shift()));
+ DPrint(16, "DeleteDir: `$dir'\n");
+ for my $sec (0, 2, 5) {
+ warn("Can't delete directory `$dir', retrying in $sec seconds...\n"), sleep($sec) if $sec;
+ eval { local $gEvalerr = 1; File::Path::rmtree($dir) };
+ return if !-d($dir);
+ RunSystemCmd($gWinOS ? 'rmdir /q /s "' . PathConv($dir, 1) . '"' :
+ "rm -fr '$dir'", 2);
+ sleep(1);
+ return if !-d($dir);
+ }
+ $dir = "Can't delete directory `$dir'.\n";
+ shift() ? warn($dir) : die($dir);
+}
+
+sub FindDir($$$$)
+{
+ my ($dir, $inclpat, $exclpat, $opt) = @_;
+ $opt = "" if !defined($opt);
+ push(@gFindresult, Find($dir, $inclpat, $exclpat, $opt =~ /r/, 1, local $_));
+}
+
+sub MakeDir($)
+{
+ return if -d(my $dir = shift());
+ eval { local $gEvalerr = 1; File::Path::mkpath($dir = GetAbsDirname($dir)) };
+ if (-d($dir)) {
+ DPrint(16, "MakeDir: `" . GetAbsDirname($dir) ."'\n");
+ } else {
+ DPrint(16, "MakeDir: `$dir'\n");
+ die("Can't create directory `$dir'.\n");
+ }
+}
+
+sub MakeChangeDir($)
+{
+ MakeDir(my $dir = shift());
+ ChangeDir($dir);
+}
+
+sub SetWorkdir($)
+{
+ MakeChangeDir(shift());
+ $gWorkdrive = (Cwd::cwd() =~ /^([a-z]:)/i ? uc($1) : "");
+ $gWorkdir = GetAbsDirname(".");
+}
+
+sub OpenFile(*$$;$)
+{
+ my ($fhandle, $file, $binmode, $print) = @_;
+ MakeDir(GetDirname($file)) if $file =~ /^>/;
+ DPrint(16, defined($print) ? $print : ($file =~ /^>/ ? "Write" : "Read") . "File: `$file'\n");
+ return(open($fhandle, $file)) if !$binmode;
+ return(open($fhandle, $file) and binmode($fhandle));
+}
- die("Can't run `$ENV{IMAKER_MAKE}' properly:\n$cmdout") if !defined($targets);
- map { $cmdarg =~ s/\s+\Q$_\E\s+/ / } split(/\s+/, $targets);
+sub Test($)
+{
+ if (-d(my $file = shift())) {
+ DPrint(16, "TestDir: `" . GetAbsDirname($file) . "'\n");
+ } elsif (-f($file)) {
+ DPrint(16, "TestFile: `" . GetAbsFname($file) . "'\n");
+ } else {
+ DPrint(16, "Test: `$file'\n");
+ die("File or directory `$file' doesn't exist.\n");
+ }
+}
+
+sub CutFile($$$$$)
+{
+ my ($msg, $src, $dest, $head, $len) = @_;
+ my ($buf, $srctmp) = (undef, "$src.tmp");
+
+ OpenFile(*INFILE, $src, 1, $msg) or
+ die("Can't read file `$src'.\n"), return;
+
+ my $out = GetWriteFname($head ? $dest : $srctmp);
+ OpenFile(*OUTFILE, $out, 1) or die("Can't write to `$out'.\n"), return;
+ while ($len > 0) {
+ read(INFILE, $buf, $len < READBUFSIZE ? $len : READBUFSIZE);
+ print(OUTFILE $buf);
+ $len -= READBUFSIZE;
+ }
+ close(OUTFILE);
+
+ $out = GetWriteFname($head ? $srctmp : $dest);
+ OpenFile(*OUTFILE, $out, 1) or die("Can't write to `$out'.\n"), return;
+ print(OUTFILE $buf) while read(INFILE, $buf, READBUFSIZE);
+ close(OUTFILE);
+ close(INFILE);
+ Move($srctmp, $src);
+}
+
+sub Copy($$;$)
+{
+ my ($src, $dest, $dir) = @_;
+ $dir = defined($dir) && $dir;
+ my $file = !($dir || -d($src));
+ $src = ($file ? GetAbsFname($src) : GetAbsDirname($src));
+ $dest = ($file ? GetAbsFname(-d($dest) ? "$dest/" . GetBasename($src) : $dest) :
+ GetAbsDirname($dir ? $dest : "$dest/" . GetBasename($src)));
+ if ($file && ($dest =~ /^>>[^>]/)) {
+ OpenFile(*FILE, $dest, 1, "AppendFile: `$src' => `$dest'\n")
+ or die("Can't append to `$dest'.\n"), return;
+ File::Copy::copy($src, *FILE) and
+ close(FILE) and return;
+ }
+ elsif ($file) {
+ MakeDir(GetDirname($dest));
+ DPrint(16, "CopyFile: `$src' => `$dest'\n");
+ warn("CopyFile: Destination file `$dest' already exists\n") if -f($dest);
+ File::Copy::copy($src, $dest) and return;
+ } else {
+ DPrint(16, "CopyDir: `$src' => `$dest'\n");
+ return if !RunSystemCmd(!$gWinOS ? "cp \"$src\"/* \"$dest\" -frv" :
+ 'xcopy "' . PathConv($src, 1) . '" "' . PathConv($dest, 1) . '" /e /h /i /q /y /z', 2);
+ }
+ die("Can't copy `$src' to `$dest'.\n");
+}
+
+sub CopyIby($$)
+{
+ my ($file, $dir) = (GetAbsFname(shift()), shift());
+ OpenFile(*FILE, $file, 0) or die("Can't read file `$file'.\n"), return;
+ map {
+ Copy(defined($1) ? $1 : $2, "$dir/" . (defined($3) ? $3 : $4)) if $_ =~ FILESPECSTATEMENT;
+ } <FILE>;
+ close(FILE);
+}
+
+sub DeleteFile($;$)
+{
+ return if !-f(my $file = GetAbsFname(shift()));
+ DPrint(16, "DeleteFile: `$file'\n");
+ for my $sec (0, 1, 2) {
+ warn("Can't delete file `$file', retrying in $sec second(s)...\n"), sleep($sec) if $sec;
+ unlink($file);
+ return if !-f($file);
+ }
+ $file = "Can't delete file `$file'.\n";
+ shift() ? warn($file) : die($file);
+}
+
+sub FindFile($$$$)
+{
+ my ($dir, $inclpat, $exclpat, $opt) = @_;
+ $opt = "" if !defined($opt);
+ my @find = Find($opt !~ /f/ ? $dir : GetDirname($dir), $opt !~ /f/ ? $inclpat : GetBasename($dir),
+ $exclpat, $opt =~ /r/, 0, local $_);
+ push(@gFindresult, $opt !~ /f/ ? @find : map("|$_|$inclpat", @find));
+}
+
+sub HeadFile($$$)
+{
+ my ($src, $dest, $len) = (GetAbsFname(shift()), GetAbsFname(shift()), shift());
+ $len = hex($len) if $len =~ /^0x/;
+ CutFile("HeadFile: Cut first $len bytes from `$src' => `$dest'\n", $src, $dest, 1, $len);
+}
- my $tmptarg = $targets = " $targets";
- my $hptarg = 0;
- while ($tmptarg =~ /(\s+(help-\S+))/g) {
- $hptarg = $1, $targets =~ s/\Q$hptarg\E(.*)$/ $1$hptarg/ if $2 ne "help-config";
+sub TailFile($$$)
+{
+ my ($src, $dest, $len) = (GetAbsFname(shift()), GetAbsFname(shift()), shift());
+ $len = hex($len) if $len =~ /^0x/;
+ CutFile("TailFile: Cut last $len bytes from `$src' => `$dest'\n", $src, $dest, 0, (-s($src) ? -s($src) : 0) - $len);
+}
+
+sub TypeFile($;$)
+{
+ my ($file, $str, $mode) = (GetAbsFname(shift()), "", shift() || "");
+ OpenFile(*FILE, $file, $mode, "TypeFile: `$file'" .
+ ($gOutfilter && ($mode ne "b") ? ", filter: `/$gOutfilter/i'" : "") . "\n") or
+ die("Can't read file `$file'.\n"), return;
+ DPrint(8, STARTSTR . "\n");
+ read(FILE, $str, -s($file));
+ if ($mode eq "b") {
+ DPrint(1, Byte2Str(0, map(ord(), split(//, $str))));
+ } else {
+ $str = Uni2Ascii($str) if $mode eq "u";
+ DPrint(1, map("$_\n", grep(!$gOutfilter || /$gOutfilter/i, split(/\n/, $str))));
+ $gOutfilter = "";
+ }
+ DPrint(8, ENDSTR . "\n");
+ close(FILE);
+}
+
+sub ReadFile($$)
+{
+ my ($file, $warn) = (GetAbsFname(shift()), shift());
+ OpenFile(*RFILE, $file, 0) or
+ ($warn ? (warn("Can't read file `$file'.\n"), return(())) : die("Can't read file `$file'.\n"));
+ my @file = map(chomp() ? $_ : $_, grep(!/^\s*$/, <RFILE>));
+ close(RFILE);
+ return(@file);
+}
+
+sub WriteFile($$$;$$)
+{
+ my ($file, $str, $mode, $opt) = (GetAbsFname(shift()), shift(), shift() || "", shift());
+ OpenFile(*WFILE, GetWriteFname($file), $mode) or
+ die("Can't write to `$file'.\n"), return;
+ if ($mode eq "b") {
+ my @byte = Str2Byte($str);
+ DPrint(64, Byte2Str($file =~ s/^>>(?!>)// ? -s($file) : 0, @byte));
+ print(WFILE map(chr(), @byte));
+ } else {
+ $opt = "" if !defined($opt);
+ $str = Unquote($str) if ($opt !~ /q/);
+ $str =~ s/(?<=\S)\/\//\//g if ($opt =~ /c/);
+ DPrint(16, $str) if shift();
+ $str = Ascii2Uni($str) if ($mode eq "u");
+ print(WFILE $str);
+ }
+ close(WFILE);
+}
+
+sub UnzipFile($$)
+{
+ my ($zipfile, $dir) = (GetAbsFname(shift()), GetAbsDirname(shift()));
+ DPrint(16, "UnzipFile: `$zipfile'");
+ Archive::Zip::setErrorHandler(sub{});
+ my ($error, $zip) = (0, Archive::Zip->new());
+ if ($zip->read($zipfile) != AZ_OK) {
+ DPrint(16, " to directory `$dir'\n");
+ die("Can't read zip archive `$zipfile'.\n");
+ return;
+ }
+ my @files = map($_->fileName(), grep(!$_->isDirectory(), $zip->members()));
+ DPrint(16, ", " . @files . " files to directory `$dir'\n");
+ foreach my $file (@files) {
+ DPrint(16, "ExtractFile: `$dir/$file'");
+ eval { local $gEvalerr = 1; $error = ($zip->extractMember($file, "$dir/$file") != AZ_OK) };
+ DPrint(16, $error ? " Failed\n" : "\n");
+ die("Can't extract file `$file' to directory `$dir'.\n") if $error;
+ $error = 0;
+ }
+}
+
+sub Zip($$$$@)
+{
+ my ($zipfile, $dir, $opt, $prefix) = (GetAbsFname(shift()), shift(), shift(), shift());
+
+ $opt = (defined($opt) ? ", options: `$opt'" : "");
+ $prefix = GetAbsDirname($prefix) if $prefix ne "";
+ my %files = ();
+ foreach my $file (@_) {
+ my $zname = "";
+ ($file, $zname) = ($1, $2) if ($file =~ /^\|(.*)\|(.*)$/);
+ next if !($file = (!$dir ? (-f($file) ? GetAbsFname($file) : "") : (-d($file) ? GetAbsDirname($file) : "")));
+ ($zname = ($zname eq "" ? $file : (!$dir ?
+ GetAbsFname($zname) : GetAbsDirname($zname)))) =~ s/^(?:$gEpocroot|[a-z]:)?\/+//i;
+ if ($opt !~ /j/) {
+ $zname =~ s/^.*?\/+/$prefix\// if ($prefix ne "");
+ } else {
+ $zname = ($dir ? "" : GetBasename($file)) if ($prefix eq "") || !s/^$prefix//;
}
- $hptarg = $1, $targets =~ s/\Q$hptarg\E(.*)$/ $1$hptarg/ while $tmptarg =~ /(\s+print-\S+)/g;
- $targets =~ s/^\s+|\s+(?=\s)|\s$//g;
+ $files{lc($zname)} = [$file, $zname];
+ }
+
+ DPrint(16, ($dir ? "ZipDir: `$zipfile'$opt, " . keys(%files) . " directories" :
+ "ZipFile: `$zipfile'$opt, " . keys(%files) . " files") . ($prefix ? ", prefix: $prefix\n" : "\n"));
+
+ Archive::Zip::setErrorHandler(sub{});
+ my ($error, $zip) = (0, Archive::Zip->new());
+ $zip->read($zipfile) if (my $ziptmp = ($zipfile =~ s/^>>(?!>)// ? "$zipfile.tmp" : ""));
+ $zip->zipfileComment("iMaker-generated zip archive `$zipfile'$opt.");
- my $mainmk = "-f $ENV{IMAKER_DIR}/imaker.mk";
- $makecmd .= " -I " . imaker::GetAbsDirname($ENV{CONFIGROOT}, 0, 1) . " $mainmk";
+ foreach my $file (sort({lc($$a[0]) cmp lc($$b[0])} values(%files))) {
+ DPrint(16, "Add" . ($dir ? "Dir" : "File") . ": `$$file[0]' => `$$file[1]'") if ($opt !~ /q/);
+ eval {
+ my $warn = 0;
+ local $gEvalerr = 1; local $SIG{__WARN__} = sub{ $warn = 1 };
+ $error = ($dir ? $zip->addTree($$file[0], $$file[1]) != AZ_OK :
+ !$zip->addFile($$file[0], $$file[1])) || $warn;
+ };
+ DPrint(16, $error ? " Failed\n" : "\n") if ($opt !~ /q/);
+ warn("Can't add " . ($dir ? "directory tree" : "file") . "`$$file[0]' to zip archive `$zipfile'.\n") if $error;
+ $error = 0;
+ }
+ ($zip->writeToFileNamed($ziptmp ? $ziptmp : $zipfile) == AZ_OK) or
+ die("Can't create zip archive `$zipfile'.\n");
+ Move($ziptmp, $zipfile) if $ziptmp;
+}
+
+sub Move($$)
+{
+ my ($src, $dest) = @_;
+ my $dir = -d($src);
+ $src = ($dir ? GetAbsDirname($src) : GetAbsFname($src));
+ MakeDir(GetDirname($dest));
+ $dest = ($dir ? GetAbsDirname($dest) : GetAbsFname($dest));
+ DPrint(16, "Move" . ($dir ? "Dir" : "File") . ": `$src' => `$dest'\n");
+ File::Copy::move($src, $dest) or
+ die("Can't move `$src' to `$dest'.\n");
+}
+
+sub Touch($@)
+{
+ my $time = (shift() =~ /^(\d\d\d\d)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)$/ ?
+ Time::Local::timelocal($6, $5, $4, $3, $2 - 1, $1 - 1900) : time);
+ if (@_ != 1) {
+ DPrint(16, "Touch: " . scalar(@_) . " files/dirs, " .
+ POSIX::strftime("%Y-%m-%d %H:%M:%S", localtime($time)) . "\n");
+ utime($time, $time, @_) == @_ or
+ die("Can't touch all the " . scalar(@_) . " files/dirs.\n");
+ return;
+ }
+ my $file = shift();
+ my $dir = -d($file);
+ $file = ($dir ? GetAbsDirname($file) : GetAbsFname($file));
+ DPrint(16, "Touch" . ($dir ? "Dir" : "File") . ": `$file', " .
+ POSIX::strftime("%Y-%m-%d %H:%M:%S", localtime($time)) . "\n");
+ utime($time, $time, $file) == 1 or
+ die("Can't touch " . ($dir ? "directory" : "file") . " `$file'.\n");
+}
+
+sub SetLogfile($)
+{
+ return if !(my $file = GetAbsFname(shift()));
+ my $append = (($file =~ s/^>>(?!>)//) || exists($gLogfiles{$file}) ? ">>" : "");
+ CloseLog();
+ OpenFile(*LOG, GetWriteFname($file = "$append$file"), 0) or
+ warn("Can't log to file `$file'.\n"), return;
+ $gLogfiles{$gLogfiles{__prev__} = $gLogfile = $file} = 1;
+}
+
+
+###############################################################################
+#
+
+sub RunSystemCmd($;$$$)
+{
+ return if ($gICmd !~ $gFiltercmd);
+ my ($cmd, $keepgoing, $null, $file) = @_;
+ DPrint(1, "$cmd\n"), return if $gPrintcmd;
+ local $gError = 0 if ($keepgoing = (defined($keepgoing) && ($keepgoing =~ /^[123]$/) ? $keepgoing : 0));
+ local $gKeepgoing = Max($gKeepgoing, $keepgoing) if $keepgoing;
+ $file = (defined($file) ? GetAbsFname($file) : "");
+ @gCmdoutbuf = ();
+ DPrint(4, local $_ = "RunSystemCmd(" . GetAbsDirname(".") . "): `$cmd'" .
+ ($keepgoing ? ", keep going" . ($keepgoing > 1 ? "($keepgoing)" : "") : "") .
+ ($file ? ", redirect to `$file'" : "") . ($null ? ", redirect stdout to null" : "") .
+ ($gOutfilter ? ", filter: `/$gOutfilter/i'" : "") . "\n");
+ OpenFile(*CMDFILE, GetWriteFname($file), 0) or
+ (die("Can't write to `$file'.\n"), $file = "") if $file;
+ print(CMDFILE $_) if $file;
+ my $dur = time();
+ open(CMD, "$cmd 2>&1 |");
+ DPrint(8, STARTSTR . "\n");
+ while ($_ = <CMD>) {
+ chomp();
+ push(@gCmdoutbuf, $_);
+ next if ($gOutfilter && !/$gOutfilter/i);
+ DPrint(8, "$_\n") if !$null;
+ print(CMDFILE "$_\n") if $file;
+ }
+ close(CMD);
+ my $error = ($? >> 8);
+ close(CMDFILE) if $file;
+ push(@gStepDur, $dur = time() - $dur);
+ $gOutfilter = "";
+ print(map("$_\n", @gCmdoutbuf)) if ($error && !$gKeepgoing && !$null && $gVerbose && !($gVerbose & 8));
+ $dur = Sec2Min($dur);
+ DPrint(8, substr(ENDSTR, 0, -16) . $dur . substr(ENDSTR, length($dur) - 16) . "\n");
+ die("Command `$cmd' failed ($error) in `" . GetAbsDirname(".") . "'.\n") if $error;
+ return($error);
+}
+
+
+###############################################################################
+#
+
+sub ParseSystemCmd($$$$$)
+{
+ return if SkipICmd();
+ my ($title, $inclre, $exclre, $file, $lines) = @_;
+ ($inclre, $exclre) = (eval("qr$inclre"), $exclre ne "" ? eval("qr$exclre") : qr/^$/);
+ $lines = ($lines ? $lines - 1 : 0);
- foreach my $target ($hptarg || $targets eq "" ? $targets : split(/\s/, $targets)) {
- ($cmdarg, $target) = imaker::Menu($makecmd, $mainmk, $cmdarg) if $target eq "menu";
- system($ENV{IMAKER_MAKECMD} = "$makecmd TIMESTAMP=" . imaker::GetTimestamp() . " $cmdarg $mainmk $target")
- if $target ne "menu";
- $error = ($? >> 8) if ($? >> 8);
+ my @parse = ();
+ for (my $i = 0; $i < @gCmdoutbuf; $i++) {
+ next if ($gCmdoutbuf[$i] !~ $inclre);
+ push(@parse, join(" | ", @gCmdoutbuf[$i .. $i + $lines])) if ($gCmdoutbuf[$i] !~ $exclre);
+ $i += $lines;
+ }
+ return if !@parse;
+ if (!$file) {
+ DPrint(1, "$title\n", map(sprintf("%" . length(@parse) . "s", $_) . ") $parse[$_ - 1]\n", 1 .. @parse));
+ } else {
+ WriteFile($title, join("\n", @parse), "", "q");
+ }
+}
+
+
+###############################################################################
+#
+
+sub GenExclfile($$$$$)
+{
+ return if SkipICmd();
+
+ my ($exclfile, $base, $prefix, $exclfiles, @exclfiles) = (shift(), GetAbsDirname(shift()), shift(), "", ());
+
+ if (!-f($exclfile)) {
+ WriteFile($exclfile, "", "");
+ } else {
+ OpenFile(*FILE, $exclfile, 1) or die("Can't read file `$exclfile'.\n"), return;
+ read(FILE, $exclfiles, -s($exclfile));
+ close(FILE);
+ @exclfiles = split(/\n/, Uni2Ascii($exclfiles));
+ }
+
+ my $findfiles = 0;
+ my @addfiles = map($_ ne "**" ? $_ : "*", grep(!(($_ eq "*") && ++$findfiles),
+ map(Trim(Unquote(Trim($_))), grep(!/^\s*(?:#.*)?$/, split(/(?<!\\)\\n/, shift())))));
+
+ if ($findfiles) {
+ $exclfiles = "";
+ foreach (@exclfiles, @addfiles, map(Trim(Unquote(Trim($_))), grep(!/^\s*(?:#.*)?$/, split(/(?<!\\)\\n/, shift())))) {
+ (my $file = $_) =~ tr/\\/\//;
+ $file =~ s/^(?:[a-z]:)?\/*//i;
+ $exclfiles .= ($exclfiles ne "" ? "|" : "") . Wcard2Restr($file);
}
+ push(@addfiles, map(GetRelFname($_, $base), Find($base, "*", "/^\\/(?:$exclfiles)\$/i", 1, 0, local $_)));
+ }
-# imaker::DPrint(1, "\nTotal duration: " . imaker::Sec2Min(time() - $start) . "\n");
- exit($error || 0);
+ $prefix =~ s/[\/\\]+$//;
+ WriteFile($exclfile, join("", map("$_\n", @exclfiles,
+ map(s/^(?:[a-z]:)?\\*/$prefix\\/i ? $_ : $_, map(tr/\//\\/ ? $_ : $_, @addfiles)))), "u", "q");
+}
+
+sub GenIbyfile($$$)
+{
+ return if SkipICmd();
+ my ($ibyfile, $ibystr, $oride, $prevoride) = (shift(), "", "", "");
+
+ map {
+ die("GenIbyfile: Invalid file list configuration: `$_'\n"), return
+ if !/^\s*(?:"(.+?)"|(\S+))\s+(?:"(.+?)"|(\S+))\s*$/;
+ $_ = [defined($1) ? $1 : $2, defined($3) ? $3 : $4];
+ } (my @files = map(Unquote($_), grep(!/^\s*(?:#.*)?$/, split(/(?<!\\)\\n/, shift()))));
+
+ my @ibyconf = map(Unquote($_), grep(!/^\s*(?:#.*)?$/, split(/(?<!\\)\\n/, shift())));
+
+ foreach (@ibyconf) {
+ die("GenIbyfile: Invalid configuration: `$_'\n"), return
+ if !/^\s*(?:"(.+?)"|(\S+))\s+(hide|remove|(?:replace|udeb|urel)(?:-add)?)\s+(\*|core|rofs[2-6])\s*$/i;
+ next if ($4 ne "*") && (uc($4) ne $gImgtype);
+ my $action = lc($3);
+ my $file = Wcard2Restr(defined($1) ? $1 : $2);
+ $file = qr/(?:^|\\|\/)$file$/i;
+ foreach (@files) {
+ next if (@$_[1] !~ $file);
+ $oride = ($action =~ /add$/ ? "ADD" : ($action eq "hide" ? "" : "SKIP"));
+ my $src = ($action eq "remove" ? "empty" : @$_[0]);
+ if ($action =~ /^udeb/) {
+ $src =~ s/(?<=[\/\\])urel(?=[\/\\])/udeb/i;
+ } elsif ($action =~ /^urel/) {
+ $src =~ s/(?<=[\/\\])udeb(?=[\/\\])/urel/i;
+ }
+ $ibystr .= ($prevoride && ($oride ne $prevoride) ? "OVERRIDE_END\n" : "") .
+ ($oride && ($oride ne $prevoride) ? "OVERRIDE_REPLACE/$oride\n" : "") .
+ ($oride ? "override=\"$src\" " : "hide=") . "\"@$_[1]\"\n";
+ $prevoride = $oride;
+ }
+ }
+ WriteFile($ibyfile, ($ibyfile =~ /^>>([^>].*)$/ && -f($1) ? "" : "// Generated `$ibyfile'") .
+ "\n\n/* Custom override configuration\n" . join("\n", @ibyconf) . "\n*/\n$ibystr" .
+ ($oride ? "OVERRIDE_END\n" : ""), "", "q");
+}
+
+sub GenObyfile($$$$@)
+{
+ return if SkipICmd();
+
+ my ($ibyfile, $srcdir, $subdir, $finddir) = (GetAbsFname(shift()), shift(), shift(), shift());
+ my ($header, $footer, $body, %files) = ("", "", "", ());
+
+ foreach my $dir (split(/\s+/, $srcdir)) {
+ $dir = GetAbsDirname($dir);
+ my ($found, $total, $lines) = (0, 0, "");
+ my @param = @_;
+ while (@param) {
+ my ($filepat, $format, @lines) = (shift(@param), shift(@param), ());
+ $header = $format, next if $filepat =~ /^__header__$/i;
+ $footer = $format, next if $filepat =~ /^__footer__$/i;
+ foreach my $src (Find($dir, $filepat, "", $subdir, $finddir, $total)) {
+ next if $files{$src};
+ $files{$src} = 1;
+ (my $line = $format) =~ s/%1/$src/g;
+ $line =~ s/%2/GetRelFname($src, $dir, 1)/ge;
+ $line =~ s/%3/GetAbsFname($src)/ge;
+ if ($line =~ /%4/) {
+ my $attrib = "";
+ if ($gWinOS) {
+ Win32::File::GetAttributes($src, $attrib);
+ $attrib = (($attrib & WIN32_FILE_HIDDEN) ? "attrib=H" : "");
+ }
+ $line =~ s/%4/$attrib/ge;
+ }
+ push(@lines, Trim($line));
+ }
+ $found += @lines;
+ $lines .= "//\n// Format: `$format', " . @lines . ($finddir ? " empty directories" : " files") .
+ ": `$filepat'\n" . (@lines ? "//\n" . join("\n", @lines) . "\n" : "");
+ }
+ $body .= "\n// Collected entries $found/$total from directory `$dir'" .
+ ($subdir ? " and subdirectories" : "") . "\n$lines";
}
- #==========================================================================
+ my $append = ($ibyfile =~ s/^>>(?!>)// && -f($ibyfile) && ">>" || "");
+ (my $fname = "__" . uc(GetBasename($ibyfile)) . "__") =~ s/\W/_/g;
+ my @previby = ();
+
+ if ($append) {
+ OpenFile(*FILE, $ibyfile, 0) or die("Can't read file `$ibyfile'.\n"), return;
+ @previby = <FILE>;
+ close(FILE);
+ $previby[0] =~ s/(, collected )(\d+)( entries)$/$1.($2 + keys(%files)).$3/e;
+ $previby[@previby - 1] = "";
+ }
+ WriteFile($ibyfile, join("", @previby) . ($append ? "// Appended" : "// Generated") .
+ " `$append$ibyfile', collected " . keys(%files) . " entries\n" .
+ ($append ? "" : "\n#ifndef $fname\n#define $fname\n") .
+ ($header ? Unquote("\\n$header\\n") : "") . $body . ($footer ? Unquote("\\n$footer\\n") : "") .
+ "\n#endif // $fname\n", "", "q");
+}
+
+sub GenWidgetConf($$$$)
+{
+ return if SkipICmd();
+ my ($wgzini, $ini, $dir) = (shift(), GetAbsFname(shift()), GetAbsDirname(shift()));
+ my @ini = ($ini eq "" ? () : ReadFile($ini, 0));
+ my $files = ($dir eq "" ? "" : join("\n", Find($dir, "*", '/\/(?:' . join("|",
+ map(GetBasename($_), ($ini, map(!/^\s*[#[]/ && /^\s*(?:"(.+?)"|(\S+))/ &&
+ -e(local $_ = (defined($1) ? $1 : $2)) ? $_ : (), @ini)))) . ')$/i', 0, 0, local $_)));
+
+ WriteFile($wgzini, Unquote(shift()) .
+ (@ini ? "# Copied lines from `$ini':\n" . join("\n", @ini) : "") . "\n" .
+ ($files ? (@ini ? "\n" : "") . "# Collected files from `$dir':\n$files\n" : ""), "", "q");
+}
+
+
+###############################################################################
+#
+
+sub GenMakefile($$$$$)
+{
+ return if SkipICmd();
+ my ($hdrfile, $mkfile, $filter, $prepros, $assignop) =
+ (GetAbsFname(shift()), GetAbsFname(shift()), shift(), shift(), shift());
+ ChangeDir(GetDirname($hdrfile));
+ RunSystemCmd("$prepros " . GetBasename($hdrfile));
+ my $maxdef = Max(map(/^\s*\#define\s+($filter)/ && length($1), @gCmdoutbuf));
+ WriteFile($mkfile, join('\n',
+ map(/^\s*\#define\s+($filter)\s*(.*?)\s*$/ ? sprintf("%-${maxdef}s $assignop %s", $1, $2 eq "" ? 1 : $2) : (), sort(@gCmdoutbuf))) . '\n', "");
+}
+
+
+###############################################################################
+#
+
+sub AddImageHeader($$$$$)
+{
+ return if SkipICmd();
+ my ($file, $hdrfile, $hdrstr, $hdrsize, $align) =
+ (GetAbsFname(shift()), GetAbsFname(shift()), shift(), shift(), shift());
+
+ $hdrstr =~ s/\/\*.*?\*\///g;
+ $hdrstr =~ s/,\s*$//;
+ WriteFile($hdrfile, $hdrstr, "b");
+ die("Invalid image header size: " . sprintf("0x%X", -s($hdrfile)) . " (!=$hdrsize).\n"), return
+ if -s($hdrfile) ne hex($hdrsize);
+
+ $align = Max(hex($align), hex($hdrsize)) - hex($hdrsize);
+ WriteFile(">>$hdrfile", ("0," x ($align - 1)) . "0", "b") if $align;
+ Copy($file, ">>$hdrfile") if $file ne "";
+}
+
+
+###############################################################################
+#
- my ($opt_cmdfile, $opt_incdir, $opt_logfile, $opt_printcmd, $opt_step, $opt_verbose, $opt_workdir) =
- ( "", "", "", 0, "", 1, ".");
- Getopt::Long::GetOptions(
- "cmdfile=s" => \$opt_cmdfile,
- "incdir=s" => \$opt_incdir,
- "logfile=s" => \$opt_logfile,
- "printcmd" => \$opt_printcmd,
- "step=s" => \$opt_step,
- "verbose=s" => \$opt_verbose,
- "workdir=s" => \$opt_workdir,
- "<>" => sub { $error .= ($error ? ", `@_'" : "Unknown imaker.pl option: `@_'") });
+sub Sleep($)
+{
+ return if SkipICmd();
+ sleep(shift());
+}
+
+
+###############################################################################
+#
+
+sub FindSOSFiles($$$$)
+{
+ return if SkipICmd();
+
+ my ($dirs, $imgoby, $pluglog, $opt) = @_;
+ my ($file, %files) = ("", ());
+ local $_;
+
+ foreach my $dir (GlobFiles($dirs)) {
+ my ($featvar, @pluglog) = ("", Find($dir = GetAbsDirname($dir), $pluglog, "", 1, 0, $_));
+
+ foreach $file (@pluglog) {
+ OpenFile(*FILE, $file, 0) or warn("Can't read file `$file'.\n"), last;
+ while (<FILE>) {
+ last if !/^.+?\.pm: Initializing; /;
+ $featvar = $1, last if / feature variant = `(.+)'$/;
+ }
+ close(FILE);
+ last if ($featvar ne "");
+ }
- if ($opt_incdir) {
- my $bsf = ($opt_incdir =~ s/:bsf$//);
- print(map("$_\n", imaker::GetFeatvarIncdir($opt_incdir, $bsf)));
- exit;
+ foreach $file (Find($dir, $imgoby, "", 1, 0, $_)) {
+ OpenFile(*FILE, $file, 0) or warn("Can't read file `$file'.\n"), last;
+ while (<FILE>) {
+ next if ($_ !~ FILESPECSTATEMENT) && ($_ !~ BOOTBINARYSTATEMENT);
+ $file = GetAbsFname(defined($1) ? $1 : $2);
+ $files{lc($file)} = $file if !exists($files{lc($file)});
+ next if ($file !~ s/\.[0-9a-f]{32}\./\./i);
+ $file .= (-f("$file.$featvar.vmap") ? ".$featvar.vmap" : ".vmap");
+ $files{lc($file)} = $file if !exists($files{lc($file)});
+ }
+ close(FILE);
+ }
+
+ my ($incfile, $spifile, $plugfile, $patchfile) = (0, 0, 0, 0);
+ foreach $file (@pluglog) {
+ OpenFile(*FILE, $file, 0) or warn("Can't read file `$file'.\n"), last;
+ while (<FILE>) {
+ $incfile = 1, next if /^Finding include hierarchy from /;
+ $incfile = 0, next if ($incfile && /^Found \d+ different include files$/);
+ $spifile = 1, next if /^Finding SPI input files from /;
+ $spifile = 0, next if ($spifile && /^Found \d+ SPI input files$/);
+ $plugfile = 1, next if /^Reading (ROM|ROFS1|UDEB|UREL) files from /;
+ $plugfile = 0, next if ($plugfile && /^Found \d+ entries$/);
+ $patchfile = 1, next if /^Finding ROM-patched components$/;
+ $patchfile = 0, next if ($patchfile && /^Found \d+ ROM-patched components$/);
+ $files{lc($file)} = $file, next
+ if (($incfile || $spifile || $plugfile) && /`(.+)'$/ && !exists($files{lc($file = GetAbsFname($1))}));
+ next if (!$patchfile || !/^`(.+)'$/);
+ $file = GetAbsFname($1) . ".map";
+ $files{lc($file)} = $file, next if -f($file);
+ $file =~ s/(\..*?\.map)$/\.\*$1/;
+ foreach (glob($file =~ /\s/ ? "\"$file\"" : $file)) {
+ ($file = lc()) =~ s/\.map$//;
+ $files{lc()} = $_, last if exists($files{$file});
+ }
+ }
+ close(FILE);
+ }
+
+ $dir .= "/" if $dir !~ /\/$/;
+ foreach $file (keys(%files)) {
+ delete($files{$file}) if ($file =~ /^$dir/i);
+ }
}
- $opt_verbose = imaker::SetVerbose($opt_verbose);
+ @gFindresult = () if (!defined($opt) || $opt !~ /a/);
+ push(@gFindresult, values(%files));
+}
+
+
+###############################################################################
+#
+
+sub CheckTool(@)
+{
+ return if SkipICmd();
+ my ($maxtlen, $maxvlen, @tools) = (4, 9, ());
+ while (@_) {
+ my ($tool, $vquery, $getver, $version, $md5sum) = (shift(), shift(), shift(), " -", " ?");
+ if (length($vquery) > 1) {
+ RunSystemCmd($vquery, 3, 1);
+ $version = (join("\n", @gCmdoutbuf) =~ eval($getver =~ /^\// ? "qr$getver" : "qr/$getver/ims") ?
+ (defined($1) && defined($2) && "`$1 $2'" || defined($1) && "`$1'" || " ?") : " ?");
+ }
+ OpenFile(*FILE, $tool, 1) and $md5sum = "`" . md5_hex(<FILE>) . "'";
+ close(FILE);
+ $maxtlen = Max($maxtlen, length($tool));
+ $maxvlen = Max($maxvlen, length($version));
+ push(@tools, "`$tool'", $version, $md5sum);
+ }
+ $maxtlen += 2;
+ @_ = (" Tool", " Version", " MD5 Checksum", "-" x $maxtlen, "-" x $maxvlen, "-" x 34, @tools);
+ DPrint(1, sprintf("%-${maxtlen}s %-${maxvlen}s ", shift(), shift()) . shift() . "\n") while(@_);
+}
+
+
+###############################################################################
+#
+
+sub OpCacheInstall($$$)
+{
+ return if SkipICmd();
+ my ($ini, $conf, $tmpdir) = @_;
+ my %opt = (-e => "", -i => "", -m => "", -o => "", -u => "");
- imaker::DPrint(2, "=" x 79 . "\nTIME: " . localtime() . ", USER: " . getlogin() .
- ", HOST: " . ($ENV{HOSTNAME} || $ENV{COMPUTERNAME} || "?") . "\n$^X (v$perlver-$^O)\n");
+ foreach $conf ("opcache_config=$conf", ($ini ne "" ? grep(!/^\s*#/, ReadFile($ini, 0)) : ())) {
+ (local $_, my $error, my %tmpopt) = ($conf, 0, %opt);
+ if (!($error = !(s/^\s*opcache_config\s*[=\s]//i || s/^\s*opcache_content\s*[=\s]/-i /i))) {
+ my @opt = ParseCmdWords($_);
+ while (@opt) {
+ last if ($error = ((($_ = shift(@opt)) !~ /^-[eimou]$/i) ||
+ !defined($tmpopt{$_} = shift(@opt))));
+ $tmpopt{$_} =~ s/EPOCROOT/$gEpocroot/g;
+ }
+ }
+ die("OpCacheInstall: Invalid configuration entry: `$conf'\n"), next if $error;
+ %opt = %tmpopt;
+ }
+ if (-d($opt{-i})) {
+ $opt{-i} = GetAbsDirname($opt{-i});
+ } elsif (-f($opt{-i})) {
+ DeleteDir($tmpdir);
+ MakeDir($tmpdir);
+ RunSystemCmd("$gTool{unzip} x -y \"" . GetAbsFname($opt{-i}) . "\"" .
+ " -o\"" . ($tmpdir = GetAbsDirname($tmpdir)) . "\"", 0, 1);
+ $opt{-i} = $tmpdir;
+ }
+ RunSystemCmd("$gTool{opcache} -u \"$opt{-u}\" -e \"$opt{-e}\" -m \"" .
+ GetAbsFname($opt{-m}) . "\" -i \"$opt{-i}\" -o \"" . GetAbsDirname($opt{-o}) . "\"");
+}
+
+
+###############################################################################
+#
+
+sub SisInstall($$$$$$$$)
+{
+ return if SkipICmd();
+
+ my ($ini, $intini, $conf, $hda, $hdata, $idata, $outdir, $log) =
+ (GetAbsFname(shift()), GetAbsFname(shift()), shift(), GetAbsFname(shift()),
+ shift(), shift(), GetAbsDirname(shift()), shift());
+ my %gopt = (-d => "C", -k => "5.4", -w => "info", '--ignore-err' => 0);
- imaker::SetLogfile($opt_logfile);
- die("$error.\n") if $error;
+ my %haldata = ();
+ map { $haldata{uc($1)} = $2 if /^\s*(\S+)\s+(\S+)\s*$/ } split(/(?<!\\)\\n/, $hdata);
+ $gOutfilter = '\S';
+ RunSystemCmd("$gTool{cpp} -nostdinc -undef \"$hda\"", 1, 1, $log) if ($hda ne "");
+
+ local @_ = (map(!/^\s*E(\S+)\s*=\s*(\S+)\s*$/ ? () : (uc($1) . " = " .
+ (exists($haldata{uc($2)}) ? $haldata{uc($2)} : (exists($haldata{uc("E$1_$2")}) ?
+ $haldata{uc("E$1_$2")} : $2)) . "\n"), @gCmdoutbuf),
+ map(/^\s*$/ ? () : Trim($_) . "\n", split(/(?<!\\)\\n/, $idata)));
+
+ WriteFile($intini, join("", @_), "", "q");
+ RunSystemCmd("$gTool{interpretsis} -i \"$intini\"", 3, 1);
+ map { $_[$1 - 1] = undef if /Unsupported keyword.+?(\d+)/i } @gCmdoutbuf;
+ WriteFile($intini, join("", grep(defined(), @_)), "", "q");
+
+ my ($clean, @dir) = (0, Find($outdir, "*", "", 1, 1, $_));
+ @_ = ("sis_config=$conf", ($ini ne "" ? grep(!/^\s*#/, ReadFile($ini, 0)) : ()), "sis_content=");
+
+ for (my $i = 0; $i < @_; $i++) {
+ local $_ = $_[$i];
+ my ($error, $global, $runtool, %opt) = (0, 0, 0, %gopt);
+ if (!($error = !(s/^\s*sis_(config)\s*[=\s]//i || s/^\s*sis_(content)\s*[=\s]/-s /i))) {
+ $global = ($1 =~ /config/i);
+ my @opt = ParseCmdWords($_);
+ while (@opt) {
+ $_ = shift(@opt);
+ shift(@opt) if ((my $next = (@opt ? ($opt[0] !~ /^!?[-+]/ ? $opt[0] : "") : "")) ne "");
+ next if /^!?-[cilwx]$/;
+ if (s/^!//) { delete($opt{$_}) }
+ else {
+ $_[$#_] .= "\"$next\"", next if (!$i && /^-s$/);
+ ($opt{$_} = $next) =~ s/EPOCROOT/$gEpocroot/g;
+ $runtool = ($next !~ /^\s*$/) if /^-s$/;
+ }
+ }
+ }
+ die("SisInstall: Invalid configuration entry: `$_[$i]'\n"), next if $error;
+ %gopt = %opt if $global;
+ next if !$runtool;
- foreach (split(/-+/, $opt_step)) {
- $error .= ($error ? ", `$_'" : "Unknown imaker.pl step: `$_'")
- if (!/^\w+:?([cbk\d]+)?$/i) || $1 && ($1 =~ /c.*c|b.*b|k.*k|\d[^\d]+\d/i);
+ foreach (-d($opt{-s}) ? Find($opt{-s}, '/\.sisx?$/i', "", 0, 0, $_) : (GetAbsFname($opt{-s}))) {
+ ($opt{-s}, my $puid) = ($_, "?");
+ OpenFile(*SISFILE, $_, 1, "") and sysread(SISFILE, $puid, 3 * 4) and
+ $puid = sprintf("%08X", unpack("V", substr($puid, 8, 4)));
+ close(SISFILE);
+ DPrint(16, "SisInstall: `$_', pUID: $puid" . ($opt{'--ignore-err'} ? ", ignore errors\n" : "\n"));
+
+ my $icmd = $gTool{interpretsis} . (join("", map(($opt{$_} ne "" ? " $_ \"$opt{$_}\"" : " $_"),
+ sort({lc($a) cmp lc($b)} grep(/^-[^s]/ && !/^--ignore-err$/, keys(%opt)))))) .
+ " -c \"" . (GetAbsDirname($outdir)) . "\" -i \"" . (GetAbsFname($intini)) . "\"";
+ $error = RunSystemCmd("$icmd -s \"$opt{-s}\"" . join("", map(" $_",
+ sort({lc($a) cmp lc($b)} grep(/^\+/, keys(%opt))))), 1, 1, ">>$log");
+ my $errmsg = join(" | ", grep(s/^ERR\s*:\s*//, @gCmdoutbuf));
+
+ $_ = join(", ", map(/^INFO:\s+Installing file:\s+\w:\\sys\\bin\\(.+?.exe)\s*$/io &&
+ ($_ = $1) && (qx($gTool{elf2e32} --dump=h --e32input "$outdir/sys/bin/$_") =~
+ /^Uids:\s+.+?\s+([0-9a-f]+)\s+\(/imo) ? "$_: " . uc($1) : (), @gCmdoutbuf));
+ DPrint(16, "SisInstall: `" . GetBasename($opt{-s}) . "', exe UIDs: $_\n")
+ if ($_ && (!($error ||= $errmsg) || $opt{'--ignore-err'}));
+
+ warn("Installation of SIS file `$opt{-s}' failed" . ($errmsg ? ": `$errmsg'.\n" : ".\n"))
+ if ($gErrwarn = $error);
+ next if (!$error || $opt{'--ignore-err'});
+ $clean = 1;
+ warn("Removing installation of SIS file `$opt{-s}'.\n");
+ RunSystemCmd("$icmd -x $puid", 3, 1, ">>$log");
+ }
}
- die("$error.\n") if $error;
+ return if !$clean;
+ my $i = 0;
+ foreach (Find($outdir, "*", "", 1, 1, $_)) {
+ if (($i <= $#dir) && ($_ eq $dir[$i])) { $i++ }
+ else { DeleteDir($_) }
+ }
+}
+
+
+###############################################################################
+#
+
+sub GetIPar(;$)
+{
+ my $par = shift(@gIcmd);
+ $par = ((my $empty = !defined($par)) ? "<UNDEFINED>" : PEval($par));
+ $gParamcnt = 0 if shift();
+ DPrint(32, "iPar: $gParamcnt. `$par'\n") if $gParamcnt && ($gICmd =~ $gFiltercmd);
+ $gParamcnt++;
+ return($empty ? undef : $par);
+}
- imaker::SetWorkdir($opt_workdir);
- imaker::ReadICmdFile($opt_cmdfile);
+sub PEval($)
+{
+ local $_ = shift();
+ while (/\@PEVAL{.*}LAVEP\@/) {
+ my $start = rindex($_, '@PEVAL{', my $end = index($_, '}LAVEP@') + 7);
+ my ($expr, $eval, $evalerr) = (substr($_, $start + 7, $end - $start - 14), undef, "");
+ eval {
+ local $_;
+ local $gEvalerr = (SkipICmd() ? 1 : 2);
+ $eval = eval($expr);
+ ($evalerr = $@) =~ s/^(.+?) at .*/$1/s;
+ };
+# DPrint(64, "PEval: Evaluate `$expr' = `" . (defined($eval) ? $eval : "") . "'\n");
+ if (!defined($eval)) {
+ $eval = "";
+ warn("PEval: Evaluation of `$expr' failed: $evalerr.\n") if !SkipICmd();
+ }
+ substr($_, $start, $end - $start) = $eval;
+ }
+ return($_);
+}
+
+sub PeekICmd($)
+{
+ return(defined($gIcmd[0]) && $gIcmd[0] =~ /^$_[0]$/i);
+}
+
+sub SkipICmd()
+{
+ return($gPrintcmd || defined($gICmd) && ($gICmd !~ $gFiltercmd));
+}
- my (@step, @stepdur) = (split(/-+/, lc($opt_step)), ());
- my ($durstr, $maxslen, $maxdlen) = ("", 6, 8);
+sub GetICmd()
+{
+ $gICmd = GetIPar(1);
+ DPrint(32, "iCmd: " . ++$gCmdcnt . ". `$gICmd'\n") if defined($gICmd) && ($gICmd ne "") && ($gICmd =~ $gFiltercmd);
+}
+
+sub EndICmd()
+{
+ GetICmd(), return(1) if !defined($gIcmd[0]) || PeekICmd("end");
+ return(0);
+}
+
+
+###############################################################################
+#
+
+sub SplitStep($)
+{
+ (my $step = shift()) =~ s/(?<!(\\|\s))\|/ \|/g; # ???
+ return(map((s/^\s+|(?<!\\)\s+$//g, s/\\\|/\|/g) ? $_ : $_, split(/(?<!\\)\|/, "$step ")));
+}
+
+sub RunStep($)
+{
+ ($gStep, my $dur, @gStepDur) = (shift(), time(), ());
+ ChangeDir($gWorkdir);
+ DPrint(2, "=" x 79 . "\nENTER: `$gStep'\n");
+
+ push(@gReport, $gLogfile ? ("iMaker log", $gLogfile =~ /^>>?([^>].*)$/ ? $1 : $gLogfile, "f") : (),
+ SplitStep($gStepIcmd{"REPORT_$gStep"})) if exists($gStepIcmd{"REPORT_$gStep"});
+
+ foreach my $step ("INIT_$gStep", "CLEAN_$gStep", "BUILD_$gStep") {
+ next if (!exists($gStepIcmd{$step}) || $gStepIcmd{$step} =~ /^\s*$/);
+ DPrint(64, "$step = `$gStepIcmd{$step}'\n");
+ @gIcmd = SplitStep($gStepIcmd{$step});
+ my ($file, $iferror, @iffi) = ("", 0, ());
- foreach my $stepnum (0 .. $#step) {
- $step[$stepnum] =~ /^(\w+):?([cbk\d]+)?$/;
- my $step = uc($1);
- $_ = (defined($2) ? $2 : "");
- my @dur = imaker::MakeStep($step, /c/, /b/, /k/, /(\d+)/ ? $1 : $opt_verbose, $opt_printcmd);
- imaker::SetVerbose($opt_verbose);
- my ($cmddur, $stepdur) = (0, pop(@dur));
- $durstr = imaker::Sec2Min($stepdur);
- if (@dur) {
- $durstr .= " (";
- foreach my $dur (@dur) {
- $cmddur += $dur;
- $durstr .= imaker::Sec2Min($dur) . " + ";
+ while (GetICmd(), defined($gICmd)) {
+ next if (local $_ = lc($gICmd)) eq "";
+ if (/^if$/) {
+ push(@iffi, (my $if = GetIPar()), $gFiltercmd);
+ $gFiltercmd = qr/^X$/ if !$if;
+ }
+ elsif (/^else$/) {
+ $gFiltercmd = ($iffi[$#iffi - 1] ? qr/^X$/ : $iffi[$#iffi]);
+ }
+ elsif (/^fi$/) {
+ $gFiltercmd = pop(@iffi);
+ pop(@iffi);
+ }
+ elsif (/^(error|warning)$/) {
+ my ($errwarn, $msg) = (GetIPar(), GetIPar() . "\n");
+ next if SkipICmd();
+ die($msg) if $errwarn && /e/;
+ warn($msg) if $errwarn && /w/;
+ }
+ elsif (/^echo(\d+)?(-q)?$/) {
+ Echo((defined($1) && ($1 < 128) ? $1 : 1), GetIPar(), defined($2));
+ }
+ elsif (/^filter$/) {
+ $gOutfilter = GetIPar();
+ }
+ elsif (/^cmd(tee)?(-(k[0123]?|n)+)?$/) {
+ RunSystemCmd(GetIPar(), (/k(\d)/ ? int($1) : (/k/ ? 1 : 0)), /n/, /tee/ ? GetIPar() : "");
+ }
+ elsif (/^parse(f)?(?:-(\d+))?$/) {
+ ParseSystemCmd(GetIPar(), GetIPar(), GetIPar(), $1, $2);
+ }
+ elsif (/^(cd|copy(dir|iby)?|del(dir)?|find(dir)?(-[afr]+)?|headb|logfile|mkcd|mkdir|move|tailb|test|touch|type[bu]?|unzip|workdir|write[bu]?(-[cq]+)?|zip(dir)?(-[jq]+)?)$/) {
+ my @files = GlobFiles(GetIPar());
+ my $par1 = GetIPar() if /^(?:copy|find|head|move|tail|touch|(un)?zip|write)/;
+ my $par2 = GetIPar() if /^(?:find|head|tail|zip)/;
+ next if SkipICmd();
+ @gFindresult = () if /find(?:dir)?(-[afr]+)?/ && (!defined($1) || ($1 !~ /a/));
+ Touch($par1, @files), next if /touch/;
+ foreach $file (@files) {
+ ChangeDir($file) if /^cd/;
+ DeleteDir($file) if /deldir/;
+ FindDir($file, $par1, $par2, $1) if /finddir(-[ar]+)?/;
+ MakeDir($file) if /mkdir/;
+ MakeChangeDir($file) if /mkcd/;
+ SetWorkdir($file) if /workdir/;
+ Zip($file, 1, $1, $par2, GlobFiles($par1)) if /zipdir(-[jq]+)?/;
+ DeleteFile($file) if /del/;
+ FindFile($file, $par1, $par2, $1) if /find(-[afr]+)?$/;
+ HeadFile($file, $par1, $par2) if /headb/;
+ SetLogfile($file) if /logfile/;
+ TailFile($file, $par1, $par2) if /tailb/;
+ TypeFile($file, $1) if /type(b|u)?/;
+ UnzipFile($file, $par1) if /unzip/;
+ WriteFile($file, $par1, $1, $2) if /write(b|u)?(-[cq]+)?/;
+ Zip($file, 0, $1, $par2, GlobFiles($par1)) if /^zip(-[jq]+)?$/;
+ Copy($file, $par1, $1) if /copy(dir)?$/;
+ CopyIby($file, $par1) if /copyiby/;
+ Move($file, $par1) if /move/;
+ Test($file) if /test/;
+ }
+ }
+ elsif (/^filtercmd$/) {
+ $gFiltercmd = GetIPar();
+ $gFiltercmd = ($gFiltercmd eq "" ? qr/\S/ : qr/$gFiltercmd/i);
+ }
+ elsif (/^genexclst$/) {
+ GenExclfile(GetIPar(), GetIPar(), GetIPar(), GetIPar(), GetIPar());
+ }
+ elsif (/^geniby(-[dr]+)?$/) {
+ my ($opt, $iby, $dir, @par) = ($1 || "", GetIPar(), GetIPar(), ());
+ push(@par, GetIPar(), GetIPar()) while !EndICmd();
+ GenObyfile($iby, $dir, $opt =~ /r/, $opt =~ /d/ ? 2 : 0, @par);
+ }
+ elsif (/^genorideiby$/) {
+ GenIbyfile(GetIPar(), GetIPar(), GetIPar());
+ }
+ elsif (/^genmk$/) {
+ GenMakefile(GetIPar(), GetIPar(), GetIPar(), GetIPar(), GetIPar());
+ }
+ elsif (/^genwgzcfg$/) {
+ GenWidgetConf(GetIPar(), GetIPar(), GetIPar(), GetIPar());
+ }
+ elsif (/^iferror$/) {
+ $iferror++;
+ $gError = 0, next if $gError;
+ while (defined($gIcmd[0])) {
+ GetICmd(), last if PeekICmd("endif") && !--$iferror;
+ $iferror++ if shift(@gIcmd) =~ /^iferror$/i;
+ }
+ }
+ elsif (/^endif$/ && $iferror--) {
+ }
+ elsif (/^imghdr$/) {
+ AddImageHeader(GetIPar(), GetIPar(), GetIPar(), GetIPar(), GetIPar());
+ }
+ elsif (/^pause$/) {
+ DPrint(0, "Press Enter to continue...\n");
+ getc();
+ }
+ elsif (/^sleep$/) {
+ Sleep(GetIPar());
+ }
+ elsif (/^sosfind(-a)?$/) {
+ my $opt = $1;
+ FindSOSFiles(GetIPar(), GetIPar(), GetIPar(), $opt);
+ }
+ elsif (/^tool-(\w+)$/) {
+ $gTool{$1} = GetIPar();
+# DPrint(2, "SetTool: $1: `$gTool{$1}'\n");
+ }
+ elsif (/^toolchk$/) {
+ my @tools = ();
+ push(@tools, GetIPar(), GetIPar(), GetIPar()) while !EndICmd();
+ CheckTool(@tools);
+ }
+ elsif (/^opcache$/) {
+ OpCacheInstall(GetIPar(), GetIPar(), GetIPar());
+ }
+ elsif (/^sisinst$/) {
+ SisInstall(GetIPar(), GetIPar(), GetIPar(), GetIPar(),
+ GetIPar(), GetIPar(), GetIPar(), GetIPar());
+ }
+ elsif (!$gImakerext || !RunIExtCmd($_)) {
+ die("Unknown iMaker command `$gICmd'.\n");
}
- $durstr .= imaker::Sec2Min($stepdur - $cmddur) . ")";
+ }
+ }
+ DPrint(2, "EXIT: `$gStep', duration: " . Sec2Min($dur = time() - $dur) . "\n");
+ push(@gStepDur, $dur);
+}
+
+
+###############################################################################
+#
+
+sub GetConfmkList(;$)
+{
+ if (!%gConfmkList) {
+ my ($dir, $incl, $excl, $depth) = split(/,/, $ENV{IMAKER_MKCONF});
+ $dir = GetAbsDirname($dir, 0, 1, $gEpocdrive);
+ ($incl, $excl) = (qr/$incl/, qr/$excl/);
+ local $_;
+ DPrint(16, "FindFile: GetConfmkList: `$ENV{IMAKER_MKCONF}'");
+ find(sub { $gConfmkList{$1} = $File::Find::name
+ if (/$incl/ && !/$excl/ && (($File::Find::name =~ tr/\///) > (($dir =~ tr/\///) + $depth)));
+ }, $dir);
+ DPrint(16, ", found " . keys(%gConfmkList) . " files\n");
+ $gConfmkList{""} = "" if !%gConfmkList;
+ }
+ return(sort({lc($a) cmp lc($b)} grep($_ ne "", values(%gConfmkList)))) if shift();
+}
+
+sub GetFeatvarIncdir($)
+{
+ open(FILE, "$gEpocroot/epoc32/tools/variant/" . shift() . ".var") or
+ return("Invalid SBV feature variant");
+ my @featdata = <FILE>;
+ close(FILE);
+ my @incdir = ("@featdata" =~ /^\s*EXTENDS\s+(.+?)\s*$/m ? GetFeatvarIncdir($1) : ());
+ @incdir = () if ("@incdir" =~ /^Invalid/);
+ foreach (@featdata) {
+ next if !/^\s*ROM_INCLUDE\s+(\S+)\s+(.+?)\s*$/;
+ if ($1 eq "set") { @incdir = ($2) }
+ elsif ($1 eq "prepend") { unshift(@incdir, $2) }
+ elsif ($1 eq "append") { push(@incdir, $2) }
+ }
+ return(map("$_/" =~ /^$gEpocroot\// ? $_ : $gEpocroot . PathConv($_, 0, 1, $gEpocdrive),
+ map(PathConv($_, 0, 0, $gEpocdrive), @incdir)));
+}
+
+
+###############################################################################
+#
+
+sub SetVerbose($;$)
+{
+ my $verbose = Trim(shift());
+ $verbose = 127 if $verbose =~ /^debug$/i;
+ $gVerbose = int($1), return if ($verbose =~ /^(\d+)$/) && ($1 < 128);
+ $gVerbose = 1;
+ warn("Verbose level `$verbose' is not integer between 0 - 127\n") if !shift();
+}
+
+sub CloseLog()
+{
+ close(LOG) if $gLogfile;
+ $gLogfile = "";
+}
+
+
+###############################################################################
+#
+
+sub RunIMakerCmd($$$$$@)
+{
+ my ($makecmd, $cmdarg, $tgtext, $mklevel, $skipsteps, %prevtgt) = @_;
+ $ENV{IMAKER_MKLEVEL} = $mklevel;
+
+ ($cmdarg, my $hptgt, my @targets) = HandleCmdArg($cmdarg);
+
+ foreach my $tgt (@targets) {
+ my $skipstep = ($tgt =~ s/#$//) || $skipsteps;
+ (my $target = "$tgt$tgtext") =~ s/(\[\d+\])(.+)$/$2$1/;
+ if ($target eq "menu") {
+ ($cmdarg, $target) = Menu($cmdarg);
+ next if ($target eq "menu");
+ ($cmdarg) = HandleCmdArg($cmdarg);
}
- $step = sprintf("%" . length(@step."") . "s", $stepnum + 1) . ". $step";
- push(@stepdur, $step, $durstr);
- $maxslen = imaker::Max($maxslen, length($step));
- $maxdlen = imaker::Max($maxdlen, length($durstr));
+ $prevtgt{$target =~ /^([^-]+)/ ? $1 : $target} = 1;
+ push(@gReport, Trim((($target !~ /^(.+)\[\d+\]$/) || ($gVerbose & 64) ? $target : $1) .
+ ($skipstep ? "#" : "") . " $hptgt"), -1, -$mklevel - 1);
+ my $tgtind = $#gReport;
+ my @targets = RunMakeCmd("$makecmd $cmdarg" . ($target eq "defaultgoals" ? "" : " \"$target\"") .
+ join("", map(" \"$_\"", split(/\s+/, $hptgt))), $skipstep);
+ $gReport[$tgtind - 2] .= " (intermediate)" if @targets;
+ $gReport[$tgtind - 1] = pop(@gStepDur);
+ $gReport[$tgtind] = $mklevel + 1 if !$gError;
+ delete(@gReport[$tgtind - 2 .. $tgtind]) if (@targets && !$gError && !($gVerbose & 64));
+ map {
+ RunIMakerCmd($makecmd, "$cmdarg $_ $hptgt", $target =~ /(-.*)$/ ? $1 : "", $mklevel + 1, $skipstep, %prevtgt)
+ if !exists($prevtgt{$_});
+ } @targets;
+ }
+}
+
+sub RunMakeCmd($$)
+{
+ ($gStartmk, $gMakecmd, $gError) = (time(), Trim(shift()), 0);
+ my ($skipstep, $mkstart, $start, $restart, $cwd, %env) = (shift(), 0, 0, 0, Cwd::cwd(), %ENV);
+ my @stepdur = my @targets = ();
+ $ENV{IMAKER_MKRESTARTS} = -1;
+
+ do {
+ InitMkglobals();
+ ($gTgterr, my $printvar, my @steps) = (1, "", ());
+ $ENV{IMAKER_MKRESTARTS}++;
+
+ if ($gExportvar{""}) {
+ if (!$ENV{IMAKER_EXPORTMK}) {
+ (my $tmpfh, $ENV{IMAKER_EXPORTMK}) = File::Temp::tempfile(
+ File::Spec->tmpdir() . "/imaker_temp_XXXXXXXX", SUFFIX => ".mk", UNLINK => 1);
+ close($tmpfh);
+ $ENV{IMAKER_EXPORTMK} =~ tr-\\-\/-;
+ }
+ WriteFile($ENV{IMAKER_EXPORTMK}, "# Generated temporary makefile `$ENV{IMAKER_EXPORTMK}'\n" .
+ "ifndef __IMAKER_EXPORTMK__\n__IMAKER_EXPORTMK__ := 1\n" .
+ join("", map(/^([^:]+)(?:\:(.+))?$/ && !defined($2) ? "$1=$gExportvar{$_}\n" :
+ "ifeq (\$(filter $1,\$(TARGETNAME)),)\n$2=$gExportvar{$_}\nendif\n",
+ sort({($a =~ /([^:]+)$/ && uc($1)) cmp ($b =~ /([^:]+)$/ && uc($1))}
+ grep(!/^(?:|.*[+:?])$/, keys(%gExportvar))))) .
+ "else\n" .
+ join("", map(/^\d{3}(.+[+:?])$/ ? "$1=$gExportvar{$_}\n" : (), sort({$a cmp $b} keys(%gExportvar)))) .
+ "endif # __IMAKER_EXPORTMK__\n", "", "q", 1);
+ $gExportvar{""} = 0;
+ }
+
+ open(MCMD, "$gMakecmd 2>&1 |");
+ while (local $_ = <MCMD>) {
+ chomp();
+ DPrint(1, "$_\n"), next if !s/^#iMaker\x1E//;
+# DPrint(64, "#iMaker#$_\n");
+
+ if (/^BEGIN$/) {
+ $mkstart = time();
+ $start = $mkstart if !$start;
+ next;
+ }
+ if (/^STEPS=(.*)$/) {
+ my $steps = $1;
+ @steps = split(/\s+/, $steps), next if ($steps !~ s/^target://);
+ @targets = grep($_ ne "", map(Trim($_), split(/(?<!\\)\|/, $steps)));
+ next;
+ }
+ $gImgtype = $1, next if /^IMAGE_TYPE=(.*)$/;
+ $gKeepgoing = $1, next if /^KEEPGOING=(.*)$/;
+ $gPrintcmd = $1, next if /^PRINTCMD=(.*)$/;
+ SetVerbose($1), next if /^VERBOSE=(.*)$/;
+ $gStepIcmd{$1} = $2, next if /^((?:BUILD|CLEAN|INIT|REPORT)_\S+?)=(.*)$/;
+
+ if (/^env (\S+?)=(.*)$/) {
+ DPrint(64, "$1 = `" . ($ENV{$1} = $2) . "'\n")
+ if (!defined($ENV{$1}) || ($ENV{$1} ne $2));
+ next;
+ }
+ if (/^var (\S+?)=(.*)$/) {
+ my ($var, $val) = ($1, $2);
+ my $upd = ($var !~ s/\?$//);
+ $gExportvar{$var} = $val, $gExportvar{""}++
+ if (!exists($gExportvar{$var}) || ($upd && $gExportvar{$var} ne $val));
+ next;
+ }
+ if (/^print (\d+) (\S+?)=(.*)$/) {
+ $printvar = ("=" x 79) . sprintf("\n%-$1s = `$gMakecmd'\n", "Make command") if ($printvar eq "");
+ $printvar .= sprintf("%-$1s = `$3'\n", $2);
+ next;
+ }
+
+ push(@stepdur, [$restart ? "ReMake" : "Make", Sec2Min(time() - $mkstart)]) if /^END$/;
+ PrintEnv(2);
+ DPrint(2, $printvar);
+ die("Unknown iMaker entry: `$_'\n"), next if !/^END$/;
+
+ pop(@steps) if ($restart = (@steps && $steps[$#steps] eq "RESTART"));
+ my $durstr = "";
+ foreach my $step (@steps) {
+ next if $skipstep;
+ RunStep($step);
+ my ($cmddur, $stepdur) = (0, pop(@gStepDur));
+ $durstr = Sec2Min($stepdur);
+ if (@gStepDur) {
+ $durstr .= " (";
+ foreach my $dur (@gStepDur) {
+ $cmddur += $dur;
+ $durstr .= Sec2Min($dur) . " + ";
+ }
+ $durstr .= Sec2Min($stepdur - $cmddur) . ")";
+ }
+ push(@stepdur, [$step, $durstr]);
+ }
+
+ $printvar = "";
+ my @env = ($ENV{IMAKER_EXPORTMK}, $ENV{IMAKER_MKRESTARTS});
+ %ENV = %env;
+ ($ENV{IMAKER_EXPORTMK}, $ENV{IMAKER_MKRESTARTS}) = @env;
+ InitMkglobals();
+ ChangeDir($cwd);
+
+ last if $restart;
+
+ my ($maxilen, $maxslen, $maxdlen) = (length(@stepdur . ""),
+ Max(map(length(@$_[0]), @stepdur)), Max(8, map(length(@$_[1]), @stepdur)));
+ DPrint(2, "=" x 79 . "\nStep" . " " x ($maxilen + $maxslen - 1) . "Duration\n" .
+ "=" x ($maxilen + $maxslen + 2) . " " . "=" x $maxdlen . "\n",
+ map(sprintf("%${maxilen}s. %-${maxslen}s", $_ + 1, $stepdur[$_][0]) .
+ " $stepdur[$_][1]\n", 0 .. $#stepdur),
+ "-" x ($maxilen + $maxslen + 2) . " " . "-" x $maxdlen . "\n" .
+ "Total" . " " x ($maxilen + $maxslen - 2) . Sec2Min(time() - $start) . "\n");
+ ($start, @stepdur) = (time(), ());
+ }
+ close(MCMD);
+ die("\n") if ($? >> 8);
+ die("Command `$gMakecmd' failed in `" . GetAbsDirname(".") . "'.\n") if ($gTgterr = $gError);
+ CloseLog();
+ } until !$restart;
+ push(@gStepDur, time() - $gStartmk);
+ return(@targets);
+}
+
+
+###############################################################################
+#
+
+sub HandleCmdArg($)
+{
+ my $cmdarg = shift();
+ my $origarg = $cmdarg = (defined($cmdarg) ? $cmdarg : "");
+
+ my @cmdout = qx($ENV{PERL} -x $0 --splitarg $cmdarg);
+ die("Can't parse Make arguments: `$cmdarg'.\n") if $?;
+
+ map {
+ chomp();
+ s/ /\x1E/g;
+ s/\"/\\\"/g;
+ s/(\\+)$/$1$1/;
+ } @cmdout;
+ $cmdarg = " " . join(" ", @cmdout) . " ";
+
+ if ($cmdarg =~ /^.* VERBOSE\x1E*=(\S*) /) {
+ (my $verbose = $1) =~ s/\x1E/ /g;
+ SetVerbose($verbose, 1);
}
- imaker::DPrint(2, "=" x 79 . "\n");
- @stepdur = ("Step", "Duration", "=" x $maxslen, "=" x $maxdlen, @stepdur,
- "-" x $maxslen, "-" x $maxdlen, "Total", imaker::Sec2Min(time() - $start));
- imaker::DPrint(2, sprintf("%-${maxslen}s %-${maxdlen}s ", shift(@stepdur), shift(@stepdur)) . "\n")
- while(@stepdur);
+ if ($cmdarg =~ /\s+--?conf=(\S*)\s+/) {
+ (my $prj = $1) =~ /(.*?)(?:;(.*))?$/;
+ ($prj, my $conf) = ($1, defined($2) ? $2 : "");
+ $cmdarg =~ s/\s+--?conf=\S*\s+/ USE_CONE=mk CONE_PRJ=$prj CONE_CONF=$conf cone-pre defaultgoals /;
+ }
+
+ $cmdarg = " " . HandleExtCmdArg($cmdarg) . " " if $gImakerext;
+
+ $gMakecmd = "$ENV{IMAKER_MAKE} -f $0" . join("", map(" \"$_\"", split(/\s+/, Trim($cmdarg))));
+ warn("Can't parse Make targets.\n")
+ if (!(my $targets = (qx($gMakecmd 2>&1) =~ /\|MAKECMDGOALS=(.*?)\|/ ? " $1 " : "")) &&
+ ($cmdarg !~ /\s-(?:-?v(?:ersion?|ersi?|er?)?|versio\S+)\s/));
+
+ GetConfmkList() if
+ grep(!/^(help(-.+)?|print-.+)$/ || /^help-config$/, my @targets = split(/\s+/, Trim($targets)));
+
+ my ($mkfile, $mkfiles, $hptgt) = ("", "", "");
+ map {
+ $cmdarg =~ s/\s+\Q$_\E\s+/ /;
+ if (exists($gConfmkList{$_})) {
+ ($mkfile = $gConfmkList{$_}) =~ s/ /\x1E/g;
+ $mkfiles .= " -f $mkfile";
+ $targets =~ s/\s+\Q$_\E\s+/ /;
+ }
+ } @targets;
+ $cmdarg = "$mkfiles$cmdarg";
+
+ map { $targets =~ s/\s\Q$_\E\s/ /; $hptgt .= " $_" }
+ grep(/^help-.+$/ && !/^help-config$/, @targets);
+ map { $targets =~ s/\s\Q$_\E\s/ /; $hptgt .= " $_" }
+ grep(/^print-.+$/, @targets);
+ $hptgt = Trim($hptgt);
- imaker::CloseLog();
+ if ($targets =~ s/ default(?= )//g) {
+ ($targets = Trim($targets)) =~ s/ /\x1E/g;
+ $cmdarg .= "TARGET_DEFAULT=$targets" if ($targets ne "");
+ $targets = "default";
+ }
+ @targets = ("defaultgoals@targets") if
+ !(@targets = map(s/\x1E/ /g ? $_ : $_, split(/\s+/, Trim($targets)))) || ("@targets" eq "#");
+
+ $mkfiles = "";
+ while ($cmdarg =~ s/\s+(-f\s?|--(?:file?|fi?|makefile?|makefi?|make?)[=\s]|IMAKER_CONFMK\x1E*=)(\S+)\s+/ /) {
+ $mkfile = $2;
+ ($mkfile = GetAbsFname(scalar($mkfile =~ s/\x1E/ /g, $mkfile))) =~ s/ /\\\x1E/g
+ if ($1 !~ /^IMAKER_CONFMK/);
+ $mkfiles .= ($mkfiles eq "" ? "" : chr(0x1E)) . $mkfile;
+ }
+ while ($cmdarg =~ s/\s+(\S+?)\x1E*([+:?])=\x1E*(\S+?)\s+/ /) {
+ ($gExportvar{sprintf("%03s", ++$gExportvar{""}) . "$1$2"} = $3) =~ s/\x1E/ /g;
+ }
+ $cmdarg = join(" ", map(scalar(s/\x1E/ /g, "\"$_\""), split(/\s+/, Trim($cmdarg .
+ ($mkfiles eq "" && ($ENV{IMAKER_MKLEVEL} || grep(/^default$/, @targets)) ? "" : " IMAKER_CONFMK=$mkfiles")))));
+
+ DPrint(2, "HandleCmdArg: `$origarg' => `$cmdarg', `" . join(" ", @targets) . "', `$hptgt'\n");
+ return($cmdarg, $hptgt, @targets);
+}
+
+
+###############################################################################
+#
+
+sub MenuRuncmd($)
+{
+ $ENV{IMAKER_CMDARG} = shift();
+ return(map(chomp() ? $_ : $_, qx($ENV{PERL} -x $0 2>&1)));
}
+sub Menu($)
+{
+ (my $cmdarg = " " . shift() . " ") =~ s/\s+"IMAKER_CONFMK="\s+/ /;
+ my ($prodind, $product, @product) = (0, "", ());
+ my ($tgtind, $target, $tgtcols, $tgtrows, @target) = (0, "", 4, 0, ());
+ my ($vartype, $varudeb, $varsym);
+ my $cfgfile = "./imaker_menu.cfg";
+
+ $cmdarg = ($cmdarg =~ /^\s*$/ ? "" : " " . Trim($cmdarg));
+ open(FILE, "<$cfgfile") and
+ (($prodind, $tgtind, $vartype, $varudeb, $varsym) = map(chomp() ? $_ : $_, <FILE>)) and close(FILE);
+ ($prodind, $tgtind, $vartype, $varudeb, $varsym) =
+ ($prodind || 0, $tgtind || 0, $vartype || "rnd", $varudeb || 0, $varsym || 0);
+
+ while (1) {
+ print("\nPRODUCTS\n--------\n");
+ #
+ if (!@product) {
+ @product = sort({lc($a) cmp lc($b)} grep($_ ne "", keys(%gConfmkList)));
+ $prodind = 0 if ($prodind > @product);
+ }
+ $product = ($prodind ? " $product[$prodind - 1]" : "");
+ my $maxlen = Max(map(length($_), @product));
+ map {
+ printf(" %" . (length(@product)) . "s) %-${maxlen}s %s\n", $_ + 1, $product[$_], $gConfmkList{$product[$_]});
+ } (0 .. $#product);
+ print(" NO PRODUCTS FOUND!\n") if !@product;
+
+ print("\nTARGETS\n-------\n");
+ #
+ if (!@target) {
+ @target = grep(s/^== (.+) ==$/$1/, MenuRuncmd("$product PRINTCMD=0 VERBOSE=1 help-target-*-wiki"));
+ $tgtind = 0 if ($tgtind > @target);
+ $tgtrows = int($#target / $tgtcols + 1);
+ my $maxind = 0;
+ map {
+ if (!($_ % $tgtrows)) {
+ $maxind = length(Min($_ + $tgtrows, $#target + 1)) + 1;
+ $maxlen = Max(map(length(), @target[$_ .. Min($_ + $tgtrows - 1, $#target)]));
+ }
+ $target[$_] = sprintf("%${maxind}s) %-${maxlen}s", "t" . ($_ + 1), $target[$_]);
+ } (0 .. $#target);
+ }
+ ($target = ($tgtind ? $target[$tgtind - 1] : "")) =~ s/^.+?(\S+)\s*$/$1/;
+ foreach my $row (1 .. $tgtrows) {
+ foreach my $col (1 .. $tgtcols) {
+ my $ind = ($col - 1) * $tgtrows + $row - 1;
+ print(($ind < @target ? " $target[$ind]" : "") . ($col != $tgtcols ? " " : "\n"));
+ }
+ }
+ print(" NO TARGETS FOUND!\n") if !@target;
+
+ print("\nCONFIGURATION\n-------------\n");
+ #
+ print(
+ " Product: " . ($prodind ? $product[$prodind - 1] : "NOT SELECTED!") . "\n" .
+ " Target : " . ($tgtind ? $target : "NOT SELECTED!") . "\n" .
+ " Type : " . ucfirst($vartype) . "\n" .
+ " Debug : " . ($varudeb ? ($varudeb =~ /full/i ? "Full debug" : "Enabled") : "Disabled") . "\n" .
+ " Symbols: " . ($varsym ? "Created\n" : "Not created\n"));
+
+ print("\nOPTIONS\n-------\n");
+ #
+ print(
+ " t) Toggle type between rnd/prd/subcon\n" .
+ " u) Toggle debug between urel/udeb/udeb full\n" .
+ " s) Toggle symbol creation on/off\n" .
+ " r) Reset configuration\n" .
+ " h) Print usage information\n" .
+ " x) Exit\n\n" .
+ "Hit Enter to run: imaker$product$cmdarg TYPE=$vartype USE_UDEB=$varudeb USE_SYMGEN=$varsym $target\n");
+
+ print("\nSelection: ");
+ #
+ my $input = <STDIN>;
+ ($input = (defined($input) ? $input : "?")) =~ s/^\s*(.*?)\s*$/\L$1\E/;
+
+ if ($input =~ /^(\d+)$/ && ($1 > 0) && ($1 <= @product) && ($1 != $prodind)) {
+ $prodind = $1;
+ ($tgtind, @target) = (0, ());
+ }
+ elsif ($input =~ /^t(\d+)$/ && ($1 > 0) && ($1 <= @target) && ($1 != $tgtind)) {
+ $tgtind = $1;
+ }
+ elsif ($input eq "t") {
+ $vartype = ($vartype =~ /rnd/i ? "prd" : ($vartype =~ /prd/i ? "subcon" : "rnd"));
+ }
+ elsif ($input eq "u") {
+ $varudeb = (!$varudeb ? 1 : ($varudeb !~ /full/i ? "full" : 0));
+ }
+ elsif ($input eq "s") {
+ $varsym = ($varsym ? 0 : 1);
+ }
+ elsif ($input eq "r") {
+ ($prodind, @product) = (0, ());
+ ($tgtind, @target) = (0, ());
+ ($vartype, $varudeb, $varsym) = ("rnd", 0, 0);
+ }
+ elsif ($input eq "h") {
+ print("\nTODO: Help");
+ sleep(2);
+ }
+ elsif ($input =~ /^(x|)$/) {
+ open(FILE, ">$cfgfile") and
+ print(FILE map("$_\n", ($prodind, $tgtind, $vartype, $varudeb, $varsym))) and close(FILE);
+ return(("", "menu")) if ($input eq "x");
+ $cmdarg = "$product$cmdarg TYPE=$vartype USE_UDEB=$varudeb USE_SYMGEN=$varsym";
+ $ENV{IMAKER_CMDARG} = Trim("$cmdarg $target");
+ return(($cmdarg, $target eq "" ? "defaultgoals" : $target));
+ }
+ }
+}
+
+
+###############################################################################
+#
+
+sub Install($$$)
+{
+ my ($clean, $bldinf, $destdir) = @_;
+ my $srcdir = GetDirname($bldinf = GetAbsFname($bldinf));
+ $destdir = GetAbsDirname($destdir) if $destdir;
+
+ print(($clean ? "\nCleaning" : "\nInstalling") . " `$bldinf'" . ($destdir ? " to `$destdir'\n" : "\n"));
+
+ my $export = 0;
+ foreach (grep(!/^\s*\/\//, ReadFile($bldinf, 0))) {
+ $export = 1, next if /^\s*PRJ_EXPORTS\s*$/i;
+ next if !$export;
+ Install($clean, "$srcdir$1", $destdir), next if /^\s*#include\s+"(.+)"\s*$/;
+ die("Unknown line `$_'.\n") if !/^\s*(\S+)\s+(.+?)\s*$/;
+ my ($src, $dest) = ("$srcdir$1", $2);
+ $dest = "$gEpocroot/epoc32$dest" if ($dest =~ s/^\+//);
+ $dest .= GetBasename($src) if ($dest =~ s/\s+\/\/$//);
+ ($src, $dest) = (GetAbsFname($src), GetAbsFname($dest));
+ next if ($destdir && ($dest !~ /^$gEpocroot\/epoc32\/tools\//i));
+ $dest = "$destdir/" . GetBasename($dest) if $destdir;
+ print(($clean ? "Delete" : "Copy `$src' =>") . " `$dest'\n");
+ unlink($dest);
+ die("Deletion failed.\n") if ($clean && -e($dest));
+ next if $clean;
+ File::Path::mkpath(GetDirname($dest));
+ File::Copy::copy($src, $dest) or die("Copying failed.\n");
+ chmod(0777, $dest);
+ }
+}
+
+
+###############################################################################
+#
+
+END {
+ if (!$gArgv) {
+ (my $keepgoing, $gStartmk) = ($gKeepgoing, time() - $gStartmk);
+ $gKeepgoing = 1;
+ SetLogfile($gLogfiles{__prev__}) if %gLogfiles;
+ PrintEnv(0) if $gError;
+ die("Command `$gMakecmd' failed in `" . GetAbsDirname(".") . "'.\n")
+ if ($gTgterr && !$keepgoing);
+
+ map { UnsubstDrive($_) } sort({$a cmp $b} keys(%gSubstdrv));
+
+ @gIcmd = @gReport;
+ (my $report, @gReport) = (2, ());
+ my ($maxtlen, $maxvlen, %uniq) = (0, 0, ());
+ while (@gIcmd) {
+ my ($tgtvar, $durval, $type) = (GetIPar(1), GetIPar(1), GetIPar(1));
+ if ($type =~ /^-?\d+$/) {
+ push(@gReport, [$tgtvar, $durval, $type]);
+ ($maxtlen, %uniq) = (Max($maxtlen, length($tgtvar)), ());
+ } else {
+ $report = 1, push(@gReport, [$tgtvar, $durval, $type])
+ if ($tgtvar ne "") && !($uniq{"$tgtvar|$durval"}++);
+ $maxvlen = Max($maxvlen, length($tgtvar));
+ }
+ }
+
+ my ($tgtcnt, $warn) = (0, 0);
+ DPrint($report, "=" x 79 . "\n" . join("\n", map(@$_[2] =~ /^-?\d+$/ ?
+ ($tgtcnt++ ? "-" x 79 . "\n" : "") .
+ "Target: " . sprintf("%-${maxtlen}s", @$_[0]) .
+ " Duration: " . Sec2Min(@$_[1] < 0 ? $gStartmk : @$_[1]) .
+ " Status: " . (@$_[2] < 0 ? ($warn = "FAILED") : "OK")
+ : sprintf("%-${maxvlen}s", @$_[0]) . " = `@$_[1]'" .
+ ((@$_[2] =~ /^[fd]$/i) && !-e(@$_[1]) ? " - DOESN'T EXIST" : ""), @gReport)) .
+ (@gReport ? "\n" . "-" x 79 . "\n" : "") .
+ "Total duration: " . Sec2Min(time() - $gStarttime) .
+ " Status: " . ($gError && !$keepgoing ? "FAILED" : "OK" .
+ ($warn ? " (with keep-going)" : "")) .
+ "\n" . "=" x 79 . "\n");
+
+ warn("\$_ has been changed in an uncontrolled manner!\n")
+ if !/^default input and pattern-searching space$/;
+ CloseLog();
+ exit(1) if ($gError && !$keepgoing);
+ }
+}
+
+
__END__ # OF IMAKER.PL