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