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