mirror of
https://git.savannah.gnu.org/git/gperf.git
synced 2025-12-02 13:09:22 +00:00
build: Use gnulib-tool with a module list.
* .gitmodules: New file. * gitsub.sh: New file, from gnulib/top/gitsub.sh. * autopull.sh: Use gitsub.sh to check out gnulib. * autogen.sh: Accept --skip-gnulib option. Invoke gnulib-tool, assuming a gnulib checkout is already present. * Makefile.devel (lib/aclocal.m4): Update reference to macros directory. * lib/configure.ac: Likewise. Invoke gl_EARLY, gl_INIT. * lib/Makefile.am (EXTRA_DIST, MOSTLYCLEANFILES): New variables. Include Makefile.gnulib.
This commit is contained in:
519
gitsub.sh
Executable file
519
gitsub.sh
Executable file
@@ -0,0 +1,519 @@
|
||||
#! /bin/sh
|
||||
#
|
||||
# Copyright (C) 2019-2025 Free Software Foundation, Inc.
|
||||
# Written by Bruno Haible <bruno@clisp.org>, 2019.
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
# Program that manages the subdirectories of a git checkout of a package
|
||||
# that come from other packages (called "dependency packages").
|
||||
#
|
||||
# This program is similar in spirit to 'git submodule', with three
|
||||
# essential differences:
|
||||
#
|
||||
# 1) Its options are easy to remember, and do not require knowledge of
|
||||
# 'git submodule'.
|
||||
#
|
||||
# 2) The developer may choose to work on a different checkout for each
|
||||
# dependency package. This is important when the developer is
|
||||
# preparing simultaneous changes to the package and the dependency
|
||||
# package, or is using the dependency package in several packages.
|
||||
#
|
||||
# The developer indicates this different checkout by setting the
|
||||
# environment variable <SUBDIR>_SRCDIR (e.g. GNULIB_SRCDIR) to point to it.
|
||||
#
|
||||
# 3) The package maintainer may choose to use or not use git submodules.
|
||||
#
|
||||
# The advantages of management through a git submodule are:
|
||||
# - Changes to the dependency package cannot suddenly break your package.
|
||||
# In other words, when there is an incompatible change that will cause
|
||||
# a breakage, you can fix things at your pace; you are not forced to
|
||||
# cope with such breakages in an emergency.
|
||||
# - When you need to make a change as a response to a change in the
|
||||
# dependency package, your co-developers cannot accidentally mix things
|
||||
# up (for example, use a combination of your newest change with an
|
||||
# older version of the dependency package).
|
||||
#
|
||||
# The advantages of management without a git submodule (just as a plain
|
||||
# subdirectory, let's call it a "subcheckout") are:
|
||||
# - The simplicity: you are conceptually always using the newest revision
|
||||
# of the dependency package.
|
||||
# - You don't have to remember to periodically upgrade the dependency.
|
||||
# Upgrading the dependency is an implicit operation.
|
||||
|
||||
# This program is meant to be copied to the top-level directory of the package,
|
||||
# together with a configuration file. The configuration is supposed to be
|
||||
# named '.gitmodules' and to define:
|
||||
# * The git submodules, as described in "man 5 gitmodules" or
|
||||
# <https://git-scm.com/docs/gitmodules>. For example:
|
||||
#
|
||||
# [submodule "gnulib"]
|
||||
# url = https://git.savannah.gnu.org/git/gnulib.git
|
||||
# path = gnulib
|
||||
#
|
||||
# You don't add this piece of configuration to .gitmodules manually. Instead,
|
||||
# you would invoke
|
||||
# $ git submodule add --name "gnulib" -- https://git.savannah.gnu.org/git/gnulib.git gnulib
|
||||
#
|
||||
# * The subdirectories that are not git submodules, in a similar syntax. For
|
||||
# example:
|
||||
#
|
||||
# [subcheckout "gnulib"]
|
||||
# url = https://git.savannah.gnu.org/git/gnulib.git
|
||||
# path = gnulib
|
||||
#
|
||||
# Here the URL is the one used for anonymous checkouts of the dependency
|
||||
# package. If the developer needs a checkout with write access, they can
|
||||
# either set the GNULIB_SRCDIR environment variable to point to that checkout
|
||||
# or modify the gnulib/.git/config file to enter a different URL.
|
||||
|
||||
scriptname="$0"
|
||||
scriptversion='2019-04-01'
|
||||
nl='
|
||||
'
|
||||
IFS=" "" $nl"
|
||||
|
||||
# func_usage
|
||||
# outputs to stdout the --help usage message.
|
||||
func_usage ()
|
||||
{
|
||||
echo "\
|
||||
Usage: gitsub.sh pull [SUBDIR]
|
||||
gitsub.sh upgrade [SUBDIR]
|
||||
gitsub.sh checkout SUBDIR REVISION
|
||||
|
||||
Operations:
|
||||
|
||||
gitsub.sh pull [GIT_OPTIONS] [SUBDIR]
|
||||
You should perform this operation after 'git clone ...' and after
|
||||
every 'git pull'.
|
||||
It brings your checkout in sync with what the other developers of
|
||||
your package have committed and pushed.
|
||||
If an environment variable <SUBDIR>_SRCDIR is set, with a non-empty
|
||||
value, nothing is done for this SUBDIR.
|
||||
Supported GIT_OPTIONS (for expert git users) are:
|
||||
--reference <repository>
|
||||
--depth <depth>
|
||||
--recursive
|
||||
If no SUBDIR is specified, the operation applies to all dependencies.
|
||||
|
||||
gitsub.sh upgrade [SUBDIR]
|
||||
You should perform this operation periodically, to ensure currency
|
||||
of the dependency package revisions that you use.
|
||||
This operation pulls and checks out the changes that the developers
|
||||
of the dependency package have committed and pushed.
|
||||
If an environment variable <SUBDIR>_SRCDIR is set, with a non-empty
|
||||
value, nothing is done for this SUBDIR.
|
||||
If no SUBDIR is specified, the operation applies to all dependencies.
|
||||
|
||||
gitsub.sh checkout SUBDIR REVISION
|
||||
Checks out a specific revision for a dependency package.
|
||||
If an environment variable <SUBDIR>_SRCDIR is set, with a non-empty
|
||||
value, this operation fails.
|
||||
|
||||
This script requires the git program in the PATH and an internet connection.
|
||||
"
|
||||
}
|
||||
|
||||
# func_version
|
||||
# outputs to stdout the --version message.
|
||||
func_version ()
|
||||
{
|
||||
year=`echo "$scriptversion" | sed -e 's/^\(....\)-.*/\1/'`
|
||||
echo "\
|
||||
gitsub.sh (GNU gnulib) $scriptversion
|
||||
Copyright (C) 2019-$year Free Software Foundation, Inc.
|
||||
License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>
|
||||
This is free software: you are free to change and redistribute it.
|
||||
There is NO WARRANTY, to the extent permitted by law.
|
||||
"
|
||||
printf "Written by %s.\n" "Bruno Haible"
|
||||
}
|
||||
|
||||
# func_fatal_error message
|
||||
# outputs to stderr a fatal error message, and terminates the program.
|
||||
# Input:
|
||||
# - scriptname name of this program
|
||||
func_fatal_error ()
|
||||
{
|
||||
echo "$scriptname: *** $1" 1>&2
|
||||
echo "$scriptname: *** Stop." 1>&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
# func_warning message
|
||||
# Outputs to stderr a warning message,
|
||||
func_warning ()
|
||||
{
|
||||
echo "gitsub.sh: warning: $1" 1>&2
|
||||
}
|
||||
|
||||
# func_note message
|
||||
# Outputs to stdout a note message,
|
||||
func_note ()
|
||||
{
|
||||
echo "gitsub.sh: note: $1"
|
||||
}
|
||||
|
||||
# Unset CDPATH. Otherwise, output from 'cd dir' can surprise callers.
|
||||
(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
|
||||
|
||||
# Command-line option processing.
|
||||
mode=
|
||||
while test $# -gt 0; do
|
||||
case "$1" in
|
||||
--help | --hel | --he | --h )
|
||||
func_usage
|
||||
exit $? ;;
|
||||
--version | --versio | --versi | --vers | --ver | --ve | --v )
|
||||
func_version
|
||||
exit $? ;;
|
||||
-- )
|
||||
# Stop option processing
|
||||
shift
|
||||
break ;;
|
||||
-* )
|
||||
echo "gitsub.sh: unknown option $1" 1>&2
|
||||
echo "Try 'gitsub.sh --help' for more information." 1>&2
|
||||
exit 1 ;;
|
||||
* )
|
||||
break ;;
|
||||
esac
|
||||
done
|
||||
if test $# = 0; then
|
||||
echo "gitsub.sh: missing operation argument" 1>&2
|
||||
echo "Try 'gitsub.sh --help' for more information." 1>&2
|
||||
exit 1
|
||||
fi
|
||||
case "$1" in
|
||||
pull | upgrade | checkout )
|
||||
mode="$1"
|
||||
shift ;;
|
||||
*)
|
||||
echo "gitsub.sh: unknown operation '$1'" 1>&2
|
||||
echo "Try 'gitsub.sh --help' for more information." 1>&2
|
||||
exit 1 ;;
|
||||
esac
|
||||
if { test $mode = upgrade && test $# -gt 1; } \
|
||||
|| { test $mode = checkout && test $# -gt 2; }; then
|
||||
echo "gitsub.sh: too many arguments in '$mode' mode" 1>&2
|
||||
echo "Try 'gitsub.sh --help' for more information." 1>&2
|
||||
exit 1
|
||||
fi
|
||||
if test $# = 0 && test $mode = checkout; then
|
||||
echo "gitsub.sh: too few arguments in '$mode' mode" 1>&2
|
||||
echo "Try 'gitsub.sh --help' for more information." 1>&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Read the configuration.
|
||||
# Output:
|
||||
# - subcheckout_names space-separated list of subcheckout names
|
||||
# - submodule_names space-separated list of submodule names
|
||||
if test -f .gitmodules; then
|
||||
subcheckout_names=`git config --file .gitmodules --get-regexp --name-only 'subcheckout\..*\.url' | sed -e 's/^subcheckout\.//' -e 's/\.url$//' | tr -d '\r' | tr '\n' ' '`
|
||||
submodule_names=`git config --file .gitmodules --get-regexp --name-only 'submodule\..*\.url' | sed -e 's/^submodule\.//' -e 's/\.url$//' | tr -d '\r' | tr '\n' ' '`
|
||||
else
|
||||
subcheckout_names=
|
||||
submodule_names=
|
||||
fi
|
||||
|
||||
# func_validate SUBDIR
|
||||
# Verifies that the state on the file system is in sync with the declarations
|
||||
# in the configuration file.
|
||||
# Input:
|
||||
# - subcheckout_names space-separated list of subcheckout names
|
||||
# - submodule_names space-separated list of submodule names
|
||||
# Output:
|
||||
# - srcdirvar Environment that the user can set
|
||||
# - srcdir Value of the environment variable
|
||||
# - path if $srcdir = "": relative path of the subdirectory
|
||||
# - needs_init if $srcdir = "" and $path is not yet initialized:
|
||||
# true
|
||||
# - url if $srcdir = "" and $path is not yet initialized:
|
||||
# the repository URL
|
||||
func_validate ()
|
||||
{
|
||||
srcdirvar=`echo "$1" | LC_ALL=C sed -e 's/[^a-zA-Z0-9]/_/g' | LC_ALL=C tr '[a-z]' '[A-Z]'`"_SRCDIR"
|
||||
eval 'srcdir=$'"$srcdirvar"
|
||||
path=
|
||||
url=
|
||||
if test -n "$srcdir"; then
|
||||
func_note "Ignoring '$1' because $srcdirvar is set."
|
||||
else
|
||||
found=false
|
||||
needs_init=
|
||||
case " $subcheckout_names " in *" $1 "*)
|
||||
found=true
|
||||
# It ought to be a subcheckout.
|
||||
path=`git config --file .gitmodules "subcheckout.$1.path"`
|
||||
if test -z "$path"; then
|
||||
path="$1"
|
||||
fi
|
||||
if test -d "$path"; then
|
||||
if test -d "$path/.git"; then
|
||||
# It's a plain checkout.
|
||||
:
|
||||
else
|
||||
if test -f "$path/.git"; then
|
||||
# It's a submodule.
|
||||
func_fatal_error "Subdirectory '$path' is supposed to be a plain checkout, but it is a submodule."
|
||||
else
|
||||
func_warning "Ignoring '$path' because it exists but is not a git checkout."
|
||||
fi
|
||||
fi
|
||||
else
|
||||
# The subdir does not yet exist.
|
||||
needs_init=true
|
||||
url=`git config --file .gitmodules "subcheckout.$1.url"`
|
||||
if test -z "$url"; then
|
||||
func_fatal_error "Property subcheckout.$1.url is not defined in .gitmodules"
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
case " $submodule_names " in *" $1 "*)
|
||||
found=true
|
||||
# It ought to be a submodule.
|
||||
path=`git config --file .gitmodules "submodule.$1.path"`
|
||||
if test -z "$path"; then
|
||||
path="$1"
|
||||
fi
|
||||
if test -d "$path"; then
|
||||
if test -d "$path/.git" || test -f "$path/.git"; then
|
||||
# It's likely a submodule.
|
||||
:
|
||||
else
|
||||
path_if_empty=`find "$path" -prune -empty 2>/dev/null`
|
||||
if test -n "$path_if_empty"; then
|
||||
# The subdir is empty.
|
||||
needs_init=true
|
||||
else
|
||||
# The subdir is not empty.
|
||||
# It is important to report an error, because we don't want to erase
|
||||
# the user's files and 'git submodule update gnulib' sometimes reports
|
||||
# "fatal: destination path '$path' already exists and is not an empty directory."
|
||||
# but sometimes does not.
|
||||
func_fatal_error "Subdir '$path' exists but is not a git checkout."
|
||||
fi
|
||||
fi
|
||||
else
|
||||
# The subdir does not yet exist.
|
||||
needs_init=true
|
||||
fi
|
||||
# Another way to determine needs_init could be:
|
||||
# if git submodule status "$path" | grep '^-' > /dev/null; then
|
||||
# needs_init=true
|
||||
# fi
|
||||
if test -n "$needs_init"; then
|
||||
url=`git config --file .gitmodules "submodule.$1.url"`
|
||||
if test -z "$url"; then
|
||||
func_fatal_error "Property submodule.$1.url is not defined in .gitmodules"
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
if ! $found; then
|
||||
func_fatal_error "Subdir '$1' is not configured as a subcheckout or a submodule in .gitmodules"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# func_cleanup_current_git_clone
|
||||
# Cleans up the current 'git clone' operation.
|
||||
# Input:
|
||||
# - path
|
||||
func_cleanup_current_git_clone ()
|
||||
{
|
||||
rm -rf "$path"
|
||||
func_fatal_error "git clone failed"
|
||||
}
|
||||
|
||||
# func_pull SUBDIR GIT_OPTIONS
|
||||
# Implements the 'pull' operation.
|
||||
func_pull ()
|
||||
{
|
||||
func_validate "$1"
|
||||
if test -z "$srcdir"; then
|
||||
case " $subcheckout_names " in *" $1 "*)
|
||||
# It's a subcheckout.
|
||||
if test -d "$path"; then
|
||||
if test -d "$path/.git"; then
|
||||
(cd "$path" && git pull) || func_fatal_error "git operation failed"
|
||||
fi
|
||||
else
|
||||
# The subdir does not yet exist. Create a plain checkout.
|
||||
trap func_cleanup_current_git_clone HUP INT PIPE TERM
|
||||
git clone $2 "$url" "$path" || func_cleanup_current_git_clone
|
||||
trap - HUP INT PIPE TERM
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
case " $submodule_names " in *" $1 "*)
|
||||
# It's a submodule.
|
||||
if test -n "$needs_init"; then
|
||||
# Create a submodule checkout.
|
||||
git submodule init -- "$path" && git submodule update $2 -- "$path" || func_fatal_error "git operation failed"
|
||||
else
|
||||
# See https://stackoverflow.com/questions/1030169/easy-way-to-pull-latest-of-all-git-submodules
|
||||
# https://stackoverflow.com/questions/4611512/is-there-a-way-to-make-git-pull-automatically-update-submodules
|
||||
git submodule update "$path" || func_fatal_error "git operation failed"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
}
|
||||
|
||||
# func_upgrade SUBDIR
|
||||
# Implements the 'upgrade' operation.
|
||||
func_upgrade ()
|
||||
{
|
||||
func_validate "$1"
|
||||
if test -z "$srcdir"; then
|
||||
if test -d "$path"; then
|
||||
case " $subcheckout_names " in *" $1 "*)
|
||||
# It's a subcheckout.
|
||||
if test -d "$path/.git"; then
|
||||
(cd "$path" && git pull) || func_fatal_error "git operation failed"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
case " $submodule_names " in *" $1 "*)
|
||||
# It's a submodule.
|
||||
if test -z "$needs_init"; then
|
||||
(cd "$path" \
|
||||
&& git fetch \
|
||||
&& branch=`git branch --show-current` \
|
||||
&& { test -n "$branch" || branch=HEAD; } \
|
||||
&& sed_escape_dots='s/\([.]\)/\\\1/g' \
|
||||
&& branch_escaped=`echo "$branch" | sed -e "${sed_escape_dots}"` \
|
||||
&& remote=`git branch -r | sed -n -e "s| origin/${branch_escaped} -> ||p"` \
|
||||
&& { test -n "$remote" || remote="origin/${branch}"; } \
|
||||
&& echo "In subdirectory $path: Running \"git merge $remote\"" \
|
||||
&& git merge "$remote"
|
||||
) || func_fatal_error "git operation failed"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
else
|
||||
# The subdir does not yet exist.
|
||||
func_fatal_error "Subdirectory '$path' does not exist yet. Use 'gitsub.sh pull' to create it."
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# func_checkout SUBDIR REVISION
|
||||
# Implements the 'checkout' operation.
|
||||
func_checkout ()
|
||||
{
|
||||
func_validate "$1"
|
||||
if test -z "$srcdir"; then
|
||||
if test -d "$path"; then
|
||||
case " $subcheckout_names " in *" $1 "*)
|
||||
# It's a subcheckout.
|
||||
if test -d "$path/.git"; then
|
||||
(cd "$path" && git checkout "$2") || func_fatal_error "git operation failed"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
case " $submodule_names " in *" $1 "*)
|
||||
# It's a submodule.
|
||||
if test -z "$needs_init"; then
|
||||
(cd "$path" && git checkout "$2") || func_fatal_error "git operation failed"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
else
|
||||
# The subdir does not yet exist.
|
||||
func_fatal_error "Subdirectory '$path' does not exist yet. Use 'gitsub.sh pull' to create it."
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
case "$mode" in
|
||||
pull )
|
||||
git_options=""
|
||||
while test $# -gt 0; do
|
||||
case "$1" in
|
||||
--reference=* | --depth=* | --recursive)
|
||||
git_options="$git_options $1"
|
||||
shift
|
||||
;;
|
||||
--reference | --depth)
|
||||
git_options="$git_options $1 $2"
|
||||
shift; shift
|
||||
;;
|
||||
*)
|
||||
break
|
||||
;;
|
||||
esac
|
||||
done
|
||||
if test $# -gt 1; then
|
||||
echo "gitsub.sh: too many arguments in '$mode' mode" 1>&2
|
||||
echo "Try 'gitsub.sh --help' for more information." 1>&2
|
||||
exit 1
|
||||
fi
|
||||
if test $# = 0; then
|
||||
for sub in $subcheckout_names $submodule_names; do
|
||||
func_pull "$sub" "$git_options"
|
||||
done
|
||||
else
|
||||
valid=false
|
||||
for sub in $subcheckout_names $submodule_names; do
|
||||
if test "$sub" = "$1"; then
|
||||
valid=true
|
||||
fi
|
||||
done
|
||||
if $valid; then
|
||||
func_pull "$1" "$git_options"
|
||||
else
|
||||
func_fatal_error "Subdir '$1' is not configured as a subcheckout or a submodule in .gitmodules"
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
|
||||
upgrade )
|
||||
if test $# = 0; then
|
||||
for sub in $subcheckout_names $submodule_names; do
|
||||
func_upgrade "$sub"
|
||||
done
|
||||
else
|
||||
valid=false
|
||||
for sub in $subcheckout_names $submodule_names; do
|
||||
if test "$sub" = "$1"; then
|
||||
valid=true
|
||||
fi
|
||||
done
|
||||
if $valid; then
|
||||
func_upgrade "$1"
|
||||
else
|
||||
func_fatal_error "Subdir '$1' is not configured as a subcheckout or a submodule in .gitmodules"
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
|
||||
checkout )
|
||||
valid=false
|
||||
for sub in $subcheckout_names $submodule_names; do
|
||||
if test "$sub" = "$1"; then
|
||||
valid=true
|
||||
fi
|
||||
done
|
||||
if $valid; then
|
||||
func_checkout "$1" "$2"
|
||||
else
|
||||
func_fatal_error "Subdir '$1' is not configured as a subcheckout or a submodule in .gitmodules"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
Reference in New Issue
Block a user