diff options
Diffstat (limited to 'init-repository')
-rwxr-xr-x | init-repository | 672 |
1 files changed, 22 insertions, 650 deletions
diff --git a/init-repository b/init-repository index 2e0ddcde..8becca95 100755 --- a/init-repository +++ b/init-repository @@ -1,658 +1,30 @@ -#!/usr/bin/env perl -############################################################################# -## -## Copyright (C) 2015 The Qt Company Ltd. -## Contact: http://www.qt.io/licensing/ -## -## This file is part of the utilities of the Qt Toolkit. -## -## $QT_BEGIN_LICENSE:LGPL21$ -## 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 The Qt Company. For licensing terms -## and conditions see http://www.qt.io/terms-conditions. For further -## information use the contact form at http://www.qt.io/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 or version 3 as published by the Free -## Software Foundation and appearing in the file LICENSE.LGPLv21 and -## LICENSE.LGPLv3 included in the packaging of this file. Please review the -## following information to ensure the GNU Lesser General Public License -## requirements will be met: https://www.gnu.org/licenses/lgpl.html and -## http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -## -## As a special exception, The Qt Company gives you certain additional -## rights. These rights are described in The Qt Company LGPL Exception -## version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -## -## $QT_END_LICENSE$ -## -############################################################################# +#!/bin/sh +# Copyright (C) 2024 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 -use v5.8; -use strict; -use warnings; +set -eu -package Qt::InitRepository; +script_dir_path=`dirname $0` +script_dir_path=`(cd "$script_dir_path"; /bin/pwd)` +optfile=init-repository.opt +opttmpfile=init-repository.opt.in -sub printUsage($) -{ - my ($ex) = @_; +# check whether cmake exists +if ! [ -x "$(command -v cmake)" ]; then + echo "Error: cmake was not found. You need to install it before configuring Qt." + exit 1 +fi - print <<EOF ; -Usage: - ./init-repository [options] +# Posix compatible way to truncate file +: > "$optfile" +: > "$opttmpfile" - This script may be run after an initial `git clone' of Qt5 in order to - check out all submodules. It fetches them from canonical URLs inferred - from the clone's origin. +# For consistency, use QtWriteArgsFile.cmake to write the optfile like we do on Windows. +# We do the same with the configure script in qtbase. +for arg in "$@"; do echo \"$arg\" >> "$opttmpfile"; done -Options: - Global options: +cmake -DIN_FILE="${opttmpfile}" -DOUT_FILE="${optfile}" -P "${script_dir_path}/cmake/QtWriteArgsFile.cmake" - --force, -f - Force initialization (even if the submodules are already checked - out). - - --force-hooks - Force initialization of hooks (even if there are already hooks in - checked out submodules). - - --quiet, -q - Be quiet. Will exit cleanly if the repository is already - initialized. - - Module options: - - --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 strings - "essential", "addon", "preview", "deprecated", "obsolete", and - "ignore" refer to classes of modules; "default" maps to - "essential,addon,preview,deprecated", which corresponds with the - set of maintained modules and is also the default set. Module - names may be prefixed with a dash to exclude them from a bigger - set, e.g. "all,-ignore". - - --no-update - Skip the `git submodule update' command. - - --no-fetch - Skip the `git fetch' commands. Implied by --no-update. - - --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. - - --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. - - Repository options: - - --berlin - Switch to internal URLs and make use of the Berlin git mirrors. - (Implies `--mirror'). - - --oslo - Switch to internal URLs and make use of the Oslo git mirrors. - (Implies `--mirror'). - - --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. - - --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'. - - 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 "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. - - --copy-objects - When `--alternates' is used, automatically do a "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'. - - --mirror <url-base> - Uses <url-base> as the base URL for submodule git mirrors. - - For example: - - --mirror user\@machine:/foo/bar/qt/ - - ...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. - -EOF - exit($ex); -} - -use Carp qw( confess ); -use Cwd qw( getcwd abs_path ); -use English qw( -no_match_vars ); -use File::Spec::Functions qw ( rel2abs ); -use Getopt::Long qw( GetOptions ); - -my $script_path = abs_path($0); -$script_path =~ s,[/\\][^/\\]+$,,; - -my $GERRIT_SSH_BASE - = 'ssh://@USER@codereview.qt-project.org@PORT@/qt/'; - -my $BER_MIRROR_URL_BASE - = 'git://hegel/qt/'; - -my $OSLO_MIRROR_URL_BASE - = 'git://qilin/qt/'; - -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) = @_; - - %{$self} = (%{$self}, - 'alternates' => "", - 'branch' => 0, - 'codereview-username' => "", - 'detach-alternates' => 0 , - 'force' => 0 , - 'force-hooks' => 0 , - 'ignore-submodules' => 0 , - 'mirror-url' => "", - 'update' => 1 , - 'fetch' => 1 , - 'module-subset' => "default", - ); - - GetOptions( - '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 }}, - 'fetch!' => \$self->{qw{ fetch }}, - 'module-subset=s' => \$self->{qw{ module-subset }}, - - 'help|?' => sub { printUsage(1); }, - - 'berlin' => sub { - $self->{'mirror-url'} = $BER_MIRROR_URL_BASE; - }, - 'oslo' => sub { - $self->{'mirror-url'} = $OSLO_MIRROR_URL_BASE; - }, - ) || printUsage(2); - @ARGV && printUsage(2); - - # Replace any double trailing slashes from end of mirror - $self->{'mirror-url'} =~ s{//+$}{/}; - - $self->{'module-subset'} =~ s/\bdefault\b/preview,essential,addon,deprecated/; - $self->{'module-subset'} = [ split(/,/, $self->{'module-subset'}) ]; - - $self->{'fetch'} = 0 if (!$self->{'update'}); - - 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; -} - -use constant { - STS_PREVIEW => 1, - STS_ESSENTIAL => 2, - STS_ADDON => 3, - STS_DEPRECATED => 4, - STS_OBSOLETE => 5 -}; - -sub git_clone_all_submodules -{ - my ($self, $my_repo_base, $co_branch, $alternates, @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 'none'); - } elsif ($2 eq "status") { - if ($3 eq "preview") { - $subinits{$1} = STS_PREVIEW; - } elsif ($3 eq "essential") { - $subinits{$1} = STS_ESSENTIAL; - } elsif ($3 eq "addon") { - $subinits{$1} = STS_ADDON; - } elsif ($3 eq "deprecated") { - $subinits{$1} = STS_DEPRECATED; - } elsif ($3 eq "obsolete") { - $subinits{$1} = STS_OBSOLETE; - } elsif ($3 eq "ignore") { - delete $subinits{$1}; - } else { - die("Invalid subrepo status '$3' for '$1'.\n"); - } - } - } - - my %include = (); - foreach my $mod (@subset) { - my $del = ($mod =~ s/^-//); - my $fail = 0; - my @what; - if ($mod eq "all") { - @what = keys %subbases; - } elsif ($mod eq "essential") { - @what = grep { ($subinits{$_} || 0) eq STS_ESSENTIAL } keys %subbases; - } elsif ($mod eq "addon") { - @what = grep { ($subinits{$_} || 0) eq STS_ADDON } keys %subbases; - } elsif ($mod eq "preview") { - @what = grep { ($subinits{$_} || 0) eq STS_PREVIEW } keys %subbases; - } elsif ($mod eq "deprecated") { - @what = grep { ($subinits{$_} || 0) eq STS_DEPRECATED } keys %subbases; - } elsif ($mod eq "obsolete") { - @what = grep { ($subinits{$_} || 0) eq STS_OBSOLETE } keys %subbases; - } elsif ($mod eq "ignore") { - @what = grep { ($subinits{$_} || 0) eq 0 } keys %subbases; - } elsif (defined($subdirs{$mod})) { - push @what, $mod; - } else { - $fail = 1; - } - if ($del) { - print "Warning: excluding non-existent module '$mod'.\n" - if ($fail); - map { delete $include{$_} } @what; - } else { - die("Error: module subset names non-existent '$mod'.\n") - if ($fail); - map { $include{$_} = 1; } @what; - } - } - - 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://code.qt.io/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'); - } - } - - my $any_bad = 0; - foreach my $module (@modules) { - $any_bad = 1 - if ($self->git_stat_one_submodule($subdirs{$module})); - } - die("Dirty submodule(s) present; cannot proceed.\n") - if ($any_bad); - - foreach my $module (@modules) { - $self->git_clone_one_submodule($subdirs{$module}, $subbases{$module}, - $co_branch && $subbranches{$module}, $alternates); - } - - 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(); - my $module_dir = $subdirs{$module}; - chdir($module_dir) or confess "chdir $module_dir: $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', '--force', '--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, "$alternates/$module", "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_stat_one_submodule -{ - my ($self, $submodule) = @_; - - return 0 if (! -e "$submodule/.git"); - - my $orig_cwd = getcwd(); - chdir($submodule) or confess "chdir $submodule: $OS_ERROR"; - - my @sts = qx(git status --porcelain --untracked=no --ignore-submodules=all); - - # After a git clone --no-checkout, git status reports all files as - # staged for deletion, but we still want to update the submodule. - # It's unlikely that a genuinely dirty index would have _only_ this - # type of modifications, and it doesn't seem like a horribly big deal - # to lose them anyway, so ignore them. - @sts = grep(!/^D /, @sts); - - chdir($orig_cwd) or confess "cd $orig_cwd: $OS_ERROR"; - - return 0 if (!@sts); - - print STDERR "$submodule is dirty.\n"; - - return -1; -} - -sub git_clone_one_submodule -{ - my ($self, $submodule, $repo_basename, $branch, $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 $do_clone = (! -e "$submodule/.git"); - - my $url = $self->{'base-url'}.$repo_basename; - my $mirror; - if ($mirror_url && ($do_clone || $self->{fetch})) { - $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; - } - } - - if ($do_clone) { - if ($branch) { - push @reference_args, '--branch', $branch; - } else { - push @reference_args, '--no-checkout'; - } - $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->{fetch}) { - # 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->{fetch}) || $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 = $orig_cwd."/.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}); - if ($^O ne "msys" && $^O ne "MSWin32") { - 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"; - # Make the path palatable for MSYS. - $src =~ s,\\,/,g; - $src =~ s,^(.):/,/$1/,g; - print SCRIPT "#!/bin/sh\nexec $src \"\$\@\"\n"; - close SCRIPT; -} - -sub git_install_hooks -{ - my ($self) = @_; - - my $hooks = $script_path.'/qtrepotools/git-hooks'; - return if (!-d $hooks); - - my @configresult = qx(git config --list --local); - foreach my $line (@configresult) { - next if ($line !~ /submodule\.([^.=]+)\.url=/); - my $module = $1; - my $module_gitdir = $module.'/.git'; - if (!-d $module_gitdir) { - open GITD, $module_gitdir or die "Cannot open $module: $!\n"; - my $gd = <GITD>; - close GITD; - chomp($gd); - $gd =~ s/^gitdir: // or die "Malformed .git file $module_gitdir\n"; - $module_gitdir = rel2abs($gd, $module); - if (open COMD, $module_gitdir.'/commondir') { - my $cd = <COMD>; - chomp($cd); - $module_gitdir .= '/'.$cd; - $module_gitdir = abs_path($module_gitdir); - close COMD; - } - } - $self->ensure_link($hooks.'/gerrit_commit_msg_hook', $module_gitdir.'/hooks/commit-msg'); - $self->ensure_link($hooks.'/git_post_commit_hook', $module_gitdir.'/hooks/post-commit'); - $self->ensure_link($hooks.'/clang-format-pre-commit', $module_gitdir.'/hooks/pre-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/qt5$//; - $self->{'base-url'} = $url; - - $self->git_clone_all_submodules('qt5', $self->{branch}, $self->{alternates}, @{$self->{'module-subset'}}); - - $self->git_add_remotes('qt5'); - - $self->git_install_hooks; - - return; -} - -#============================================================================== - -Qt::InitRepository->new()->run if (!caller); -1; +cmake_script_path="$script_dir_path/cmake/QtIRScript.cmake" +exec cmake -DOPTFILE="${optfile}" -P "$cmake_script_path" |