#! /usr/pkg/bin/perl -w
#
# $eterna: runmknative,v 1.9 2021/08/16 07:57:42 mrg Exp $
#
# run netbsd 'mknative-gcc' for a list of platforms

$version = 'runmknative $Revision: 1.9 $' . "\n";

require 'getopts.pl';
#use Getopt::Std;

$help = 'runmknative [options] [<arch list>]
main options:
     -a version          handle GDB version <version> (GDB 7.10 and 7.12 [default] supported)
     -A                  bootstrap with MKGCC=yes
     -b version          handle binutils version <version> (binutils 2.39 [default], 2.34 supported)
     -B                  bootstrap; set MKGCC=no and MKCXX=no
     -e                  just print environment, no builds
     -g version          handle GCC version <version> (GCC 5, 7, and 10 [default], 12 supported)
     -G                  check GCC crtstuff
     -h                  print this help
     -I                  ignore failures in "make includes" phase
     -j N                pass "-jN" to make
     -k                  ignore system failure
     -K                  ignore single arch failure
     -L                  display arch list as environment
     -M                  display arch list as machine:arch
     -n                  no run
     -N newconfigdir     set NEWCONFIGDIR env var (default: /home/current/src)
     -o baseobjdir       set objdir (default: /var/obj/mknative)
     -r                  do not build "<arch list>" (no default)
     -R version          use defaults for release "X" ("8" -> GCC 5, "9" -> GCC 7, "current"|"10" -> GCC 10)
     -s srcdir           set srcdir (default: /usr/src)
     -T tool             tool to build (default "gcc")
     -v                  version
uptime options:
     -c                  do not obj/dependall/install crtstuff, csu
     -i                  do not do libgcc4 obj & includes
     -l                  do not obj/dependall/install libgcc or lib
     -m                  do not install gmp/mpfr/mpc
     -O                  do not to distrib-dirs, obj and global includes
     -S                  do not install in src/share/mk
     -t                  do not build tools
     -C                  do not run native-tool
';

$opt_I = $opt_a = $opt_A = $opt_B = $opt_e = $opt_c = $opt_m = $opt_r =
	 $opt_O = $opt_i = $opt_t = $opt_v = $opt_h = $opt_s = $opt_o =
	 $opt_j = $opt_l = $opt_N = $opt_S = $opt_L = $opt_M = '';

$opt_n = 0;
$opt_k = 0;
$opt_K = 0;
$opt_G = 0;
$opt_C = 0;

Getopts('Ab:BcCeg:GhiIj:kKlLmMN:no:OrR:s:StT:v');

die $version if $opt_v;
die $help if $opt_h;

die $help if $opt_g and $opt_g ne "5" and $opt_g ne "7" and $opt_g ne "10" and $opt_g ne "12";
die $help if $opt_a and $opt_a ne "7.10" and $opt_a ne "7.12";
die $help if $opt_b and $opt_b ne "2.39" and $opt_b ne "2.34";
die $help if $opt_R and $opt_R ne "8" and $opt_R ne "9" and $opt_R ne "10" and $opt_R ne "current";

$opt_T = "gcc" if not defined $opt_T;
$opt_R = "current" if not defined $opt_R;
$opt_b = "2.39" if not defined $opt_b;

my $srcdir = $opt_s || "/usr/src";
my $objdirbase = $opt_o || "/var/obj/mknative";
my $tool = $opt_T;
my $target = "native-$tool";

my @ignore_inc = $opt_I ? ("-k") : ();

if ($tool eq "libgcc" or $tool eq "libstdc++") {
	$target = "bootstrap-$tool";
	$tool = "gcc";
	$opt_i = 1;
	$opt_l = 1;
	$opt_m = 1;
}

my $cur_machine = '';
my $cur_arch = '';

# these are special since we don't normally install them so ./configure
# fails to find them.
my @gcc_dep_libs = ();

my $libgcc4;
my $libcrtstuff4;
my $gcc_subdir;

my $use_armv5 = 1;

my @fail_arch = ();

