File indexing completed on 2025-01-26 05:13:52
0001 #!/usr/bin/perl 0002 # Copyright (C) 2006-2010 Thomas Zander <zander@kde.org> 0003 $COMMAND = "unsercmake"; 0004 $VERSION = "$COMMAND 0.32"; 0005 # This script allows you to build a target library including dependency checking, but it 0006 # limits the dependency checking to the current dir and its subdirs meaning a lot of 0007 # unwanted steps are skipped. 0008 0009 0010 # user settings... 0011 # if we assume the user has something like: /kde/project as a sourcedir and /kde/current/project 0012 # as a build dir then we can auto-switch between those dirs 0013 # Please alter the variable to set the basedir and the builddir. Like this: 0014 # BASE=BUILD 0015 # Examples: 0016 # $buildDirType = "/kde=/kde/current"; 0017 # $buildDirType = "kde/{project}=kde/{project}/build"; 0018 $buildDirType = ""; 0019 0020 ########################## 0021 # Copyright (c) 2006-2010 Thomas Zander <zander@kde.org> 0022 # 0023 # This program is free software; you can redistribute it and/or modify 0024 # it under the terms of the GNU General Public License as published by 0025 # the Free Software Foundation; either version 2 of the License, 0026 # or (at your option) any later version. 0027 # 0028 # This program is distributed in the hope that it will be useful, 0029 # but WITHOUT ANY WARRANTY; without even the implied warranty of 0030 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0031 # GNU General Public License for more details. 0032 # 0033 # You should have received a copy of the GNU General Public License 0034 # along with this program; if not, write to the Free Software 0035 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 0036 0037 if ($buildDirType eq "") { 0038 print "Not setup!\n"; 0039 print " This tool can automatically switch from your source dir to the builddir.\n"; 0040 print " To do this correctly you have to alter the $COMMAND using your editor\n"; 0041 print " and alter the buildDirType variable according to the comments.\n"; 0042 exit; 0043 } 0044 0045 #argument options 0046 $argMap{"C"}="directory"; 0047 $argMap{"j"}="jobs"; 0048 $argMap{"n"}="just-print"; 0049 $argMap{"v"}="verbose"; 0050 $argMap{"k"}="keep-going"; 0051 $argMap{"p"}="projecthelp"; 0052 $argMap{"h"}="help"; 0053 $argMap{"e"}="exclude"; 0054 $argMap{"q"}="quiet"; 0055 @options=split(" ", "help directory jobs just-print verbose keep-going version projecthelp exclude quiet"); 0056 @argCommands=split(" ","directory jobs exclude"); 0057 0058 &parseArguments(@ARGV); 0059 if(defined $help) { 0060 print "Usage: $COMMAND [OPTION] ...\n"; 0061 print "Available options:\n\n"; 0062 print " -h, --help This help.\n"; 0063 print " -p --projecthelp print project help information\n"; 0064 print " -j, --jobs=N Allow N parallel jobs.\n"; 0065 print " -k, --keep-going Keep going when some targets can't be made.\n"; 0066 print " -C, --directory=dir Change to directory dir before doing anything.\n"; 0067 print " -v, --verbose Show verbose output.\n"; 0068 print " --version Show version information and copyright notice.\n"; 0069 print " -n --just-print Only print out the commands to call.\n"; 0070 print " -q --quiet Don't print details\n"; 0071 print " -e --exclude=RegEx Exclude libraries and subdirs based on a regular expression\n"; 0072 exit; 0073 } 0074 0075 if(defined $version) { 0076 print "$VERSION\nWritten by Thomas Zander\n\nThis is free software see the source for copying conditions. There is NO\nwarranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"; 0077 exit; 0078 } 0079 0080 if(defined $directory) { 0081 chdir $directory || die "Failed to change directory to '$directory'\n"; 0082 } 0083 0084 while(! -f "CMakeFiles" && ! -f "CMakeLists.txt") { 0085 # for the case where there is a subdir but that subdir does not have a cmakefile 0086 chdir ".." || die "Error: Can not find a cmake based basedir\nPlease start from the source dir\n"; 0087 if(`pwd` eq "/\n") { 0088 die "Error: Can not find a cmake based basedir\nPlease start from the source dir\n"; 0089 } 0090 } 0091 0092 $dir=`pwd`; 0093 chomp($dir); 0094 $builddir = "$dir/"; 0095 0096 # find the build-dir where the Makefile lives 0097 @bd = split("=", $buildDirType); 0098 if($#bd > 0) { 0099 # make sure slashes are all round 0100 $bd[0]="/$bd[0]/"; 0101 $bd[1]="/$bd[1]/"; 0102 $bd[0]=~s/\/\//\//g; 0103 $bd[1]=~s/\/\//\//g; 0104 my $wildcard=""; 0105 if($bd[0]=~/(\{.*\})/) { # used {project} wildcard 0106 $wildcard=$1; 0107 if($bd[1]=~/$wildcard/) { 0108 $bd[0]=~s/$wildcard/(.*?)/; 0109 } 0110 } 0111 if($builddir=~/$bd[0]/ && $builddir=~/$bd[1]/ == false) { 0112 my $project=$wildcard; 0113 $builddir=~/$bd[0]/; 0114 if($builddir=~m/$bd[0]/) { 0115 $project=$1; 0116 } 0117 $builddir=~s/$bd[0]/$bd[1]/; 0118 if($wildcard ne "" && $project ne "") { 0119 $builddir=~s/$wildcard/$project/; 0120 } 0121 #print "$dir => $builddir\n"; 0122 } 0123 } 0124 0125 $makeDependNeeded = 0; 0126 &readCmakeFile("."); 0127 0128 if(defined $projecthelp) { 0129 print "\nLibraries:\n\n"; 0130 foreach $key (sort {uc($a) cmp uc($b)} @libraries) { 0131 print " $key"; 0132 if(defined $exclude && $key=~/$exclude/) { 0133 print " [excluded]"; 0134 } 0135 print "\n"; 0136 } 0137 exit; 0138 } 0139 0140 $doInstall = 0; 0141 if($#userArgs >= 0) { # limit to user request 0142 foreach $lib (@userArgs) { 0143 if($lib eq "install") { 0144 $doInstall = 1; 0145 if($#userArgs == 0) { 0146 @targets = @libraries; 0147 } 0148 next; 0149 } 0150 0151 my $pureLib = $lib; 0152 $pureLib=~s/^[\/ \\]*//; 0153 $pureLib=~s/[\/ \\]*$//; 0154 if(&contains($pureLib, @libraries)) { 0155 push @targets, $pureLib; 0156 } 0157 else { 0158 push @nonLibTargets, $lib; 0159 } 0160 } 0161 } 0162 else { 0163 @targets=@libraries; 0164 } 0165 0166 foreach $lib (@targets) { # add dependencies 0167 @deps = &dependencies(0, $lib); 0168 LIB: foreach $l (@deps) { 0169 if($l eq "ERROR") { 0170 exit 1; 0171 } 0172 if(defined $exclude && $l=~/$exclude/) { 0173 next; 0174 } 0175 foreach $x (@maketargets) { 0176 if($l eq $x) { 0177 next LIB; 0178 } 0179 } 0180 push @maketargets, $l; 0181 } 0182 } 0183 0184 # cd to builddir 0185 chdir $builddir || die "Can't find builddir '$builddir'\n"; 0186 if($makeDependNeeded) { 0187 system ("make depend") == 0 or exit 1; 0188 } 0189 0190 @actualTargets=`make help`; 0191 foreach $target (@nonLibTargets) { 0192 foreach $h (@actualTargets) { 0193 if($h=~/\b$target\b/) { 0194 push @maketargets, $target; 0195 } 0196 } 0197 } 0198 0199 if($doInstall == 0 && $#maketargets < 0) { 0200 print "No targets\n"; 0201 exit; 0202 } 0203 0204 if(defined $jobs) { 0205 $jobs="-j $jobs"; 0206 } 0207 if(defined $verbose) { 0208 $verbose="VERBOSE=0"; 0209 } 0210 foreach $lib (@maketargets) { 0211 chdir "$builddir/$locations{$lib}"; 0212 $dir = $locations{$lib}; 0213 $dir=~s/^\.\///; 0214 if(&checkTargetAvail($lib) == 1) { 0215 if(! defined $quiet) { 0216 print "Building lib: $lib (in $dir)\n"; 0217 } 0218 if(defined $just_print) { 0219 next; 0220 } 0221 foreach $h (@actualTargets) { 0222 if($h=~/(\b$lib\_automoc)/) { 0223 system ("make $jobs $verbose $1"); 0224 last; 0225 } 0226 } 0227 0228 my $rc = system ("make $jobs $verbose $lib/fast"); 0229 if(!(defined $keep_going) && $rc != 0) { 0230 exit 1; 0231 } 0232 } 0233 elsif(! defined $quiet) { 0234 print "Skipping lib: $lib (in $dir)\n"; 0235 } 0236 } 0237 0238 # install! 0239 if($doInstall == 1) { 0240 if(! defined $quiet) { 0241 print "Installing changed files"; 0242 #if($dir ne ".") { print " in '$dir'"; } 0243 print "\n"; 0244 } 0245 if(! defined $just_print) { 0246 @output=`(cd $builddir && make install/fast)`; 0247 my $line; 0248 foreach $line (@output) { 0249 if ($line=~/^-- (Installing: .*)$/) { 0250 print "$1\n"; 0251 } 0252 } 0253 } 0254 } 0255 0256 ############### 0257 sub readCmakeFile { 0258 my $dir=shift(@_); 0259 my $file="CMakeLists.txt"; 0260 0261 if($makeDependNeeded == 0) { 0262 my $buildAge = (stat("$builddir/$dir/CMakeFiles/CMakeDirectoryInformation.cmake"))[9]; 0263 my $fileAge = (stat("$dir/$file"))[9]; 0264 if($fileAge > $buildAge) { 0265 $makeDependNeeded = 1; 0266 print "makeDepend needed due to $dir\n"; 0267 } 0268 } 0269 0270 my @subdirs; 0271 open FILE, "<", "$dir/$file"; 0272 my $inLink=0; 0273 my $lib = ""; 0274 foreach $line (<FILE>) { 0275 if($inLink == 0 && $line=~/^\s*add_subdirectory\s*\((.*)\)/i) { 0276 my $d=$1; 0277 $d=~s/^\s*//; #chomp 0278 $d=~s/\s*$//; 0279 push @subdirs, $d; 0280 } 0281 0282 my $libs = ""; 0283 if($inLink == 1) { 0284 $libs = $line; 0285 } 0286 if($line=~/^\s*target_link_libraries\s*\((.*)/) { 0287 $libs = $1; 0288 $inLink=1; 0289 } 0290 if($inLink == 1 && $libs ne "") { 0291 foreach $i (split(" ", $libs)) { 0292 if($i=~/\)$/) { # incase no space was used 0293 $i=~s/\)$//; 0294 $inLink = 0; 0295 } 0296 if($i eq ")") { #incase a space was used 0297 $inLink = 0; 0298 } 0299 elsif($inLink==1 && $lib eq "") { # a library is defined in this file 0300 $lib = $i; 0301 push @libraries, $lib; 0302 #print "found lib: $lib\n"; 0303 $locations{$lib} = "$dir"; 0304 } 0305 else { # and that libraries dependencies 0306 $$lib .= "$i "; 0307 #print " supportlib $i\n"; 0308 } 0309 } 0310 $libs =""; 0311 } 0312 if($inLink == 0) { 0313 $lib = ""; 0314 } 0315 } 0316 close FILE; 0317 0318 # only go into subdir when the DartTestfile lists the subdir. 0319 open FILE, "<", "$builddir/$dir/DartTestfile.txt"; 0320 foreach $line (<FILE>) { 0321 if($line=~/^SUBDIRS\s*\((.*)\)/) { 0322 my $subdir; 0323 foreach $subdir (@subdirs) { 0324 if(defined $exclude && "/$subdir/"=~/$exclude/) { 0325 next; 0326 } 0327 if($line=~/\b$subdir\b/) { 0328 &readCmakeFile("$dir/$subdir"); 0329 } 0330 } 0331 last; 0332 } 0333 } 0334 close FILE; 0335 } 0336 0337 sub dependencies() { 0338 my $level=shift(@_); 0339 my $target=shift(@_); 0340 if($level > 12) { 0341 print "ERROR; dependency checking hit 12 levels deep, probably a circular dependency, baling out\n"; 0342 print " dependencies traceback (bottom depends on top):\n"; 0343 print " $target\n"; 0344 return "ERROR"; 0345 } 0346 #my $inset =""; 0347 #for($i=0; $i < $level; $i++) { $inset .=" "; } 0348 #print "$inset |dependencies of '$target'\n"; 0349 0350 my @depend; 0351 my $lib; 0352 #print "$inset depsLine='$$target'\n"; 0353 foreach $lib (split(" ", $$target)) { 0354 my $found=0; 0355 my $l; 0356 # if not in our libraries, skip it. 0357 foreach $l (@libraries) { 0358 if($l eq $lib) { 0359 $found=1; 0360 last; 0361 } 0362 } 0363 if($found == 0) { 0364 next; 0365 } 0366 #print "$inset has dependency $lib\n"; 0367 0368 if(&contains($lib, @depend) == 0) { 0369 #print "$inset $lib not yet present in: \""; 0370 #foreach $dslf (@depend) { 0371 # print " $dslf"; 0372 #} 0373 #print "\"\n"; 0374 # before we add it, lets find the dependencies of that library 0375 foreach $l (&dependencies($level+1, $lib)) { 0376 if($l eq "ERROR") { 0377 print " $target\n"; 0378 return "ERROR"; 0379 } 0380 if(&contains($l, @depend) == 0) { 0381 push @depend, $l; 0382 } 0383 } 0384 push @depend, $lib; 0385 } 0386 } 0387 if(&contains($target, @depend) == 0) { 0388 push @depend, $target; 0389 } 0390 0391 #foreach $dslf (@depend) { 0392 # print "$inset += '$dslf'\n"; 0393 #} 0394 0395 return @depend; 0396 } 0397 0398 ## Returns 1 if the first arg is already present in the rest of the array, 0 otherwise 0399 sub contains() { 0400 my $new=shift(@_); 0401 @array = @_; 0402 my $item; 0403 foreach $item (@array) { 0404 if($item eq $new) { 0405 return 1; 0406 } 0407 } 0408 return 0; 0409 } 0410 0411 sub parseArguments() { 0412 @arguments=@_; 0413 for($i=0; $i<=$#arguments; $i++) { 0414 if($arguments[$i]=~/(-{1,2}\S+?)(=\S+|)$/) { 0415 $command=$1; 0416 $param=$2; 0417 if($command=~/^-\b(.)(.*)/) { 0418 my $char=$1; 0419 $param=$2; 0420 $command=$argMap{$char}; 0421 } 0422 $command=~s/^--//; 0423 if(&contains($command, @options) == 0) { 0424 print "unrecognized option '$arguments[$i]'\n"; 0425 print "Try `$COMMAND --help' for more information.\n"; 0426 exit 1; 0427 } 0428 if($param eq "" && &contains($command, @argCommands) == 1) { 0429 $i++; 0430 if($arguments[$i]=~/^-/ || $#arguments < $i) { 0431 print "Missing argument: $arguments[$i-1]\n"; 0432 exit 1; 0433 } 0434 $param = $arguments[$i]; 0435 } 0436 if($param eq "") { 0437 $param =1; 0438 } 0439 $command=~s/\W/_/g; 0440 # print "setting param $command\n"; 0441 $$command=$param; 0442 } 0443 else { 0444 push @userArgs, $arguments[$i]; 0445 } 0446 } 0447 } 0448 0449 sub checkTargetAvail() { 0450 my $target=shift(@_); 0451 open FILE, "<", "Makefile"; 0452 foreach $line (<FILE>) { 0453 if($line=~/^$target:/) { 0454 close FILE; 0455 return 1; 0456 } 0457 } 0458 close FILE; 0459 return 0; 0460 } 0461