chiark / gitweb /
src/cross-run.in: New tool to run command in multiple cross-build envs.
[mdw-cross] / src / cross-run.in
1 #! /usr/bin/perl
2
3 use Data::Dumper;
4
5 use File::FnMatch qw(fnmatch);
6 use MdwOpt;
7
8 our $CROSSDIR = "@crossdir@";
9 (our $ME = $0) =~ s:^.*/::;
10
11 ###--------------------------------------------------------------------------
12 ### Utilities.
13
14 sub usage (*) {
15   my ($fh) = @_;
16   print $fh
17     "usage: $ME [-j N] =ARCHGLOB|:FLAVOUR ... [--] CMD [ARGS ...]\n";
18 }
19 sub barf ($) {
20   my ($msg) = @_;
21   print STDERR "$0: $msg\n";
22   exit 2;
23 }
24
25 ###--------------------------------------------------------------------------
26 ### Read and prepare the configuration.
27
28 our %C = ();
29 open my $fh, "<", "$CROSSDIR/etc/config.sh" or barf "open config: $!";
30 while (<$fh>) {
31   chomp;
32   if (/^\s*(#|$)/) { next; }
33   if (!/^([^=]+)='(.*)'$/) { barf "bad config line `$_'"; }
34   my ($k, $v) = ($1, $2);
35   $v =~ s/'\\''/'/g;
36   $C{$k} = $v;
37 }
38
39 our @ALLARCH;
40 our %FLAVOUR;
41 for my $f (split ' ', $C{"FLAVOURS"}) {
42   (my $v = $f) =~ tr/-/_/;
43   my @a = split ' ', $C{"${v}_ARCHS"};
44   @{$FLAVOUR{$f}} = @a;
45   push @ALLARCH, @a;
46 }
47
48 ###--------------------------------------------------------------------------
49 ### Parse the command line.
50
51 our @ARCH = ();
52 our $JOBS = undef;
53 our $DIR = ".";
54 our $MARKER = "{}";
55 our @CMD = ();
56
57 my $mo = new MdwOpt "hC:j:m:",
58   { "help" => { 'return' => "h" },
59     "directory" => { 'return' => 'C', 'arg' => 'req' },
60     "jobs" => { 'return' => "j", 'arg' => 'req' },
61     "marker" => { 'return' => "m", 'arg' => 'req' } },
62   \@ARGV,
63   ['inorder'];
64 my $bad = 0;
65 OPT: for (;;) {
66   my ($opt, $arg) = $mo->read;
67   last OPT unless defined $opt;
68   if ($opt eq "h") { usage STDOUT; exit 0; }
69   elsif ($opt eq "C") { $DIR = $arg; }
70   elsif ($opt eq "j")
71     { $arg =~ /^[+]?\d+$/ or barf "bad integer `$arg'"; $JOBS = $arg; }
72   elsif ($opt eq "m") { $MARKER = $arg; }
73   elsif ($opt eq '') {
74     if ($arg =~ /^=(.*)$/) {
75       my $pat = $1;
76       my $match = 0;
77       ARCH: for my $a (@ALLARCH) {
78         next ARCH unless fnmatch $pat, $a;
79         $match = 1; push @ARCH, $a;
80       }
81       $match or barf "no architectures match `$pat'";
82     } elsif ($arg =~ /^:(.*)$/) {
83       my $f = $1;
84       exists $FLAVOUR{$f} or barf "no architeture flavour `$f'";
85       push @ARCH, @{%FLAVOUR{$f}};
86     } else {
87       push @CMD, $arg;
88       last OPT;
89     }
90   } else {
91     $bad = 1;
92   }
93 }
94 $bad = 1 unless @ARCH && @CMD;
95 push @CMD, $mo->rest, @{$mo->{argv}};
96 if ($bad) { usage STDERR; exit 2; }
97
98 ###--------------------------------------------------------------------------
99 ### Hack the arguments and run `make'.
100
101 sub hack ($) {
102   my ($x) = @_;
103   $x =~ s/'/'\\''/g;
104   $x =~ s/\$/\$\$/g;
105   $x =~ s/\Q$MARKER/\$*/g;
106   return "'$x'";
107 }
108
109 our $TARGET = "run";
110 if ($CMD[0] eq 'make') { $TARGET = "run-make"; shift @CMD; }
111 our @MAKE = ("make");
112 push @MAKE, "-f$CROSSDIR/Makefile";
113 push @MAKE, "-j$JOBS" if defined $JOBS;
114 push @MAKE, $TARGET;
115 push @MAKE, "ARCHS=" . join " ", @ARCH;
116 push @MAKE, "ARGS=" . join " ", map { hack $_ } @CMD;
117 push @MAKE, "CROSSDIR=$CROSSDIR";
118 push @MAKE, "DIR=" . hack $DIR;
119 exec @MAKE;
120 barf "make: $!";
121
122 ###----- That's all, folks --------------------------------------------------