if ($opt_R eq "current") {
	if (not defined $opt_g or $opt_g eq '') {
		$opt_g = "10";
	}
	if ($opt_g eq "12") {
		$gcc_subdir = "gcc";
	} elsif ($opt_g eq "10") {
		$gcc_subdir = "gcc.old";
	} else {
		die "NetBSD/current only supports GCC 10 and 12, not: $opt_g\n";
	}
} elsif ($opt_R eq "10") {
	if (not defined $opt_g or $opt_g eq '') {
		$opt_g = "10";
	}
	if ($opt_g ne "10" or $opt_R eq "current") {
		die "NetBSD/10 only supports GCC 10, not: $opt_g\n";
	}
	$gcc_subdir = "gcc";
} elsif ($opt_R eq "9") {
	if (not defined $opt_g or $opt_g eq '') {
		$opt_g = "7";
	}
	if ($opt_g ne "7" or $opt_R eq "current") {
		die "NetBSD/9 only supports GCC 7, not: $opt_g\n";
	}
	$gcc_subdir = "gcc";
	$use_armv5 = 0;
} elsif ($opt_R eq "8") {
	if (not defined $opt_g or $opt_g eq '') {
		$opt_g = "5";
	}
	if ($opt_g ne "5" or $opt_R eq "current") {
		die "NetBSD/8 only supports GCC 5, not: $opt_g\n";
	}
	$gcc_subdir = "gcc";
	$use_armv5 = 0;
} else {
	die "Only support -8, -9, -10, and -current, not $opt_R\n";
}
# Allow env over-ride, like the system does.
if (defined $ENV{EXTERNAL_GCC_SUBDIR}) {
	$gcc_subdir = $ENV{EXTERNAL_GCC_SUBDIR};
} else {
	$ENV{EXTERNAL_GCC_SUBDIR} = $gcc_subdir;
}
print "gcc_subdir = $gcc_subdir\n";

$libgcc4 = "$srcdir/external/gpl3/$gcc_subdir/lib/libgcc";
$libcrtstuff4 = "$srcdir/external/gpl3/$gcc_subdir/lib/crtstuff";
my $mpcdir = "$srcdir/external/lgpl3/mpc/lib/libmpc";
if ( ! -f $mpcdir ) {
	my $mpcdir = "$srcdir/external/lgpl2/mpc/lib/libmpc";
}
@gcc_dep_libs = ("$srcdir/external/lgpl3/gmp/lib/libgmp",
		 "$srcdir/external/lgpl3/mpfr/lib/libmpfr",
		 $mpcdir);
my @have_gcc = ();
my @have_binutils = ();
my @have_gdb = ();
if ($tool eq "gcc") {
	my $hg;

	# Post netbsd-8 -current switched to HAVE_GCC=<n> for n in 6,7
	if ($opt_R eq "current") {
		$hg = $opt_g;
	} elsif ($opt_R eq "10") {
		$hg = "10";
	} elsif ($opt_R eq "9") {
		$hg = "7";
	} elsif ($opt_R eq "8") {
		$hg = "5";
	} else {
		die "don't know release $opt_R\n";
	}
	$ENV{HAVE_GCC} = $hg;
	@have_gcc = ("-V", "HAVE_GCC=$ENV{HAVE_GCC}");
}
if ($tool eq "gdb") {
	$ENV{HAVE_GDB} = ($opt_a eq "7.10") ? "710" : "712";
	@have_gdb = ("-V", "HAVE_GDB=$ENV{HAVE_GDB}");
}
if ($tool eq "binutils") {
	$ENV{HAVE_BINUTILS} = ($opt_b eq "2.34") ? "234" : "239";
	@have_binutils = ("-V", "HAVE_BINUTILS=$ENV{HAVE_BINUTILS}");
}

my $libcsu = "$srcdir/lib/csu";
my $libdes = "$srcdir/lib/libdes";
my $libc = "$srcdir/lib/libc";
my $liblua = "$srcdir/external/mit/lua/lib/liblua";
my $libm = "$srcdir/lib/libm";
my $lib = "$srcdir/lib";
my $sharemk = "$srcdir/share/mk";
my $tools_tooldir = "$srcdir/tools/$tool";

# force this on.
$ENV{MKMAINTAINERTOOLS} = "yes";

# build faster tools.
$ENV{HOST_DBG} = "-O2 -g";

