From: Bruno Haible Date: Tue, 2 Apr 2019 15:51:13 +0000 (+0200) Subject: build: Separate git operations from build operations. X-Git-Tag: v0.20~91 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=1ed481e88ff4bd8c9ab5fab9bfee762b62b82ae2;p=thirdparty%2Fgettext.git build: Separate git operations from build operations. * gitsub.sh: New file, from gnulib. * autogen.sh: Remove all git operations and the --no-git option. Look at the GNULIB_SRCDIR environment variable. Ignore the GNULIB_TOOL environment variable. * HACKING: Explain when to use gitsub.sh. --- diff --git a/HACKING b/HACKING index 7de67f7b8..a2bc2884e 100644 --- a/HACKING +++ b/HACKING @@ -192,15 +192,21 @@ $ sudo localedef -i tr_TR -f UTF-8 tr_TR.UTF-8 $ sudo localedef -i zh_CN -f GB18030 zh_CN.GB18030 -Building off the git repository +Building off the Git repository =============================== -Access to the git repository is described at - https://savannah.gnu.org/git/?group=gettext +Access to the Git repository is described at +https://savannah.gnu.org/git/?group=gettext . -After fetching the sources from the repository, peek at the comments in -autogen.sh, then run "./autogen.sh"; then you can proceed with "./configure" -as usual. +After fetching the sources from the Git repository, peek at the comments in +autogen.sh, then run + ./gitsub.sh pull + ./autogen.sh +Then you can proceed with "./configure" as usual. + +Each time you want to update the source, do not only "git pull". Instead do + git pull && ./gitsub.sh pull + ./autogen.sh Submitting patches diff --git a/autogen.sh b/autogen.sh index f67c05ed8..4c04f7f32 100755 --- a/autogen.sh +++ b/autogen.sh @@ -21,401 +21,333 @@ # - Autoconf # - Automake >= 1.13 # - Wget -# - Git # - XZ Utils -# -# By default, it fetches Gnulib as a git submodule. If you already -# have a local copy of Gnulib, you can avoid extra network traffic by -# setting the GNULIB_SRCDIR environment variable pointing to the path. +# If not used from a released tarball, it also requires either +# - the GNULIB_SRCDIR environment variable pointing to a gnulib checkout, or +# - a preceding invocation of './gitsub.sh pull'. # # In addition, it fetches the archive.dir.tar.gz file, which contains # data files used by the autopoint program. If you already have the # file, place it under gettext-tools/misc, before running this script. # -# Usage: ./autogen.sh [--skip-gnulib] [--no-git] -# -# Usage after a git clone: ./autogen.sh -# Usage from a released tarball: ./autogen.sh --skip-gnulib -# This does not use a gnulib checkout. +# Usage: ./autogen.sh [--skip-gnulib] # Nuisances. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH skip_gnulib=false -# Use git to update gnulib sources -use_git=true - while :; do case "$1" in --skip-gnulib) skip_gnulib=true; shift;; - --no-git) use_git=false; shift;; *) break ;; esac done -if ! $use_git && ! test -d "$GNULIB_SRCDIR"; then - echo "error: --no-git requires GNULIB_SRCDIR to be set" 1>&2 - exit 1 -fi - -cleanup_gnulib() { - status=$? - rm -fr "$gnulib_path" - exit $status -} - -git_modules_config () { - test -f .gitmodules && git config --file .gitmodules "$@" -} - -gnulib_path=$(git_modules_config submodule.gnulib.path) -test -z "$gnulib_path" && gnulib_path=gnulib - # The tests in gettext-tools/tests are not meant to be executable, because # they have a TESTS_ENVIRONMENT that specifies the shell explicitly. if ! $skip_gnulib; then - # Get gnulib files. - case ${GNULIB_SRCDIR--} in - -) - if git_modules_config submodule.gnulib.url >/dev/null; then - echo "$0: getting gnulib files..." - git submodule init || exit $? - git submodule update || exit $? - - elif [ ! -d "$gnulib_path" ]; then - echo "$0: getting gnulib files..." - - trap cleanup_gnulib 1 2 13 15 - - shallow= - git clone -h 2>&1 | grep -- --depth > /dev/null && shallow='--depth 2' - git clone $shallow git://git.sv.gnu.org/gnulib "$gnulib_path" || - cleanup_gnulib - - trap - 1 2 13 15 - fi - GNULIB_SRCDIR=$gnulib_path - ;; - *) - # Use GNULIB_SRCDIR as a reference. - if $use_git && test -d "$GNULIB_SRCDIR"/.git && \ - git_modules_config submodule.gnulib.url >/dev/null; then - echo "$0: getting gnulib files..." - if git submodule -h | grep -- --reference > /dev/null; then - # Prefer the one-liner available in git 1.6.4 or newer. - git submodule update --init --reference "$GNULIB_SRCDIR" \ - "$gnulib_path" || exit $? - else - # This fallback allows at least git 1.5.5. - if test -f "$gnulib_path"/gnulib-tool; then - # Since file already exists, assume submodule init already complete. - git submodule update || exit $? - else - # Older git can't clone into an empty directory. - rmdir "$gnulib_path" 2>/dev/null - git clone --reference "$GNULIB_SRCDIR" \ - "$(git_modules_config submodule.gnulib.url)" "$gnulib_path" \ - && git submodule init && git submodule update \ - || exit $? - fi - fi - GNULIB_SRCDIR=$gnulib_path - fi - ;; - esac - # Now it should contain a gnulib-tool. - if test -f "$GNULIB_SRCDIR"/gnulib-tool; then - GNULIB_TOOL="$GNULIB_SRCDIR"/gnulib-tool + if test -n "$GNULIB_SRCDIR"; then + test -d "$GNULIB_SRCDIR" || { + echo "*** GNULIB_SRCDIR is set but does not point to an existing directory." 1>&2 + exit 1 + } else - echo "** warning: gnulib-tool not found" 1>&2 - fi - # Skip the gnulib-tool step if gnulib-tool was not found. - if test -n "$GNULIB_TOOL"; then - # In gettext-runtime: - GNULIB_MODULES_RUNTIME_FOR_SRC=' - atexit - basename - binary-io - closeout - error - getopt-gnu - gettext-h - havelib - memmove - progname - propername - relocatable-prog - setlocale - sigpipe - stdbool - stdio - stdlib - strtoul - unistd - unlocked-io - xalloc - ' - GNULIB_MODULES_RUNTIME_OTHER=' - gettext-runtime-misc - ansi-c++-opt - csharpcomp-script - java - javacomp-script - ' - $GNULIB_TOOL --dir=gettext-runtime --lib=libgrt --source-base=gnulib-lib --m4-base=gnulib-m4 --no-libtool --local-dir=gnulib-local --local-symlink \ - --import $GNULIB_MODULES_RUNTIME_FOR_SRC $GNULIB_MODULES_RUNTIME_OTHER || exit $? - # In gettext-runtime/libasprintf: - GNULIB_MODULES_LIBASPRINTF=' - alloca - errno - verify - xsize - ' - GNULIB_MODULES_LIBASPRINTF_OTHER=' - ' - $GNULIB_TOOL --dir=gettext-runtime/libasprintf --source-base=. --m4-base=gnulib-m4 --lgpl=2 --makefile-name=Makefile.gnulib --libtool --local-dir=gnulib-local --local-symlink \ - --import $GNULIB_MODULES_LIBASPRINTF $GNULIB_MODULES_LIBASPRINTF_OTHER || exit $? - $GNULIB_TOOL --copy-file m4/intmax_t.m4 gettext-runtime/libasprintf/gnulib-m4/intmax_t.m4 || exit $? - $GNULIB_TOOL --copy-file m4/wchar_t.m4 gettext-runtime/libasprintf/gnulib-m4/wchar_t.m4 || exit $? - $GNULIB_TOOL --copy-file m4/wint_t.m4 gettext-runtime/libasprintf/gnulib-m4/wint_t.m4 || exit $? - # In gettext-tools: - GNULIB_MODULES_TOOLS_FOR_SRC=' - alloca-opt - atexit - backupfile - basename - binary-io - bison-i18n - byteswap - c-ctype - c-strcase - c-strcasestr - c-strstr - clean-temp - closedir - closeout - copy-file - csharpcomp - csharpexec - error - error-progname - execute - fd-ostream - file-ostream - filename - findprog - fnmatch - fopen - fstrcmp - full-write - fwriteerror - gcd - getline - getopt-gnu - gettext - gettext-h - hash - html-styled-ostream - iconv - javacomp - javaexec - libunistring-optional - localcharset - locale - localename - lock - memchr - memmove - memset - minmax - mkdir - obstack - open - opendir - openmp - ostream - pipe-filter-ii - progname - propername - read-file - readdir - relocatable-prog - relocatable-script - setlocale - sh-quote - sigpipe - sigprocmask - spawn-pipe - stdbool - stdio - stdlib - stpcpy - stpncpy - strchrnul - strcspn - strerror - strpbrk - strtol - strtoul - styled-ostream - sys_select - sys_stat - sys_time - term-styled-ostream - trim - unictype/ctype-space - unilbrk/ulc-width-linebreaks - uniname/uniname - unistd - unistr/u8-check - unistr/u8-mbtouc - unistr/u8-mbtoucr - unistr/u8-uctomb - unistr/u16-mbtouc - uniwidth/width - unlocked-io - vasprintf - wait-process - write - xalloc - xconcat-filename - xerror - xmalloca - xmemdup0 - xsetenv - xstriconv - xstriconveh - xvasprintf - ' - # Common dependencies of GNULIB_MODULES_TOOLS_FOR_SRC and GNULIB_MODULES_TOOLS_FOR_LIBGREP. - GNULIB_MODULES_TOOLS_FOR_SRC_COMMON_DEPENDENCIES=' - alloca-opt - extensions - gettext-h - include_next - locale - localcharset - malloc-posix - mbrtowc - mbsinit - multiarch - snippet/arg-nonnull - snippet/c++defs - snippet/warn-on-use - ssize_t - stdbool - stddef - stdint - stdlib - streq - unistd - verify - wchar - wctype-h - ' - GNULIB_MODULES_TOOLS_OTHER=' - gettext-tools-misc - ansi-c++-opt - csharpcomp-script - csharpexec-script - java - javacomp-script - javaexec-script - stdint - ' - GNULIB_MODULES_TOOLS_LIBUNISTRING_TESTS=' - unilbrk/u8-possible-linebreaks-tests - unilbrk/ulc-width-linebreaks-tests - unistr/u8-mbtouc-tests - unistr/u8-mbtouc-unsafe-tests - uniwidth/width-tests - ' - $GNULIB_TOOL --dir=gettext-tools --lib=libgettextlib --source-base=gnulib-lib --m4-base=gnulib-m4 --tests-base=gnulib-tests --makefile-name=Makefile.gnulib --libtool --with-tests --local-dir=gnulib-local --local-symlink \ - --import --avoid=hash-tests --avoid=fdutimensat-tests --avoid=futimens-tests --avoid=utime-tests --avoid=utimens-tests --avoid=utimensat-tests \ - `for m in $GNULIB_MODULES_TOOLS_LIBUNISTRING_TESTS; do echo --avoid=$m; done` $GNULIB_MODULES_TOOLS_FOR_SRC $GNULIB_MODULES_TOOLS_FOR_SRC_COMMON_DEPENDENCIES $GNULIB_MODULES_TOOLS_OTHER || exit $? - # In gettext-tools/libgrep: - GNULIB_MODULES_TOOLS_FOR_LIBGREP=' - mbrlen - regex - ' - $GNULIB_TOOL --dir=gettext-tools --macro-prefix=grgl --lib=libgrep --source-base=libgrep --m4-base=libgrep/gnulib-m4 --witness-c-macro=IN_GETTEXT_TOOLS_LIBGREP --makefile-name=Makefile.gnulib --local-dir=gnulib-local --local-symlink \ - --import `for m in $GNULIB_MODULES_TOOLS_FOR_SRC_COMMON_DEPENDENCIES; do if test \`$GNULIB_TOOL --extract-applicability $m\` != all; then echo --avoid=$m; fi; done` $GNULIB_MODULES_TOOLS_FOR_LIBGREP || exit $? - # In gettext-tools/libgettextpo: - # This is a subset of the GNULIB_MODULES_FOR_SRC. - GNULIB_MODULES_LIBGETTEXTPO=' - basename - close - c-ctype - c-strcase - c-strstr - error - error-progname - file-ostream - filename - fopen - fstrcmp - fwriteerror - gcd - getline - gettext-h - hash - iconv - libunistring-optional - markup - minmax - open - ostream - progname - relocatable-lib - sigpipe - stdbool - stdio - stdlib - stpcpy - stpncpy - strchrnul - strerror - unictype/ctype-space - unilbrk/ulc-width-linebreaks - unistr/u8-mbtouc - unistr/u8-mbtoucr - unistr/u8-uctomb - unistr/u16-mbtouc - uniwidth/width - unlocked-io - vasprintf - xalloc - xconcat-filename - xmalloca - xerror - xstriconv - xvasprintf - ' - # Module 'realloc-posix' is enabled in gettext-tools/config.status, because - # it occurs as dependency of some module ('read-file') in - # GNULIB_MODULES_TOOLS_FOR_SRC. Therefore on mingw, libgettextpo/stdlib.h - # contains '#define realloc rpl_realloc'. Therefore we need to include - # realloc.lo in libgettextpo.la. - GNULIB_MODULES_LIBGETTEXTPO_OTHER=' - realloc-posix - ' - $GNULIB_TOOL --dir=gettext-tools --source-base=libgettextpo --m4-base=libgettextpo/gnulib-m4 --macro-prefix=gtpo --makefile-name=Makefile.gnulib --libtool --local-dir=gnulib-local --local-symlink \ - --import $GNULIB_MODULES_LIBGETTEXTPO $GNULIB_MODULES_LIBGETTEXTPO_OTHER || exit $? - # Overwrite older versions of .m4 files with the up-to-date version. - cp gettext-runtime/m4/gettext.m4 gettext-tools/gnulib-m4/gettext.m4 - cp gettext-runtime/m4/intl.m4 gettext-tools/gnulib-m4/intl.m4 - # Import build tools. We use --copy-file to avoid directory creation. - $GNULIB_TOOL --copy-file tests/init.sh gettext-tools || exit $? - $GNULIB_TOOL --copy-file build-aux/git-version-gen || exit $? - $GNULIB_TOOL --copy-file build-aux/gitlog-to-changelog || exit $? - $GNULIB_TOOL --copy-file build-aux/update-copyright || exit $? - $GNULIB_TOOL --copy-file build-aux/useless-if-before-free || exit $? - $GNULIB_TOOL --copy-file build-aux/vc-list-files || exit $? - $GNULIB_TOOL --copy-file top/GNUmakefile . || exit $? - $GNULIB_TOOL --copy-file top/maint.mk . || exit $? + GNULIB_SRCDIR=`pwd`/gnulib + test -d "$GNULIB_SRCDIR" || { + echo "*** Subdirectory 'gnulib' does not yet exist. Use './gitsub.sh pull' to create it, or set the environment variable GNULIB_SRCDIR." 1>&2 + exit 1 + } fi + # Now it should contain a gnulib-tool. + GNULIB_TOOL="$GNULIB_SRCDIR/gnulib-tool" + test -f "$GNULIB_TOOL" || { + echo "*** gnulib-tool not found." 1>&2 + exit 1 + } + # In gettext-runtime: + GNULIB_MODULES_RUNTIME_FOR_SRC=' + atexit + basename + binary-io + closeout + error + getopt-gnu + gettext-h + havelib + memmove + progname + propername + relocatable-prog + setlocale + sigpipe + stdbool + stdio + stdlib + strtoul + unistd + unlocked-io + xalloc + ' + GNULIB_MODULES_RUNTIME_OTHER=' + gettext-runtime-misc + ansi-c++-opt + csharpcomp-script + java + javacomp-script + ' + $GNULIB_TOOL --dir=gettext-runtime --lib=libgrt --source-base=gnulib-lib --m4-base=gnulib-m4 --no-libtool --local-dir=gnulib-local --local-symlink \ + --import $GNULIB_MODULES_RUNTIME_FOR_SRC $GNULIB_MODULES_RUNTIME_OTHER || exit $? + # In gettext-runtime/libasprintf: + GNULIB_MODULES_LIBASPRINTF=' + alloca + errno + verify + xsize + ' + GNULIB_MODULES_LIBASPRINTF_OTHER=' + ' + $GNULIB_TOOL --dir=gettext-runtime/libasprintf --source-base=. --m4-base=gnulib-m4 --lgpl=2 --makefile-name=Makefile.gnulib --libtool --local-dir=gnulib-local --local-symlink \ + --import $GNULIB_MODULES_LIBASPRINTF $GNULIB_MODULES_LIBASPRINTF_OTHER || exit $? + $GNULIB_TOOL --copy-file m4/intmax_t.m4 gettext-runtime/libasprintf/gnulib-m4/intmax_t.m4 || exit $? + $GNULIB_TOOL --copy-file m4/wchar_t.m4 gettext-runtime/libasprintf/gnulib-m4/wchar_t.m4 || exit $? + $GNULIB_TOOL --copy-file m4/wint_t.m4 gettext-runtime/libasprintf/gnulib-m4/wint_t.m4 || exit $? + # In gettext-tools: + GNULIB_MODULES_TOOLS_FOR_SRC=' + alloca-opt + atexit + backupfile + basename + binary-io + bison-i18n + byteswap + c-ctype + c-strcase + c-strcasestr + c-strstr + clean-temp + closedir + closeout + copy-file + csharpcomp + csharpexec + error + error-progname + execute + fd-ostream + file-ostream + filename + findprog + fnmatch + fopen + fstrcmp + full-write + fwriteerror + gcd + getline + getopt-gnu + gettext + gettext-h + hash + html-styled-ostream + iconv + javacomp + javaexec + libunistring-optional + localcharset + locale + localename + lock + memchr + memmove + memset + minmax + mkdir + obstack + open + opendir + openmp + ostream + pipe-filter-ii + progname + propername + read-file + readdir + relocatable-prog + relocatable-script + setlocale + sh-quote + sigpipe + sigprocmask + spawn-pipe + stdbool + stdio + stdlib + stpcpy + stpncpy + strchrnul + strcspn + strerror + strpbrk + strtol + strtoul + styled-ostream + sys_select + sys_stat + sys_time + term-styled-ostream + trim + unictype/ctype-space + unilbrk/ulc-width-linebreaks + uniname/uniname + unistd + unistr/u8-check + unistr/u8-mbtouc + unistr/u8-mbtoucr + unistr/u8-uctomb + unistr/u16-mbtouc + uniwidth/width + unlocked-io + vasprintf + wait-process + write + xalloc + xconcat-filename + xerror + xmalloca + xmemdup0 + xsetenv + xstriconv + xstriconveh + xvasprintf + ' + # Common dependencies of GNULIB_MODULES_TOOLS_FOR_SRC and GNULIB_MODULES_TOOLS_FOR_LIBGREP. + GNULIB_MODULES_TOOLS_FOR_SRC_COMMON_DEPENDENCIES=' + alloca-opt + extensions + gettext-h + include_next + locale + localcharset + malloc-posix + mbrtowc + mbsinit + multiarch + snippet/arg-nonnull + snippet/c++defs + snippet/warn-on-use + ssize_t + stdbool + stddef + stdint + stdlib + streq + unistd + verify + wchar + wctype-h + ' + GNULIB_MODULES_TOOLS_OTHER=' + gettext-tools-misc + ansi-c++-opt + csharpcomp-script + csharpexec-script + java + javacomp-script + javaexec-script + stdint + ' + GNULIB_MODULES_TOOLS_LIBUNISTRING_TESTS=' + unilbrk/u8-possible-linebreaks-tests + unilbrk/ulc-width-linebreaks-tests + unistr/u8-mbtouc-tests + unistr/u8-mbtouc-unsafe-tests + uniwidth/width-tests + ' + $GNULIB_TOOL --dir=gettext-tools --lib=libgettextlib --source-base=gnulib-lib --m4-base=gnulib-m4 --tests-base=gnulib-tests --makefile-name=Makefile.gnulib --libtool --with-tests --local-dir=gnulib-local --local-symlink \ + --import --avoid=hash-tests --avoid=fdutimensat-tests --avoid=futimens-tests --avoid=utime-tests --avoid=utimens-tests --avoid=utimensat-tests \ + `for m in $GNULIB_MODULES_TOOLS_LIBUNISTRING_TESTS; do echo --avoid=$m; done` $GNULIB_MODULES_TOOLS_FOR_SRC $GNULIB_MODULES_TOOLS_FOR_SRC_COMMON_DEPENDENCIES $GNULIB_MODULES_TOOLS_OTHER || exit $? + # In gettext-tools/libgrep: + GNULIB_MODULES_TOOLS_FOR_LIBGREP=' + mbrlen + regex + ' + $GNULIB_TOOL --dir=gettext-tools --macro-prefix=grgl --lib=libgrep --source-base=libgrep --m4-base=libgrep/gnulib-m4 --witness-c-macro=IN_GETTEXT_TOOLS_LIBGREP --makefile-name=Makefile.gnulib --local-dir=gnulib-local --local-symlink \ + --import `for m in $GNULIB_MODULES_TOOLS_FOR_SRC_COMMON_DEPENDENCIES; do if test \`$GNULIB_TOOL --extract-applicability $m\` != all; then echo --avoid=$m; fi; done` $GNULIB_MODULES_TOOLS_FOR_LIBGREP || exit $? + # In gettext-tools/libgettextpo: + # This is a subset of the GNULIB_MODULES_FOR_SRC. + GNULIB_MODULES_LIBGETTEXTPO=' + basename + close + c-ctype + c-strcase + c-strstr + error + error-progname + file-ostream + filename + fopen + fstrcmp + fwriteerror + gcd + getline + gettext-h + hash + iconv + libunistring-optional + markup + minmax + open + ostream + progname + relocatable-lib + sigpipe + stdbool + stdio + stdlib + stpcpy + stpncpy + strchrnul + strerror + unictype/ctype-space + unilbrk/ulc-width-linebreaks + unistr/u8-mbtouc + unistr/u8-mbtoucr + unistr/u8-uctomb + unistr/u16-mbtouc + uniwidth/width + unlocked-io + vasprintf + xalloc + xconcat-filename + xmalloca + xerror + xstriconv + xvasprintf + ' + # Module 'realloc-posix' is enabled in gettext-tools/config.status, because + # it occurs as dependency of some module ('read-file') in + # GNULIB_MODULES_TOOLS_FOR_SRC. Therefore on mingw, libgettextpo/stdlib.h + # contains '#define realloc rpl_realloc'. Therefore we need to include + # realloc.lo in libgettextpo.la. + GNULIB_MODULES_LIBGETTEXTPO_OTHER=' + realloc-posix + ' + $GNULIB_TOOL --dir=gettext-tools --source-base=libgettextpo --m4-base=libgettextpo/gnulib-m4 --macro-prefix=gtpo --makefile-name=Makefile.gnulib --libtool --local-dir=gnulib-local --local-symlink \ + --import $GNULIB_MODULES_LIBGETTEXTPO $GNULIB_MODULES_LIBGETTEXTPO_OTHER || exit $? + # Overwrite older versions of .m4 files with the up-to-date version. + cp gettext-runtime/m4/gettext.m4 gettext-tools/gnulib-m4/gettext.m4 + cp gettext-runtime/m4/intl.m4 gettext-tools/gnulib-m4/intl.m4 + # Import build tools. We use --copy-file to avoid directory creation. + $GNULIB_TOOL --copy-file tests/init.sh gettext-tools || exit $? + $GNULIB_TOOL --copy-file build-aux/git-version-gen || exit $? + $GNULIB_TOOL --copy-file build-aux/gitlog-to-changelog || exit $? + $GNULIB_TOOL --copy-file build-aux/update-copyright || exit $? + $GNULIB_TOOL --copy-file build-aux/useless-if-before-free || exit $? + $GNULIB_TOOL --copy-file build-aux/vc-list-files || exit $? + $GNULIB_TOOL --copy-file top/GNUmakefile . || exit $? + $GNULIB_TOOL --copy-file top/maint.mk . || exit $? fi # Fetch config.guess, config.sub. diff --git a/gitsub.sh b/gitsub.sh new file mode 100755 index 000000000..966519806 --- /dev/null +++ b/gitsub.sh @@ -0,0 +1,483 @@ +#! /bin/sh +# +# Copyright (C) 2019 Free Software Foundation, Inc. +# Written by Bruno Haible , 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 . + +# 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 _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 periodially 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 +# . For example: +# +# [submodule "gnulib"] +# url = git://git.savannah.gnu.org/gnulib.git +# path = gnulib +# +# You don't add this piece of configuration to .gitmodules manually. Instead, +# you would invoke +# $ git submodule add --name "gnulib" -- git://git.savannah.gnu.org/gnulib.git gnulib +# +# * The subdirectories that are not git submodules, in a similar syntax. For +# example: +# +# [subcheckout "gnulib"] +# url = git://git.savannah.gnu.org/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 [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 _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 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 _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 _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 +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 $# = 2 && 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 +# 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 1 2 13 15 + git clone "$url" "$path" || func_cleanup_current_git_clone + trap - 1 2 13 15 + 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 -- "$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 && git merge origin/master) || 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 ) + if test $# = 0; then + for sub in $subcheckout_names $submodule_names; do + func_pull "$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_pull "$1" + 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