mirror of https://github.com/qt/qt5.git
586 lines
18 KiB
Perl
Executable File
586 lines
18 KiB
Perl
Executable File
#!/usr/bin/env perl
|
|
#############################################################################
|
|
##
|
|
## Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
|
|
## Contact: http://www.qt-project.org/legal
|
|
##
|
|
## This file is part of the utilities of the Qt Toolkit.
|
|
##
|
|
## $QT_BEGIN_LICENSE:LGPL$
|
|
## Commercial License Usage
|
|
## Licensees holding valid commercial Qt licenses may use this file in
|
|
## accordance with the commercial license agreement provided with the
|
|
## Software or, alternatively, in accordance with the terms contained in
|
|
## a written agreement between you and Digia. For licensing terms and
|
|
## conditions see http://qt.digia.com/licensing. For further information
|
|
## use the contact form at http://qt.digia.com/contact-us.
|
|
##
|
|
## GNU Lesser General Public License Usage
|
|
## Alternatively, this file may be used under the terms of the GNU Lesser
|
|
## General Public License version 2.1 as published by the Free Software
|
|
## Foundation and appearing in the file LICENSE.LGPL included in the
|
|
## packaging of this file. Please review the following information to
|
|
## ensure the GNU Lesser General Public License version 2.1 requirements
|
|
## will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
|
##
|
|
## In addition, as a special exception, Digia gives you certain additional
|
|
## rights. These rights are described in the Digia Qt LGPL Exception
|
|
## version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
|
##
|
|
## GNU General Public License Usage
|
|
## Alternatively, this file may be used under the terms of the GNU
|
|
## General Public License version 3.0 as published by the Free Software
|
|
## Foundation and appearing in the file LICENSE.GPL included in the
|
|
## packaging of this file. Please review the following information to
|
|
## ensure the GNU General Public License version 3.0 requirements will be
|
|
## met: http://www.gnu.org/copyleft/gpl.html.
|
|
##
|
|
##
|
|
## $QT_END_LICENSE$
|
|
##
|
|
#############################################################################
|
|
|
|
use v5.8;
|
|
use strict;
|
|
use warnings;
|
|
|
|
package Qt::InitRepository;
|
|
|
|
|
|
=head1 NAME
|
|
|
|
init-repository - initialize the Qt5 repository and all submodules
|
|
|
|
=head1 SYNOPSIS
|
|
|
|
./init-repository [options]
|
|
|
|
This script may be run after an initial `git clone' of Qt5 in order to check
|
|
out all submodules.
|
|
|
|
|
|
=head1 OPTIONS
|
|
|
|
B<Global options:>
|
|
|
|
=over
|
|
|
|
=item --force, -f
|
|
|
|
Force initialization (even if the submodules are already checked out).
|
|
|
|
|
|
=item --force-hooks
|
|
|
|
Force initialization of hooks (even if there are already hooks in checked out
|
|
submodules).
|
|
|
|
|
|
=item --quiet, -q
|
|
|
|
Be quiet. Will exit cleanly if the repository is already initialized.
|
|
|
|
=back
|
|
|
|
|
|
B<Module options:>
|
|
|
|
=over
|
|
|
|
=item --no-webkit
|
|
|
|
Skip webkit and webkit examples submodules.
|
|
It may be desirable to skip these modules due to the large size of the webkit
|
|
git repository.
|
|
|
|
=item --module-subset=<module1>,<module2>...
|
|
|
|
Only initialize the specified subset of modules given as the argument. Specified
|
|
modules must already exist in .gitmodules.
|
|
The string "all" results in cloning all known modules. The default is the set of
|
|
maintained modules.
|
|
Module names may be prefixed with a dash to exclude them from a bigger set.
|
|
|
|
=item --no-update
|
|
|
|
Skip the `git submodule update' command.
|
|
|
|
=item --branch
|
|
|
|
Instead of checking out specific SHA1s, check out the submodule branches that
|
|
correspond with the current supermodule commit.
|
|
By default, this option will cause local commits in the submodules to be rebased.
|
|
With --no-update, the branches will be checked out, but their heads will not move.
|
|
|
|
=item --ignore-submodules
|
|
|
|
Set git config to ignore submodules by default when doing operations on the
|
|
qt5 repo, such as `pull', `fetch', `diff' etc.
|
|
|
|
After using this option, pass `--ignore-submodules=none' to git to override
|
|
it as needed.
|
|
|
|
=back
|
|
|
|
|
|
B<Repository options:>
|
|
|
|
=over
|
|
|
|
=item --berlin
|
|
|
|
Switch to internal URLs and make use of the Berlin git mirrors.
|
|
(Implies `--mirror').
|
|
|
|
=item --oslo
|
|
|
|
Switch to internal URLs and make use of the Oslo git mirrors.
|
|
(Implies `--mirror').
|
|
|
|
|
|
=item --codereview-username <Gerrit/JIRA username>
|
|
|
|
Specify the user name for the (potentially) writable `gerrit' remote
|
|
for each module, for use with the Gerrit code review tool.
|
|
|
|
If this option is omitted, the gerrit remote is created without a username
|
|
and port number, and thus relies on a correct SSH configuration.
|
|
|
|
|
|
=item --alternates <path to other Qt5 repo>
|
|
|
|
Adds alternates for each submodule to another full qt5 checkout. This makes
|
|
this qt5 checkout very small, as it will use the object store of the
|
|
alternates before unique objects are stored in its own object store.
|
|
|
|
This option has no effect when using `--no-update'.
|
|
|
|
B<NOTE:> This will make this repo dependent on the alternate, which is
|
|
potentially dangerous! The dependency can be broken by also using
|
|
the `--copy-objects' option, or by running C<git repack -a> in each
|
|
submodule, where required. Please read the note about the `--shared' option
|
|
in the documentation of `git clone' for more information.
|
|
|
|
|
|
=item --copy-objects
|
|
|
|
When `--alternates' is used, automatically do a C<git repack -a> in each
|
|
submodule after cloning, to ensure that the repositories are independent
|
|
from the source used as a reference for cloning.
|
|
|
|
Note that this negates the disk usage benefits gained from the use of
|
|
`--alternates'.
|
|
|
|
|
|
=item --mirror <url-base>
|
|
|
|
Uses <url-base> as the base URL for submodule git mirrors.
|
|
|
|
For example:
|
|
|
|
--mirror user@machine:/foo/bar/
|
|
|
|
...will use the following as a mirror for qtbase:
|
|
|
|
user@machine:/foo/bar/qt/qtbase.git
|
|
|
|
The mirror is permitted to contain a subset of the submodules; any
|
|
missing modules will fall back to the canonical URLs.
|
|
|
|
=back
|
|
|
|
=cut
|
|
|
|
use Carp qw( confess );
|
|
use English qw( -no_match_vars );
|
|
use Getopt::Long qw( GetOptionsFromArray );
|
|
use Pod::Usage qw( pod2usage );
|
|
use Cwd qw( getcwd );
|
|
|
|
my $GERRIT_SSH_BASE
|
|
= 'ssh://@USER@codereview.qt-project.org@PORT@/';
|
|
|
|
my $BER_MIRROR_URL_BASE
|
|
= 'git://hegel/';
|
|
|
|
my $OSLO_MIRROR_URL_BASE
|
|
= 'git://qilin/';
|
|
|
|
sub new
|
|
{
|
|
my ($class, @arguments) = @_;
|
|
|
|
my $self = {};
|
|
bless $self, $class;
|
|
$self->parse_arguments(@arguments);
|
|
|
|
return $self;
|
|
}
|
|
|
|
# Like `system', but possibly log the command, and die on non-zero exit code
|
|
sub exe
|
|
{
|
|
my ($self, @cmd) = @_;
|
|
|
|
if (!$self->{quiet}) {
|
|
print "+ @cmd\n";
|
|
}
|
|
|
|
if (system(@cmd) != 0) {
|
|
confess "@cmd exited with status $CHILD_ERROR";
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
sub parse_arguments
|
|
{
|
|
my ($self, @args) = @_;
|
|
|
|
%{$self} = (%{$self},
|
|
'alternates' => "",
|
|
'branch' => 0,
|
|
'codereview-username' => "",
|
|
'detach-alternates' => 0 ,
|
|
'force' => 0 ,
|
|
'force-hooks' => 0 ,
|
|
'ignore-submodules' => 0 ,
|
|
'mirror-url' => "",
|
|
'update' => 1 ,
|
|
'webkit' => 1 ,
|
|
'module-subset' => "default",
|
|
);
|
|
|
|
GetOptionsFromArray(\@args,
|
|
'alternates=s' => \$self->{qw{ alternates }},
|
|
'branch' => \$self->{qw{ branch }},
|
|
'codereview-username=s' => \$self->{qw{ codereview-username }},
|
|
'copy-objects' => \$self->{qw{ detach-alternates }},
|
|
'force|f' => \$self->{qw{ force }},
|
|
'force-hooks' => \$self->{qw{ force-hooks }},
|
|
'ignore-submodules' => \$self->{qw{ ignore-submodules }},
|
|
'mirror=s' => \$self->{qw{ mirror-url }},
|
|
'quiet' => \$self->{qw{ quiet }},
|
|
'update!' => \$self->{qw{ update }},
|
|
'webkit!' => \$self->{qw{ webkit }},
|
|
'module-subset=s' => \$self->{qw{ module-subset }},
|
|
|
|
'help|?' => sub { pod2usage(1); },
|
|
|
|
'berlin' => sub {
|
|
$self->{'mirror-url'} = $BER_MIRROR_URL_BASE;
|
|
},
|
|
'oslo' => sub {
|
|
$self->{'mirror-url'} = $OSLO_MIRROR_URL_BASE;
|
|
},
|
|
) || pod2usage(2);
|
|
|
|
# Replace any double trailing slashes from end of mirror
|
|
$self->{'mirror-url'} =~ s{//+$}{/};
|
|
|
|
$self->{'module-subset'} = [ split(/,/, $self->{'module-subset'}) ];
|
|
if (!$self->{webkit}) {
|
|
push @{$self->{'module-subset'}}, "-qtwebkit", "-qtwebkit-examples";
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
sub check_if_already_initialized
|
|
{
|
|
my ($self) = @_;
|
|
|
|
# We consider the repo as `initialized' if submodule.qtbase.url is set
|
|
if (qx(git config --get submodule.qtbase.url)) {
|
|
if (!$self->{force}) {
|
|
exit 0 if ($self->{quiet});
|
|
print "Will not reinitialize already initialized repository (use -f to force)!\n";
|
|
exit 1;
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
sub git_submodule_init
|
|
{
|
|
my ($self, @init_args) = @_;
|
|
|
|
if ($self->{quiet}) {
|
|
unshift @init_args, '--quiet';
|
|
}
|
|
$self->exe('git', 'submodule', 'init', @init_args);
|
|
|
|
my $template = getcwd()."/.commit-template";
|
|
if (-e $template) {
|
|
$self->exe('git', 'config', 'commit.template', $template);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
sub git_clone_all_submodules
|
|
{
|
|
my ($self, $my_repo_base, $co_branch, @subset) = @_;
|
|
|
|
my %subdirs = ();
|
|
my %subbranches = ();
|
|
my %subbases = ();
|
|
my %subinits = ();
|
|
my @submodconfig = qx(git config -l -f .gitmodules);
|
|
foreach my $line (@submodconfig) {
|
|
# Example line: submodule.qtqa.url=../qtqa.git
|
|
next if ($line !~ /^submodule\.([^.=]+)\.([^.=]+)=(.*)$/);
|
|
if ($2 eq "path") {
|
|
$subdirs{$1} = $3;
|
|
} elsif ($2 eq "branch") {
|
|
$subbranches{$1} = $3;
|
|
} elsif ($2 eq "url") {
|
|
my ($mod, $base) = ($1, $3);
|
|
next if ($base !~ /^\.\.\//);
|
|
$base = $my_repo_base.'/'.$base;
|
|
while ($base =~ s,/(?!\.\./)[^/]+/\.\./,/,g) {}
|
|
$subbases{$mod} = $base;
|
|
} elsif ($2 eq "update") {
|
|
push @subset, '-'.$1 if ($3 eq 'ignore');
|
|
} elsif ($2 eq "initrepo") {
|
|
$subinits{$1} = ($3 eq "yes" or $3 eq "true");
|
|
}
|
|
}
|
|
|
|
my %include = ();
|
|
foreach my $mod (@subset) {
|
|
if ($mod eq "all") {
|
|
map { $include{$_} = 1; } keys %subbases;
|
|
} elsif ($mod eq "default") {
|
|
map { $include{$_} = 1; } grep { $subinits{$_} } keys %subbases;
|
|
} elsif ($mod =~ s/^-//) {
|
|
delete $include{$mod};
|
|
} else {
|
|
$include{$mod} = 1;
|
|
}
|
|
}
|
|
|
|
my @modules = sort keys %include;
|
|
|
|
$self->git_submodule_init(map { $subdirs{$_} } @modules);
|
|
|
|
# manually clone each repo here, so we can easily use reference repos, mirrors etc
|
|
my @configresult = qx(git config -l);
|
|
foreach my $line (@configresult) {
|
|
# Example line: submodule.qtqa.url=git://gitorious.org/qt/qtqa.git
|
|
next if ($line !~ /submodule\.([^.=]+)\.url=/);
|
|
my $module = $1;
|
|
|
|
if (!defined($include{$module})) {
|
|
$self->exe('git', 'config', '--remove-section', "submodule.$module");
|
|
next;
|
|
}
|
|
|
|
if ($self->{'ignore-submodules'}) {
|
|
$self->exe('git', 'config', "submodule.$module.ignore", 'all');
|
|
}
|
|
}
|
|
|
|
foreach my $module (@modules) {
|
|
$self->git_clone_one_submodule($subdirs{$module}, $subbases{$module}, $subbranches{$module});
|
|
}
|
|
|
|
if ($co_branch) {
|
|
foreach my $module (@modules) {
|
|
my $branch = $subbranches{$module};
|
|
die("No branch defined for submodule $module.\n") if (!defined($branch));
|
|
my $orig_cwd = getcwd();
|
|
chdir($module) or confess "chdir $module: $OS_ERROR";
|
|
my $br = qx(git rev-parse -q --verify $branch);
|
|
if (!$br) {
|
|
$self->exe('git', 'checkout', '-b', $branch, "origin/$branch");
|
|
} else {
|
|
$self->exe('git', 'checkout', $branch);
|
|
}
|
|
chdir("$orig_cwd") or confess "chdir $orig_cwd: $OS_ERROR";
|
|
}
|
|
}
|
|
if ($self->{update}) {
|
|
my @cmd = ('git', 'submodule', 'update', '--no-fetch');
|
|
push @cmd, '--remote', '--rebase' if ($co_branch);
|
|
$self->exe(@cmd);
|
|
|
|
foreach my $module (@modules) {
|
|
if (-f $module.'/.gitmodules') {
|
|
my $orig_cwd = getcwd();
|
|
chdir($module) or confess "chdir $module: $OS_ERROR";
|
|
$self->git_clone_all_submodules($subbases{$module}, 0, "all");
|
|
chdir("$orig_cwd") or confess "chdir $orig_cwd: $OS_ERROR";
|
|
}
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
sub git_add_remotes
|
|
{
|
|
my ($self, $gerrit_repo_basename) = @_;
|
|
|
|
my $gerrit_repo_url = $GERRIT_SSH_BASE;
|
|
# If given a username, make a "verbose" remote.
|
|
# Otherwise, rely on proper SSH configuration.
|
|
if ($self->{'codereview-username'}) {
|
|
$gerrit_repo_url =~ s,\@USER\@,$self->{'codereview-username'}\@,;
|
|
$gerrit_repo_url =~ s,\@PORT\@,:29418,;
|
|
} else {
|
|
$gerrit_repo_url =~ s,\@[^\@]+\@,,g;
|
|
}
|
|
|
|
$gerrit_repo_url .= $gerrit_repo_basename;
|
|
$self->exe('git', 'config', 'remote.gerrit.url', $gerrit_repo_url);
|
|
$self->exe('git', 'config', 'remote.gerrit.fetch', '+refs/heads/*:refs/remotes/gerrit/*', '/heads/');
|
|
}
|
|
|
|
sub git_clone_one_submodule
|
|
{
|
|
my ($self, $submodule, $repo_basename, $branch) = @_;
|
|
|
|
my $alternates = $self->{ 'alternates' };
|
|
my $mirror_url = $self->{ 'mirror-url' };
|
|
my $protocol = $self->{ 'protocol' };
|
|
|
|
# `--reference FOO' args for the clone, if any.
|
|
my @reference_args;
|
|
|
|
if ($alternates) {
|
|
# alternates is a qt5 repo, so the submodule will be under that.
|
|
if (-e "$alternates/$submodule/.git") {
|
|
@reference_args = ('--reference', "$alternates/$submodule");
|
|
}
|
|
else {
|
|
print " *** $alternates/$submodule not found, ignoring alternate for this submodule\n";
|
|
}
|
|
}
|
|
|
|
my $url = $self->{'base-url'}.$repo_basename;
|
|
my $mirror;
|
|
if ($mirror_url) {
|
|
$mirror = $mirror_url.$repo_basename;
|
|
}
|
|
|
|
if ($mirror) {
|
|
# Only use the mirror if it can be reached.
|
|
eval { $self->exe('git', 'ls-remote', $mirror, 'test/if/mirror/exists') };
|
|
if ($@) {
|
|
warn "mirror [$mirror] is not accessible; $url will be used\n";
|
|
undef $mirror;
|
|
}
|
|
}
|
|
|
|
my $do_clone = (! -e "$submodule/.git");
|
|
if ($do_clone) {
|
|
push @reference_args, '--branch', $branch if ($branch);
|
|
$self->exe('git', 'clone', @reference_args,
|
|
($mirror ? $mirror : $url), $submodule);
|
|
}
|
|
|
|
my $orig_cwd = getcwd();
|
|
chdir($submodule) or confess "chdir $submodule: $OS_ERROR";
|
|
|
|
if ($mirror) {
|
|
# This is only for the user's convenience - we make no use of it.
|
|
$self->exe('git', 'config', 'remote.mirror.url', $mirror);
|
|
$self->exe('git', 'config', 'remote.mirror.fetch', '+refs/heads/*:refs/remotes/mirror/*');
|
|
}
|
|
|
|
if (!$do_clone && $self->{update}) {
|
|
# If we didn't clone, fetch from the right location. We always update
|
|
# the origin remote, so that submodule update --remote works.
|
|
$self->exe('git', 'config', 'remote.origin.url', ($mirror ? $mirror : $url));
|
|
$self->exe('git', 'fetch', 'origin');
|
|
}
|
|
|
|
if (!($do_clone || $self->{update}) || $mirror) {
|
|
# Leave the origin configured to the canonical URL. It's already correct
|
|
# if we cloned/fetched without a mirror; otherwise it may be anything.
|
|
$self->exe('git', 'config', 'remote.origin.url', $url);
|
|
}
|
|
|
|
my $template = getcwd()."/../.commit-template";
|
|
if (-e $template) {
|
|
$self->exe('git', 'config', 'commit.template', $template);
|
|
}
|
|
|
|
$self->git_add_remotes($repo_basename);
|
|
|
|
if ($self->{'detach-alternates'}) {
|
|
$self->exe('git', 'repack', '-a');
|
|
|
|
my $alternates_path = '.git/objects/info/alternates';
|
|
if (-e $alternates_path) {
|
|
unlink($alternates_path) || confess "unlink $alternates_path: $OS_ERROR";
|
|
}
|
|
}
|
|
|
|
chdir($orig_cwd) or confess "cd $orig_cwd: $OS_ERROR";
|
|
|
|
return;
|
|
}
|
|
|
|
sub ensure_link
|
|
{
|
|
my ($self, $src, $tgt) = @_;
|
|
return if (!$self->{'force-hooks'} and -f $tgt);
|
|
unlink($tgt); # In case we have a dead symlink or pre-existing hook
|
|
print "Aliasing $src\n as $tgt ...\n" if (!$self->{quiet});
|
|
return if eval { symlink($src, $tgt) };
|
|
# Windows doesn't do (proper) symlinks. As the post_commit script needs
|
|
# them to locate itself, we write a forwarding script instead.
|
|
open SCRIPT, ">".$tgt or die "Cannot create forwarding script $tgt: $!\n";
|
|
print SCRIPT "#!/bin/sh\nexec `dirname \$0`/$src \"\$\@\"\n";
|
|
close SCRIPT;
|
|
}
|
|
|
|
sub git_install_hooks
|
|
{
|
|
my ($self) = @_;
|
|
|
|
return if (!-d 'qtrepotools/git-hooks');
|
|
|
|
# Force C locale as git submodule returns the localized string "Entering"
|
|
local $ENV{LC_ALL} = 'C';
|
|
chomp(my @modules = `git submodule foreach :`);
|
|
push @modules, "";
|
|
for my $module (@modules) {
|
|
$module =~ s,^Entering \'([^\']+)\'$,$1/,;
|
|
my $rel = $module;
|
|
$rel =~ s,[^/]+,..,g;
|
|
$rel .= "../../qtrepotools/git-hooks/";
|
|
$self->ensure_link($rel.'gerrit_commit_msg_hook', $module.'.git/hooks/commit-msg');
|
|
$self->ensure_link($rel.'git_post_commit_hook', $module.'.git/hooks/post-commit');
|
|
}
|
|
}
|
|
|
|
sub run
|
|
{
|
|
my ($self) = @_;
|
|
|
|
$self->check_if_already_initialized;
|
|
|
|
chomp(my $url = `git config remote.origin.url`);
|
|
die("Have no origin remote.\n") if (!$url);
|
|
$url =~ s,\.git$,,;
|
|
$url =~ s,qt/qt5$,,;
|
|
$self->{'base-url'} = $url;
|
|
|
|
$self->git_clone_all_submodules('qt/qt5', $self->{branch}, @{$self->{'module-subset'}});
|
|
|
|
$self->git_add_remotes('qt/qt5');
|
|
|
|
$self->git_install_hooks;
|
|
|
|
return;
|
|
}
|
|
|
|
#==============================================================================
|
|
|
|
Qt::InitRepository->new(@ARGV)->run if (!caller);
|
|
1;
|