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