1a3e332e2580bf5a01edf1fc8fd35e679e5b9a31
[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 ## GNU Lesser General Public License Usage
12 ## This file may be used under the terms of the GNU Lesser General Public
13 ## License version 2.1 as published by the Free Software Foundation and
14 ## appearing in the file LICENSE.LGPL included in the packaging of this
15 ## file. Please review the following information to ensure the GNU Lesser
16 ## General Public License version 2.1 requirements will be met:
17 ## http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 ##
19 ## In addition, as a special exception, Nokia gives you certain additional
20 ## rights. These rights are described in the Nokia Qt LGPL Exception
21 ## version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 ##
23 ## GNU General Public License Usage
24 ## Alternatively, this file may be used under the terms of the GNU General
25 ## Public License version 3.0 as published by the Free Software Foundation
26 ## and appearing in the file LICENSE.GPL included in the packaging of this
27 ## file. Please review the following information to ensure the GNU General
28 ## Public License version 3.0 requirements will be met:
29 ## http://www.gnu.org/copyleft/gpl.html.
30 ##
31 ## Other Usage
32 ## Alternatively, this file may be used in accordance with the terms and
33 ## conditions contained in a signed written agreement between you and Nokia.
34 ##
35 ##
36 ##
37 ##
38 ##
39 ## $QT_END_LICENSE$
40 ##
41 #############################################################################
42
43 use v5.10;
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/--brisbane.
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 --brisbane
123
124 Switch to internal Nokia URLs and make use of the Brisbane git mirrors.
125 (Implies `--mirror' and `--mirror-webkit').
126
127 =item --berlin
128
129 Switch to internal Nokia URLs and make use of the Berlin git mirrors.
130 (Implies `--mirror' and `--mirror-webkit').
131
132
133 =item --ssh
134
135 Use the SSH protocol for git operations.  This may be useful if the git
136 protocol is blocked by a firewall. Note that this requires a user account
137 with an uploaded SSH key on all servers used.  (Implies `--nokia-developer').
138
139 The `--ssh' option does not affect the gerrit remotes.
140
141
142 =item --http
143
144 Use the HTTP protocol for git operations.  This may be useful if the git
145 protocol is blocked by a firewall.  Note that this only works with the
146 external Gitorious server.
147
148 The `--http' option does not affect the gerrit remotes.
149
150
151 =item --codereview-username <Gerrit/JIRA username>
152
153 Adds a (potentially) writable remote named `gerrit' for each module,
154 for use with the Gerrit code review tool.
155 This requires a username for SSH access to the codereview.qt.nokia.com
156 server, which will be the same username you have for the bugtracker at
157 bugreports.qt.nokia.com.
158
159 If this option is omitted, the gerrit remote is created with read-only
160 access (using HTTP protocol).
161
162
163 =item --alternates <path to other Qt5 repo>
164
165 Adds alternates for each submodule to another full qt5 checkout. This makes
166 this qt5 checkout very small, as it will use the object store of the
167 alternates before unique objects are stored in its own object store.
168
169 This option has no effect when using `--no-update'.
170
171 B<NOTE:> This will make this repo dependent on the alternate, which is
172 potentially dangerous!  The dependency can be broken by also using
173 the `--copy-objects' option, or by running C<git repack -a> in each
174 submodule, where required.  Please read the note about the `--shared' option
175 in the documentation of `git clone' for more information.
176
177
178 =item --copy-objects
179
180 When `--alternates' is used, automatically do a C<git repack -a> in each
181 submodule after cloning, to ensure that the repositories are independent
182 from the source used as a reference for cloning.
183
184 Note that this negates the disk usage benefits gained from the use of
185 `--alternates'.
186
187
188 =item --mirror <url-base>
189
190 Uses <url-base> as the base URL for submodule git mirrors.
191
192 For example:
193
194   --mirror user@machine:/foo/bar
195
196 ...will use the following as a mirror for qtbase:
197
198   user@machine:/foo/bar/qtbase.git
199
200
201 =item --mirror-webkit <url>
202
203 Uses <url> as the URL for the webkit git mirror.
204
205 =back
206
207 =cut
208
209 use Carp         qw( confess             );
210 use English      qw( -no_match_vars      );
211 use Getopt::Long qw( GetOptionsFromArray );
212 use Pod::Usage   qw( pod2usage           );
213 use Cwd          qw( getcwd              );
214
215 my %PROTOCOLS = (
216     'internal'  => 'git://scm.dev.nokia.troll.no/' ,
217     'ssh'       => 'git@scm.dev.nokia.troll.no:'   ,
218     'http'      => 'http://git.gitorious.org/'     ,
219 );
220
221 my %GERRIT_REPOS = map { $_ => "qt/$_" } qw(
222     qt5
223     qlalr
224     qtactiveqt
225     qtbase
226     qtdeclarative
227     qtdoc
228     qtdocgallery
229     qtfeedback
230     qtlocation
231     qtmultimedia
232     qtmultimediakit
233     qtphonon
234     qtqa
235     qtrepotools
236     qtscript
237     qtsensors
238     qtsvg
239     qtsystems
240     qttools
241     qttranslations
242     qtwebkit-examples-and-demos
243     qtxmlpatterns
244 );
245 $GERRIT_REPOS{qtquick3d} = "qt/quick3d";
246
247 # Protocol-specific repo overrides, if they differ from the values set in the git submodule config
248 # (e.g. because public vs private names differ for whatever reason)
249 my %PROTOCOL_REPOS = (
250     qtquick3d   =>  {
251         internal    =>  "qt/quick3d",   # instead of qt-quick3d/qt-quick3d on gitorious
252         ssh         =>  "qt/quick3d",
253     },
254 );
255
256 my $GERRIT_SSH_BASE
257     = 'ssh://codereview.qt.nokia.com:29418/';
258
259 my $GERRIT_HTTP_BASE
260     = 'http://codereview.qt.nokia.com/p/';
261
262 my $BNE_MIRROR_URL_BASE
263     = 'git://bq-git.apac.nokia.com/qtsoftware/';
264
265 my $BNE_MIRROR_WEBKIT_URL
266     = 'git://bq-git.apac.nokia.com/qtsoftware/research/gitorious-org-webkit-qtwebkit-mirror.git';
267
268 my $BNE_MIRROR_V8_URL
269     = 'git://bq-git.apac.nokia.com/github/v8.git';
270
271 my $BER_MIRROR_URL_BASE
272     = 'git://ber-git.europe.nokia.com/';
273
274 my $BER_MIRROR_WEBKIT_URL
275     = 'git://ber-git.europe.nokia.com/qtwebkit/qtwebkit.git';
276
277
278 sub new
279 {
280     my ($class, @arguments) = @_;
281
282     my $self = {};
283     bless $self, $class;
284     $self->parse_arguments(@arguments);
285
286     return $self;
287 }
288
289 # Like `system', but possibly log the command, and die on non-zero exit code
290 sub exe
291 {
292     my ($self, @cmd) = @_;
293
294     if (!$self->{quiet}) {
295         print "+ @cmd\n";
296     }
297
298     if (system(@cmd) != 0) {
299         confess "@cmd exited with status $CHILD_ERROR";
300     }
301
302     return;
303 }
304
305 sub parse_arguments
306 {
307     my ($self, @args) = @_;
308
309     %{$self} = (%{$self},
310         'alternates'          => "",
311         'codereview-username' => "",
312         'detach-alternates'   => 0 ,
313         'force'               => 0 ,
314         'ignore-submodules'   => 0 ,
315         'mirror-url'          => "",
316         'mirror-webkit-url'   => "",
317         'mirror-v8-url'       => "",
318         'nokia-developer'     => 0 ,
319         'protocol'            => "",
320         'update'              => 1 ,
321         'webkit'              => 1 ,
322         'module-subset'       => "",
323     );
324
325     GetOptionsFromArray(\@args,
326         'alternates=s'      =>  \$self->{qw{ alternates        }},
327         'codereview-username=s' => \$self->{qw{ codereview-username }},
328         'copy-objects'      =>  \$self->{qw{ detach-alternates }},
329         'force'             =>  \$self->{qw{ force             }},
330         'ignore-submodules' =>  \$self->{qw{ ignore-submodules }},
331         'mirror-webkit=s'   =>  \$self->{qw{ mirror-webkit-url }},
332         'mirror=s'          =>  \$self->{qw{ mirror-url        }},
333         'nokia-developer'   =>  \$self->{qw{ nokia-developer   }},
334         'quiet'             =>  \$self->{qw{ quiet             }},
335         'update!'           =>  \$self->{qw{ update            }},
336         'webkit!'           =>  \$self->{qw{ webkit            }},
337         'module-subset=s'   =>  \$self->{qw{ module-subset     }},
338
339         'help|?'            =>  sub { pod2usage(1);               },
340         'http'              =>  sub { $self->{protocol} = 'http'; },
341         'ssh|ssh-protocol'  =>  sub { $self->{protocol} = 'ssh';  },
342
343         'brisbane|brisbane-nokia-developer' => sub {
344             $self->{'nokia-developer'}   = 1;
345             $self->{'protocol'}          = 'internal';
346             $self->{'mirror-url'}        = $BNE_MIRROR_URL_BASE;
347             $self->{'mirror-v8-url'}     = $BNE_MIRROR_V8_URL;
348             $self->{'mirror-webkit-url'} = $BNE_MIRROR_WEBKIT_URL;
349             $self->{'ignore-submodules'} = 1;
350         },
351
352         'berlin|berlin-nokia-developer' => sub {
353             $self->{'nokia-developer'}   = 1;
354             $self->{'protocol'}          = 'internal';
355             $self->{'mirror-url'}        = $BER_MIRROR_URL_BASE;
356             $self->{'mirror-webkit-url'} = $BER_MIRROR_WEBKIT_URL;
357         },
358
359         'nokia-developer' => sub {
360             $self->{'nokia-developer'}   = 1;
361             $self->{'protocol'}          = 'internal';
362             $self->{'ignore-submodules'} = 1;
363         },
364     ) || pod2usage(2);
365
366     if ($self->{'nokia-developer'} && $self->{'protocol'} eq 'http') {
367         print "*** Ignoring use of HTTP protocol, as it's only usable with external server\n";
368         $self->{'protocol'} = '';
369     }
370
371     # Replace any double trailing slashes from end of mirror
372     $self->{'mirror-url'} =~ s{//+$}{/};
373
374     if ($self->{'module-subset'}) {
375         $self->{'module-subset'} = {
376             map { $_ => 1 } split(qr{,}, $self->{'module-subset'})
377         };
378     }
379
380     return;
381 }
382
383 sub check_if_already_initialized
384 {
385     my ($self) = @_;
386
387     # We consider the repo as `initialized' if submodule.qtbase.url is set
388     if (qx(git config --get submodule.qtbase.url)) {
389         if ($self->{force}) {
390             my @configresult = qx(git config -l);
391             foreach (@configresult) {
392                 # Example line: submodule.qtqa.url=git://gitorious.org/qt/qtqa.git
393                 if (/(submodule\.[^.=]+)\.url=.*/) {
394                     $self->exe('git', 'config', '--remove-section', $1);
395                 }
396             }
397         }
398         else {
399             exit 0 if ($self->{quiet});
400             print "Will not reinitialize already initialized repository (use -f to force)!\n";
401             exit 1;
402         }
403     }
404
405     return;
406 }
407
408 sub git_submodule_init
409 {
410     my ($self) = @_;
411
412     my @init_args;
413     if ($self->{quiet}) {
414         push @init_args, '--quiet';
415     }
416     $self->exe('git', 'submodule', 'init', @init_args);
417
418     my $template = getcwd()."/.commit-template";
419     if (-e $template) {
420         $self->exe('git', 'config', 'commit.template', $template);
421     }
422
423     return;
424 }
425
426 sub git_disable_webkit_submodule
427 {
428     my ($self) = @_;
429
430     $self->exe('git', 'config', '--remove', 'submodule.qtwebkit');
431     $self->exe('git', 'config', '--remove', 'submodule.qtwebkit-examples-and-demos');
432
433     return;
434 }
435
436 sub git_prune_submodules
437 {
438     my ($self) = @_;
439
440     my @configresult = qx(git config -l);
441     foreach my $line (@configresult) {
442         if ($line =~ /submodule\.([^.=]+)\.url=/) {
443             my $module_name = $1;
444             if (!$self->{'module-subset'}{$module_name}) {
445                 $self->exe('git', 'config', '--remove', "submodule.$module_name");
446             }
447         }
448     }
449 }
450
451 sub git_set_submodule_config
452 {
453     my ($self) = @_;
454
455     my @configresult          = qx(git config -l);
456     my $protocol              = $self->{protocol};
457     my $url_base_for_protocol = $PROTOCOLS{$protocol};
458
459     foreach my $line (@configresult) {
460         # Example line: submodule.qtqa.url=git://gitorious.org/qt/qtqa.git
461         next if ($line !~ /submodule\.([^.=]+)\.url=(.*)/);
462
463         my $key   = $1;
464         my $value = $2;
465
466         if ($protocol) {
467             # WebKit is special, and has only external link.
468             if ($key ne 'qtwebkit') {
469                 # qt-labs projects are still hosted under qt internally.
470                 if ($protocol ne 'http') {
471                     $value =~ s,^git://gitorious\.org/qt-labs/,${url_base_for_protocol}qt/,;
472                 }
473
474                 if ($PROTOCOL_REPOS{$key}->{$protocol}) {
475                     # If this repo has an explicitly set basename for this protocol, use it...
476                     # e.g.   'git@example.com:'     . 'qt/quick3d'
477                     $value = $url_base_for_protocol . $PROTOCOL_REPOS{$key}->{$protocol};
478                 }
479                 else {
480                     # ...otherwise, assume the selected protocol uses same naming structure
481                     # as gitorious.org
482                     # e.g. git://gitorious.org/qt/qt5 => git@example.com:qt/qt5
483                     $value =~ s,^git://gitorious\.org/,$url_base_for_protocol,;
484                 }
485             }
486         }
487
488         $self->exe('git', 'config', "submodule.$key.url", $value);
489
490         if ($self->{'ignore-submodules'}) {
491             $self->exe('git', 'config', "submodule.$key.ignore", 'all');
492         }
493     }
494
495     return;
496 }
497
498 sub git_clone_all_submodules
499 {
500     my ($self) = @_;
501
502     # manually clone each repo here, so we can easily use reference repos, mirrors etc
503     my @configresult = qx(git config -l);
504     foreach my $line (@configresult) {
505         if ($line =~ /submodule\.([^.=]+)\.url=(.*)/) {
506             $self->git_clone_one_submodule($1, $2);
507         }
508     }
509
510     $self->exe('git', 'submodule', 'update');
511
512     return;
513 }
514
515 sub git_add_remotes
516 {
517     my ($self, $repo_basename) = @_;
518
519     my $protocol              = $self->{protocol};
520     my $url_base_for_protocol = $PROTOCOLS{$protocol};
521
522     my %current_remotes;
523     for my $line (qx(git remote show)) {
524         chomp $line;
525         $current_remotes{$line} = 1;
526     }
527
528     my $gerrit_repo_basename = $GERRIT_REPOS{$repo_basename};
529     if ($gerrit_repo_basename && !$current_remotes{'gerrit'}) {
530         my $gerrit_repo_url;
531
532         # If given a username, we use writable remote (ssh).
533         # Otherwise, we use read-only (http).
534         if ($self->{'codereview-username'}) {
535             $gerrit_repo_url = $GERRIT_SSH_BASE;
536             $gerrit_repo_url =~ s[^ssh://][ssh://$self->{'codereview-username'}@];
537         }
538         else {
539             $gerrit_repo_url = $GERRIT_HTTP_BASE;
540         }
541
542         $gerrit_repo_url .= $gerrit_repo_basename;
543         $self->exe('git', 'remote', 'add', 'gerrit', $gerrit_repo_url);
544
545         $current_remotes{'gerrit'} = 1;
546     }
547
548     # if repo still has no gerrit repo defined, alias it to origin
549     if (!$current_remotes{'gerrit'}) {
550         my @configresult = qx(git remote -v);
551         foreach (@configresult) {
552             if (/^origin\s+(\S+) \(fetch\)/) {
553                 $self->exe('git', 'remote', 'add', 'gerrit', $1);
554             }
555         }
556     }
557
558     return;
559 }
560
561 sub git_clone_one_submodule
562 {
563     my ($self, $submodule, $url) = @_;
564
565     my $alternates            = $self->{ 'alternates'        };
566     my $mirror_url            = $self->{ 'mirror-url'        };
567     my $mirror_webkit_url     = $self->{ 'mirror-webkit-url' };
568     my $mirror_v8_url         = $self->{ 'mirror-v8-url'     };
569     my $protocol              = $self->{ 'protocol'          };
570
571     # `--reference FOO' args for the clone, if any.
572     my @reference_args;
573
574     if ($alternates) {
575         # alternates is a qt5 repo, so the submodule will be under that.
576         if (-d "$alternates/$submodule") {
577             @reference_args = ('--reference', "$alternates/$submodule");
578         }
579         else {
580             print " *** $alternates/$submodule not found, ignoring alternate for this submodule\n";
581         }
582     }
583
584     my $mirror;
585     if ($mirror_url && ($submodule ne 'qtwebkit')) {
586         $mirror = $mirror_url.( $PROTOCOL_REPOS{$submodule}->{internal} // "qt/$submodule" );
587         $mirror .= ".git" unless (-d $mirror); # Support local disk mirror
588     }
589     elsif ($mirror_webkit_url && ($submodule eq 'qtwebkit')) {
590         $mirror = $mirror_webkit_url;
591     }
592
593     my $do_clone = (! -d "$submodule/.git");
594     if ($do_clone) {
595         $self->exe('git', 'clone', @reference_args, ($mirror ? $mirror : $url), $submodule);
596     }
597
598     chdir($submodule) or confess "chdir $submodule: $OS_ERROR";
599
600     if (!$do_clone) {
601         $self->exe('git', 'fetch', ($mirror ? $mirror : $url));
602     }
603
604     my $template = getcwd()."/../.commit-template";
605     if (-e $template) {
606         $self->exe('git', 'config', 'commit.template', $template);
607     }
608
609     if ($mirror) {
610         $self->exe('git', 'config', 'remote.origin.url', $url);
611
612         # In `force' mode, remove the mirror if it already exists,
613         # since we may be reinitializing the module.
614         if ($self->{force}) {
615             eval { $self->exe('git', 'remote', 'rm', 'mirror'); }; # failure is OK
616         }
617
618         $self->exe('git', 'remote', 'add', 'mirror', $mirror);
619     }
620
621     $self->git_add_remotes($submodule);
622
623     if ($self->{'detach-alternates'}) {
624         $self->exe('git', 'repack', '-a');
625
626         my $alternates_path = '.git/objects/info/alternates';
627         unlink($alternates_path) || confess "unlink $alternates_path: $OS_ERROR";
628     }
629
630     if ($submodule eq "qtdeclarative") { #Extra step needed to setup declarative 
631         $self->exe('git', 'submodule', 'init'); 
632         if ($mirror_v8_url || $protocol eq 'http') {
633             my @configresult = qx(git config -l);
634             my $v8url;
635             foreach my $line (@configresult) {
636                 # Example line: submodule.qtqa.url=git://gitorious.org/qt/qtqa.git
637                 next if ($line !~ /submodule.src\/3rdparty\/v8.url=(.*)/);
638                 $v8url = $1;
639             }
640
641             if ($protocol eq 'http') {
642                 # rewrite the git:// url to https://
643                 if ($v8url =~ s{^git://github}{https://github}) {
644                     $self->exe('git', 'config', 'submodule.src/3rdparty/v8.url', $v8url);
645                 }
646                 else {
647                     warn 'You requested git over http, but I could not figure out how to '
648                         ."rewrite v8's giturl of $v8url";
649                 }
650             }
651
652             if ($mirror_v8_url && $do_clone) {
653                 chdir('src/3rdparty/') or confess "chdir $submodule/src/3rdparty: $OS_ERROR";
654                 $self->exe('git', 'clone', $mirror_v8_url, 'v8');
655                 chdir('v8') or confess "chdir $submodule/src/3rdparty/v8: $OS_ERROR";
656                 $self->exe('git', 'config', 'remote.origin.url', $v8url);
657                 chdir('../../..') or confess "cd ../../..: $OS_ERROR";
658             }
659         }
660         $self->exe('git', 'submodule', 'update'); 
661     } 
662
663     chdir("..") or confess "cd ..: $OS_ERROR";
664
665     return;
666 }
667
668 sub run
669 {
670     my ($self) = @_;
671
672     $self->check_if_already_initialized;
673     $self->git_submodule_init;
674
675     if (!$self->{webkit}) {
676         $self->git_disable_webkit_submodule;
677     }
678
679     if ($self->{'module-subset'}) {
680         $self->git_prune_submodules;
681     }
682
683     $self->git_set_submodule_config;
684
685     if ($self->{update}) {
686         $self->git_clone_all_submodules;
687     }
688
689     $self->git_add_remotes('qt5');
690
691     return;
692 }
693
694 #==============================================================================
695
696 Qt::InitRepository->new(@ARGV)->run if (!caller);
697 1;