File indexing completed on 2024-04-28 09:37:08
0001 #!/usr/bin/perl -w 0002 # -*- cperl-indent-level: 2 -*- 0003 ############################################################################### 0004 # Parses the KDE Projects XML Database and prints project protocol-url lines # 0005 # for each project in the specified component/module. # 0006 # # 0007 # Copyright (C) 2011,2012,2014,2017 by Allen Winter <winter@kde.org> # 0008 # Copyright (C) 2011 by David Faure <faure@kde.org> # 0009 # # 0010 # This program is free software; you can redistribute it and/or modify # 0011 # it under the terms of the GNU General Public License as published by # 0012 # the Free Software Foundation; either version 2 of the License, or # 0013 # (at your option) any later version. # 0014 # # 0015 # This program is distributed in the hope that it will be useful, # 0016 # but WITHOUT ANY WARRANTY; without even the implied warranty of # 0017 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # 0018 # GNU General Public License for more details. # 0019 # # 0020 # You should have received a copy of the GNU General Public License along # 0021 # with this program; if not, write to the Free Software Foundation, Inc., # 0022 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # 0023 ############################################################################### 0024 0025 # TODO 0026 # - validation (once a DTD is available) 0027 0028 use strict; 0029 use Getopt::Long; 0030 use XML::Parser; 0031 use LWP::Simple; # used to fetch the xml db 0032 0033 my($Prog) = 'kde-checkout-list.pl'; 0034 my($Version) = '0.97'; 0035 0036 my($help) = ''; 0037 my($version) = ''; 0038 my($searchComponent) = ''; 0039 my($searchModule) = ''; 0040 my($searchProtocol) = "git"; 0041 my($allmatches) = 0; 0042 my($printDesc) = 0; 0043 my($doClone) = 0; 0044 my($doPrune) = 0; 0045 my($dryRun) = 0; 0046 my($quitOnError) = 0; 0047 my($gitSuffix) = 0; 0048 my($branch) = ''; 0049 0050 exit 1 0051 if (!GetOptions('help' => \$help, 'version' => \$version, 0052 'component=s' => \$searchComponent, 0053 'module=s' => \$searchModule, 0054 'protocol=s' => \$searchProtocol, 0055 'all' => \$allmatches, 0056 'desc' => \$printDesc, 0057 'clone' => \$doClone, 0058 'prune' => \$doPrune, 0059 'dry-run' => \$dryRun, 0060 'quit-on-error' => \$quitOnError, 0061 'gitsuffix' => \$gitSuffix, 0062 'branch=s' => \$branch 0063 )); 0064 0065 &Help() if ($help); 0066 0067 if ($searchProtocol ne "git" && 0068 $searchProtocol ne "http" && 0069 $searchProtocol ne "ssh" && 0070 $searchProtocol ne "tarball") { 0071 print "Invalid protocol \"$searchProtocol\" specified.\n"; 0072 print "Run $Prog --help for more info\n"; 0073 exit 1; 0074 } 0075 &Version() if ($version); 0076 0077 if ($searchModule && !$searchComponent) { 0078 print "Module specified, but not in which component. Please use --component kde for instance.\n"; 0079 print "Run $Prog --help for more info\n"; 0080 exit 1; 0081 } 0082 0083 my $kdebranch=''; 0084 my $kdedash=''; 0085 if ($branch) { 0086 $kdebranch = "KDE/" . $branch; 0087 $kdedash = "kde-" . $branch; 0088 } 0089 0090 my $curComponent = ""; 0091 my $curModule = ""; 0092 my $curProject = ""; 0093 my $curPath = ""; 0094 my $curUrl = ""; 0095 my $curActive = 1; 0096 my $curDesc = ""; 0097 my $skipModule = 0; 0098 my $inRepo = 0; 0099 my $inPath = 0; 0100 my $inUrl = 0; 0101 my $inActive = 0; 0102 my $inDesc = 0; 0103 0104 my @element_stack; # remember which elements are open 0105 my %output; # project name -> project data 0106 my %projectByPath; # project path -> project name 0107 0108 my $projects = get("https://projects.kde.org/kde_projects.xml"); 0109 die "Failed to download kde_projects.xml" unless defined $projects; 0110 0111 # sanity check 0112 my @lines = split('\n',$projects); 0113 if ($lines[0] !~ m/xml version/ || $lines[$#lines] !~ m+</kdeprojects>+) { 0114 print "The kde_projects.xml downloaded is invalid somehow. Try again\n"; 0115 exit 1; 0116 } 0117 0118 # initialize the parser 0119 my $parser = XML::Parser->new( Handlers => 0120 { 0121 Start=>\&handle_start, 0122 End=>\&handle_end, 0123 Char=>\&char_handler, 0124 }); 0125 0126 $parser->parse( $projects ); 0127 0128 # print results 0129 my($proj,$ret); 0130 foreach $proj (sort keys %output) { 0131 if ( $output{$proj}{'active'} || $allmatches ) { 0132 my $subdir = $output{$proj}{'path'}; 0133 my $url = $output{$proj}{'url'}; 0134 my $desc = ""; 0135 if ( defined($output{$proj}{'desc'}) ) { 0136 $desc = $output{$proj}{'desc'}; 0137 } 0138 if ( !$printDesc ) { 0139 print "$subdir $url\n"; 0140 } else { 0141 print "$subdir $url $desc\n"; 0142 } 0143 0144 if ( $doClone ) { 0145 my $command; 0146 my $newCheckout = 0; 0147 if ( ! -d "$subdir" ) { 0148 $newCheckout = 1; 0149 #modules without the "KDE/" in the branchname are: 0150 # kdebase/kate => only KDE/4.7 and above 0151 # kdeexamples => No branches 0152 # superbuild => No branches 0153 0154 if ( $branch ) { 0155 next if ( $subdir =~ m+/kdeexamples+ || $subdir =~ m+/superbuild+ ); 0156 if ( $subdir !~ m+/kdelibs+ ) { 0157 $command = "git clone -b $kdebranch --single-branch $url $subdir && cd $subdir && git checkout $kdebranch"; 0158 # $command = "git clone $url $subdir && cd $subdir && git checkout -b $kdebranch origin/$kdebranch"; 0159 } else { 0160 $command = "git clone $url $subdir && cd $subdir && git checkout $kdebranch"; 0161 } 0162 } else { 0163 $command = "git clone $url $subdir"; 0164 } 0165 } else { 0166 if ($branch) { 0167 next if ( $subdir =~ m+/kdeexamples+ || $subdir =~ m+/superbuild+ ); 0168 $command = "cd $subdir && git config remote.origin.url $url && git checkout $kdebranch && git pull --ff"; 0169 } else { 0170 $command = "cd $subdir && git config remote.origin.url $url && git pull --ff"; 0171 } 0172 } 0173 $ret = &runCommand( $command ); 0174 if ($ret) { 0175 #check if there is a branch by this name in the repo. If not, then no error. 0176 my($ohno) = 1; 0177 if (-d "$subdir/.git" && $branch) { 0178 $ret = &runCommand( "cd $subdir && git checkout $kdebranch" ); 0179 if ($ret) { 0180 $ohno = 0; 0181 } 0182 } 0183 if ($newCheckout) { 0184 printf "REMOVING CLONE DUE TO GIT FAILURE\n"; 0185 runCommand("rm -rf $subdir"); 0186 } 0187 if ($ohno) { 0188 if ($quitOnError) { 0189 printf "Exiting due to quit-on-error option\n"; 0190 } else { 0191 printf "Continuing anyway\n" 0192 } 0193 } else { 0194 printf "FYI: $subdir does not have a branch called $kdebranch. Continuing normally\n" 0195 } 0196 } 0197 } 0198 } 0199 } 0200 0201 # wipe out old checkouts, if requested 0202 if ( $doPrune ) { 0203 my $startDir = "."; 0204 if ( $searchComponent ) { 0205 $startDir = $searchComponent; 0206 if ($branch) { 0207 my $foo = $searchComponent . "-" . $branch; 0208 $startDir =~ s+$searchComponent+$foo+; 0209 } 0210 if ( $searchModule ) { 0211 $startDir .= "/$searchModule"; 0212 } 0213 } 0214 if ( -d $startDir ) { 0215 open(my $F, "find $startDir -name .git |"); 0216 while (my $line = <$F>) { 0217 chomp $line; 0218 $line =~ s,/\.git,,; 0219 $line =~ s,^\./,,; 0220 if ( not exists $projectByPath{$line} ) { 0221 print STDERR "Deleting old git checkout: $line\n"; 0222 runCommand( "rm -rf \"$line\"" ); 0223 } 0224 } 0225 } 0226 } 0227 0228 sub runCommand { 0229 my ( $command ) = @_; 0230 my $ret = 0; 0231 if ( $dryRun ) { 0232 print STDERR "$command\n"; 0233 } else { 0234 $ret = system( $command ); 0235 $ret = $ret >> 8; 0236 } 0237 return $ret; 0238 } 0239 0240 # process a start-of-element event: print message about element 0241 # 0242 sub handle_start { 0243 my( $expat, $element, %attrs ) = @_; 0244 0245 # ask the expat object about our position 0246 my $line = $expat->current_line; 0247 0248 # remember this element and its starting position by pushing a 0249 # little hash onto the element stack 0250 push( @element_stack, { element=>$element, line=>$line }); 0251 0252 #print STDERR "-- $element\n"; 0253 0254 if ( $element eq "component" ) { 0255 my $value = $attrs{"identifier"}; 0256 #print STDERR "component identifier=$value\n"; 0257 if ( (!$searchComponent or ($value eq $searchComponent)) ) { 0258 $curComponent = $value; 0259 $curModule = ""; 0260 $curProject = ""; 0261 $inRepo = 0; 0262 0263 #print STDERR "BEGIN component $curComponent.\n"; 0264 } 0265 } 0266 0267 if ( $curComponent && $element eq "module" ) { 0268 my $value = $attrs{"identifier"}; 0269 $curProject = ""; 0270 if ( !$searchModule or ($value eq $searchModule) ) { 0271 $curModule = $value; 0272 #print STDERR "BEGIN module $curModule\n"; 0273 $skipModule = 0; 0274 } else { 0275 $skipModule = 1; 0276 #print STDERR "SKIP module $value\n"; 0277 } 0278 } 0279 0280 if ( $curComponent && !$skipModule && $element eq "project" ) { 0281 $curProject = $attrs{"identifier"}; 0282 if (!$curModule) { 0283 #print STDERR "project without a module! $curProject\n"; 0284 } 0285 #print STDERR "BEGIN project $curProject\n"; 0286 } 0287 0288 if ($curComponent && !$skipModule) { 0289 if ( $element eq "path" ) { 0290 $inPath = 1; 0291 } elsif ( $element eq "repo" ) { 0292 $inRepo = 1; 0293 $curActive = 1; # assume all repos are active by default 0294 } elsif ( $inRepo && $element eq "url" ) { 0295 my $value = $attrs{"protocol"}; 0296 if ( $value eq $searchProtocol ) { 0297 $inUrl = 1; 0298 } 0299 } elsif ( $inRepo && $element eq "active" ) { 0300 $inActive = 1; 0301 } elsif ( $element eq "description" ) { 0302 $inDesc = 1; 0303 } 0304 } 0305 } 0306 0307 # process an end-of-element event 0308 # 0309 sub handle_end { 0310 my( $expat, $element ) = @_; 0311 0312 # We'll just pop from the element stack with blind faith that 0313 # we'll get the correct closing element, since XML::Parser will scream 0314 # bloody murder if any well-formedness errors creep in. 0315 my $element_record = pop( @element_stack ); 0316 0317 if ( $element eq "component" && $curComponent ) { 0318 #print "END of component $curComponent\n"; 0319 $curComponent = ""; 0320 } 0321 if ( $element eq "module" && $curComponent && $curModule ) { 0322 #print "END of module $curModule\n"; 0323 $curModule = ""; 0324 } 0325 if ( $element eq "project" && $curComponent && $curModule && $curProject ) { 0326 #print "END of project $curProject\n"; 0327 $curProject = ""; 0328 $curUrl = ""; 0329 } 0330 if ( $element eq "repo" && $curComponent && $inRepo ) { 0331 #print STDERR "repo in $curPath: $curUrl\n"; 0332 $inRepo = 0; 0333 if ( $curUrl && $curPath ) { 0334 if ($branch) { 0335 my $foo = $curComponent . "-" . $branch; 0336 $curPath =~ s+$curComponent+$foo+; 0337 } 0338 my $subdir = $curPath; 0339 $curPath .= "-git" if ($gitSuffix && -d "$curPath/.svn"); 0340 # $subdir is the logical name (extragear/network/konversation) 0341 # while $curPath is the physical path (extragear/network/konversation-git) 0342 $output{$subdir}{'path'} = $curPath; 0343 $output{$subdir}{'url'} = $curUrl; 0344 $output{$subdir}{'active'} = $curActive; 0345 $output{$subdir}{'desc'} = $curDesc; 0346 $curDesc = ""; 0347 $projectByPath{$curPath} = $subdir; 0348 } else { 0349 if (!$curUrl) { 0350 print STDERR "ERROR: repo without url! $curComponent $curModule $curProject $curPath\n"; 0351 } elsif (!$curPath) { 0352 print STDERR "ERROR: repo without path! $curComponent $curModule $curProject $curUrl\n"; 0353 } 0354 } 0355 } 0356 if ( $element eq "path" ) { 0357 $inPath = 0; 0358 } elsif ( $element eq "url" ) { 0359 $inUrl = 0; 0360 } elsif ( $element eq "active" ) { 0361 $inActive = 0; 0362 } elsif ( $element eq "description" ) { 0363 $inDesc = 0; 0364 } 0365 } 0366 0367 sub char_handler 0368 { 0369 my ($p, $data) = @_; 0370 0371 $data =~ s/\n/\n\t/g; 0372 if ( $inPath ) { 0373 $curPath = $data; 0374 } elsif ( $inUrl ) { 0375 $curUrl = $data; 0376 } elsif ( $inActive ) { 0377 $curActive = !( $data =~ m/false/i || $data =~ m/off/i ); 0378 } elsif ( $inDesc && !$curDesc ) { 0379 if ( $data !~ m/^\s*$/ ) { 0380 $curDesc = $data; 0381 } 0382 } 0383 0384 } # End of default_handler 0385 0386 # Help function: print help message and exit. 0387 sub Help { 0388 &Version(); 0389 print "Parses the KDE Projects XML Database and prints project protocol-url lines\n"; 0390 print "for each project in the specified component/module.\n\n"; 0391 print " --help display help message and exit\n"; 0392 print " --version display version information and exit\n"; 0393 print " --component search for projects within this component\n"; 0394 print " --module search for projects within this module (requires --component)\n"; 0395 print " --branch git checkout the specified branch, i.e. 4.6\n"; 0396 print " --protocol print the URI for the specified protocol (default=\"git\")\n"; 0397 print " possible values are \"git\", \"http\", \"ssh\" or \"tarball\"\n"; 0398 print " --all print all projects, not just active-only projects\n"; 0399 print " --desc print the project description too\n"; 0400 print "\n"; 0401 print " --clone actually do a git clone or pull of every repo found\n"; 0402 print " Note: this is meant for servers like lxr/ebn rather than for developers.\n"; 0403 print " --gitsuffix append '-git' to the directory name when cloning, if a svn dir exists.\n"; 0404 print " --prune remove old git checkouts that are not listed anymore\n"; 0405 print " --dry-run show git and prune commands but don't execute them.\n"; 0406 print "\n"; 0407 print "Examples:\n\n"; 0408 print "To print the active projects in extragear network with git protocol:\n"; 0409 print "% $Prog --component=extragear --module=network\n"; 0410 print "\n"; 0411 print "To print all projects in playground utils with the ssh protocol:\n"; 0412 print "% $Prog --component=playground --module=utils --protocol=ssh --all\n"; 0413 print "\n"; 0414 exit 0 if $help; 0415 } 0416 0417 # Version function: print the version number and exit. 0418 sub Version { 0419 print "$Prog, version $Version\n"; 0420 exit 0 if $version; 0421 }