my @mkgcc = $opt_B ? ("MKGCC=no") : ();
my @mkcxx = ($opt_B or $opt_A) ? ("MKCXX=no") : ();

# anything else to add here?  MKPIC?
if (0) {
$ENV{MKLINT} = "no";
$ENV{MKPUFFS} = "no";
}
$ENV{MKDOC} = "no";
$ENV{MKMAN} = "no";
$ENV{MKLLVMRT} = "no";
$ENV{MKSANITIZER} = "no";
$ENV{MKCROSSGDB} = "no";
$ENV{MKINFO} = "no";
$ENV{MKHTML} = "no";
$ENV{MKRUMP} = "no";
$ENV{MKPAM} = "no";
$ENV{MAKECONF} = "/dev/null";
$ENV{MAKEVERBOSE} = "1";
if ($opt_N) {
	$ENV{NEWCONFIGDIR} = $opt_N;
} else {
	my $nsrcdir = $srcdir;
	$nsrcdir =~ s,^/usr/src,/home/current/src,;
	$nsrcdir =~ s,^/usr/([0-9]+)/src,/home/netbsd-$1/src,;
	$ENV{NEWCONFIGDIR} = $nsrcdir;
}

my @minusj = ();
@minusj = ("-j$opt_j") if $opt_j;

sub cmd {
	print "running: @_\n";
	if (not $opt_n and system (@_)) {
		return 1 if $opt_k;
		if ($opt_K) {
			push @fail_arch, "$cur_machine:$cur_arch): $?";
			return 0;
		}
		die "system failed ($cur_machine:$cur_arch): $?"
	}
	return 1;
}

sub cd {
	print "chdir to: $_[0]\n";
	return if $opt_n;
	chdir $_[0] or die "can't chdir $_[0]: $!\n";
}

my @tools;
if ($tool eq "gcc") {
	@tools = ("MKGCC=yes", "MKBFD=no");
	#@tools = ("MKGCC=no", "MKBFD=no");
} elsif ($tool eq "gdb") {
	@tools = ("MKGDB=no", "MKBINUTILS=no");
} else {
	@tools = ("MKBFD=no");
}

mkdir "$objdirbase" unless -d "$objdirbase";

my $uname_s = `uname -s`; chomp $uname_s;
my $uname_r = `uname -r`; chomp $uname_r;
my $uname_m = `uname -m || uname -p`; chomp $uname_m;
$host_ostype = "$uname_s-$uname_r-$uname_m";

my %def_buildlist;
my %def_tags;
while (<DATA>) {
	next if /^#/;
	# no armeb
	next if /:armeb:/ and $use_armv5;
	chomp;

	my ($machine, $arch, $tags);
	if (m/(\w+):(\w+)(:(\S+))?/) {
		$machine = "$1:$2";
		$arch = $2;
		if (defined $4) {
			$tags = $4;
		}
	} else {
		($machine, $arch) = split " ";
	}
	$def_buildlist{$machine} = $arch;
	$def_tags{$machine} = $tags;

	#$tags = "" if not defined $tags;
	#print "def: machine[$machine] = [$arch] with tags [$tags]\n";
}

my %buildlist;
if (@ARGV) {
	while (@ARGV) {
		my $build_machine = shift @ARGV;
		my $arch;
		my $no_gcc_ver;

		if ($build_machine =~ m/(\w+):(\w+)/) {
			$machine = $1;
			$arch = $2;
		} elsif (defined $def_buildlist{$build_machine}) {
			$machine = $build_machine;
			$arch = $def_buildlist{$machine};
		} else {
			for $m (keys %def_buildlist) {
				if ($def_buildlist{$m} eq $build_machine) {
					if (0 and $m =~ m/(\w+):(\w+)/) {
						$machine = $1;
						$arch = $2;
					} else {
						$machine = $m;
						$arch = $def_buildlist{$machine};
					}
					$build_machine = $machine;
					last;
				}
			}
		}

		die "don't know \"arch\" for $machine, try ${machine}:arch\n"
			unless $arch;
		$buildlist{$build_machine} = $arch;
	}
	if ($opt_r) {
		my %keeplist;
		my $machine;

		for $machine (keys %def_buildlist) {
			next if defined $buildlist{$machine};
			$keeplist{$machine} = $def_buildlist{$machine};
		}
		%buildlist = %keeplist;
	}
} else {
	%buildlist = %def_buildlist;
}

