File indexing completed on 2024-03-24 05:44: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