1#! /bin/sh -- 2eval '(exit $?0)' && eval 'PERL_BADLANG=x;export PERL_BADLANG;: \ 3;exec perl -T -S -- "$0" ${1+"$@"};#'if 0; 4eval 'setenv PERL_BADLANG x;exec perl -x -S -- "$0" $argv:q;#'.q 5#!perl -w 6+($0=~/(.*)/s);do$1;die$@if$@;__END__+if 0; 7# Don't touch/remove lines 1--7: http://www.inf.bme.hu/~pts/Magic.Perl.Header 8# 9# ccdep.pl v0.32 -- semiautomatic dependency discovery for C/C++ programs 10# by pts@math.bme.hu at Fri May 31 13:36:29 CEST 2002 11# 0.31 by pts@math.bme.hu at Sat Jun 1 15:19:55 CEST 2002 12# 0.32 by pts@math.bme.hu at Tue Sep 3 19:12:20 CEST 2002 13# 0.33 by pts@math.bme.hu at Thu Oct 31 09:47:25 CET 2002 14# 15# Dat: no -T (tainting checks) anymore, does bad to our readpipe() 16# OK : emit `TARGETS_yes = ...' 17# Imp: make #warning etc. in *.h files work as expected 18# OK : generated.h 19# Imp: add external libraries (-L...) 20# Imp: abort if gcc command not found... 21# Imp: abort if not all .o are mentioned in gcc output 22# Imp: avoid $ etc. in Makefile 23# OK : all 8 combinations of PROVIDES|REQUIRES|CONFLICTS 24# 25BEGIN { eval { require integer; import integer } } 26BEGIN { eval { require strict ; import strict } } 27 28# Typical invocation: ccdep.pl --FAL=assert,no,yes,checker $(CXX) 29my @FAL=(); 30if (@ARGV and $ARGV[0]=~/\A--FAL=(.*)/s) { @FAL=split/\W+/,$1; shift@ARGV } 31my $GCCP; # 'g++' or 'gcc': C/C++ preproc with `-E -MG' switches 32$GCCP="@ARGV"; 33$GCCP="gcc" if $GCCP!~y/ \t//c; 34 35# Make sure we get English error messages from gcc. 36delete $ENV{LANG}; 37delete $ENV{LANGUAGE}; 38$ENV{LC_ALL} = "C"; 39 40# --- 41 42# Returns true iff the passed filename is a C or C++ source file (and not .o 43# or .h or anything else). 44sub is_ds($) { 45 my $FN = $_[0]; 46 return $FN =~ /\.(c(|c|xx|pp|[+][+])|C)\Z(?!\n)/; 47} 48 49sub find_ds() { 50 #** @return a list of .ds file in the current directory 51 my @L; my $E; 52 die unless opendir DIR, '.'; 53 while (defined($E=readdir DIR)) { 54 push @L, $E if is_ds($E) and -f $E; 55 } 56 @L 57} 58 59sub expand_glob($$ $) { 60 #** @param $_[0] string containing globs (? and *) 61 #** @param $_[1] listref of names 62 #** @param $_[2] dest listref 63 my $S=quotemeta($_[0]); 64 if (0==($S=~s@\\([?*])@.$1@g)) { push @{$_[2]}, $_[0]; return } 65 my $RE=eval"+sub{\$_[0]=~/$S/}"; 66 die$@if$@; 67 for my $E (@{$_[1]}) { push @{$_[2]}, $E if $RE->($E) } 68} 69 70sub mustbe_subset_of($$ $$) { 71 #** A must be a subset (sublist) of B 72 #** @param $_[0] caption A 73 #** @param $_[1] ref(array(string)) A 74 #** @param $_[2] caption B 75 #** @param $_[3] ref(array(string)) B 76 my @A=sort @{$_[1]}; 77 my @B=sort @{$_[3]}; 78 my($AV,$BV); 79 while (defined($AV=pop@A)) { 80 1 while defined($BV=pop@B) and $BV gt $AV; 81 if (!defined($BV) or $BV ne $AV) { 82 print STDERR "$0: $_[0] are: @{$_[1]}\n"; 83 print STDERR "$0: $_[2] are: @{$_[3]}\n"; 84 die "$0: $_[0] must be a subset of $_[2]\n"; 85 } 86 } 87} 88 89# --- 90 91print "$0: running.\n"; 92 93sub unix_shq($) { 94 my $S=$_[0]; 95 $S=~s@'@'\\''@g; 96 "'$S'" 97} 98 99sub shq($) { 100 my $S=$_[0]; 101 return $S if $S!~/[^\w.-]/; 102 if ($^O =~ /(?:win32|win64|windows)/i) { # $^O eq 'MSWin32' 103 # assume the called program is CygWin/Ming32; see later 104 # Arguments are delimited by white space, which is either a space or a tab. 105 # . A string surrounded by double quotation marks is interpreted as a 106 # single argument, regardless of white space contained within. A quoted 107 # string can be embedded in an argument. Note that the caret (^) is not 108 # recognized as an escape character or delimiter. 109 # . A double quotation mark preceded by a backslash, \", is interpreted as 110 # a literal double quotation mark ("). 111 # . Backslashes are interpreted literally, unless they immediately precede 112 # a double quotation mark. 113 # . If an even number of backslashes is followed by a double quotation 114 # mark, then one backslash (\) is placed in the argv array for every pair 115 # of backslashes (\\), and the double quotation mark (") is interpreted as 116 # a string delimiter. 117 # . If an odd number of backslashes is followed by a double quotation 118 # mark, then one backslash (\) is placed in the argv array for every pair 119 # of backslashes (\\) and the double quotation mark is interpreted as an 120 # escape sequence by the remaining backslash, causing a literal double 121 # quotation mark (") to be placed in argv. 122 $S =~ s@(\\+)(?="|\Z(?!\n))@$1$1@; 123 $S=~s@"@\\"@g; return qq{"$S"} 124 } else { 125 $S=~s@'@'\\''@g; return qq{'$S'} 126 } 127} 128#die shq(q{foo\b"ar\\}) eq q{"foo\b\"ar\\\\"}; # For Win32. 129 130sub backtick(@) { 131 my $S=$_[0]; 132 if ($^O eq 'MSWin32') { 133 # assume the called program is CygWin/Ming32; and we have proper /bin/sh 134 $S="sh -c ".unix_shq($S); # /bin/sh can handle IO-redirections such as `2>&1' right 135 } else { 136 # assume UNIX 137 } 138 print "+ $S\n"; 139 #die unless $ENV{PATH}=~/(.*)/s; # Dat: untaint() 140 #$ENV{PATH}=$1; 141 #die "$ENV{PATH}"; 142 # Dat: if `.' is part of $ENV{PATH}, `Insecure directory...' is reported 143 #die unless $S=~/(.*)/s; # Dat: untaint() 144 readpipe $S # `` qx`` 145} 146 147my @DS=find_ds(); 148my @DSQ=map{shq$_}@DS; 149# g++-4.6 has it, clang++-3.4 doesn't but we don't care to rerun. 150my $DIAG = ""; 151my $Q="$GCCP -DOBJDEP$DIAG -M -MG -E 2>&1 @DSQ"; 152my $R=backtick($Q); 153 154if ($R!~/#\s*warning\s/) { 155 # config2.h:314:4: warning: #warning REQUIRES: c_lgcc3.o 156 # Dat: g++-3.3 and g++-4.8 omit #warning (implicit -w) with -M -MG -E. 157 # clang++-3.4 still emits warnings, so no need to rerun here. 158 $R.="\n".backtick("$GCCP -DOBJDEP$DIAG -E 2>&1 >/dev/null @DSQ"); 159} 160 161$R =~ s@\\\n@@g; # Merge line continuations emitted by `gcc -M -MG'. 162 163#** $idep{"x.ds"} contains the .h and .ds dependency line for x.ds 164my %idep; 165#** $odep{"x.o"} is "x.ds" 166my %odep; 167my $included_from; 168 169my @instructions; 170 171while ($R=~/\G(.*)\n?/g) { 172 my $S=$1; 173 174 if ($S=~/\AIn file included from (?:[.]\/)*([^:]+)/) { 175 # If foo.cpp includes foo.hpp, which includes bar.hpp, then 176 # clang++-3.4 emits these in a row: foo.cpp, ./foo.hpp, and no bar.hpp. 177 # We want to keep only foo.cpp, hence we check 178 # `if !defined($included_from)'. 179 $included_from=$1 if !defined($included_from); 180 # Bottommost includer. 181 } elsif ($S=~/\A\s{3,}from (?:[.]\/)*([^:]+)/) { # From gcc-3.2. 182 $included_from=$1; # Higher includer, we override the previous one, because we need the topmost one. 183 } elsif ($S=~/\A(?:[.]\/)*([^:]+):\d+:(\d+:)? warning: (?:#warning )?([A-Z][-A-Z]{2,}):(.*)\Z/) { 184 # ^^^ (\d+:)? added for gcc-3.1 185 # ^^^ clang: appliers.cpp:554:6: warning: REQUIRES: out_gif.o [-W#warnings] 186 my($DS,$B,$features)=($1, $3, $4); # $B is e.g. 'PROVIDES'. 187 if (defined $included_from) { $DS=$included_from; undef $included_from } 188 die "$0: #include detection broken: expected non-header source file, got $DS in line: $S\n" if 189 not is_ds($DS); 190 die "$0: unknown dependency verb $B in line: $S\n" if 191 $B !~ m@\A(NULL-PROVIDES|PROVIDES|CONFLICTS|REQUIRES)\Z(?!\n)@; 192 for my $feature (split ' ',$features) { 193 if ($feature !~ m@\A\[-W@) { # g++-4.8 generates extra [-Wcpp] lines. 194 #print "INSTR $DS $B $feature\n"; 195 push @instructions, [$DS, $B, $feature]; 196 } 197 } 198 } elsif ($S=~/\A([^: ]+)\.o:( ([^: ]+?)\.([^ \n]+).*)\Z/s and $1 eq $3) { 199 # Dependency output of `gcc -M -MG'. 200 201 # $O: The .o file. 202 # $B: All the source dependencies (.cpp, .cxx, .cc, .C, .c, .ci etc.). 203 # $DS: The .ds file: the source file corresponding to the .o file, e.g. 204 # $O is 'encoder.o', $DS is 'encoder.cpp'. 205 my($O,$B,$DS)=("$1.o",$2,"$3.$4"); 206 # ^^^ Dat: maybe both t.c and t.cxx 207 $B =~ s@ /[^ ]+@@g; # remove absolute pathnames 208 $B =~ s@\s+@ @g; $B =~ s@\A\s*@ @; $B =~ s@\s*\Z(?!\n)@ @; 209 die "$0: .o file in sources for $DS: $B" if $B =~ m@ [.]o @; 210 # Example reason: foo.c and foo.cpp both present as source files. 211 die "$0: .o file generated from multiple sources: $O and $odep{$O}" if 212 exists $odep{$O}; 213 die if exists $idep{$DS}; # Shouldn't happen, $odep{$O} would exist first. 214 $odep{$O}=$DS; 215 $idep{$DS}=$B; 216 push @instructions, [$DS, 'PROVIDES', $O]; 217 undef $included_from; 218 } elsif ($S=~/: (?:warning|error|fatal error|fatal):/) { 219 undef $included_from; 220 } else { 221 # Be resilient, and just ignore every other possible message here. This is 222 # for future compatibility with compilers. The infamous `invalid depret' 223 # error by ccdep.pl used to be here, but now it's gone. 224 } 225} 226undef $included_from; # Save memory. 227%odep=(); # Save memory. 228 229#** $pro{"x.ds"} is the list of features provided by "x.ds"; multiplicity 230my %pro; 231#** $req{"x.ds"} is the list of features required by "x.ds"; multiplicity 232my %req; 233#** $con{"x.ds"} is the list of features conflicted by "x.ds"; multiplicity 234my %con; 235#** $repro{"feature"} is a hash=>1 of .ds that provide "feature" 236my %repro; 237#** $mapro{"executable"} eq "x.ds" if "x.ds" provides main() for "executable". 238#** It looks like values are never used. 239my %mapro; 240#** hash=>1 of "feature"s of NULL-PROVIDES 241my %nullpro; 242for my $instruction (@instructions) { 243 my($DS, $verb, $feature) = @$instruction; 244 if ($verb eq 'NULL-PROVIDES') { 245 $nullpro{$feature} = 1; 246 } elsif ($verb eq 'PROVIDES') { 247 push @{$pro{$DS}}, $feature; 248 push @{$con{$DS}}, $feature; 249 $repro{$feature}{$DS}=1; 250 if ($feature=~/\A(.*)_main\Z/) { $mapro{$1}=$DS } 251 } elsif ($verb eq 'REQUIRES') { 252 push @{$req{$DS}}, $feature; 253 } elsif ($verb eq 'CONFLICTS') { 254 push @{$con{$DS}}, $feature; 255 } else { 256 die "$0: unknown verb in instruction: @$instruction\n"; # Never happens. 257 } 258} 259@instructions=(); # Save memory. 260 261mustbe_subset_of "providers"=>[sort keys(%pro)], "dep_sources"=>[sort keys(%idep)]; 262mustbe_subset_of "dep_sources"=>[sort keys(%idep)], "sources"=>\@DS; 263 264{ my @K=keys %repro; 265 for my $DS (sort keys%con) { 266 my $L = $con{$DS}; 267 my @R=(); 268 for my $feature (@$L) { expand_glob $feature, \@K, \@R } 269 # ^^^ multiplicity remains 270 $con{$DS}=\@R; 271 } 272} 273 274my $outfn = "Makedep"; 275die unless open MD, "> $outfn"; 276die unless print MD ' 277ifndef CCALL 278CCALL=$(CC) $(CFLAGS) $(CFLAGSB) $(CPPFLAGS) $(INCLUDES) 279endif 280ifndef CXXALL 281CXXALL=$(CXX) $(CXXFLAGS) $(CXXFLAGSB) $(CPPFLAGS) $(INCLUDES) 282endif 283ifndef LDALL 284LDALL=$(LDY) $(LDFLAGS) $(LIBS) 285endif 286ifndef CC 287CC=gcc 288endif 289ifndef CXX 290CXX=g++ 291endif 292ifndef LD 293LD=$(CC) -s 294endif 295ifndef LDXX 296LDXX=$(CXX) -s 297endif 298ifndef LDY 299LDY=$(LD) 300endif 301ifndef CFLAGS 302CFLAGS=-O2 -W -Wall -fsigned-char 303endif 304ifndef CXXFLAGS 305CXXFLAGS=-O2 -W -Wall -fsigned-char 306endif 307ifndef GLOBFILES 308GLOBFILES=Makefile $outfn 309endif 310'; 311 312die unless print MD "ALL +=", join(' ',sort keys%mapro), "\n"; 313die unless print MD "TARGETS =", join(' ',sort keys%mapro), "\n"; 314 315# vvv Thu Oct 31 09:49:02 CET 2002 316# (not required) 317#my %targets_fal; 318#for my $FA (@FAL) { $targets_fal{$FA}="TARGETS_$FA =" } 319 320for my $EXE (sort keys%mapro) { 321 print "exe $EXE\n"; 322 my @REQO = (); # Will be list of .o files required by $EXE. 323 my @REQDS = (); # Will be list of .ds files required $EXE. 324 my @REQH = (); # Will be list of .h files required $EXE. 325 326 # Find the transitive dependencies of $EXE, save to @REQO, @REQDS, @REQH. 327 { 328 my %CON=(); # hash=>1 of features already conflicted 329 my %PRO=%nullpro; # hash=>1 of features already provided 330 my $feature; 331 my @features_to_analyze=("${EXE}_main"); 332 while (defined($feature=pop@features_to_analyze)) { 333 next if exists $PRO{$feature}; 334 #print "feat $feature (@features_to_analyze)\n"; ## 335 # vvv Dat: r.b.e == required by executable 336 die "$0: feature $feature r.b.e $EXE conflicts\n" if exists $CON{$feature}; 337 my @L=sort keys%{$repro{$feature}}; 338 die "$0: feature $feature r.b.e $EXE unprovided\n" if!@L; 339 die "$0: feature $feature r.b.e $EXE overprovided: @L\n" if$#L>=1; 340 # Now: $L[0] is a .ds providing the feature 341 push @REQDS, $L[0]; 342 my $O=$L[0]; $O=~s@\.[^.]+\Z@.o@; 343 push @REQO, $O; 344 345 $PRO{$feature}=1; 346 for my $feature2 (@{$pro{$L[0]}}) { 347 die "$0: extra feature $feature2 r.b.e $EXE conflicts\n" if exists $CON{$feature2} and not exists $PRO{$feature2}; 348 $PRO{$feature2}=1; 349 } 350 for my $feature2 (@{$req{$L[0]}}) { 351 push @features_to_analyze, $feature2 if!exists $PRO{$feature2} 352 } 353 for my $feature2 (@{$con{$L[0]}}) { $CON{$feature2}=1 } 354 # die if! exists $PRO{$feature}; # assert 355 } 356 my %REQH; 357 for my $DS (@REQDS) { 358 for my $HDS (split' ', $idep{$DS}) { 359 $REQH{$HDS} = 1 if !is_ds($HDS); 360 } 361 } 362 push @REQH, sort keys %REQH; 363 } 364 365 die unless print MD "${EXE}_DS=@REQDS\n${EXE}_H=@REQH\n". 366 "$EXE: \$(GLOBFILES) @REQO\n\t\$(LDALL) @REQO -o $EXE\n\t". 367 q!@echo "Created executable file: !.$EXE. 368 q! (size: `perl -e 'print -s "!.$EXE.q!"'`)."!. "\n"; 369 # vvv Sat Jun 1 15:40:19 CEST 2002 370 for my $FA (@FAL) { 371 die unless print MD 372 "$EXE.$FA: \$(GLOBFILES) \$(${EXE}_DS) \$(${EXE}_H)\n\t". 373 "\$(CXD_$FA) \$(CXDFAL) \$(${EXE}_DS) -o $EXE.$FA\n"; 374 # $targets_fal{$FA}.=" $EXE.$FA"; 375 } 376} 377print MD "\n"; 378 379# vvv Thu Oct 31 09:49:02 CET 2002 380# (not required) 381# for my $FA (@FAL) { print MD "$targets_fal{$FA}\n" } 382# print MD "\n"; 383 384for my $K (sort keys%idep) { 385 my $V = $idep{$K}; 386 my $O=$K; $O=~s@\.([^.]+)\Z@.o@; my $srcext = $1; 387 my @V = sort split' ', $V; 388 my $compiler = $srcext eq'c'?"CC":"CXX"; 389 print MD "$O: \$(GLOBFILES) @V\n\t\$(${compiler}ALL) -c $K\n" 390} 391print MD "\n"; 392 393die unless close MD; 394 395print "$0: done, created $outfn\n"; 396 397__END__ 398