From 6cb0a33a221e7d98629e10cb3d77c94772951410 Mon Sep 17 00:00:00 2001 Message-Id: <6cb0a33a221e7d98629e10cb3d77c94772951410.1715336232.git.mdw@distorted.org.uk> From: Mark Wooding Date: Wed, 19 Dec 2018 12:41:41 +0000 Subject: [PATCH] src/cross-run.in: New tool to run command in multiple cross-build envs. Organization: Straylight/Edgeware From: Mark Wooding Possibly in parallel. (Ab)uses GNU make for the parallelism, so that descendent `make's share the available job slots properly. --- Makefile | 22 ++++++++- src/cross-run.in | 122 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 143 insertions(+), 1 deletion(-) create mode 100644 src/cross-run.in diff --git a/Makefile b/Makefile index 5c1ffc1..cc63a45 100644 --- a/Makefile +++ b/Makefile @@ -48,7 +48,7 @@ $(SETUPS): etc/%-setup.sh: $$(call setup-flavour,$$*)-setup.sh.in Makefile $(call v_tag,SED)sed 's/@arch@/$($*_ARCH)/g' $< >$@.new && \ mv $@.new $@ -SCRIPTS = mdw-cross crosstool +SCRIPTS = mdw-cross crosstool cross-run BINSCRIPTS = $(addprefix bin/,$(SCRIPTS)) TARGETS += $(BINSCRIPTS) $(BINSCRIPTS): bin/%: src/%.in @@ -73,4 +73,24 @@ all:: $(TARGETS) CLEANFILES += $(TARGETS) clean::; rm -f $(CLEANFILES) +run-program = exec 8>&1 && rc=$$( exec 7>&1 && \ + { exec 9>&1 && \ + { ($1 9>&-; echo $$? >&7) | \ + while IFS= read -r line; \ + do printf >&9 "%-21s | %s\n" "$2" "$$line"; done; } 2>&1 | \ + while IFS= read -r line; \ + do printf >&9 "%-21s * %s\n" "$2" "$$line"; done; } | cat >&8 ) && \ + exit $$rc + +run: $(addprefix run/,$(ARCHS)) +$(addprefix run/,$(ARCHS)): run/%: + @cd $(DIR) && \ + $(call run-program,$(CROSSDIR)/bin/mdw-cross $* $(ARGS),$*) +.PHONY: run $(addprefix run/,$(ARCHS)) + +run-make: $(addprefix run-make/,$(ARCHS)) +$(addprefix run-make/,$(ARCHS)): run-make/%: + @+$(call run-program,$(CROSSDIR)/bin/mdw-cross $* $(MAKE) -C$(DIR) $(ARGS),$*) +.PHONY: run-make $(addprefix run-make/,$(ARCHS)) + show:; : $(VAR) diff --git a/src/cross-run.in b/src/cross-run.in new file mode 100644 index 0000000..0124572 --- /dev/null +++ b/src/cross-run.in @@ -0,0 +1,122 @@ +#! /usr/bin/perl + +use Data::Dumper; + +use File::FnMatch qw(fnmatch); +use MdwOpt; + +our $CROSSDIR = "@crossdir@"; +(our $ME = $0) =~ s:^.*/::; + +###-------------------------------------------------------------------------- +### Utilities. + +sub usage (*) { + my ($fh) = @_; + print $fh + "usage: $ME [-j N] =ARCHGLOB|:FLAVOUR ... [--] CMD [ARGS ...]\n"; +} +sub barf ($) { + my ($msg) = @_; + print STDERR "$0: $msg\n"; + exit 2; +} + +###-------------------------------------------------------------------------- +### Read and prepare the configuration. + +our %C = (); +open my $fh, "<", "$CROSSDIR/etc/config.sh" or barf "open config: $!"; +while (<$fh>) { + chomp; + if (/^\s*(#|$)/) { next; } + if (!/^([^=]+)='(.*)'$/) { barf "bad config line `$_'"; } + my ($k, $v) = ($1, $2); + $v =~ s/'\\''/'/g; + $C{$k} = $v; +} + +our @ALLARCH; +our %FLAVOUR; +for my $f (split ' ', $C{"FLAVOURS"}) { + (my $v = $f) =~ tr/-/_/; + my @a = split ' ', $C{"${v}_ARCHS"}; + @{$FLAVOUR{$f}} = @a; + push @ALLARCH, @a; +} + +###-------------------------------------------------------------------------- +### Parse the command line. + +our @ARCH = (); +our $JOBS = undef; +our $DIR = "."; +our $MARKER = "{}"; +our @CMD = (); + +my $mo = new MdwOpt "hC:j:m:", + { "help" => { 'return' => "h" }, + "directory" => { 'return' => 'C', 'arg' => 'req' }, + "jobs" => { 'return' => "j", 'arg' => 'req' }, + "marker" => { 'return' => "m", 'arg' => 'req' } }, + \@ARGV, + ['inorder']; +my $bad = 0; +OPT: for (;;) { + my ($opt, $arg) = $mo->read; + last OPT unless defined $opt; + if ($opt eq "h") { usage STDOUT; exit 0; } + elsif ($opt eq "C") { $DIR = $arg; } + elsif ($opt eq "j") + { $arg =~ /^[+]?\d+$/ or barf "bad integer `$arg'"; $JOBS = $arg; } + elsif ($opt eq "m") { $MARKER = $arg; } + elsif ($opt eq '') { + if ($arg =~ /^=(.*)$/) { + my $pat = $1; + my $match = 0; + ARCH: for my $a (@ALLARCH) { + next ARCH unless fnmatch $pat, $a; + $match = 1; push @ARCH, $a; + } + $match or barf "no architectures match `$pat'"; + } elsif ($arg =~ /^:(.*)$/) { + my $f = $1; + exists $FLAVOUR{$f} or barf "no architeture flavour `$f'"; + push @ARCH, @{%FLAVOUR{$f}}; + } else { + push @CMD, $arg; + last OPT; + } + } else { + $bad = 1; + } +} +$bad = 1 unless @ARCH && @CMD; +push @CMD, $mo->rest, @{$mo->{argv}}; +if ($bad) { usage STDERR; exit 2; } + +###-------------------------------------------------------------------------- +### Hack the arguments and run `make'. + +sub hack ($) { + my ($x) = @_; + $x =~ s/'/'\\''/g; + $x =~ s/\$/\$\$/g; + $x =~ s/\Q$MARKER/\$*/g; + return "'$x'"; +} + +our $TARGET = "run"; +if ($CMD[0] eq 'make') { $TARGET = "run-make"; shift @CMD; } +our @MAKE = ("make"); +push @MAKE, "-f$CROSSDIR/Makefile"; +push @MAKE, "-j$JOBS" if defined $JOBS; +push @MAKE, $TARGET; +push @MAKE, "ARCHS=" . join " ", @ARCH; +push @MAKE, "ARGS=" . join " ", map { hack $_ } @CMD; +push @MAKE, "CROSSDIR=$CROSSDIR"; +push @MAKE, "DIR=" . hack $DIR; +exec @MAKE; +barf "make: $!"; + +###----- That's all, folks -------------------------------------------------- -- [mdw]