7 #use Data::Dump qw(dump);
9 # we only process english messages
10 $ENV{LC_MESSAGES
} = "C";
13 my $this_path = abs_path
();
14 my $cflags = defined($ENV{CFLAGS
}) ?
$ENV{CFLAGS
} : "";
15 my $ldflags = defined($ENV{LDFLAGS
}) ?
$ENV{LDFLAGS
} : "";
17 sub min
{ my ($a, $b) = @_; return $a < $b ?
$a : $b; }
20 die "syntax: $0 [--new --force --verbose --step --ignore-errors] mainfile.c [-lc -lm -lncurses]\n" .
21 "--new will ignore an existing .rcb file and rescan the deps\n" .
22 "--force will force a complete rebuild despite object file presence.\n" .
23 "--verbose will print the complete linker output and other info\n" .
24 "--debug adds -O0 -g3 to CFLAGS\n" .
25 "--step will add one dependency after another, to help finding hidden deps\n" .
26 "--hdrsrc=ulz:../lib/include replace <ulz> in header inclusion with \"../lib/include\"\n" .
27 " this redirects rcb header lookup so rcb tags can point to the right dir.\n";
53 $l-- while($l && substr($x, $l, 1) ne ".");
54 return substr($x, 0, $l + 1) if($l);
61 $l-- while($l && substr($x, $l, 1) ne ".");
62 return substr($x, $l) if($l);
78 my $colstr = "\033[%dm";
84 sub process_include_dirs
{
85 my @p = split / /, $cflags;
87 push @includedirs, ""; # empty entry for the first iteration in scandep_doit
88 for($i = 0; $i < scalar(@p); $i++) {
91 push @includedirs, $p[$i];
92 } elsif($p[$i] =~ /^\-I(.+)/) {
93 push @includedirs, $1;
100 printf $colstr, $colors->{$color};
104 printf $colstr, $colors->{"end"};
107 my $ignore_errors = 0;
110 my ($self, $na) = @_;
111 my $is_header = ($na =~ /\.h$/);
112 for my $i (@includedirs) {
113 my $delim = ($i eq "") ?
"" : "/";
114 my $nf = $i . $delim . $na;
115 my $np = dirname
($nf);
116 my $nb = basename
($nf);
117 if(!defined($hdep{abs_path
($nf)})) {
125 last unless $is_header;
127 printc
("red", "failed to find dependency $na referenced from $self!\n");
128 die unless $is_header || $ignore_errors;
132 my ($basepath, $relpath) = @_;
133 #print "$basepath ::: $relpath\n";
134 die "both path's must start with /" if(substr($basepath, 0, 1) ne "/" || substr($relpath, 0, 1) ne "/");
135 $basepath .= "/" if($basepath !~ /\
/$/ && -d
$basepath);
136 $relpath .= "/" if($relpath !~ /\
/$/ && -d
$relpath);
140 my $min = min
(length($basepath), length($relpath));
141 $l++ while($l < $min && substr($basepath, $l, 1) eq substr($relpath, $l, 1));
143 $l-- if($l < $min && substr($basepath, $l, 1) eq "/");
144 $l-- while(substr($basepath, $l, 1) ne "/");
146 $l++ if substr($relpath, $l, 1) eq "/";
147 my $res = substr($relpath, $l);
149 while($l2 < length($basepath)) {
150 $sl++ if substr($basepath, $l2, 1) eq "/";
154 for ($i = 0; $i < $sl; $i++) {
162 my ($self, $path, $tf) = @_;
163 my $is_header = ($tf =~ /\.h$/);
164 my $absolute = substr($tf, 0, 1) eq "/";
166 my $fullpath = abs_path
($path) . "/" . $tf;
167 # the stuff in the || () part is to pass headers which are in the CFLAGS include path
168 # unmodified to scandep_doit
169 my $nf = $absolute || ($is_header && $tf !~ /^\./ && ! -e
$fullpath) ?
$tf : $fullpath;
170 printc
("red", "[RcB] warning: $tf not found, continuing...\n"), return if !defined($nf);
173 if ($nf =~ /^\// && ! $is_header) {
174 $nf = make_relative
($this_path, $nf);
176 die "problem processing $self, $path, $tf" if(!defined($nf));
178 my @deps = glob($nf);
180 scandep_doit
($self, $d);
183 scandep_doit
($self, $nf);
188 my $forcerebuild = 0;
192 my $mainfile = undef;
193 my $debug_cflags = 0;
197 my ($path, $file) = @_;
199 my $self = $path . "/" . $file;
203 $hdep{abs_path
($self)} = 1;
204 open($fp, "<", $self) or die "could not open file $self: $!";
207 if ($line =~ /^\/\
/RcB: (\w{3,7}) \"(.+?)\"/) {
210 if($command eq "DEP") {
211 next if $skipinclude;
213 print "found RcB DEP $self -> $tf\n" if $verbose;
214 scandep
($self, $path, $tf);
215 } elsif ($command eq "LINK") {
216 next if $skipinclude;
217 print "found RcB LINK $self -> $arg\n" if $verbose;
219 } elsif ($command eq "SKIPON") {
220 $skipinclude++ if $cflags =~ /-D\Q$arg\E/;
221 } elsif ($command eq "SKIPOFF") {
222 $skipinclude-- if $cflags =~ /-D\Q$arg\E/;
223 } elsif ($command eq "SKIPUON") {
224 $skipinclude++ unless $cflags =~ /-D\Q$arg\E/;
225 } elsif ($command eq "SKIPUOFF") {
226 $skipinclude-- unless $cflags =~ /-D\Q$arg\E/;
228 } elsif($line =~ /^\s*#\s*include\s+\"([\w\.\/_\
-]+?
)\"/) {
230 next if file_ext
($tf) eq ".c";
232 print "skipping $self -> $tf\n" if $verbose;
235 print "found header ref $self -> $tf\n" if $verbose;
236 scandep
($self, $path, $tf);
237 } elsif($line =~ /^\s*#\s*include\s+<([\w\.\/_\
-]+?
)>/) {
239 for my $subst(keys %hdrsubsts) {
240 if($tf =~ /\Q$subst\E/) {
242 $tf =~ s/\Q$subst\E/$hdrsubsts{$subst}/;
244 print "skipping $self -> $tf\n" if $verbose;
247 print "applied header subst in $self: $tfold -> $tf\n" if $verbose;
248 scandep
($self, $path, $tf);
257 push @adep, $self if $file =~ /[\w_-]+\.[c]{1}$/; #only add .c files to deps...
261 my $arg1 = shift @ARGV or syntax
;
262 if($arg1 eq "--force") {
265 } elsif($arg1 eq "--verbose") {
268 } elsif($arg1 eq "--new") {
271 } elsif($arg1 eq "--step") {
274 } elsif($arg1 eq "--ignore-errors") {
277 } elsif($arg1 eq "--debug") {
280 } elsif($arg1 =~ /--hdrsrc=(.*?):(.*)/) {
287 $mainfile = shift unless defined($mainfile);
288 syntax
unless defined($mainfile);
291 if (defined($ENV{CC
})) {
295 printc
"blue", "[RcB] \$CC not set, defaulting to cc\n";
298 process_include_dirs
();
300 $cflags .= $debug_cflags ?
" -O0 -g3" : "";
303 if (defined($ENV{NM
})) {
307 printc
"blue", "[RcB] \$NM not set, defaulting to nm\n";
312 printc
"magenta", "[CC] ", $cmdline, "\n";
313 my $reslt = `$cmdline 2>&1`;
315 printc
"red", "ERROR ", $!, "\n";
322 $link = expandarr
(@ARGV) . " ";
324 my $cnd = name_wo_ext
($mainfile);
325 my $cndo = $cnd . "o";
326 my $bin = $cnd . "out";
328 my $cfgn = name_wo_ext
($mainfile) . "rcb";
329 my $haveconfig = (-e
$cfgn);
330 if($haveconfig && !$ignore_rcb) {
331 printc
"blue", "[RcB] config file $cfgn found. trying single compile.\n";
332 @adep = `cat $cfgn | grep "^DEP " | cut -b 5-`;
333 my @rcb_links = `cat $cfgn | grep "^LINK" | cut -b 6-`;
334 my $cs = expandarr
(@adep);
335 my $ls = expandarr
(@rcb_links);
336 $link = $ls if (defined($ls) && $ls ne "");
337 my $res = compile
("$cc $cflags $cs $link -o $bin $ldflags");
338 if($res =~ /undefined reference to/) {
339 printc
"red", "[RcB] undefined reference[s] found, switching to scan mode\n";
342 printc
"red", "[RcB] error. exiting.\n";
344 printc
"green", "[RcB] success. $bin created.\n";
350 printc
"blue", "[RcB] scanning deps...";
352 scanfile dirname
(abs_path
($mainfile)), basename
($mainfile);
354 printc
"green", "done\n";
357 printc
"blue", "[RcB] compiling main file...\n";
358 my $op = compile
("$cc $cflags -c $mainfile -o $cndo");
359 exit 1 if($op =~ /error:/g);
373 printc
"blue", "[RcB] resolving linker deps...\n";
376 if($i + 1 >= @adep) { #last element of the array is the already build mainfile
381 %glob_missym = %missym, last unless $relink;
383 my %missym_old = %missym;
385 my $ex = expandhash
(\
%obj);
386 printc
"blue", "[RcB] trying to link ...\n";
387 my $cmd = "$cc $cflags $ex $link -o $bin $ldflags";
388 printc
"cyan", "[LD] ", $cmd, "\n";
391 if(/undefined reference to [\'\`\"]{1}([\w\._]+)[\'\`\"]{1}/) {
396 /([\/\w\
._\
-]+): file
not recognized
: File format
not recognized
/ ||
397 /architecture of input file [\'\`\"]{1}([\/\w\
._\
-]+)[\'\
`\"]{1} is incompatible with/ ||
398 /fatal error: ([\/\w\._\-]+): unsupported ELF machine number/ ||
399 /ld: ([\/\w\._\-]+): Relocations in generic ELF/
402 $i = delete $obj{$cnd};
403 printc "red", "[RcB] incompatible object file $cnd, rebuilding...\n";
408 %missym = %missym_old;
411 /collect2: ld returned 1 exit status/ ||
412 /collect2: error: ld returned 1 exit status/ ||
413 /In function [\'\`\"]{1}[\w_
]+[\'\
`\"]{1}:/ ||
414 /more undefined references to/
417 printc "red", "[RcB] Warning: unexpected linker output!\n";
420 if(!scalar(keys %missym)) {
428 goto skip unless defined $cnd;
432 $cndo = name_wo_ext($cnd) . "o";
433 if(($forcerebuild || $rebuildflag || ! -e $cndo) && !defined($rebuilt{$cndo})) {
434 my $op = compile("$cc $cflags -c $cnd -o $cndo");
435 if($op =~ /error:/) {
436 exit 1 unless($ignore_errors);
441 @opa = `$nm -g
$cndo 2>&1`;
445 if(/[\da-fA-F]{8,16}\s+[TWRBCD]{1}\s+([\w_]+)/) {
447 $symhash{$symname} = 1;
449 } elsif (/File format not recognized/) {
450 printc "red", "[RcB] nm doesn't recognize the format of $cndo, rebuilding...\n";
456 $sym{$cndo} = \%symhash;
459 if(defined($symhash{$_})) {
463 if($objfail || $step) {
466 printc "red", "[RcB] adding $cndo to the bunch...\n" if $step;
477 printc "red", "[RcB] failed to resolve the following symbols, check your DEP tags\n";
478 for(keys %glob_missym) {
482 printc "green", "[RcB] success. $bin created.\n";
483 printc "blue", "saving required dependencies to $cfgn\n";
485 open($fh, ">", $cfgn);
487 print { $fh } "DEP ", name_wo_ext($_), "c\n";
489 print { $fh } "LINK ", $link, "\n" if($link);