for my $machine (sort keys %buildlist) {
	my $arch;
	my $tags = $def_tags{$machine};

	if (defined $tags) {
		if ($tags =~ m/!gcc$opt_g/) {
#print "tags '$tags' opt_g '$opt_g'\n";
			print "Skipping $machine: not for GCC $opt_g\n"
				unless $opt_M;
			next;
		}
		if ($tags =~ m/!binutils/ and $tool eq "binutils") {
			print "Skipping $machine: not for binutils\n"
				unless $opt_M;
			next;
		}
		if ($tags =~ m/!gdb/ and $tool eq "gdb") {
			print "Skipping $machine: not for gdb\n"
				unless $opt_M;
			next;
		}
		if ($tags =~ m/!gmp/ and $tool eq "gmp") {
			print "Skipping $machine: not for gmp\n"
				unless $opt_M;
			next;
		}
	}

	if ($machine =~ m/(\w+):(\w+)/) {
		$machine = $1;
		$arch = $2;
	} else {
		$arch = $buildlist{$machine};
	}

	if ($opt_L) {
		print "MACHINE=$machine MACHINE_ARCH=$arch\n";
		next;
	}

	if ($opt_M) {
		if ($machine eq $arch) {
			print "$machine\n";
		} else {
			print "${machine}:$arch\n";
		}
		next;
	}

	$cur_machine = $machine;
	$cur_arch = $arch;

	# /var/obj/$MACHINE$MACHINE_ARCH:N$MACHINE:C/(.)/-\1/
	my $objdirbasemachine;
	if ($machine ne $arch) {
		$objdirbasemachine = "$objdirbase/${machine}-${arch}";
	} else {
		$objdirbasemachine = "$objdirbase/${machine}";
	}

	#$ENV{MAKEOBJDIRPREFIX} = $objdirbasemachine;

	if ($opt_e) {
		for my $e (keys %ENV) {
			print "$e=$ENV{$e}\n";
		}
		next;
	}

	print "MAKEOBJDIRPREFIX=$objdirbasemachine\n";
	mkdir "$objdirbasemachine" unless -d "$objdirbasemachine";

	cd $srcdir;
	my $tooldir = "$objdirbasemachine$srcdir/tooldir.$host_ostype";
	my $bindir = "$tooldir/bin";
	my $nbmake = "$bindir/nbmake-$machine";

	unless ($opt_t) {
		cmd "./build.sh",
			"-uU", @minusj,
			@have_gcc,
			@have_binutils,
			@have_gdb,
			"-M", $objdirbasemachine,
			"-m", $machine,
			"-a", $arch, "tools" or next;
	}

	if ($opt_B) {
		cd $tools_tooldir;
		cmd $nbmake, "obj", "bootstrap-libgcc";
		next;
	}

	unless ($opt_O) {
		cmd $nbmake, @minusj, "obj", @tools or next;
		cmd $nbmake, @minusj, "do-distrib-dirs", @tools or next;
		{ my $o_opt_k = $opt_k;
		  local $opt_k = 1;
		cmd $nbmake, @minusj, "includes", @ignore_inc, @tools, @mkgcc, @mkcxx or $opt_I or next;
		}
	}

	unless ($opt_S) {
		cd $sharemk;
		cmd $nbmake, @minusj, "install" or next;
	}

	unless ($opt_i) {
		cd $libgcc4;
		# no mkgcc/mkcxx for obj, might need it later
		cmd $nbmake, @minusj, "obj" or next;
		cmd $nbmake, @minusj, @mkgcc, @mkcxx, @ignore_inc, "includes" or $opt_I or next;
	}

	unless ($opt_c) {
		if ($opt_G) {
			print "running: $nbmake -V USE_COMPILERCRTSTUFF\n";
			my $gcc_crtstuff = `$nbmake -V USE_COMPILERCRTSTUFF`;
			chomp($gcc_crtstuff);
			print "got: $gcc_crtstuff\n";
			my @libs = ();
			if ($gcc_crtstuff eq "yes") {
				push @libs, $libcrtstuff4;
			}
		}
		push @libs, $libcsu;
		unless ($opt_i) {
			push @libs, $libgcc4;
		}
		for $dir (@libs) {
			cd $dir;
			cmd $nbmake, @minusj, "obj", @tools or next;
			cmd $nbmake, @minusj, "dependall", @tools or next;
			cmd $nbmake, @minusj, "install", @tools or next;
		}
	}

	unless ($opt_l) {
		#for $dir ($libc, $libm, $liblua, $lib) {
			#cd $dir;
			#cmd $nbmake, @minusj, "obj", @mkgcc, "MKBFD=no", @mkcxx or next;
			#cmd $nbmake, @minusj, "build_install", @mkgcc, "MKBFD=no", @mkcxx or next;
			#cmd $nbmake, @minusj, "install", @mkgcc, "MKBFD=no", @mkcxx or next;
		#}
		cd $lib;
		cmd $nbmake, @minusj, "build_install", @mkgcc, "MKBFD=no", @mkcxx or next;
	}

	unless ($opt_m) {
		if ($tool eq 'gcc') {
			for $dir (@gcc_dep_libs) {
				cd $dir;
				cmd $nbmake, @minusj, "obj", "LIBISPRIVATE=no", "MKPIC=no", "MKPROFILE=no" or next;
				cmd $nbmake, @minusj, "dependall", "LIBISPRIVATE=no", "MKPIC=no", "MKPROFILE=no" or next;
				cmd $nbmake, @minusj, "includes", @ignore_inc, "LIBISPRIVATE=no", "MKPIC=no", "MKPROFILE=no" or $opt_I or next;
				cmd $nbmake, @minusj, "install", "LIBISPRIVATE=no", "MKPIC=no", "MKPROFILE=no" or next;
			}
		}
	}

	unless ($opt_C or $tool eq '') {
		cd $tools_tooldir;

		cmd $nbmake, "obj", "$target" or next;
	}
}

