a801bffa65b939cddf2673ee464dd004cb4c8e0d
[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     qtpim
235     qtqa
236     qtrepotools
237     qtscript
238     qtsensors
239     qtsvg
240     qtsystems
241     qttools
242     qttranslations
243     qtwebkit-examples-and-demos
244     qtxmlpatterns
245 );
246 $GERRIT_REPOS{qtquick3d} = "qt/quick3d";
247
248 # Protocol-specific repo overrides, if they differ from the values set in the git submodule config
249 # (e.g. because public vs private names differ for whatever reason)
250 my %PROTOCOL_REPOS = (
251     qtquick3d   =>  {
252         internal    =>  "qt/quick3d",   # instead of qt-quick3d/qt-quick3d on gitorious
253         ssh         =>  "qt/quick3d",
254     },
255 );
256
257 my $GERRIT_SSH_BASE
258     = 'ssh://codereview.qt.nokia.com:29418/';
259
260 my $GERRIT_HTTP_BASE
261     = 'http://codereview.qt.nokia.com/p/';
262
263 my $BNE_MIRROR_URL_BASE
264     = 'git://bq-git.apac.nokia.com/qtsoftware/';
265
266 my $BNE_MIRROR_WEBKIT_URL
267     = 'git://bq-git.apac.nokia.com/qtsoftware/research/gitorious-org-webkit-qtwebkit-mirror.git';
268
269 my $BNE_MIRROR_V8_URL
270     = 'git://bq-git.apac.nokia.com/github/v8.git';
271
272 my $BER_MIRROR_URL_BASE
273     = 'git://ber-git.europe.nokia.com/';
274
275 my $BER_MIRROR_WEBKIT_URL
276     = 'git://ber-git.europe.nokia.com/qtwebkit/qtwebkit.git';
277
278
279 sub new
280 {
281     my ($class, @arguments) = @_;
282
283     my $self = {};
284     bless $self, $class;
285     $self->parse_arguments(@arguments);
286
287     return $self;
288 }
289
290 # Like `system', but possibly log the command, and die on non-zero exit code
291 sub exe
292 {
293     my ($self, @cmd) = @_;
294
295     if (!$self->{quiet}) {
296         print "+ @cmd\n";
297     }
298
299     if (system(@cmd) != 0) {
300         confess "@cmd exited with status $CHILD_ERROR";
301     }
302
303     return;
304 }
305
306 sub parse_arguments
307 {
308     my ($self, @args) = @_;
309
310     %{$self} = (%{$self},
311         'alternates'          => "",
312         'codereview-username' => "",
313         'detach-alternates'   => 0 ,
314         'force'               => 0 ,
315         'ignore-submodules'   => 0 ,
316         'mirror-url'          => "",
317         'mirror-webkit-url'   => "",
318         'mirror-v8-url'       => "",
319         'nokia-developer'     => 0 ,
320         'protocol'            => "",
321         'update'              => 1 ,
322         'webkit'              => 1 ,
323         'module-subset'       => "",
324     );
325
326     GetOptionsFromArray(\@args,
327         'alternates=s'      =>  \$self->{qw{ alternates        }},
328         'codereview-username=s' => \$self->{qw{ codereview-username }},
329         'copy-objects'      =>  \$self->{qw{ detach-alternates }},
330         'force'             =>  \$self->{qw{ force             }},
331         'ignore-submodules' =>  \$self->{qw{ ignore-submodules }},
332         'mirror-webkit=s'   =>  \$self->{qw{ mirror-webkit-url }},
333         'mirror=s'          =>  \$self->{qw{ mirror-url        }},
334         'nokia-developer'   =>  \$self->{qw{ nokia-developer   }},
335         'quiet'             =>  \$self->{qw{ quiet             }},
336         'update!'           =>  \$self->{qw{ update            }},
337         'webkit!'           =>  \$self->{qw{ webkit            }},
338         'module-subset=s'   =>  \$self->{qw{ module-subset     }},
339
340         'help|?'            =>  sub { pod2usage(1);               },
341         'http'              =>  sub { $self->{protocol} = 'http'; },
342         'ssh|ssh-protocol'  =>  sub { $self->{protocol} = 'ssh';  },
343
344         'brisbane|brisbane-nokia-developer' => sub {
345             $self->{'nokia-developer'}   = 1;
346             $self->{'protocol'}          = 'internal';
347             $self->{'mirror-url'}        = $BNE_MIRROR_URL_BASE;
348             $self->{'mirror-v8-url'}     = $BNE_MIRROR_V8_URL;
349             $self->{'mirror-webkit-url'} = $BNE_MIRROR_WEBKIT_URL;
350             $self->{'ignore-submodules'} = 1;
351         },
352
353         'berlin|berlin-nokia-developer' => sub {
354             $self->{'nokia-developer'}   = 1;
355             $self->{'protocol'}          = 'internal';
356             $self->{'mirror-url'}        = $BER_MIRROR_URL_BASE;
357             $self->{'mirror-webkit-url'} = $BER_MIRROR_WEBKIT_URL;
358         },
359
360         'nokia-developer' => sub {
361             $self->{'nokia-developer'}   = 1;
362             $self->{'protocol'}          = 'internal';
363             $self->{'ignore-submodules'} = 1;
364         },
365     ) || pod2usage(2);
366
367     if ($self->{'nokia-developer'} && $self->{'protocol'} eq 'http') {
368         print "*** Ignoring use of HTTP protocol, as it's only usable with external server\n";
369         $self->{'protocol'} = '';
370     }
371
372     # Replace any double trailing slashes from end of mirror
373     $self->{'mirror-url'} =~ s{//+$}{/};
374
375     if ($self->{'module-subset'}) {
376         $self->{'module-subset'} = {
377             map { $_ => 1 } split(qr{,}, $self->{'module-subset'})
378         };
379     }
380
381     return;
382 }
383
384 sub check_if_already_initialized
385 {
386     my ($self) = @_;
387
388     # We consider the repo as `initialized' if submodule.qtbase.url is set
389     if (qx(git config --get submodule.qtbase.url)) {
390         if ($self->{force}) {
391             my @configresult = qx(git config -l);
392             foreach (@configresult) {
393                 # Example line: submodule.qtqa.url=git://gitorious.org/qt/qtqa.git
394                 if (/(submodule\.[^.=]+)\.url=.*/) {
395                     $self->exe('git', 'config', '--remove-section', $1);
396                 }
397             }
398         }
399         else {
400             exit 0 if ($self->{quiet});
401             print "Will not reinitialize already initialized repository (use -f to force)!\n";
402             exit 1;
403         }
404     }
405
406     return;
407 }
408
409 sub git_submodule_init
410 {
411     my ($self) = @_;
412
413     my @init_args;
414     if ($self->{quiet}) {
415         push @init_args, '--quiet';
416     }
417     $self->exe('git', 'submodule', 'init', @init_args);
418
419     my $template = getcwd()."/.commit-template";
420     if (-e $template) {
421         $self->exe('git', 'config', 'commit.template', $template);
422     }
423
424     return;
425 }
426
427 sub git_disable_webkit_submodule
428 {
429     my ($self) = @_;
430
431     $self->exe('git', 'config', '--remove', 'submodule.qtwebkit');
432     $self->exe('git', 'config', '--remove', 'submodule.qtwebkit-examples-and-demos');
433
434     return;
435 }
436
437 sub git_prune_submodules
438 {
439     my ($self) = @_;
440
441     my @configresult = qx(git config -l);
442     foreach my $line (@configresult) {
443         if ($line =~ /submodule\.([^.=]+)\.url=/) {
444             my $module_name = $1;
445             if (!$self->{'module-subset'}{$module_name}) {
446                 $self->exe('git', 'config', '--remove', "submodule.$module_name");
447             }
448         }
449     }
450 }
451
452 sub git_set_submodule_config
453 {
454     my ($self) = @_;
455
456     my @configresult          = qx(git config -l);
457     my $protocol              = $self->{protocol};
458     my $url_base_for_protocol = $PROTOCOLS{$protocol};
459
460     foreach my $line (@configresult) {
461         # Example line: submodule.qtqa.url=git://gitorious.org/qt/qtqa.git
462         next if ($line !~ /submodule\.([^.=]+)\.url=(.*)/);
463
464         my $key   = $1;
465         my $value = $2;
466
467         if ($protocol) {
468             # WebKit is special, and has only external link.
469             if ($key ne 'qtwebkit') {
470                 # qt-labs projects are still hosted under qt internally.
471                 if ($protocol ne 'http') {
472                     $value =~ s,^git://gitorious\.org/qt-labs/,${url_base_for_protocol}qt/,;
473                 }
474
475                 if ($PROTOCOL_REPOS{$key}->{$protocol}) {
476                     # If this repo has an explicitly set basename for this protocol, use it...
477                     # e.g.   'git@example.com:'     . 'qt/quick3d'
478                     $value = $url_base_for_protocol . $PROTOCOL_REPOS{$key}->{$protocol};
479                 }
480                 else {
481                     # ...otherwise, assume the selected protocol uses same naming structure
482                     # as gitorious.org
483                     # e.g. git://gitorious.org/qt/qt5 => git@example.com:qt/qt5
484                     $value =~ s,^git://gitorious\.org/,$url_base_for_protocol,;
485                 }
486             }
487         }
488
489         $self->exe('git', 'config', "submodule.$key.url", $value);
490
491         if ($self->{'ignore-submodules'}) {
492             $self->exe('git', 'config', "submodule.$key.ignore", 'all');
493         }
494     }
495
496     return;
497 }
498
499 sub git_clone_all_submodules
500 {
501     my ($self) = @_;
502
503     # manually clone each repo here, so we can easily use reference repos, mirrors etc
504     my @configresult = qx(git config -l);
505     foreach my $line (@configresult) {
506         if ($line =~ /submodule\.([^.=]+)\.url=(.*)/) {
507             $self->git_clone_one_submodule($1, $2);
508         }
509     }
510
511     $self->exe('git', 'submodule', 'update');
512
513     return;
514 }
515
516 sub git_add_remotes
517 {
518     my ($self, $repo_basename) = @_;
519
520     my $protocol              = $self->{protocol};
521     my $url_base_for_protocol = $PROTOCOLS{$protocol};
522
523     my %current_remotes;
524     for my $line (qx(git remote show)) {
525         chomp $line;
526         $current_remotes{$line} = 1;
527     }
528
529     my $gerrit_repo_basename = $GERRIT_REPOS{$repo_basename};
530     if ($gerrit_repo_basename && !$current_remotes{'gerrit'}) {
531         my $gerrit_repo_url;
532
533         # If given a username, we use writable remote (ssh).
534         # Otherwise, we use read-only (http).
535         if ($self->{'codereview-username'}) {
536             $gerrit_repo_url = $GERRIT_SSH_BASE;
537             $gerrit_repo_url =~ s[^ssh://][ssh://$self->{'codereview-username'}@];
538         }
539         else {
540             $gerrit_repo_url = $GERRIT_HTTP_BASE;
541         }
542
543         $gerrit_repo_url .= $gerrit_repo_basename;
544         $self->exe('git', 'remote', 'add', 'gerrit', $gerrit_repo_url);
545
546         $current_remotes{'gerrit'} = 1;
547     }
548
549     # if repo still has no gerrit repo defined, alias it to origin
550     if (!$current_remotes{'gerrit'}) {
551         my @configresult = qx(git remote -v);
552         foreach (@configresult) {
553             if (/^origin\s+(\S+) \(fetch\)/) {
554                 $self->exe('git', 'remote', 'add', 'gerrit', $1);
555             }
556         }
557     }
558
559     return;
560 }
561
562 sub git_clone_one_submodule
563 {
564     my ($self, $submodule, $url) = @_;
565
566     my $alternates            = $self->{ 'alternates'        };
567     my $mirror_url            = $self->{ 'mirror-url'        };
568     my $mirror_webkit_url     = $self->{ 'mirror-webkit-url' };
569     my $mirror_v8_url         = $self->{ 'mirror-v8-url'     };
570     my $protocol              = $self->{ 'protocol'          };
571
572     # `--reference FOO' args for the clone, if any.
573     my @reference_args;
574
575     if ($alternates) {
576         # alternates is a qt5 repo, so the submodule will be under that.
577         if (-d "$alternates/$submodule") {
578             @reference_args = ('--reference', "$alternates/$submodule");
579         }
580         else {
581             print " *** $alternates/$submodule not found, ignoring alternate for this submodule\n";
582         }
583     }
584
585     my $mirror;
586     if ($mirror_url && ($submodule ne 'qtwebkit')) {
587         $mirror = $mirror_url.( $PROTOCOL_REPOS{$submodule}->{internal} // "qt/$submodule" );
588         $mirror .= ".git" unless (-d $mirror); # Support local disk mirror
589     }
590     elsif ($mirror_webkit_url && ($submodule eq 'qtwebkit')) {
591         $mirror = $mirror_webkit_url;
592     }
593
594     my $do_clone = (! -d "$submodule/.git");
595     if ($do_clone) {
596         $self->exe('git', 'clone', @reference_args, ($mirror ? $mirror : $url), $submodule);
597     }
598
599     chdir($submodule) or confess "chdir $submodule: $OS_ERROR";
600
601     if (!$do_clone) {
602         $self->exe('git', 'fetch', ($mirror ? $mirror : $url));
603     }
604
605     my $template = getcwd()."/../.commit-template";
606     if (-e $template) {
607         $self->exe('git', 'config', 'commit.template', $template);
608     }
609
610     if ($mirror) {
611         $self->exe('git', 'config', 'remote.origin.url', $url);
612
613         # In `force' mode, remove the mirror if it already exists,
614         # since we may be reinitializing the module.
615         if ($self->{force}) {
616             eval { $self->exe('git', 'remote', 'rm', 'mirror'); }; # failure is OK
617         }
618
619         $self->exe('git', 'remote', 'add', 'mirror', $mirror);
620     }
621
622     $self->git_add_remotes($submodule);
623
624     if ($self->{'detach-alternates'}) {
625         $self->exe('git', 'repack', '-a');
626
627         my $alternates_path = '.git/objects/info/alternates';
628         unlink($alternates_path) || confess "unlink $alternates_path: $OS_ERROR";
629     }
630
631     if ($submodule eq "qtdeclarative") { #Extra step needed to setup declarative 
632         $self->exe('git', 'submodule', 'init'); 
633         if ($mirror_v8_url || $protocol eq 'http') {
634             my @configresult = qx(git config -l);
635             my $v8url;
636             foreach my $line (@configresult) {
637                 # Example line: submodule.qtqa.url=git://gitorious.org/qt/qtqa.git
638                 next if ($line !~ /submodule.src\/3rdparty\/v8.url=(.*)/);
639                 $v8url = $1;
640             }
641
642             if ($protocol eq 'http') {
643                 # rewrite the git:// url to https://
644                 if ($v8url =~ s{^git://github}{https://github}) {
645                     $self->exe('git', 'config', 'submodule.src/3rdparty/v8.url', $v8url);
646                 }
647                 else {
648                     warn 'You requested git over http, but I could not figure out how to '
649                         ."rewrite v8's giturl of $v8url";
650                 }
651             }
652
653             if ($mirror_v8_url && $do_clone) {
654                 chdir('src/3rdparty/') or confess "chdir $submodule/src/3rdparty: $OS_ERROR";
655                 $self->exe('git', 'clone', $mirror_v8_url, 'v8');
656                 chdir('v8') or confess "chdir $submodule/src/3rdparty/v8: $OS_ERROR";
657                 $self->exe('git', 'config', 'remote.origin.url', $v8url);
658                 chdir('../../..') or confess "cd ../../..: $OS_ERROR";
659             }
660         }
661         $self->exe('git', 'submodule', 'update'); 
662     } 
663
664     chdir("..") or confess "cd ..: $OS_ERROR";
665
666     return;
667 }
668
669 sub run
670 {
671     my ($self) = @_;
672
673     $self->check_if_already_initialized;
674     $self->git_submodule_init;
675
676     if (!$self->{webkit}) {
677         $self->git_disable_webkit_submodule;
678     }
679
680     if ($self->{'module-subset'}) {
681         $self->git_prune_submodules;
682     }
683
684     $self->git_set_submodule_config;
685
686     if ($self->{update}) {
687         $self->git_clone_all_submodules;
688     }
689
690     $self->git_add_remotes('qt5');
691
692     return;
693 }
694
695 #==============================================================================
696
697 Qt::InitRepository->new(@ARGV)->run if (!caller);
698 1;