From f6a0ad4be71a337e2e8787788a4b467204a4c4bb Mon Sep 17 00:00:00 2001 From: Dan Jacques Date: Tue, 10 Apr 2018 11:05:42 -0400 Subject: [PATCH 1/7] Makefile: generate Perl header from template file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, the generated Perl script headers are emitted by commands in the Makefile. This mechanism restricts options to introduce alternative header content, needed by Perl runtime prefix support, and obscures the origin of the Perl script header. Change the Makefile to generate a header by processing a template file and move the header content into the "perl/" subdirectory. The generated header content will now be stored in the "GIT-PERL-HEADER" file. This allows the content of the Perl header to be controlled by changing the path of the template in the Makefile. Signed-off-by: Dan Jacques Thanks-to: Ævar Arnfjörð Bjarmason Thanks-to: Johannes Schindelin Signed-off-by: Junio C Hamano --- .gitignore | 1 + Makefile | 27 +++++++++++-------- .../header_templates/fixed_prefix.template.pl | 1 + 3 files changed, 18 insertions(+), 11 deletions(-) create mode 100644 perl/header_templates/fixed_prefix.template.pl diff --git a/.gitignore b/.gitignore index 833ef3b0b7..89bd7bd8a7 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ /GIT-LDFLAGS /GIT-PREFIX /GIT-PERL-DEFINES +/GIT-PERL-HEADER /GIT-PYTHON-VARS /GIT-SCRIPT-DEFINES /GIT-USER-AGENT diff --git a/Makefile b/Makefile index f181687250..7c316b963b 100644 --- a/Makefile +++ b/Makefile @@ -1987,20 +1987,15 @@ git.res: git.rc GIT-VERSION-FILE $(SCRIPT_PERL_GEN): GIT-BUILD-OPTIONS ifndef NO_PERL -$(SCRIPT_PERL_GEN): - +PERL_HEADER_TEMPLATE = perl/header_templates/fixed_prefix.template.pl PERL_DEFINES = $(PERL_PATH_SQ):$(PERLLIB_EXTRA_SQ):$(perllibdir_SQ) -$(SCRIPT_PERL_GEN): % : %.perl GIT-PERL-DEFINES GIT-VERSION-FILE + +$(SCRIPT_PERL_GEN): % : %.perl GIT-PERL-DEFINES GIT-PERL-HEADER GIT-VERSION-FILE $(QUIET_GEN)$(RM) $@ $@+ && \ - INSTLIBDIR='$(perllibdir_SQ)' && \ - INSTLIBDIR_EXTRA='$(PERLLIB_EXTRA_SQ)' && \ - INSTLIBDIR="$$INSTLIBDIR$${INSTLIBDIR_EXTRA:+:$$INSTLIBDIR_EXTRA}" && \ sed -e '1{' \ -e ' s|#!.*perl|#!$(PERL_PATH_SQ)|' \ - -e ' h' \ - -e ' s=.*=use lib (split(/$(pathsep)/, $$ENV{GITPERLLIB} || "'"$$INSTLIBDIR"'"));=' \ - -e ' H' \ - -e ' x' \ + -e ' rGIT-PERL-HEADER' \ + -e ' G' \ -e '}' \ -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \ $< >$@+ && \ @@ -2014,6 +2009,16 @@ GIT-PERL-DEFINES: FORCE echo "$$FLAGS" >$@; \ fi +GIT-PERL-HEADER: $(PERL_HEADER_TEMPLATE) GIT-PERL-DEFINES Makefile + $(QUIET_GEN)$(RM) $@ && \ + INSTLIBDIR='$(perllibdir_SQ)' && \ + INSTLIBDIR_EXTRA='$(PERLLIB_EXTRA_SQ)' && \ + INSTLIBDIR="$$INSTLIBDIR$${INSTLIBDIR_EXTRA:+:$$INSTLIBDIR_EXTRA}" && \ + sed -e 's=@@PATHSEP@@=$(pathsep)=g' \ + -e 's=@@INSTLIBDIR@@='$$INSTLIBDIR'=g' \ + -e 's=@@PERLLIBDIR@@='$(perllibdir_SQ)'=g' \ + $< >$@+ && \ + mv $@+ $@ .PHONY: gitweb gitweb: @@ -2793,7 +2798,7 @@ ifndef NO_TCLTK endif $(RM) GIT-VERSION-FILE GIT-CFLAGS GIT-LDFLAGS GIT-BUILD-OPTIONS $(RM) GIT-USER-AGENT GIT-PREFIX - $(RM) GIT-SCRIPT-DEFINES GIT-PERL-DEFINES GIT-PYTHON-VARS + $(RM) GIT-SCRIPT-DEFINES GIT-PERL-DEFINES GIT-PERL-HEADER GIT-PYTHON-VARS .PHONY: all install profile-clean clean strip .PHONY: shell_compatibility_test please_set_SHELL_PATH_to_a_more_modern_shell diff --git a/perl/header_templates/fixed_prefix.template.pl b/perl/header_templates/fixed_prefix.template.pl new file mode 100644 index 0000000000..857b4391a4 --- /dev/null +++ b/perl/header_templates/fixed_prefix.template.pl @@ -0,0 +1 @@ +use lib (split(/@@PATHSEP@@/, $ENV{GITPERLLIB} || '@@INSTLIBDIR@@')); From 07d90eadb50ee687a7313186b654975b0944a74e Mon Sep 17 00:00:00 2001 From: Dan Jacques Date: Tue, 10 Apr 2018 11:05:43 -0400 Subject: [PATCH 2/7] Makefile: add Perl runtime prefix support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Broaden the RUNTIME_PREFIX flag to configure Git's Perl scripts to locate the Git installation's Perl support libraries by resolving against the script's path, rather than hard-coding that path at build-time. Hard-coding at build time worked on previous RUNTIME_PREFIX configurations (i.e., Windows) because the Perl scripts were run within a virtual filesystem whose paths were consistent regardless of the location of the actual installation. This will no longer be the case for non-Windows RUNTIME_PREFIX users. When enabled, RUNTIME_PREFIX now requires Perl's system paths to be expressed relative to a common installation directory in the Makefile, and uses that relationship to locate support files based on the known starting point of the script being executed, much like RUNTIME_PREFIX does for the Git binary. This change enables Git's Perl scripts to work when their Git installation is relocated or moved to another system, even when they are not in a virtual filesystem environment. Signed-off-by: Dan Jacques Thanks-to: Ævar Arnfjörð Bjarmason Thanks-to: Johannes Schindelin Signed-off-by: Junio C Hamano --- Makefile | 65 ++++++++++++++++++- perl/Git/I18N.pm | 2 +- .../runtime_prefix.template.pl | 42 ++++++++++++ 3 files changed, 107 insertions(+), 2 deletions(-) create mode 100644 perl/header_templates/runtime_prefix.template.pl diff --git a/Makefile b/Makefile index 7c316b963b..55684d1735 100644 --- a/Makefile +++ b/Makefile @@ -441,6 +441,13 @@ all:: # # When cross-compiling, define HOST_CPU as the canonical name of the CPU on # which the built Git will run (for instance "x86_64"). +# +# Define RUNTIME_PREFIX to configure Git to resolve its ancillary tooling and +# support files relative to the location of the runtime binary, rather than +# hard-coding them into the binary. Git installations built with RUNTIME_PREFIX +# can be moved to arbitrary filesystem locations. RUNTIME_PREFIX also causes +# Perl scripts to use a modified entry point header allowing them to resolve +# support files at runtime. GIT-VERSION-FILE: FORCE @$(SHELL_PATH) ./GIT-VERSION-GEN @@ -478,6 +485,8 @@ ARFLAGS = rcs # mandir # infodir # htmldir +# localedir +# perllibdir # This can help installing the suite in a relocatable way. prefix = $(HOME) @@ -502,7 +511,9 @@ bindir_relative = $(patsubst $(prefix)/%,%,$(bindir)) mandir_relative = $(patsubst $(prefix)/%,%,$(mandir)) infodir_relative = $(patsubst $(prefix)/%,%,$(infodir)) gitexecdir_relative = $(patsubst $(prefix)/%,%,$(gitexecdir)) +localedir_relative = $(patsubst $(prefix)/%,%,$(localedir)) htmldir_relative = $(patsubst $(prefix)/%,%,$(htmldir)) +perllibdir_relative = $(patsubst $(prefix)/%,%,$(perllibdir)) export prefix bindir sharedir sysconfdir gitwebdir perllibdir localedir @@ -1751,11 +1762,13 @@ mandir_relative_SQ = $(subst ','\'',$(mandir_relative)) infodir_relative_SQ = $(subst ','\'',$(infodir_relative)) perllibdir_SQ = $(subst ','\'',$(perllibdir)) localedir_SQ = $(subst ','\'',$(localedir)) +localedir_relative_SQ = $(subst ','\'',$(localedir_relative)) gitexecdir_SQ = $(subst ','\'',$(gitexecdir)) gitexecdir_relative_SQ = $(subst ','\'',$(gitexecdir_relative)) template_dir_SQ = $(subst ','\'',$(template_dir)) htmldir_relative_SQ = $(subst ','\'',$(htmldir_relative)) prefix_SQ = $(subst ','\'',$(prefix)) +perllibdir_relative_SQ = $(subst ','\'',$(perllibdir_relative)) gitwebdir_SQ = $(subst ','\'',$(gitwebdir)) SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH)) @@ -1766,6 +1779,31 @@ TCLTK_PATH_SQ = $(subst ','\'',$(TCLTK_PATH)) DIFF_SQ = $(subst ','\'',$(DIFF)) PERLLIB_EXTRA_SQ = $(subst ','\'',$(PERLLIB_EXTRA)) +# RUNTIME_PREFIX's resolution logic requires resource paths to be expressed +# relative to each other and share an installation path. +# +# This is a dependency in: +# - Git's binary RUNTIME_PREFIX logic in (see "exec_cmd.c"). +# - The runtime prefix Perl header (see +# "perl/header_templates/runtime_prefix.template.pl"). +ifdef RUNTIME_PREFIX + +ifneq ($(filter /%,$(firstword $(gitexecdir_relative))),) +$(error RUNTIME_PREFIX requires a relative gitexecdir, not: $(gitexecdir)) +endif + +ifneq ($(filter /%,$(firstword $(localedir_relative))),) +$(error RUNTIME_PREFIX requires a relative localedir, not: $(localedir)) +endif + +ifndef NO_PERL +ifneq ($(filter /%,$(firstword $(perllibdir_relative))),) +$(error RUNTIME_PREFIX requires a relative perllibdir, not: $(perllibdir)) +endif +endif + +endif + # We must filter out any object files from $(GITLIBS), # as it is typically used like: # @@ -1986,10 +2024,31 @@ git.res: git.rc GIT-VERSION-FILE # This makes sure we depend on the NO_PERL setting itself. $(SCRIPT_PERL_GEN): GIT-BUILD-OPTIONS +# Used for substitution in Perl modules. Disabled when using RUNTIME_PREFIX +# since the locale directory is injected. +perl_localedir_SQ = $(localedir_SQ) + ifndef NO_PERL PERL_HEADER_TEMPLATE = perl/header_templates/fixed_prefix.template.pl PERL_DEFINES = $(PERL_PATH_SQ):$(PERLLIB_EXTRA_SQ):$(perllibdir_SQ) +PERL_DEFINES := $(PERL_PATH_SQ) $(PERLLIB_EXTRA_SQ) $(perllibdir_SQ) +PERL_DEFINES += $(RUNTIME_PREFIX) + +# Support Perl runtime prefix. In this mode, a different header is installed +# into Perl scripts. +ifdef RUNTIME_PREFIX + +PERL_HEADER_TEMPLATE = perl/header_templates/runtime_prefix.template.pl + +# Don't export a fixed $(localedir) path; it will be resolved by the Perl header +# at runtime. +perl_localedir_SQ = + +endif + +PERL_DEFINES += $(gitexecdir) $(perllibdir) $(localedir) + $(SCRIPT_PERL_GEN): % : %.perl GIT-PERL-DEFINES GIT-PERL-HEADER GIT-VERSION-FILE $(QUIET_GEN)$(RM) $@ $@+ && \ sed -e '1{' \ @@ -2002,6 +2061,7 @@ $(SCRIPT_PERL_GEN): % : %.perl GIT-PERL-DEFINES GIT-PERL-HEADER GIT-VERSION-FILE chmod +x $@+ && \ mv $@+ $@ +PERL_DEFINES := $(subst $(space),:,$(PERL_DEFINES)) GIT-PERL-DEFINES: FORCE @FLAGS='$(PERL_DEFINES)'; \ if test x"$$FLAGS" != x"`cat $@ 2>/dev/null`" ; then \ @@ -2017,6 +2077,9 @@ GIT-PERL-HEADER: $(PERL_HEADER_TEMPLATE) GIT-PERL-DEFINES Makefile sed -e 's=@@PATHSEP@@=$(pathsep)=g' \ -e 's=@@INSTLIBDIR@@='$$INSTLIBDIR'=g' \ -e 's=@@PERLLIBDIR@@='$(perllibdir_SQ)'=g' \ + -e 's=@@PERLLIBDIR_REL@@=$(perllibdir_relative_SQ)=g' \ + -e 's=@@GITEXECDIR_REL@@=$(gitexecdir_relative_SQ)=g' \ + -e 's=@@LOCALEDIR_REL@@=$(localedir_relative_SQ)=g' \ $< >$@+ && \ mv $@+ $@ @@ -2340,7 +2403,7 @@ endif perl/build/lib/%.pm: perl/%.pm $(QUIET_GEN)mkdir -p $(dir $@) && \ - sed -e 's|@@LOCALEDIR@@|$(localedir_SQ)|g' \ + sed -e 's|@@LOCALEDIR@@|$(perl_localedir_SQ)|g' \ -e 's|@@NO_PERL_CPAN_FALLBACKS@@|$(NO_PERL_CPAN_FALLBACKS_SQ)|g' \ < $< > $@ diff --git a/perl/Git/I18N.pm b/perl/Git/I18N.pm index dba96fff0a..bfb4fb67a1 100644 --- a/perl/Git/I18N.pm +++ b/perl/Git/I18N.pm @@ -18,7 +18,7 @@ BEGIN sub __bootstrap_locale_messages { our $TEXTDOMAIN = 'git'; - our $TEXTDOMAINDIR = $ENV{GIT_TEXTDOMAINDIR} || '@@LOCALEDIR@@'; + our $TEXTDOMAINDIR ||= $ENV{GIT_TEXTDOMAINDIR} || '@@LOCALEDIR@@'; require POSIX; POSIX->import(qw(setlocale)); diff --git a/perl/header_templates/runtime_prefix.template.pl b/perl/header_templates/runtime_prefix.template.pl new file mode 100644 index 0000000000..9d28b3d863 --- /dev/null +++ b/perl/header_templates/runtime_prefix.template.pl @@ -0,0 +1,42 @@ +# BEGIN RUNTIME_PREFIX generated code. +# +# This finds our Git::* libraries relative to the script's runtime path. +sub __git_system_path { + my ($relpath) = @_; + my $gitexecdir_relative = '@@GITEXECDIR_REL@@'; + + # GIT_EXEC_PATH is supplied by `git` or the test suite. + my $exec_path; + if (exists $ENV{GIT_EXEC_PATH}) { + $exec_path = $ENV{GIT_EXEC_PATH}; + } else { + # This can happen if this script is being directly invoked instead of run + # by "git". + require FindBin; + $exec_path = $FindBin::Bin; + } + + # Trim off the relative gitexecdir path to get the system path. + (my $prefix = $exec_path) =~ s/\Q$gitexecdir_relative\E$//; + + require File::Spec; + return File::Spec->catdir($prefix, $relpath); +} + +BEGIN { + use lib split /@@PATHSEP@@/, + ( + $ENV{GITPERLLIB} || + do { + my $perllibdir = __git_system_path('@@PERLLIBDIR_REL@@'); + (-e $perllibdir) || die("Invalid system path ($relpath): $path"); + $perllibdir; + } + ); + + # Export the system locale directory to the I18N module. The locale directory + # is only installed if NO_GETTEXT is set. + $Git::I18N::TEXTDOMAINDIR = __git_system_path('@@LOCALEDIR_REL@@'); +} + +# END RUNTIME_PREFIX generated code. From 226c0ddd0d63dd6c8dde497c755af2ad1f02e0c1 Mon Sep 17 00:00:00 2001 From: Dan Jacques Date: Tue, 10 Apr 2018 11:05:44 -0400 Subject: [PATCH 3/7] exec_cmd: RUNTIME_PREFIX on some POSIX systems Enable Git to resolve its own binary location using a variety of OS-specific and generic methods, including: - procfs via "/proc/self/exe" (Linux) - _NSGetExecutablePath (Darwin) - KERN_PROC_PATHNAME sysctl on BSDs. - argv0, if absolute (all, including Windows). This is used to enable RUNTIME_PREFIX support for non-Windows systems, notably Linux and Darwin. When configured with RUNTIME_PREFIX, Git will do a best-effort resolution of its executable path and automatically use this as its "exec_path" for relative helper and data lookups, unless explicitly overridden. Small incidental formatting cleanup of "exec_cmd.c". Signed-off-by: Dan Jacques Thanks-to: Robbie Iannucci Thanks-to: Junio C Hamano Signed-off-by: Junio C Hamano --- Makefile | 28 ++++- cache.h | 1 + common-main.c | 4 +- config.mak.uname | 7 ++ exec_cmd.c | 236 ++++++++++++++++++++++++++++++++++++----- exec_cmd.h | 4 +- gettext.c | 8 +- git.c | 2 +- t/t0061-run-command.sh | 2 +- 9 files changed, 253 insertions(+), 39 deletions(-) diff --git a/Makefile b/Makefile index 55684d1735..44bad53231 100644 --- a/Makefile +++ b/Makefile @@ -448,6 +448,18 @@ all:: # can be moved to arbitrary filesystem locations. RUNTIME_PREFIX also causes # Perl scripts to use a modified entry point header allowing them to resolve # support files at runtime. +# +# When using RUNTIME_PREFIX, define HAVE_BSD_KERN_PROC_SYSCTL if your platform +# supports the KERN_PROC BSD sysctl function. +# +# When using RUNTIME_PREFIX, define PROCFS_EXECUTABLE_PATH if your platform +# mounts a "procfs" filesystem capable of resolving the path of the current +# executable. If defined, this must be the canonical path for the "procfs" +# current executable path. +# +# When using RUNTIME_PREFIX, define HAVE_NS_GET_EXECUTABLE_PATH if your platform +# supports calling _NSGetExecutablePath to retrieve the path of the running +# executable. GIT-VERSION-FILE: FORCE @$(SHELL_PATH) ./GIT-VERSION-GEN @@ -1674,10 +1686,23 @@ ifdef HAVE_BSD_SYSCTL BASIC_CFLAGS += -DHAVE_BSD_SYSCTL endif +ifdef HAVE_BSD_KERN_PROC_SYSCTL + BASIC_CFLAGS += -DHAVE_BSD_KERN_PROC_SYSCTL +endif + ifdef HAVE_GETDELIM BASIC_CFLAGS += -DHAVE_GETDELIM endif +ifneq ($(PROCFS_EXECUTABLE_PATH),) + procfs_executable_path_SQ = $(subst ','\'',$(PROCFS_EXECUTABLE_PATH)) + BASIC_CFLAGS += '-DPROCFS_EXECUTABLE_PATH="$(procfs_executable_path_SQ)"' +endif + +ifdef HAVE_NS_GET_EXECUTABLE_PATH + BASIC_CFLAGS += -DHAVE_NS_GET_EXECUTABLE_PATH +endif + ifeq ($(TCLTK_PATH),) NO_TCLTK = NoThanks endif @@ -2226,6 +2251,7 @@ endif exec_cmd.sp exec_cmd.s exec_cmd.o: GIT-PREFIX exec_cmd.sp exec_cmd.s exec_cmd.o: EXTRA_CPPFLAGS = \ '-DGIT_EXEC_PATH="$(gitexecdir_SQ)"' \ + '-DGIT_LOCALE_PATH="$(localedir_relative_SQ)"' \ '-DBINDIR="$(bindir_relative_SQ)"' \ '-DPREFIX="$(prefix_SQ)"' @@ -2243,7 +2269,7 @@ attr.sp attr.s attr.o: EXTRA_CPPFLAGS = \ gettext.sp gettext.s gettext.o: GIT-PREFIX gettext.sp gettext.s gettext.o: EXTRA_CPPFLAGS = \ - -DGIT_LOCALE_PATH='"$(localedir_SQ)"' + -DGIT_LOCALE_PATH='"$(localedir_relative_SQ)"' http-push.sp http.sp http-walker.sp remote-curl.sp imap-send.sp: SPARSE_FLAGS += \ -DCURL_DISABLE_TYPECHECK diff --git a/cache.h b/cache.h index bbaf5c349a..bc6636b7df 100644 --- a/cache.h +++ b/cache.h @@ -428,6 +428,7 @@ static inline enum object_type object_type(unsigned int mode) #define GIT_ICASE_PATHSPECS_ENVIRONMENT "GIT_ICASE_PATHSPECS" #define GIT_QUARANTINE_ENVIRONMENT "GIT_QUARANTINE_PATH" #define GIT_OPTIONAL_LOCKS_ENVIRONMENT "GIT_OPTIONAL_LOCKS" +#define GIT_TEXT_DOMAIN_DIR_ENVIRONMENT "GIT_TEXTDOMAINDIR" /* * Environment variable used in handshaking the wire protocol. diff --git a/common-main.c b/common-main.c index 7d716d5a54..b2e5a86dff 100644 --- a/common-main.c +++ b/common-main.c @@ -32,14 +32,14 @@ int main(int argc, const char **argv) */ sanitize_stdfds(); + git_resolve_executable_dir(argv[0]); + git_setup_gettext(); initialize_the_repository(); attr_start(); - git_extract_argv0_path(argv[0]); - restore_sigpipe_to_default(); return cmd_main(argc, argv); diff --git a/config.mak.uname b/config.mak.uname index 6a1d0de0cc..e1cfe5e5ed 100644 --- a/config.mak.uname +++ b/config.mak.uname @@ -37,6 +37,7 @@ ifeq ($(uname_S),Linux) HAVE_GETDELIM = YesPlease SANE_TEXT_GREP=-a FREAD_READS_DIRECTORIES = UnfortunatelyYes + PROCFS_EXECUTABLE_PATH = /proc/self/exe endif ifeq ($(uname_S),GNU/kFreeBSD) HAVE_ALLOCA_H = YesPlease @@ -111,6 +112,7 @@ ifeq ($(uname_S),Darwin) BASIC_CFLAGS += -DPROTECT_HFS_DEFAULT=1 HAVE_BSD_SYSCTL = YesPlease FREAD_READS_DIRECTORIES = UnfortunatelyYes + HAVE_NS_GET_EXECUTABLE_PATH = YesPlease endif ifeq ($(uname_S),SunOS) NEEDS_SOCKET = YesPlease @@ -205,6 +207,7 @@ ifeq ($(uname_S),FreeBSD) HAVE_PATHS_H = YesPlease GMTIME_UNRELIABLE_ERRORS = UnfortunatelyYes HAVE_BSD_SYSCTL = YesPlease + HAVE_BSD_KERN_PROC_SYSCTL = YesPlease PAGER_ENV = LESS=FRX LV=-c MORE=FRX FREAD_READS_DIRECTORIES = UnfortunatelyYes endif @@ -217,6 +220,8 @@ ifeq ($(uname_S),OpenBSD) BASIC_LDFLAGS += -L/usr/local/lib HAVE_PATHS_H = YesPlease HAVE_BSD_SYSCTL = YesPlease + HAVE_BSD_KERN_PROC_SYSCTL = YesPlease + PROCFS_EXECUTABLE_PATH = /proc/curproc/file endif ifeq ($(uname_S),MirBSD) NO_STRCASESTR = YesPlease @@ -235,6 +240,8 @@ ifeq ($(uname_S),NetBSD) USE_ST_TIMESPEC = YesPlease HAVE_PATHS_H = YesPlease HAVE_BSD_SYSCTL = YesPlease + HAVE_BSD_KERN_PROC_SYSCTL = YesPlease + PROCFS_EXECUTABLE_PATH = /proc/curproc/exe endif ifeq ($(uname_S),AIX) DEFAULT_PAGER = more diff --git a/exec_cmd.c b/exec_cmd.c index ce192a2d64..38d52d90a2 100644 --- a/exec_cmd.c +++ b/exec_cmd.c @@ -2,24 +2,52 @@ #include "exec_cmd.h" #include "quote.h" #include "argv-array.h" -#define MAX_ARGS 32 -static const char *argv_exec_path; +#if defined(RUNTIME_PREFIX) + +#if defined(HAVE_NS_GET_EXECUTABLE_PATH) +#include +#endif + +#if defined(HAVE_BSD_KERN_PROC_SYSCTL) +#include +#include +#include +#endif + +#endif /* RUNTIME_PREFIX */ + +#define MAX_ARGS 32 + +static const char *system_prefix(void); #ifdef RUNTIME_PREFIX -static const char *argv0_path; + +/** + * When using a runtime prefix, Git dynamically resolves paths relative to its + * executable. + * + * The method for determining the path of the executable is highly + * platform-specific. + */ + +/** + * Path to the current Git executable. Resolved on startup by + * 'git_resolve_executable_dir'. + */ +static const char *executable_dirname; static const char *system_prefix(void) { static const char *prefix; - assert(argv0_path); - assert(is_absolute_path(argv0_path)); + assert(executable_dirname); + assert(is_absolute_path(executable_dirname)); if (!prefix && - !(prefix = strip_path_suffix(argv0_path, GIT_EXEC_PATH)) && - !(prefix = strip_path_suffix(argv0_path, BINDIR)) && - !(prefix = strip_path_suffix(argv0_path, "git"))) { + !(prefix = strip_path_suffix(executable_dirname, GIT_EXEC_PATH)) && + !(prefix = strip_path_suffix(executable_dirname, BINDIR)) && + !(prefix = strip_path_suffix(executable_dirname, "git"))) { prefix = PREFIX; trace_printf("RUNTIME_PREFIX requested, " "but prefix computation failed. " @@ -28,27 +56,179 @@ static const char *system_prefix(void) return prefix; } -void git_extract_argv0_path(const char *argv0) +/* + * Resolves the executable path from argv[0], only if it is absolute. + * + * Returns 0 on success, -1 on failure. + */ +static int git_get_exec_path_from_argv0(struct strbuf *buf, const char *argv0) { const char *slash; if (!argv0 || !*argv0) - return; + return -1; slash = find_last_dir_sep(argv0); + if (slash) { + trace_printf("trace: resolved executable path from argv0: %s\n", + argv0); + strbuf_add_absolute_path(buf, argv0); + return 0; + } + return -1; +} +#ifdef PROCFS_EXECUTABLE_PATH +/* + * Resolves the executable path by examining a procfs symlink. + * + * Returns 0 on success, -1 on failure. + */ +static int git_get_exec_path_procfs(struct strbuf *buf) +{ + if (strbuf_realpath(buf, PROCFS_EXECUTABLE_PATH, 0)) { + trace_printf( + "trace: resolved executable path from procfs: %s\n", + buf->buf); + return 0; + } + return -1; +} +#endif /* PROCFS_EXECUTABLE_PATH */ + +#ifdef HAVE_BSD_KERN_PROC_SYSCTL +/* + * Resolves the executable path using KERN_PROC_PATHNAME BSD sysctl. + * + * Returns 0 on success, -1 on failure. + */ +static int git_get_exec_path_bsd_sysctl(struct strbuf *buf) +{ + int mib[4]; + char path[MAXPATHLEN]; + size_t cb = sizeof(path); + + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_PATHNAME; + mib[3] = -1; + if (!sysctl(mib, 4, path, &cb, NULL, 0)) { + trace_printf( + "trace: resolved executable path from sysctl: %s\n", + path); + strbuf_addstr(buf, path); + return 0; + } + return -1; +} +#endif /* HAVE_BSD_KERN_PROC_SYSCTL */ + +#ifdef HAVE_NS_GET_EXECUTABLE_PATH +/* + * Resolves the executable path by querying Darwin application stack. + * + * Returns 0 on success, -1 on failure. + */ +static int git_get_exec_path_darwin(struct strbuf *buf) +{ + char path[PATH_MAX]; + uint32_t size = sizeof(path); + if (!_NSGetExecutablePath(path, &size)) { + trace_printf( + "trace: resolved executable path from Darwin stack: %s\n", + path); + strbuf_addstr(buf, path); + return 0; + } + return -1; +} +#endif /* HAVE_NS_GET_EXECUTABLE_PATH */ + +/* + * Resolves the absolute path of the current executable. + * + * Returns 0 on success, -1 on failure. + */ +static int git_get_exec_path(struct strbuf *buf, const char *argv0) +{ + /* + * Identifying the executable path is operating system specific. + * Selectively employ all available methods in order of preference, + * preferring highly-available authoritative methods over + * selectively-available or non-authoritative methods. + * + * All cases fall back on resolving against argv[0] if there isn't a + * better functional method. However, note that argv[0] can be + * used-supplied on many operating systems, and is not authoritative + * in those cases. + * + * Each of these functions returns 0 on success, so evaluation will stop + * after the first successful method. + */ + if ( +#ifdef HAVE_BSD_KERN_PROC_SYSCTL + git_get_exec_path_bsd_sysctl(buf) && +#endif /* HAVE_BSD_KERN_PROC_SYSCTL */ + +#ifdef HAVE_NS_GET_EXECUTABLE_PATH + git_get_exec_path_darwin(buf) && +#endif /* HAVE_NS_GET_EXECUTABLE_PATH */ + +#ifdef PROCFS_EXECUTABLE_PATH + git_get_exec_path_procfs(buf) && +#endif /* PROCFS_EXECUTABLE_PATH */ + + git_get_exec_path_from_argv0(buf, argv0)) { + return -1; + } + + if (strbuf_normalize_path(buf)) { + trace_printf("trace: could not normalize path: %s\n", buf->buf); + return -1; + } + + return 0; +} + +void git_resolve_executable_dir(const char *argv0) +{ + struct strbuf buf = STRBUF_INIT; + char *resolved; + const char *slash; + + if (git_get_exec_path(&buf, argv0)) { + trace_printf( + "trace: could not determine executable path from: %s\n", + argv0); + strbuf_release(&buf); + return; + } + + resolved = strbuf_detach(&buf, NULL); + slash = find_last_dir_sep(resolved); if (slash) - argv0_path = xstrndup(argv0, slash - argv0); + resolved[slash - resolved] = '\0'; + + executable_dirname = resolved; + trace_printf("trace: resolved executable dir: %s\n", + executable_dirname); } #else +/* + * When not using a runtime prefix, Git uses a hard-coded path. + */ static const char *system_prefix(void) { return PREFIX; } -void git_extract_argv0_path(const char *argv0) +/* + * This is called during initialization, but No work needs to be done here when + * runtime prefix is not being used. + */ +void git_resolve_executable_dir(const char *argv0) { } @@ -65,32 +245,28 @@ char *system_path(const char *path) return strbuf_detach(&d, NULL); } -void git_set_argv_exec_path(const char *exec_path) +static const char *exec_path_value; + +void git_set_exec_path(const char *exec_path) { - argv_exec_path = exec_path; + exec_path_value = exec_path; /* * Propagate this setting to external programs. */ setenv(EXEC_PATH_ENVIRONMENT, exec_path, 1); } - -/* Returns the highest-priority, location to look for git programs. */ +/* Returns the highest-priority location to look for git programs. */ const char *git_exec_path(void) { - static char *cached_exec_path; - - if (argv_exec_path) - return argv_exec_path; - - if (!cached_exec_path) { + if (!exec_path_value) { const char *env = getenv(EXEC_PATH_ENVIRONMENT); if (env && *env) - cached_exec_path = xstrdup(env); + exec_path_value = xstrdup(env); else - cached_exec_path = system_path(GIT_EXEC_PATH); + exec_path_value = system_path(GIT_EXEC_PATH); } - return cached_exec_path; + return exec_path_value; } static void add_path(struct strbuf *out, const char *path) @@ -103,10 +279,12 @@ static void add_path(struct strbuf *out, const char *path) void setup_path(void) { + const char *exec_path = git_exec_path(); const char *old_path = getenv("PATH"); struct strbuf new_path = STRBUF_INIT; - add_path(&new_path, git_exec_path()); + git_set_exec_path(exec_path); + add_path(&new_path, exec_path); if (old_path) strbuf_addstr(&new_path, old_path); @@ -125,7 +303,8 @@ const char **prepare_git_cmd(struct argv_array *out, const char **argv) return out->argv; } -int execv_git_cmd(const char **argv) { +int execv_git_cmd(const char **argv) +{ struct argv_array nargv = ARGV_ARRAY_INIT; prepare_git_cmd(&nargv, argv); @@ -140,8 +319,7 @@ int execv_git_cmd(const char **argv) { return -1; } - -int execl_git_cmd(const char *cmd,...) +int execl_git_cmd(const char *cmd, ...) { int argc; const char *argv[MAX_ARGS + 1]; diff --git a/exec_cmd.h b/exec_cmd.h index ff0b48048a..2522453cda 100644 --- a/exec_cmd.h +++ b/exec_cmd.h @@ -3,8 +3,8 @@ struct argv_array; -extern void git_set_argv_exec_path(const char *exec_path); -extern void git_extract_argv0_path(const char *path); +extern void git_set_exec_path(const char *exec_path); +extern void git_resolve_executable_dir(const char *path); extern const char *git_exec_path(void); extern void setup_path(void); extern const char **prepare_git_cmd(struct argv_array *out, const char **argv); diff --git a/gettext.c b/gettext.c index db727ea020..6b64d5c2e8 100644 --- a/gettext.c +++ b/gettext.c @@ -2,7 +2,8 @@ * Copyright (c) 2010 Ævar Arnfjörð Bjarmason */ -#include "git-compat-util.h" +#include "cache.h" +#include "exec_cmd.h" #include "gettext.h" #include "strbuf.h" #include "utf8.h" @@ -157,10 +158,11 @@ static void init_gettext_charset(const char *domain) void git_setup_gettext(void) { - const char *podir = getenv("GIT_TEXTDOMAINDIR"); + const char *podir = getenv(GIT_TEXT_DOMAIN_DIR_ENVIRONMENT); if (!podir) - podir = GIT_LOCALE_PATH; + podir = system_path(GIT_LOCALE_PATH); + bindtextdomain("git", podir); setlocale(LC_MESSAGES, ""); setlocale(LC_TIME, ""); diff --git a/git.c b/git.c index 3a89893712..48432f9fb8 100644 --- a/git.c +++ b/git.c @@ -83,7 +83,7 @@ static int handle_options(const char ***argv, int *argc, int *envchanged) */ if (skip_prefix(cmd, "--exec-path", &cmd)) { if (*cmd == '=') - git_set_argv_exec_path(cmd + 1); + git_set_exec_path(cmd + 1); else { puts(git_exec_path()); exit(0); diff --git a/t/t0061-run-command.sh b/t/t0061-run-command.sh index d03149be9f..c887ed5b45 100755 --- a/t/t0061-run-command.sh +++ b/t/t0061-run-command.sh @@ -145,7 +145,7 @@ test_trace () { expect="$1" shift GIT_TRACE=1 test-tool run-command "$@" run-command true 2>&1 >/dev/null | \ - sed 's/.* run_command: //' >actual && + sed -e 's/.* run_command: //' -e '/trace: .*/d' >actual && echo "$expect true" >expect && test_cmp expect actual } From c1be1cb7ea064bae5ee1cd226aa42b767f2b52c4 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Tue, 10 Apr 2018 11:05:45 -0400 Subject: [PATCH 4/7] exec_cmd: provide a new-style RUNTIME_PREFIX helper for Windows The RUNTIME_PREFIX feature comes from Git for Windows, but it was enhanced to allow support for other platforms. While changing the original idea, the concept was also improved by not forcing argv[0] to be adjusted. Let's allow the same for Windows by implementing a helper just as for the other platforms. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- Makefile | 8 ++++++++ exec_cmd.c | 22 ++++++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/Makefile b/Makefile index 44bad53231..154929f1c8 100644 --- a/Makefile +++ b/Makefile @@ -460,6 +460,10 @@ all:: # When using RUNTIME_PREFIX, define HAVE_NS_GET_EXECUTABLE_PATH if your platform # supports calling _NSGetExecutablePath to retrieve the path of the running # executable. +# +# When using RUNTIME_PREFIX, define HAVE_WPGMPTR if your platform offers +# the global variable _wpgmptr containing the absolute path of the current +# executable (this is the case on Windows). GIT-VERSION-FILE: FORCE @$(SHELL_PATH) ./GIT-VERSION-GEN @@ -1703,6 +1707,10 @@ ifdef HAVE_NS_GET_EXECUTABLE_PATH BASIC_CFLAGS += -DHAVE_NS_GET_EXECUTABLE_PATH endif +ifdef HAVE_WPGMPTR + BASIC_CFLAGS += -DHAVE_WPGMPTR +endif + ifeq ($(TCLTK_PATH),) NO_TCLTK = NoThanks endif diff --git a/exec_cmd.c b/exec_cmd.c index 38d52d90a2..6e114f8b3a 100644 --- a/exec_cmd.c +++ b/exec_cmd.c @@ -144,6 +144,24 @@ static int git_get_exec_path_darwin(struct strbuf *buf) } #endif /* HAVE_NS_GET_EXECUTABLE_PATH */ +#ifdef HAVE_WPGMPTR +/* + * Resolves the executable path by using the global variable _wpgmptr. + * + * Returns 0 on success, -1 on failure. + */ +static int git_get_exec_path_wpgmptr(struct strbuf *buf) +{ + int len = wcslen(_wpgmptr) * 3 + 1; + strbuf_grow(buf, len); + len = xwcstoutf(buf->buf, _wpgmptr, len); + if (len < 0) + return -1; + buf->len += len; + return 0; +} +#endif /* HAVE_WPGMPTR */ + /* * Resolves the absolute path of the current executable. * @@ -178,6 +196,10 @@ static int git_get_exec_path(struct strbuf *buf, const char *argv0) git_get_exec_path_procfs(buf) && #endif /* PROCFS_EXECUTABLE_PATH */ +#ifdef HAVE_WPGMPTR + git_get_exec_path_wpgmptr(buf) && +#endif /* HAVE_WPGMPTR */ + git_get_exec_path_from_argv0(buf, argv0)) { return -1; } From 86e254584bb466aaf1bacbdf4a1ade924c8bdbe9 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Tue, 10 Apr 2018 11:05:46 -0400 Subject: [PATCH 5/7] mingw/msvc: use the new-style RUNTIME_PREFIX helper This change also allows us to stop overriding argv[0] with the absolute path of the executable, allowing us to preserve e.g. the case of the executable's file name. This fixes https://github.com/git-for-windows/git/issues/1496 partially. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- compat/mingw.c | 5 ++--- config.mak.uname | 2 ++ 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/compat/mingw.c b/compat/mingw.c index a67872babf..6ded1c859f 100644 --- a/compat/mingw.c +++ b/compat/mingw.c @@ -2221,7 +2221,7 @@ void mingw_startup(void) die_startup(); /* determine size of argv and environ conversion buffer */ - maxlen = wcslen(_wpgmptr); + maxlen = wcslen(wargv[0]); for (i = 1; i < argc; i++) maxlen = max(maxlen, wcslen(wargv[i])); for (i = 0; wenv[i]; i++) @@ -2241,8 +2241,7 @@ void mingw_startup(void) buffer = malloc_startup(maxlen); /* convert command line arguments and environment to UTF-8 */ - __argv[0] = wcstoutfdup_startup(buffer, _wpgmptr, maxlen); - for (i = 1; i < argc; i++) + for (i = 0; i < argc; i++) __argv[i] = wcstoutfdup_startup(buffer, wargv[i], maxlen); for (i = 0; wenv[i]; i++) environ[i] = wcstoutfdup_startup(buffer, wenv[i], maxlen); diff --git a/config.mak.uname b/config.mak.uname index e1cfe5e5ed..a6e734c5d4 100644 --- a/config.mak.uname +++ b/config.mak.uname @@ -357,6 +357,7 @@ ifeq ($(uname_S),Windows) SNPRINTF_RETURNS_BOGUS = YesPlease NO_SVN_TESTS = YesPlease RUNTIME_PREFIX = YesPlease + HAVE_WPGMPTR = YesWeDo NO_ST_BLOCKS_IN_STRUCT_STAT = YesPlease NO_NSEC = YesPlease USE_WIN32_MMAP = YesPlease @@ -506,6 +507,7 @@ ifneq (,$(findstring MINGW,$(uname_S))) NO_SVN_TESTS = YesPlease NO_PERL_MAKEMAKER = YesPlease RUNTIME_PREFIX = YesPlease + HAVE_WPGMPTR = YesWeDo NO_ST_BLOCKS_IN_STRUCT_STAT = YesPlease NO_NSEC = YesPlease USE_WIN32_MMAP = YesPlease From 90df2173f24c941de01f9d104c64d940fcaa4581 Mon Sep 17 00:00:00 2001 From: Jonathan Nieder Date: Mon, 23 Apr 2018 16:24:22 -0700 Subject: [PATCH 6/7] Makefile: remove unused @@PERLLIBDIR@@ substitution variable Junio noticed that this variable is not quoted correctly when it is passed to sed. As a shell-quoted string, it should be inside single-quotes like $(perllibdir_relative_SQ), not outside them like $INSTLIBDIR. In fact, this substitution variable is not used. Simplify by removing it. Reported-by: Junio C Hamano Signed-off-by: Jonathan Nieder Signed-off-by: Junio C Hamano --- Makefile | 1 - 1 file changed, 1 deletion(-) diff --git a/Makefile b/Makefile index 154929f1c8..8f4cb506ff 100644 --- a/Makefile +++ b/Makefile @@ -2109,7 +2109,6 @@ GIT-PERL-HEADER: $(PERL_HEADER_TEMPLATE) GIT-PERL-DEFINES Makefile INSTLIBDIR="$$INSTLIBDIR$${INSTLIBDIR_EXTRA:+:$$INSTLIBDIR_EXTRA}" && \ sed -e 's=@@PATHSEP@@=$(pathsep)=g' \ -e 's=@@INSTLIBDIR@@='$$INSTLIBDIR'=g' \ - -e 's=@@PERLLIBDIR@@='$(perllibdir_SQ)'=g' \ -e 's=@@PERLLIBDIR_REL@@=$(perllibdir_relative_SQ)=g' \ -e 's=@@GITEXECDIR_REL@@=$(gitexecdir_relative_SQ)=g' \ -e 's=@@LOCALEDIR_REL@@=$(localedir_relative_SQ)=g' \ From 64f982b8a791a8e9e612a103d05e5f01a08fce0f Mon Sep 17 00:00:00 2001 From: Jonathan Nieder Date: Mon, 23 Apr 2018 16:25:35 -0700 Subject: [PATCH 7/7] Makefile: quote $INSTLIBDIR when passing it to sed f6a0ad4b (Makefile: generate Perl header from template file, 2018-04-10) moved code for generating the 'use lib' lines at the top of perl scripts from the $(SCRIPT_PERL_GEN) rule to a separate GIT-PERL-HEADER rule. This rule first populates INSTLIBDIR and then substitutes it into the GIT-PERL-HEADER using sed: INSTLIBDIR=... something ... sed -e 's=@@INSTLIBDIR@@='$$INSTLIBDIR'=g' $< > $@ Because $INSTLIBDIR is not surrounded by double quotes, the shell splits it at each space, causing errors if INSTLIBDIR contains an $IFS character: sed: 1: "s=@@INSTLIBDIR@@=/usr/l ...": unescaped newline inside substitute pattern Add back the missing double-quotes to make it work again. Signed-off-by: Jonathan Nieder Signed-off-by: Junio C Hamano --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 8f4cb506ff..2ba24035f5 100644 --- a/Makefile +++ b/Makefile @@ -2108,7 +2108,7 @@ GIT-PERL-HEADER: $(PERL_HEADER_TEMPLATE) GIT-PERL-DEFINES Makefile INSTLIBDIR_EXTRA='$(PERLLIB_EXTRA_SQ)' && \ INSTLIBDIR="$$INSTLIBDIR$${INSTLIBDIR_EXTRA:+:$$INSTLIBDIR_EXTRA}" && \ sed -e 's=@@PATHSEP@@=$(pathsep)=g' \ - -e 's=@@INSTLIBDIR@@='$$INSTLIBDIR'=g' \ + -e "s=@@INSTLIBDIR@@=$$INSTLIBDIR=g" \ -e 's=@@PERLLIBDIR_REL@@=$(perllibdir_relative_SQ)=g' \ -e 's=@@GITEXECDIR_REL@@=$(gitexecdir_relative_SQ)=g' \ -e 's=@@LOCALEDIR_REL@@=$(localedir_relative_SQ)=g' \