QtDeclarative and QtDoc has moved into Gerrit
[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 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
91 =item --no-update
92
93 Skip the `git submodule update' command.
94
95
96 =item --ignore-submodules
97
98 Set git config to ignore submodules by default when doing operations on the
99 qt5 repo, such as `pull', `fetch', `diff' etc.
100
101 This option is default for --nokia-developer/--brisbane.
102
103 After using this option, pass `--ignore-submodules=none' to git to override
104 it as needed.
105
106 =back
107
108
109 B<Repository options:>
110
111 =over
112
113 =item --nokia-developer
114
115 Switch to internal Nokia URLs.
116
117
118 =item --brisbane
119
120 Switch to internal Nokia URLs and make use of the Brisbane git mirrors.
121 (Implies `--mirror' and `--mirror-webkit').
122
123 =item --berlin
124
125 Switch to internal Nokia URLs and make use of the Berlin git mirrors.
126 (Implies `--mirror' and `--mirror-webkit').
127
128
129 =item --ssh
130
131 Use the SSH protocol for git operations.  This may be useful if the git
132 protocol is blocked by a firewall. Note that this requires a user account
133 with an uploaded SSH key on all servers used.  (Implies `--nokia-developer').
134
135
136 =item --http
137
138 Use the HTTP protocol for git operations.  This may be useful if the git
139 protocol is blocked by a firewall.  Note that this only works with the
140 external Gitorious server.
141
142
143 =item --codereview-username <Gerrit/JIRA username>
144
145 Adds a gerrit alias to repos under Gerrit codereview management.
146 This requires a username for SSH access to the codereview.qt.nokia.com
147 server, which will be the same username you have for the bugtracker at
148 bugreports.qt.nokia.com.
149
150
151 =item --alternates <path to other Qt5 repo>
152
153 Adds alternates for each submodule to another full qt5 checkout. This makes
154 this qt5 checkout very small, as it will use the object store of the
155 alternates before unique objects are stored in its own object store.
156
157 This option has no effect when using `--no-update'.
158
159 B<NOTE:> This will make this repo dependent on the alternate, which is
160 potentially dangerous!  The dependency can be broken by also using
161 the `--copy-objects' option, or by running C<git repack -a> in each
162 submodule, where required.  Please read the note about the `--shared' option
163 in the documentation of `git clone' for more information.
164
165
166 =item --copy-objects
167
168 When `--alternates' is used, automatically do a C<git repack -a> in each
169 submodule after cloning, to ensure that the repositories are independent
170 from the source used as a reference for cloning.
171
172 Note that this negates the disk usage benefits gained from the use of
173 `--alternates'.
174
175
176 =item --mirror <url-base>
177
178 Uses <url-base> as the base URL for submodule git mirrors.
179
180 For example:
181
182   --mirror user@machine:/foo/bar
183
184 ...will use the following as a mirror for qtbase:
185
186   user@machine:/foo/bar/qtbase.git
187
188
189 =item --mirror-webkit <url>
190
191 Uses <url> as the URL for the webkit git mirror.
192
193 =back
194
195 =cut
196
197 use Carp         qw( confess             );
198 use English      qw( -no_match_vars      );
199 use Getopt::Long qw( GetOptionsFromArray );
200 use Pod::Usage   qw( pod2usage           );
201 use Cwd          qw( getcwd              );
202
203 my %PROTOCOLS = (
204     'internal'  => 'git://scm.dev.nokia.troll.no/' ,
205     'ssh'       => 'git@scm.dev.nokia.troll.no:'   ,
206     'http'      => 'http://git.gitorious.org/'     ,
207 );
208
209 my %STAGING_REPOS = map { $_ => "git://gitorious.org/qt/$_-staging.git" } qw(
210     qt5
211     qt3support
212     qtactiveqt
213     qtbase
214     qtdeclarative
215     qtdoc
216     qtmultimedia
217     qtphonon
218     qtqa
219     qtscript
220     qtsvg
221     qttools
222     qttranslations
223     qtwebkit-examples-and-demos
224     qtxmlpatterns
225     qtlocation
226     qtsensors
227     qtsystems
228 );
229
230 my %GERRIT_REPOS = map { $_ => "codereview.qt.nokia.com:qt/$_.git" } qw(
231     qtbase
232     qtdeclarative
233     qtdoc
234 );
235
236 my $BNE_MIRROR_URL_BASE
237     = 'git://bq-git.apac.nokia.com/qtsoftware/qt/';
238
239 my $BNE_MIRROR_WEBKIT_URL
240     = 'git://bq-git.apac.nokia.com/qtsoftware/research/gitorious-org-webkit-qtwebkit-mirror.git';
241
242 my $BER_MIRROR_URL_BASE
243     = 'git://ber-git.europe.nokia.com/qt/';
244
245 my $BER_MIRROR_WEBKIT_URL
246     = 'git://ber-git.europe.nokia.com/qtwebkit/qtwebkit.git';
247
248
249 sub new
250 {
251     my ($class, @arguments) = @_;
252
253     my $self = {};
254     bless $self, $class;
255     $self->parse_arguments(@arguments);
256
257     return $self;
258 }
259
260 # Like `system', but possibly log the command, and die on non-zero exit code
261 sub exe
262 {
263     my ($self, @cmd) = @_;
264
265     if (!$self->{quiet}) {
266         print "+ @cmd\n";
267     }
268
269     if (system(@cmd) != 0) {
270         confess "@cmd exited with status $CHILD_ERROR";
271     }
272
273     return;
274 }
275
276 sub parse_arguments
277 {
278     my ($self, @args) = @_;
279
280     %{$self} = (%{$self},
281         'alternates'          => "",
282         'codereview-username' => "",
283         'detach-alternates'   => 0 ,
284         'force'               => 0 ,
285         'ignore-submodules'   => 0 ,
286         'mirror-url'          => "",
287         'mirror-webkit-url'   => "",
288         'nokia-developer'     => 0 ,
289         'protocol'            => "",
290         'update'              => 1 ,
291         'webkit'              => 1 ,
292     );
293
294     GetOptionsFromArray(\@args,
295         'alternates=s'      =>  \$self->{qw{ alternates        }},
296         'codereview-username=s' => \$self->{qw{ codereview-username }},
297         'copy-objects'      =>  \$self->{qw{ detach-alternates }},
298         'force'             =>  \$self->{qw{ force             }},
299         'ignore-submodules' =>  \$self->{qw{ ignore-submodules }},
300         'mirror-webkit=s'   =>  \$self->{qw{ mirror-webkit-url }},
301         'mirror=s'          =>  \$self->{qw{ mirror-url        }},
302         'nokia-developer'   =>  \$self->{qw{ nokia-developer   }},
303         'quiet'             =>  \$self->{qw{ quiet             }},
304         'update!'           =>  \$self->{qw{ update            }},
305         'webkit!'           =>  \$self->{qw{ webkit            }},
306
307         'help|?'            =>  sub { pod2usage(1);               },
308         'http'              =>  sub { $self->{protocol} = 'http'; },
309         'ssh|ssh-protocol'  =>  sub { $self->{protocol} = 'ssh';  },
310
311         'brisbane|brisbane-nokia-developer' => sub {
312             $self->{'nokia-developer'}   = 1;
313             $self->{'protocol'}          = 'internal';
314             $self->{'mirror-url'}        = $BNE_MIRROR_URL_BASE;
315             $self->{'mirror-webkit-url'} = $BNE_MIRROR_WEBKIT_URL;
316             $self->{'ignore-submodules'} = 1;
317         },
318
319         'berlin|berlin-nokia-developer' => sub {
320             $self->{'nokia-developer'}   = 1;
321             $self->{'protocol'}          = 'internal';
322             $self->{'mirror-url'}        = $BER_MIRROR_URL_BASE;
323             $self->{'mirror-webkit-url'} = $BER_MIRROR_WEBKIT_URL;
324         },
325
326         'nokia-developer' => sub {
327             $self->{'nokia-developer'}   = 1;
328             $self->{'protocol'}          = 'internal';
329             $self->{'ignore-submodules'} = 1;
330         },
331     ) || pod2usage(2);
332
333     if ($self->{'nokia-developer'} && $self->{'protocol'} eq 'http') {
334         print "*** Ignoring use of HTTP protocol, as it's only usable with external server\n";
335         $self->{'protocol'} = '';
336     }
337
338     # Replace any double trailing slashes from end of mirror
339     $self->{'mirror-url'} =~ s{//+$}{/};
340
341     return;
342 }
343
344 sub check_if_already_initialized
345 {
346     my ($self) = @_;
347
348     # We consider the repo as `initialized' if submodule.qtbase.url is set
349     if (qx(git config --get submodule.qtbase.url)) {
350         if ($self->{force}) {
351             my @configresult = qx(git config -l);
352             foreach (@configresult) {
353                 # Example line: submodule.qtqa.url=git://gitorious.org/qt/qtqa.git
354                 if (/(submodule\.[^.=]+)\.url=.*/) {
355                     $self->exe('git', 'config', '--remove-section', $1);
356                 }
357             }
358         }
359         else {
360             exit 0 if ($self->{quiet});
361             print "Will not reinitialize already initialized repository (use -f to force)!\n";
362             exit 1;
363         }
364     }
365
366     return;
367 }
368
369 sub git_submodule_init
370 {
371     my ($self) = @_;
372
373     my @init_args;
374     if ($self->{quiet}) {
375         push @init_args, '--quiet';
376     }
377     $self->exe('git', 'submodule', 'init', @init_args);
378
379     my $template = getcwd()."/.commit-template";
380     if (-e $template) {
381         $self->exe('git', 'config', 'commit.template', $template);
382     }
383
384     return;
385 }
386
387 sub git_disable_webkit_submodule
388 {
389     my ($self) = @_;
390
391     $self->exe('git', 'config', '--remove', 'submodule.qtwebkit');
392     $self->exe('git', 'config', '--remove', 'submodule.qtwebkit-examples-and-demos');
393
394     return;
395 }
396
397 sub git_set_submodule_config
398 {
399     my ($self) = @_;
400
401     my @configresult          = qx(git config -l);
402     my $protocol              = $self->{protocol};
403     my $url_base_for_protocol = $PROTOCOLS{$protocol};
404
405     foreach my $line (@configresult) {
406         # Example line: submodule.qtqa.url=git://gitorious.org/qt/qtqa.git
407         next if ($line !~ /(submodule\.[^.=]+\.url)=(.*)/);
408
409         my $key   = $1;
410         my $value = $2;
411
412         if ($protocol) {
413             # WebKit is special, and has only external link.
414             if ($key ne 'submodule.qtwebkit.url') {
415                 # qt-labs projects are still hosted under qt internally.
416                 if ($protocol ne 'http') {
417                     $value =~ s,^git://gitorious\.org/qt-labs/,${url_base_for_protocol}qt/,;
418                 }
419                 $value =~ s,^git://gitorious\.org/,$url_base_for_protocol,;
420             }
421         }
422
423         $self->exe('git', 'config', $key, $value);
424
425         if ($self->{'ignore-submodules'}) {
426             $key =~ s,\.url,.ignore,;
427             $self->exe('git', 'config', $key, 'all');
428         }
429     }
430
431     return;
432 }
433
434 sub git_clone_all_submodules
435 {
436     my ($self) = @_;
437
438     # manually clone each repo here, so we can easily use reference repos, mirrors and
439     # add all staging repos
440     my @configresult = qx(git config -l);
441     foreach my $line (@configresult) {
442         if ($line =~ /submodule\.([^.=]+)\.url=(.*)/) {
443             $self->git_clone_one_submodule($1, $2);
444         }
445     }
446
447     $self->exe('git', 'submodule', 'update');
448
449     return;
450 }
451
452 sub git_add_remotes
453 {
454     my ($self, $repo_basename) = @_;
455
456     my $protocol              = $self->{protocol};
457     my $url_base_for_protocol = $PROTOCOLS{$protocol};
458
459     my %current_remotes;
460     for my $line (qx(git remote show)) {
461         chomp $line;
462         $current_remotes{$line} = 1;
463     }
464
465     my @gerrit = grep { /^$repo_basename$/; } keys %GERRIT_REPOS;
466     if (!$current_remotes{'gerrit'} && $self->{'codereview-username'}) {
467         foreach my $gerrit_repo (@gerrit) {
468             my $gerrit_repo_url = $GERRIT_REPOS{$gerrit_repo};
469             $self->exe('git', 'remote', 'add', 'gerrit', $self->{'codereview-username'}."@".$gerrit_repo_url);
470         }
471     }
472
473     my @staging = grep { /^$repo_basename$/; } keys %STAGING_REPOS;
474     if (!$current_remotes{'staging'}) {
475         foreach my $staging_repo (@staging) {
476             my $staging_repo_url = $STAGING_REPOS{$staging_repo};
477             if ($protocol) {
478                 if ($protocol ne 'http') {
479                     $staging_repo_url =~ s,^git://gitorious\.org/qt-labs/,${url_base_for_protocol}qt/,;
480                 }
481                 $staging_repo_url =~ s,^git://gitorious\.org/,$url_base_for_protocol,;
482             }
483             $self->exe('git', 'remote', 'add', 'staging', $staging_repo_url);
484         }
485     }
486
487     # if repo has no staging repo defined, alias it to gerrit or origin
488     if (!$current_remotes{'staging'} && !@staging) {
489         my @configresult = qx(git remote -v);
490         my $staging_set = 0;
491         foreach (@configresult) {
492             if (/^gerrit\s+(\S+) \(fetch\)/) {
493                 $self->exe('git', 'remote', 'add', 'staging', $1);
494                 $staging_set = 1;
495             }
496         }
497         unless($staging_set) {
498             foreach (@configresult) {
499                 if (/^origin\s+(\S+) \(fetch\)/) {
500                     $self->exe('git', 'remote', 'add', 'staging', $1);
501                 }
502             }
503         }
504     }
505     #if repo has no gerrit repo defined, alias it to whatever staging now points to (could be origin)
506     if (!$current_remotes{'gerrit'} && !@gerrit) {
507         my @configresult = qx(git remote -v);
508         foreach (@configresult) {
509             if (/^staging\s+(\S+) \(fetch\)/) {
510                 $self->exe('git', 'remote', 'add', 'gerrit', $1);
511             }
512         }
513     }
514
515     return;
516 }
517
518 sub git_clone_one_submodule
519 {
520     my ($self, $submodule, $url) = @_;
521
522     my $alternates            = $self->{ 'alternates'        };
523     my $mirror_url            = $self->{ 'mirror-url'        };
524     my $mirror_webkit_url     = $self->{ 'mirror-webkit-url' };
525
526     # `--reference FOO' args for the clone, if any.
527     my @reference_args;
528
529     if ($alternates) {
530         # alternates is a qt5 repo, so the submodule will be under that.
531         if (-d "$alternates/$submodule") {
532             @reference_args = ('--reference', "$alternates/$submodule");
533         }
534         else {
535             print " *** $alternates/$submodule not found, ignoring alternate for this submodule\n";
536         }
537     }
538
539     my $mirror;
540     if ($mirror_url && ($submodule ne 'qtwebkit')) {
541         $mirror = $mirror_url.$submodule;
542         $mirror .= ".git" unless (-d $mirror); # Support local disk mirror
543     }
544     elsif ($mirror_webkit_url && ($submodule eq 'qtwebkit')) {
545         $mirror = $mirror_webkit_url;
546     }
547
548     my $do_clone = (! -d "$submodule/.git");
549     if ($do_clone) {
550         $self->exe('git', 'clone', @reference_args, ($mirror ? $mirror : $url), $submodule);
551     }
552
553     chdir($submodule) or confess "chdir $submodule: $OS_ERROR";
554
555     if (!$do_clone) {
556         $self->exe('git', 'fetch', ($mirror ? $mirror : $url));
557     }
558
559     my $template = getcwd()."/../.commit-template";
560     if (-e $template) {
561         $self->exe('git', 'config', 'commit.template', $template);
562     }
563
564     if ($mirror) {
565         $self->exe('git', 'config', 'remote.origin.url', $url);
566
567         # In `force' mode, remove the mirror if it already exists,
568         # since we may be reinitializing the module.
569         if ($self->{force}) {
570             eval { $self->exe('git', 'remote', 'rm', 'mirror'); }; # failure is OK
571         }
572
573         $self->exe('git', 'remote', 'add', 'mirror', $mirror);
574     }
575
576     $self->git_add_remotes($submodule);
577
578     if ($self->{'detach-alternates'}) {
579         $self->exe('git', 'repack', '-a');
580
581         my $alternates_path = '.git/objects/info/alternates';
582         unlink($alternates_path) || confess "unlink $alternates_path: $OS_ERROR";
583     }
584
585     if ($submodule eq "qtdeclarative") { #Extra step needed to setup declarative 
586         $self->exe('git', 'submodule', 'init'); 
587         $self->exe('git', 'submodule', 'update'); 
588     } 
589
590     chdir("..") or confess "cd ..: $OS_ERROR";
591
592     return;
593 }
594
595 sub run
596 {
597     my ($self) = @_;
598
599     $self->check_if_already_initialized;
600     $self->git_submodule_init;
601
602     if (!$self->{webkit}) {
603         $self->git_disable_webkit_submodule;
604     }
605
606     $self->git_set_submodule_config;
607
608     if ($self->{update}) {
609         $self->git_clone_all_submodules;
610     }
611
612     $self->git_add_remotes('qt5');
613
614     return;
615 }
616
617 #==============================================================================
618
619 Qt::InitRepository->new(@ARGV)->run if (!caller);
620 1;