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