for my $fa (@fail_arch) {
	printf "Failed arch: $fa\n";
}

exit

# This list is in a handful of forms:
#    machine machine_arch
#    machine:machine_arch
#    machine:machine_arch:!gccN
#    machine:machine_arch:!binutils
# the latter two mean "not gcc version N" and "not binutils, ever".
__DATA__
alpha alpha
amd64 x86_64
evbarm:earmv4:!gdb,!gmp
evbarm:earmv4eb:!gdb,!gmp
#evbarm:earmhf:!binutils,!gdb,!gmp
#evbarm:earmhfeb:!binutils,!gdb,!gmp
evbarm:earm:!binutils,!gdb,!gcc9,!gcc10
evbarm:earmeb:!binutils,!gdb,!gcc9,!gcc10
evbarm:earmv5:!gdb,!gmp
evbarm:earmv5eb:!gdb,!gmp
evbarm:earmv5hf:!binutils,!gdb,!gmp
evbarm:earmv5hfeb:!binutils,!gdb,!gmp
evbarm:earmv6:!binutils,!gdb,!gmp
evbarm:earmv6eb:!binutils,!gdb,!gmp
evbarm:earmv6hf:!binutils,!gdb,!gmp
evbarm:earmv6hfeb:!binutils,!gdb,!gmp
evbarm:earmv7:!binutils,!gdb,!gmp
evbarm:earmv7eb:!binutils,!gdb,!gmp
evbarm:earmv7hf:!binutils,!gdb,!gmp
evbarm:earmv7hfeb:!binutils,!gdb,!gmp
evbmips:mipsn64el
evbmips:mipsn64eb
evbmips:mips64el
evbppc powerpc64
hppa hppa
landisk sh3el
i386 i386
macppc powerpc
mmeye sh3eb
mvme68k m68k
newsmips mipseb
pmax mipsel
sgimips mips64eb
sparc sparc
sparc64 sparc64
evbarm:aarch64
evbarm:aarch64eb
riscv:riscv32:!gcc5,!gcc6,!gdb
riscv:riscv64:!gcc5,!gcc6,!gdb
#or1k or1k:!gcc5.3
#evbcf coldfire
sun2 m68000
vax vax
ia64 ia64