92ba994f1190bbdab991fed599afbefabad95ed6
[mirror/qt/qt5.git] / init-repository
1 #!/usr/bin/env perl
2 #############################################################################
3 ##
4 ## Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
5 ## All rights reserved.
6 ## Contact: Nokia Corporation (qt-info@nokia.com)
7 ##
8 ## This file is part of the utilities of the Qt Toolkit.
9 ##
10 ## $QT_BEGIN_LICENSE:LGPL$
11 ## No Commercial Usage
12 ## This file contains pre-release code and may not be distributed.
13 ## You may use this file in accordance with the terms and conditions
14 ## contained in the Technology Preview License Agreement accompanying
15 ## this package.
16 ##
17 ## GNU Lesser General Public License Usage
18 ## Alternatively, this file may be used under the terms of the GNU Lesser
19 ## General Public License version 2.1 as published by the Free Software
20 ## Foundation and appearing in the file LICENSE.LGPL included in the
21 ## packaging of this file.  Please review the following information to
22 ## ensure the GNU Lesser General Public License version 2.1 requirements
23 ## will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 ##
25 ## In addition, as a special exception, Nokia gives you certain additional
26 ## rights.  These rights are described in the Nokia Qt LGPL Exception
27 ## version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 ##
29 ## If you have questions regarding the use of this file, please contact
30 ## Nokia at qt-info@nokia.com.
31 ##
32 ##
33 ##
34 ##
35 ##
36 ##
37 ##
38 ##
39 ## $QT_END_LICENSE$
40 ##
41 #############################################################################
42
43 use strict;
44 use warnings;
45
46 package Qt::InitRepository;
47
48
49 =head1 NAME
50
51 init-repository - initialize the Qt5 repository and all submodules
52
53 =head1 SYNOPSIS
54
55   ./init-repository [options]
56
57 This script may be run after an initial `git clone' of Qt5 in order to check
58 out all submodules.
59
60
61 =head2 Global options:
62
63 =over
64
65 =item --force, -f
66
67 Force initialization (even if the submodules are already checked out).
68
69
70 =item --quiet, -q
71
72 Be quiet.  Will exit cleanly if the repository is already initialized.
73
74 =back
75
76
77 =head2 Module options:
78
79 =over
80
81 =item --no-webkit
82
83 Skip webkit and webkit examples submodules.
84 It may be desirable to skip these modules due to the large size of the webkit
85 git repository.
86
87
88 =item --no-update
89
90 Skip the `git submodule update' command.
91
92
93 =item --ignore-submodules
94
95 Set git config to ignore submodules by default when doing operations on the
96 qt5 repo, such as `pull', `fetch', `diff' etc.
97
98 After using this option, pass `--ignore-submodules=none' to git to override
99 it as needed.
100
101 =back
102
103
104 =head2 Repository options:
105
106 =over
107
108 =item --nokia-developer
109
110 Switch to internal Nokia URLs.
111
112
113 =item --brisbane
114
115 Switch to internal Nokia URLs and make use of the Brisbane git mirrors.
116 (Implies `--mirror' and `--mirror-webkit').
117
118
119 =item --ssh
120
121 Use the SSH protocol for git operations.  This may be useful if the git
122 protocol is blocked by a firewall. Note that this requires a user account
123 with an uploaded SSH key on all servers used.  (Implies `--nokia-developer').
124
125
126 =item --http
127
128 Use the HTTP protocol for git operations.  This may be useful if the git
129 protocol is blocked by a firewall.  Note that this only works with the
130 external Gitorious server.
131
132
133 =item --alternates <path to other Qt5 repo>
134
135 Adds alternates for each submodule to another full qt5 checkout. This makes
136 this qt5 checkout very small, as it will use the object store of the
137 alternates before unique objects are stored in its own object store.
138
139 This option has no effect when using `--no-update'.
140
141 B<NOTE:> This will make this repo dependent on the alternate, which is
142 potentially dangerous!  The dependency can be broken by also using
143 the `--copy-objects' option, or by running C<git repack -a> in each
144 submodule, where required.  Please read the note about the `--shared' option
145 in the documentation of `git clone' for more information.
146
147
148 =item --copy-objects
149
150 When `--alternates' is used, automatically do a C<git repack -a> in each
151 submodule after cloning, to ensure that the repositories are independent
152 from the source used as a reference for cloning.
153
154 Note that this negates the disk usage benefits gained from the use of
155 `--alternates'.
156
157
158 =item --mirror <url-base>
159
160 Uses <url-base> as the base URL for submodule git mirrors.
161
162 For example:
163
164   --mirror user@machine:/foo/bar
165
166 ...will use the following as a mirror for qtbase:
167
168   user@machine:/foo/bar/qtbase.git
169
170
171 =item --mirror-webkit <url>
172
173 Uses <url> as the URL for the webkit git mirror.
174
175 =back
176
177 =cut
178
179 use Carp         qw( confess             );
180 use English      qw( -no_match_vars      );
181 use Getopt::Long qw( GetOptionsFromArray );
182 use Pod::Usage   qw( pod2usage           );
183
184 my %PROTOCOLS = (
185     'internal'  => 'git://scm.dev.nokia.troll.no/' ,
186     'ssh'       => 'git@scm.dev.nokia.troll.no:'   ,
187     'http'      => 'http://git.gitorious.org/'     ,
188 );
189
190 my %STAGING_REPOS = map { $_ => "git://gitorious.org/qt/$_.git" } qw(
191     qt5-staging
192     qt3support-staging
193     qtactiveqt-staging
194     qtbase-earth-staging
195     qtbase-staging
196     qtdeclarative-staging
197     qtdoc-staging
198     qtmultimedia-staging
199     qtphonon-staging
200     qtqa-staging
201     qtscript-staging
202     qtsvg-staging
203     qttools-staging
204     qttranslations-staging
205     qtwebkit-examples-and-demos-staging
206     qtxmlpatterns-staging
207     qtlocation-staging
208 );
209
210 my $BNE_MIRROR_URL_BASE
211     = 'git://bq-git.apac.nokia.com/qtsoftware/qt/';
212
213 my $BNE_MIRROR_WEBKIT_URL
214     = 'git://bq-git.apac.nokia.com/qtsoftware/research/gitorious-org-webkit-qtwebkit-mirror.git';
215
216 sub new
217 {
218     my ($class, @arguments) = @_;
219
220     my $self = {};
221     bless $self, $class;
222     $self->parse_arguments(@arguments);
223
224     return $self;
225 }
226
227 # Like `system', but possibly log the command, and die on non-zero exit code
228 sub exe
229 {
230     my ($self, @cmd) = @_;
231
232     if (!$self->{quiet}) {
233         print "+ @cmd\n";
234     }
235
236     if (system(@cmd) != 0) {
237         confess "@cmd exited with status $CHILD_ERROR";
238     }
239
240     return;
241 }
242
243 sub parse_arguments
244 {
245     my ($self, @args) = @_;
246
247     %{$self} = (%{$self},
248         'alternates'          => "",
249         'detach-alternates'   => 0 ,
250         'force'               => 0 ,
251         'ignore-submodules'   => 0 ,
252         'mirror-url'          => "",
253         'mirror-webkit-url'   => "",
254         'nokia-developer'     => 0 ,
255         'protocol'            => "",
256         'update'              => 1 ,
257         'webkit'              => 1 ,
258     );
259
260     GetOptionsFromArray(\@args,
261         'alternates=s'      =>  \$self->{qw{ alternates        }},
262         'copy-objects'      =>  \$self->{qw{ detach-alternates }},
263         'force'             =>  \$self->{qw{ force             }},
264         'ignore-submodules' =>  \$self->{qw{ ignore_submodules }},
265         'mirror-webkit=s'   =>  \$self->{qw{ mirror-webkit-url }},
266         'mirror=s'          =>  \$self->{qw{ mirror-url        }},
267         'nokia-developer'   =>  \$self->{qw{ nokia-developer   }},
268         'quiet'             =>  \$self->{qw{ quiet             }},
269         'update!'           =>  \$self->{qw{ update            }},
270         'webkit!'           =>  \$self->{qw{ webkit            }},
271
272         'help|?'            =>  sub { pod2usage(1);               },
273         'http'              =>  sub { $self->{protocol} = 'http'; },
274         'ssh|ssh-protocol'  =>  sub { $self->{protocol} = 'ssh';  },
275
276         'brisbane|brisbane-nokia-developer' => sub {
277             $self->{'nokia-developer'}   = 1;
278             $self->{'protocol'}          = 'internal';
279             $self->{'mirror-url'}        = $BNE_MIRROR_URL_BASE;
280             $self->{'mirror-webkit-url'} = $BNE_MIRROR_WEBKIT_URL;
281         },
282
283         'nokia-developer' => sub {
284             $self->{'nokia-developer'}   = 1;
285             $self->{'protocol'}          = 'internal';
286         },
287     ) || pod2usage(2);
288
289     if ($self->{'nokia-developer'} && $self->{'protocol'} eq 'http') {
290         print "*** Ignoring use of HTTP protocol, as it's only usable with external server\n";
291         $self->{'protocol'} = '';
292     }
293
294     # Replace any double trailing slashes from end of mirror
295     $self->{'mirror-url'} =~ s{//+$}{/};
296
297     return;
298 }
299
300 sub check_if_already_initialized
301 {
302     my ($self) = @_;
303
304     # We consider the repo as `initialized' if submodule.qtbase.url is set
305     if (qx(git config --get submodule.qtbase.url)) {
306         if ($self->{force}) {
307             my @configresult = qx(git config -l);
308             foreach (@configresult) {
309                 # Example line: submodule.qtqa.url=git://gitorious.org/qt/qtqa.git
310                 if (/(submodule\.[^.=]+)\.url=.*/) {
311                     $self->exe('git', 'config', '--remove-section', $1);
312                 }
313             }
314         }
315         else {
316             exit 0 if ($self->{quiet});
317             print "Will not reinitialize already initialized repository (use -f to force)!\n";
318             exit 1;
319         }
320     }
321
322     return;
323 }
324
325 sub git_submodule_init
326 {
327     my ($self) = @_;
328
329     my @init_args;
330     if ($self->{quiet}) {
331         push @init_args, '--quiet';
332     }
333     $self->exe('git', 'submodule', 'init', @init_args);
334
335     return;
336 }
337
338 sub git_disable_webkit_submodule
339 {
340     my ($self) = @_;
341
342     $self->exe('git', 'config', '--remove', 'submodule.qtwebkit');
343     $self->exe('git', 'config', '--remove', 'submodule.qtwebkit-examples-and-demos');
344
345     return;
346 }
347
348 sub git_set_submodule_config
349 {
350     my ($self) = @_;
351
352     my @configresult          = qx(git config -l);
353     my $protocol              = $self->{protocol};
354     my $url_base_for_protocol = $PROTOCOLS{$protocol};
355
356     GITCONFIG:
357     foreach my $line (@configresult) {
358         # Example line: submodule.qtqa.url=git://gitorious.org/qt/qtqa.git
359         next GITCONFIG if ($line !~ /(submodule\.[^.=]+\.url)=(.*)/);
360
361         my $key   = $1;
362         my $value = $2;
363
364         if ($protocol) {
365             # WebKit is special, and has only external link.
366             if ($key ne 'submodule.qtwebkit.url') {
367                 # qt-labs projects are still hosted under qt internally.
368                 if ($protocol ne 'http') {
369                     $value =~ s,^git://gitorious\.org/qt-labs/,${url_base_for_protocol}qt/,;
370                 }
371                 $value =~ s,^git://gitorious\.org/,$url_base_for_protocol,;
372             }
373         }
374
375         $self->exe('git', 'config', $key, $value);
376
377         if ($self->{'ignore-submodules'}) {
378             $key =~ s,\.url,.ignore,;
379             $self->exe('git', 'config', $key, 'all');
380         }
381     }
382
383     return;
384 }
385
386 sub git_clone_all_submodules
387 {
388     my ($self) = @_;
389
390     # manually clone each repo here, so we can easily use reference repos, mirrors and
391     # add all staging repos
392     my @configresult = qx(git config -l);
393     foreach my $line (@configresult) {
394         if ($line =~ /submodule\.([^.=]+)\.url=(.*)/) {
395             $self->git_clone_one_submodule($1, $2);
396         }
397     }
398
399     $self->exe('git', 'submodule', 'update');
400
401     return;
402 }
403
404 sub git_add_staging_remote
405 {
406     my ($self, $repo_basename) = @_;
407
408     my $protocol              = $self->{protocol};
409     my $url_base_for_protocol = $PROTOCOLS{$protocol};
410
411     my %current_remotes;
412     for my $line (qx(git remote show)) {
413         chomp $line;
414         $current_remotes{$line} = 1;
415     }
416
417     # We assume that any staging starting with `$repo_basename-' relates to this
418     # repo.  For example, for the `qtbase' module, `qtbase-staging'
419     # and `qtbase-earth-staging' are considered as related staging repos.
420     my @staging = grep { /^\Q$repo_basename\E-/; } keys %STAGING_REPOS;
421
422     STAGING:
423     foreach my $staging_repo (@staging) {
424         # nothing to do if remote already exists
425         next STAGING if ($current_remotes{$staging_repo});
426
427         my $staging_repo_url = $STAGING_REPOS{$staging_repo};
428         if ($protocol) {
429             if ($protocol ne 'http') {
430                 $staging_repo_url =~ s,^git://gitorious\.org/qt-labs/,${url_base_for_protocol}qt/,;
431             }
432             $staging_repo_url =~ s,^git://gitorious\.org/,$url_base_for_protocol,;
433         }
434         $self->exe('git', 'remote', 'add', $staging_repo, $staging_repo_url);
435     }
436
437     return;
438 }
439
440 sub git_clone_one_submodule
441 {
442     my ($self, $submodule, $url) = @_;
443
444     my $alternates            = $self->{ 'alternates'        };
445     my $mirror_url            = $self->{ 'mirror-url'        };
446     my $mirror_webkit_url     = $self->{ 'mirror-webkit-url' };
447
448     # `--reference FOO' args for the clone, if any.
449     my @reference_args;
450
451     if ($alternates) {
452         # alternates is a qt5 repo, so the submodule will be under that.
453         if (-d "$alternates/$submodule") {
454             @reference_args = ('--reference', "$alternates/$submodule");
455         }
456         else {
457             print " *** $alternates/$submodule not found, ignoring alternate for this submodule\n";
458         }
459     }
460
461     my $mirror;
462     if ($mirror_url && ($submodule ne 'qtwebkit')) {
463         $mirror = $mirror_url.$submodule;
464         $mirror .= ".git" unless (-d $mirror); # Support local disk mirror
465     }
466     elsif ($mirror_webkit_url && ($submodule eq 'qtwebkit')) {
467         $mirror = $mirror_webkit_url;
468     }
469
470     my $do_clone = (! -d "$submodule/.git");
471     if ($do_clone) {
472         $self->exe('git', 'clone', @reference_args, ($mirror ? $mirror : $url), $submodule);
473     }
474
475     chdir($submodule) or confess "chdir $submodule: $OS_ERROR";
476
477     if (!$do_clone) {
478         $self->exe('git', 'fetch', ($mirror ? $mirror : $url));
479     }
480
481     if ($mirror) {
482         $self->exe('git', 'config', 'remote.origin.url', $url);
483
484         # In `force' mode, remove the mirror if it already exists,
485         # since we may be reinitializing the module.
486         if ($self->{force}) {
487             eval { $self->exe('git', 'remote', 'rm', 'mirror'); }; # failure is OK
488         }
489
490         $self->exe('git', 'remote', 'add', 'mirror', $mirror);
491     }
492
493     $self->git_add_staging_remote($submodule);
494
495     if ($self->{'detach-alternates'}) {
496         $self->exe('git', 'repack', '-a');
497
498         my $alternates_path = '.git/objects/info/alternates';
499         unlink($alternates_path) || confess "unlink $alternates_path: $OS_ERROR";
500     }
501
502     chdir("..") or confess "cd ..: $OS_ERROR";
503
504     return;
505 }
506
507 sub run
508 {
509     my ($self) = @_;
510
511     $self->check_if_already_initialized;
512     $self->git_submodule_init;
513
514     if (!$self->{webkit}) {
515         $self->git_disable_webkit_submodule;
516     }
517
518     $self->git_set_submodule_config;
519
520     if ($self->{update}) {
521         $self->git_clone_all_submodules;
522     }
523
524     $self->git_add_staging_remote('qt5');
525
526     return;
527 }
528
529 #==============================================================================
530
531 Qt::InitRepository->new(@ARGV)->run if (!caller);
532 1;