git@vger.kernel.org mailing list mirror (one of many)
 help / color / mirror / code / Atom feed
* [PATCH 0/1] RUNTIME_PREFIX on POSIX systems.
@ 2017-11-16 17:05 Dan Jacques
  2017-11-16 17:05 ` [PATCH 1/1] exec_cmd: RUNTIME_PREFIX on some " Dan Jacques
  0 siblings, 1 reply; 7+ messages in thread
From: Dan Jacques @ 2017-11-16 17:05 UTC (permalink / raw)
  To: git; +Cc: Dan Jacques

Hello! This would be my first contribution to the Git project, so please
accept my apology in advance for any mistakes and let me know what I can
do better.

This patch expands support for the RUNTIME_PREFIX configuration flag,
currently only used on Windows builds, to include Linux, Darwin, and
FreeBSD. When Git is built with RUNTIME_PREFIX enabled, it resolves its
ancillary paths relative to the runtime location of its executable
rather than hard-coding them at compile-time, allowing a Git
installation to be deployed to a path other than the one in which it
was installed.

It was useful to create Git distribution bundles that could unpack
fully-functional Git deployments to arbitrary locations in support of the
Chromium project. Chromium has been using Git bundles built with a patch
similar to this one on its Linux and Mac continuous integration fleet (plus
some developer systems) for almost a year now.

RUNTIME_PREFIX remains an optional configuration flag, so standard Git
builds will not see any changes. However, with this patch applied,
Linux, Darwin, and FreeBSD users can now optionally use "config.mak" to
enable RUNTIME_PREFIX and build relocatable Git distributions. An
example "config.mak" that builds relocatable Git binaries for Linux/Mac
is:

# BEGIN: config.mak
RUNTIME_PREFIX = YesPlease
gitexecdir = libexec/git-core
template_dir = share/git-core/templates
sysconfdir = etc
# END: config.mak

Implementation notes:

It is unfortunately not straightforward to resolve the full absolute path
of the currently-running binary. On some operating systems, notably
Windows, this path is executively supplied as argv[0]. On other
operating systems, however, argv[0] is supplied by the invoker (shell,
script, kernel, etc.), and is not a reliable source of information about
the running Git binary.

The specific method that this patch employs for binary directory resolution
varies depending on the operating system. On Linux and FreeBSD,
Git resolves "/proc/self/exe" and "/proc/curproc/file" respectively. On
Darwin, Git uses the "_NSGetExecutablePath" function. On all operating
systems, notably Windows, Git continues to fall back to resolution
against argv[0] when it is an absolute path.

When RUNTIME_PREFIX is enabled, the resolved runtime path needs to be
passed to ancillary Git tools for their own resolution requirements:

- C-source Git programs will use the EXEC_PATH_ENVIRONMENT environment
  variable that Git already exports, ensuring that any launched tools use
  the same runtime prefix as the entry point.

- PERL tooling needs to know how to locate Git's support libraries. When
  RUNTIME_PREFIX is configured, Git now exports the GITPERLLIB environment
  variable, a mechanism that Git's PERL tooling supports that appears to be
  built for testing. PERL scripts installed using MakeMaker incorporate the
  builder system's PERL version into their installation path, making
  it inconsistent to hard-code; consequently, this patch opts to disable
  MakeMaker for RUNTIME_PREFIX builds in order to deterministically control
  the destination of Git's support libraries.

- Git also exports the GIT_TEXTDOMAINDIR environment variable when
  RUNTIME_PREFIX is set so that its locale configuration can be leveraged
  by Git tooling gettext().

Please note that this patch affects Windows Git builds, since the Windows
Git project uses RUNTIME_PREFIX to support arbitrary installation paths.
Notably, PERL scripts are now always installed without MakeMaker (if they
weren't before), and EXEC_PATH_ENVIRONMENT is preferred by tools instead of
re-resolving argv[0]. Chromium uses the stock redistributable Windows Git
package, so I haven't had an opportunity to test this patch on that
platform.

Please take a look and let me know what you think. Thanks!
-Dan

Dan Jacques (1):
  exec_cmd: RUNTIME_PREFIX on some POSIX systems

 Makefile               |  29 ++++++--
 builtin/receive-pack.c |   2 +-
 cache.h                |   2 +
 common-main.c          |   4 +-
 config.mak.uname       |   4 ++
 exec_cmd.c             | 189 +++++++++++++++++++++++++++++++++++++++++++------
 exec_cmd.h             |   6 +-
 gettext.c              |   7 +-
 git.c                  |   7 +-
 http-backend.c         |   2 +-
 shell.c                |   2 +-
 upload-pack.c          |   2 +-
 12 files changed, 217 insertions(+), 39 deletions(-)

-- 
2.15.0.448.gf294e3d99a-goog


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH 1/1] exec_cmd: RUNTIME_PREFIX on some POSIX systems
  2017-11-16 17:05 [PATCH 0/1] RUNTIME_PREFIX on POSIX systems Dan Jacques
@ 2017-11-16 17:05 ` Dan Jacques
  2017-11-17  5:08   ` Junio C Hamano
  0 siblings, 1 reply; 7+ messages in thread
From: Dan Jacques @ 2017-11-16 17:05 UTC (permalink / raw)
  To: git; +Cc: Dan Jacques

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)
- 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.

Git will also always export and consume its resolved "exec_path" using
the EXEC_PATH_ENVIRONMENT regardless of whether the user has overridden
it, simplifying future lookups and ensuring consistency in Git tooling
execution.

When building with a runtime prefix, Git's PERL libraries are now
installed to a consistently-named directory. This path is resolved and
exported to Git's delegate PERL invocations using the GITPERLLIB
environment variable. This enables Git's delegate PERL scripts to import
Git's own PERL libraries from a path relative to the executable.

Small incidental formatting cleanup of "exec_cmd.c".

Signed-off-by: Dan Jacques <dnj@google.com>
---
 Makefile               |  29 ++++++--
 builtin/receive-pack.c |   2 +-
 cache.h                |   2 +
 common-main.c          |   4 +-
 config.mak.uname       |   4 ++
 exec_cmd.c             | 189 +++++++++++++++++++++++++++++++++++++++++++------
 exec_cmd.h             |   6 +-
 gettext.c              |   7 +-
 git.c                  |   7 +-
 http-backend.c         |   2 +-
 shell.c                |   2 +-
 upload-pack.c          |   2 +-
 12 files changed, 217 insertions(+), 39 deletions(-)

diff --git a/Makefile b/Makefile
index ee9d5eb11..80db01706 100644
--- a/Makefile
+++ b/Makefile
@@ -296,7 +296,8 @@ all::
 # Define PERL_PATH to the path of your Perl binary (usually /usr/bin/perl).
 #
 # Define NO_PERL_MAKEMAKER if you cannot use Makefiles generated by perl's
-# MakeMaker (e.g. using ActiveState under Cygwin).
+# MakeMaker (e.g. using ActiveState under Cygwin, or building with a fixed
+# runtime prefix).
 #
 # Define NO_PERL if you do not want Perl scripts or libraries at all.
 #
@@ -462,6 +463,7 @@ ARFLAGS = rcs
 #   mandir
 #   infodir
 #   htmldir
+#   localedir
 # This can help installing the suite in a relocatable way.
 
 prefix = $(HOME)
@@ -485,6 +487,7 @@ pathsep = :
 mandir_relative = $(patsubst $(prefix)/%,%,$(mandir))
 infodir_relative = $(patsubst $(prefix)/%,%,$(infodir))
 htmldir_relative = $(patsubst $(prefix)/%,%,$(htmldir))
+localedir_relative = $(patsubst $(prefix)/%,%,$(localedir))
 
 export prefix bindir sharedir sysconfdir gitwebdir localedir
 
@@ -1522,9 +1525,6 @@ ifdef SHA1_MAX_BLOCK_SIZE
 	LIB_OBJS += compat/sha1-chunked.o
 	BASIC_CFLAGS += -DSHA1_MAX_BLOCK_SIZE="$(SHA1_MAX_BLOCK_SIZE)"
 endif
-ifdef NO_PERL_MAKEMAKER
-	export NO_PERL_MAKEMAKER
-endif
 ifdef NO_HSTRERROR
 	COMPAT_CFLAGS += -DNO_HSTRERROR
 	COMPAT_OBJS += compat/hstrerror.o
@@ -1547,6 +1547,14 @@ else
 endif
 ifdef RUNTIME_PREFIX
 	COMPAT_CFLAGS += -DRUNTIME_PREFIX
+
+	# Control PERL library location so it can be referenced by relocatable
+	# code.
+	NO_PERL_MAKEMAKER = YesPlease
+endif
+
+ifdef NO_PERL_MAKEMAKER
+	export NO_PERL_MAKEMAKER
 endif
 
 ifdef NO_PTHREADS
@@ -1632,6 +1640,15 @@ 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
@@ -1714,6 +1731,7 @@ bindir_relative_SQ = $(subst ','\'',$(bindir_relative))
 mandir_relative_SQ = $(subst ','\'',$(mandir_relative))
 infodir_relative_SQ = $(subst ','\'',$(infodir_relative))
 localedir_SQ = $(subst ','\'',$(localedir))
+localedir_relative_SQ = $(subst ','\'',$(localedir_relative))
 gitexecdir_SQ = $(subst ','\'',$(gitexecdir))
 template_dir_SQ = $(subst ','\'',$(template_dir))
 htmldir_relative_SQ = $(subst ','\'',$(htmldir_relative))
@@ -2130,6 +2148,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)"'
 
@@ -2147,7 +2166,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/builtin/receive-pack.c b/builtin/receive-pack.c
index 4d37a160d..51203cc3d 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -1947,7 +1947,7 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
 
 	service_dir = argv[0];
 
-	setup_path();
+	setup_path_and_env();
 
 	if (!enter_repo(service_dir, 0))
 		die("'%s' does not appear to be a git repository", service_dir);
diff --git a/cache.h b/cache.h
index cb7fb7c00..9ef59f1cc 100644
--- a/cache.h
+++ b/cache.h
@@ -445,6 +445,8 @@ 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_PERL_LIB_ENVIRONMENT "GITPERLLIB"
+#define GIT_TEXT_DOMAIN_DIR_ENVIRONMENT "GIT_TEXTDOMAINDIR"
 
 /*
  * This environment variable is expected to contain a boolean indicating
diff --git a/common-main.c b/common-main.c
index 6a689007e..6516a1f89 100644
--- a/common-main.c
+++ b/common-main.c
@@ -32,12 +32,12 @@ int main(int argc, const char **argv)
 	 */
 	sanitize_stdfds();
 
+	git_resolve_executable_dir(argv[0]);
+
 	git_setup_gettext();
 
 	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 685a80d13..bd5c326d4 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
@@ -218,6 +220,7 @@ ifeq ($(uname_S),OpenBSD)
 	BASIC_LDFLAGS += -L/usr/local/lib
 	HAVE_PATHS_H = YesPlease
 	HAVE_BSD_SYSCTL = YesPlease
+	PROCFS_EXECUTABLE_PATH = /proc/curproc/file
 endif
 ifeq ($(uname_S),MirBSD)
 	NO_STRCASESTR = YesPlease
@@ -236,6 +239,7 @@ ifeq ($(uname_S),NetBSD)
 	USE_ST_TIMESPEC = YesPlease
 	HAVE_PATHS_H = YesPlease
 	HAVE_BSD_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 ce192a2d6..10ca7a75a 100644
--- a/exec_cmd.c
+++ b/exec_cmd.c
@@ -2,53 +2,187 @@
 #include "exec_cmd.h"
 #include "quote.h"
 #include "argv-array.h"
+
+#if defined(RUNTIME_PREFIX) && defined(HAVE_NS_GET_EXECUTABLE_PATH)
+#include <mach-o/dyld.h>
+#endif
+
 #define MAX_ARGS	32
 
 static const char *argv_exec_path;
 
+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.  "
-				"Using static fallback '%s'.\n", prefix);
+			     "but prefix computation failed.  "
+			     "Using static fallback '%s'.\n",
+			     prefix);
 	}
 	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("Determined 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)
+{
+	char *path = realpath(PROCFS_EXECUTABLE_PATH, NULL);
+	if (path) {
+		trace_printf("Determined executable path from procfs: %s\n",
+			     path);
+		strbuf_addstr(buf, path);
+		free(path);
+		return 0;
+	}
+	return -1;
+}
+#endif /* PROCFS_EXECUTABLE_PATH */
+
+#ifdef HAVE_NS_GET_EXECUTABLE_PATH
+/*
+ * Resolves the executable path by querying Darwin applicaton 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(
+			"Determined 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 by employing
+ * one or more platform-specific methods.
+ *
+ * Returns 0 on success, -1 on failure.
+ */
+static int git_get_exec_path(struct strbuf *buf, const char *argv0)
+{
+	if (
+#ifdef PROCFS_EXECUTABLE_PATH
+		git_get_exec_path_procfs(buf) &&
+#endif /* PROCFS_EXECUTABLE_PATH */
+#ifdef HAVE_NS_GET_EXECUTABLE_PATH
+		git_get_exec_path_darwin(buf) &&
+#endif /* HAVE_NS_GET_EXECUTABLE_PATH */
+		git_get_exec_path_from_argv0(buf, argv0)) {
+		return -1;
+	}
+
+	if (strbuf_normalize_path(buf)) {
+		trace_printf("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;
+	const char *path;
+
+	path = getenv(EXEC_PATH_ENVIRONMENT);
+	if (path) {
+		trace_printf("Determined executable path from ENV: %s\n", path);
+		executable_dirname = path;
+	} else {
+		if (git_get_exec_path(&buf, argv0)) {
+			trace_printf(
+				"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)
+			resolved[slash - resolved] = '\0';
+
+		executable_dirname = resolved;
+	}
 
-	if (slash)
-		argv0_path = xstrndup(argv0, slash - argv0);
+	trace_printf("Determined executable dir: %s\n", executable_dirname);
 }
 
 #else
 
+/**
+ * When not using a runtime prefix, Git uses a hard-coded path, and there is
+ * nothing to resolve.
+ */
+
 static const char *system_prefix(void)
 {
 	return PREFIX;
 }
 
-void git_extract_argv0_path(const char *argv0)
+void git_resolve_executable_dir(const char *argv0)
 {
 }
 
@@ -65,7 +199,7 @@ char *system_path(const char *path)
 	return strbuf_detach(&d, NULL);
 }
 
-void git_set_argv_exec_path(const char *exec_path)
+void git_set_exec_path(const char *exec_path)
 {
 	argv_exec_path = exec_path;
 	/*
@@ -74,7 +208,6 @@ void git_set_argv_exec_path(const char *exec_path)
 	setenv(EXEC_PATH_ENVIRONMENT, exec_path, 1);
 }
 
-
 /* Returns the highest-priority, location to look for git programs. */
 const char *git_exec_path(void)
 {
@@ -101,12 +234,14 @@ static void add_path(struct strbuf *out, const char *path)
 	}
 }
 
-void setup_path(void)
+void setup_path_and_env(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);
@@ -116,6 +251,20 @@ void setup_path(void)
 	setenv("PATH", new_path.buf, 1);
 
 	strbuf_release(&new_path);
+
+	/*
+	 * If RUNTIME_PREFIX is set, the relative PERL library and locale paths
+	 * must be exported for invoked programs to inherit the calculated
+	 * runtime path.
+	 *
+	 * When RUNTIME_PREFIX is defined, Git's PERL include path is always
+	 * "$(PREFIX)/lib" (by setting NO_PERL_MAKEMAKER).
+	 */
+#ifdef RUNTIME_PREFIX
+	setenv(GIT_TEXT_DOMAIN_DIR_ENVIRONMENT, system_path(GIT_LOCALE_PATH),
+	       1);
+	setenv(GIT_PERL_LIB_ENVIRONMENT, system_path("lib"), 1);
+#endif /* RUNTIME_PREFIX */
 }
 
 const char **prepare_git_cmd(struct argv_array *out, const char **argv)
@@ -125,7 +274,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 +290,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 ff0b48048..490493745 100644
--- a/exec_cmd.h
+++ b/exec_cmd.h
@@ -3,10 +3,10 @@
 
 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 void setup_path_and_env(void);
 extern const char **prepare_git_cmd(struct argv_array *out, const char **argv);
 extern int execv_git_cmd(const char **argv); /* NULL terminated */
 LAST_ARG_MUST_BE_NULL
diff --git a/gettext.c b/gettext.c
index db727ea02..f1c162cd8 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,10 @@ 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 9e96dd409..f6336bca0 100644
--- a/git.c
+++ b/git.c
@@ -65,7 +65,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);
@@ -676,8 +676,11 @@ int cmd_main(int argc, const char **argv)
 	 * precedence paths: the "--exec-path" option, the GIT_EXEC_PATH
 	 * environment, and the $(gitexecdir) from the Makefile at build
 	 * time.
+	 *
+	 * If RUNTIME_PREFIX is defined, we export some additional environment
+	 * variables to help subprocess code identify runtime-derived paths.
 	 */
-	setup_path();
+	setup_path_and_env();
 
 	while (1) {
 		int was_alias = run_argv(&argc, &argv);
diff --git a/http-backend.c b/http-backend.c
index f3dc218b2..200d4dc13 100644
--- a/http-backend.c
+++ b/http-backend.c
@@ -697,7 +697,7 @@ int cmd_main(int argc, const char **argv)
 	if (!cmd)
 		not_found(&hdr, "Request not supported: '%s'", dir);
 
-	setup_path();
+	setup_path_and_env();
 	if (!enter_repo(dir, 0))
 		not_found(&hdr, "Not a git repository: '%s'", dir);
 	if (!getenv("GIT_HTTP_EXPORT_ALL") &&
diff --git a/shell.c b/shell.c
index 234b2d4f1..82177f5bc 100644
--- a/shell.c
+++ b/shell.c
@@ -12,7 +12,7 @@ static int do_generic_cmd(const char *me, char *arg)
 {
 	const char *my_argv[4];
 
-	setup_path();
+	setup_path_and_env();
 	if (!arg || !(arg = sq_dequote(arg)) || *arg == '-')
 		die("bad argument");
 	if (!starts_with(me, "git-"))
diff --git a/upload-pack.c b/upload-pack.c
index 6d5f3c0d3..aa3d693ab 100644
--- a/upload-pack.c
+++ b/upload-pack.c
@@ -1058,7 +1058,7 @@ int cmd_main(int argc, const char **argv)
 	if (timeout)
 		daemon_mode = 1;
 
-	setup_path();
+	setup_path_and_env();
 
 	dir = argv[0];
 
-- 
2.15.0.448.gf294e3d99a-goog


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* Re: [PATCH 1/1] exec_cmd: RUNTIME_PREFIX on some POSIX systems
  2017-11-16 17:05 ` [PATCH 1/1] exec_cmd: RUNTIME_PREFIX on some " Dan Jacques
@ 2017-11-17  5:08   ` Junio C Hamano
  0 siblings, 0 replies; 7+ messages in thread
From: Junio C Hamano @ 2017-11-17  5:08 UTC (permalink / raw)
  To: Dan Jacques; +Cc: git

Dan Jacques <dnj@google.com> writes:

> 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)
> - 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.

Yay.

> Git will also always export and consume its resolved "exec_path" using
> the EXEC_PATH_ENVIRONMENT regardless of whether the user has overridden
> it, simplifying future lookups and ensuring consistency in Git tooling
> execution.

The "regardless of whether the user has overridden it" part sounded
alarming and made me wince twice.  I think you meant

 - If the user already has GIT_EXEC_PATH in the environment pointing
   somewhere, when we end up calling git_set_exec_path() to export
   the variable, the value we have at hand to be exported is what
   originally came from the user, bypassing the auto-detection logic
   this patch adds.

 - If the user did not have GIT_EXEC_PATH, the auto-detection logic
   is exercised, and the result is exported when git_set_exec_path()
   is called.  Any program that is spawned by us as a subprocess
   will inherit the same GIT_EXEC_PATH we detected.

As I have multiple installations of various versions of Git, I find
the latter somewhat disturbing.  

Suppose that I designate one stable version of Git and install it at
$HOME/git-stable/{bin,libexec,...}/, and want that version to always
be used in my hooks and other helper scripts that are spawned by
Git, even when I am trying out a newer version of Git that is under
testing.

My hooks would be running $HOME/git-stable/bin/git subcommand" (or
more realistically, it would do "PATH=$HOME/git-stable/bin:$PATH"
upfront), but with this patch (and with runtime-prefix layout) they
would use GIT_EXEC_PATH that was discovered and exported by the
version of Git under testing that invoked the hook, leading to an
inconsistent and hard to debug behaviour, no?

On the other hand, as long as existing GIT_EXEC_PATH is passed-thru
(i.e. the first point above), I think that is a sensible thing to
do.

> When building with a runtime prefix, Git's PERL libraries are now
> installed to a consistently-named directory. This path is resolved and
> exported to Git's delegate PERL invocations using the GITPERLLIB
> environment variable. This enables Git's delegate PERL scripts to import
> Git's own PERL libraries from a path relative to the executable.

Sounds good.

> Small incidental formatting cleanup of "exec_cmd.c".

We usually frown upon these because they often gets distracting, but
I didn't find the ones in this patch too bad.

> diff --git a/Makefile b/Makefile
> index ee9d5eb11..80db01706 100644
> --- a/Makefile
> +++ b/Makefile
> @@ -296,7 +296,8 @@ all::
>  # Define PERL_PATH to the path of your Perl binary (usually /usr/bin/perl).
>  #
>  # Define NO_PERL_MAKEMAKER if you cannot use Makefiles generated by perl's
> -# MakeMaker (e.g. using ActiveState under Cygwin).
> +# MakeMaker (e.g. using ActiveState under Cygwin, or building with a fixed
> +# runtime prefix).

Windows folks may want to comment (either positively or negatively)
as they are the only ones that are currently using runtime-prefix
layout, especially on this part:

> @@ -1547,6 +1547,14 @@ else
>  endif
>  ifdef RUNTIME_PREFIX
>  	COMPAT_CFLAGS += -DRUNTIME_PREFIX
> +
> +	# Control PERL library location so it can be referenced by relocatable
> +	# code.
> +	NO_PERL_MAKEMAKER = YesPlease
> +endif


> diff --git a/cache.h b/cache.h
> index cb7fb7c00..9ef59f1cc 100644
> --- a/cache.h
> +++ b/cache.h
> @@ -445,6 +445,8 @@ 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_PERL_LIB_ENVIRONMENT "GITPERLLIB"
> +#define GIT_TEXT_DOMAIN_DIR_ENVIRONMENT "GIT_TEXTDOMAINDIR"

This is a nice touch.  The other day I noticed that we stopped
defining these when we start using a new enviornment variable, which
should be rectified (this is not within the scope of this patch--I
am just welcoming this change that fixes the existing issue a bit
without getting distracing).

> diff --git a/common-main.c b/common-main.c
> index 6a689007e..6516a1f89 100644
> --- a/common-main.c
> +++ b/common-main.c
> @@ -32,12 +32,12 @@ int main(int argc, const char **argv)
>  	 */
>  	sanitize_stdfds();
>  
> +	git_resolve_executable_dir(argv[0]);
> +
>  	git_setup_gettext();
>  
>  	attr_start();
>  
> -	git_extract_argv0_path(argv[0]);
> -

I presume that this is because we may need to know where to find the
locale stuff before calling git_setup_gettext(); makes sense.

> diff --git a/exec_cmd.c b/exec_cmd.c
> index ce192a2d6..10ca7a75a 100644
> --- a/exec_cmd.c
> +++ b/exec_cmd.c
> @@ -2,53 +2,187 @@
>  #include "exec_cmd.h"
>  #include "quote.h"
>  #include "argv-array.h"
> +
> +#if defined(RUNTIME_PREFIX) && defined(HAVE_NS_GET_EXECUTABLE_PATH)
> +#include <mach-o/dyld.h>
> +#endif
> +
>  #define MAX_ARGS	32
>  
>  static const char *argv_exec_path;
>  
> +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;

OK, so that is a more appropriate name for the variable that is a
logical successor of argv0_path.  I wonder if the file-scope static
variable argv_exec_path we see above would want to move to somewhere
closer to one of these "platform specific methods", though.

>  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.  "
> -				"Using static fallback '%s'.\n", prefix);
> +			     "but prefix computation failed.  "
> +			     "Using static fallback '%s'.\n",
> +			     prefix);
>  	}
>  	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("Determined 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)
> +{
> +	char *path = realpath(PROCFS_EXECUTABLE_PATH, NULL);

I think this is our first use of realpath(), which is XSI.

    http://pubs.opengroup.org/onlinepubs/9699919799/functions/realpath.html

I'd assume it is available on any platform on which we would want to
use PROCFS_EXECUTABLE_PATH, so this probably is OK.

> +	if (path) {
> +		trace_printf("Determined executable path from procfs: %s\n",
> +			     path);
> +		strbuf_addstr(buf, path);
> +		free(path);
> +		return 0;
> +	}
> +	return -1;
> +}
> +#endif /* PROCFS_EXECUTABLE_PATH */

> +/*
> + * Resolves the absolute path of the current executable by employing
> + * one or more platform-specific methods.
> + *
> + * Returns 0 on success, -1 on failure.
> + */
> +static int git_get_exec_path(struct strbuf *buf, const char *argv0)
> +{
> +	if (
> +#ifdef PROCFS_EXECUTABLE_PATH
> +		git_get_exec_path_procfs(buf) &&
> +#endif /* PROCFS_EXECUTABLE_PATH */
> +#ifdef HAVE_NS_GET_EXECUTABLE_PATH
> +		git_get_exec_path_darwin(buf) &&
> +#endif /* HAVE_NS_GET_EXECUTABLE_PATH */
> +		git_get_exec_path_from_argv0(buf, argv0)) {

The contract between this caller and these platform-specific helpers
is for the callee to return 0 when they succeed, and -1 when they
punt with "I dunno", so the &&-cascade stops after the first one
that returns 0.  I wonder why argv0 (i.e. the full-path case) is not
the first one to try, though---isn't that one the simplest?

Also, I wonder if this caller gets simpler to read and understand if
each of these "platform specific" ones are done like so:

	#ifdef FOO_EXECUTABLE_PATH
	static int git_get_exec_path_foo(struct strbuf *buf)
	{
		...
	}
	#else /* FOO_EXECUTABLE_PATH */
	#define git_get_exec_path_foo(buf) (-1)
	#endif

Then the caller can lose the ifdef/endif that is noisier than the
actual calls we see above, i.e.

	if (!git_get_exec_path_from_argv0(buf, argv0) ||
	    !git_get_exec_path_procfs(buf) ||
            !git_get_exec_path_darwin(buf)) {
		/* at least one of them found what we wanted */
		... do the success case ...
	} else {
		... oops, we failed to find ...
	}

> +void git_resolve_executable_dir(const char *argv0)
> +{
> +	struct strbuf buf = STRBUF_INIT;
> +	char *resolved;
> +	const char *slash;
> +	const char *path;
> +
> +	path = getenv(EXEC_PATH_ENVIRONMENT);
> +	if (path) {
> +		trace_printf("Determined executable path from ENV: %s\n", path);
> +		executable_dirname = path;
> +	} else {
> +		if (git_get_exec_path(&buf, argv0)) {
> +			trace_printf(
> +				"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)
> +			resolved[slash - resolved] = '\0';
> +
> +		executable_dirname = resolved;
> +	}
>  
> -	if (slash)
> -		argv0_path = xstrndup(argv0, slash - argv0);
> +	trace_printf("Determined executable dir: %s\n", executable_dirname);
>  }

OK.

> -void git_set_argv_exec_path(const char *exec_path)
> +void git_set_exec_path(const char *exec_path)
>  {
>  	argv_exec_path = exec_path;
>  	/*

I wonder if we want to clean up this part of the system, in that
git_exec_path() is the only user of argv_exec_path which is now a
gross misnomer (the way you figured out exec_path may not be based
on argv at all).  Once resolve-executable-dir is called and we have
a value in executable_dirname, this argv_exec_path variable feels
more or less redundant.  But perhaps that is outside the scope of
this patch.  I dunno.

> @@ -101,12 +234,14 @@ static void add_path(struct strbuf *out, const char *path)
>  	}
>  }
>  
> -void setup_path(void)
> +void setup_path_and_env(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);
> @@ -116,6 +251,20 @@ void setup_path(void)
>  	setenv("PATH", new_path.buf, 1);
>  
>  	strbuf_release(&new_path);
> +
> +	/*
> +	 * If RUNTIME_PREFIX is set, the relative PERL library and locale paths
> +	 * must be exported for invoked programs to inherit the calculated
> +	 * runtime path.
> +	 *
> +	 * When RUNTIME_PREFIX is defined, Git's PERL include path is always
> +	 * "$(PREFIX)/lib" (by setting NO_PERL_MAKEMAKER).
> +	 */
> +#ifdef RUNTIME_PREFIX
> +	setenv(GIT_TEXT_DOMAIN_DIR_ENVIRONMENT, system_path(GIT_LOCALE_PATH),
> +	       1);
> +	setenv(GIT_PERL_LIB_ENVIRONMENT, system_path("lib"), 1);
> +#endif /* RUNTIME_PREFIX */
>  }

So..., now this function exports textdomain and perllib environment
when we are compiled with runtime-prefix, and exports GIT_EXEC_PATH
even when we are *not* compiled with runtime-prefix (by calling the
git_set_exec_path() function that does the exporting)?  That makes
the worry I expressed in an earlier part of the message even worse,
as I assumed this gratuitous exporting would be in effect only when
runtime-prefix is in use.  I hope I am mistaken...

> @@ -125,7 +274,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 +290,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];

These two are good changes (reducing the inter-function spacing
might not be in that "we'd probably prefer there wasn't an extra
blank line from the beginning, but it is not worth a patch noise to
remove it" sense).  Even though they are somewhat distracting,
seeing them after finishing reading the real changes is much less
bad than having to see distracting changes in the middle of real
changes during a review.

Thanks.

^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH 1/1] exec_cmd: RUNTIME_PREFIX on some POSIX systems
  2017-11-19 17:31 [PATCH v2 0/1] " Dan Jacques
@ 2017-11-19 17:31 ` Dan Jacques
  2017-11-20  1:01   ` Junio C Hamano
  2017-11-20 21:00   ` Ævar Arnfjörð Bjarmason
  0 siblings, 2 replies; 7+ messages in thread
From: Dan Jacques @ 2017-11-19 17:31 UTC (permalink / raw)
  To: git; +Cc: Dan Jacques

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.

Git's PERL tooling now responds to RUNTIME_PREFIX_PERL. When configured,
Git's generated PERL scripts resolve the Git library location relative to
their runtime paths instead of hard-coding them. Structural changes
were made to Makefile to support selective PERL header generation.

Small incidental formatting cleanup of "exec_cmd.c".

Signed-off-by: Dan Jacques <dnj@google.com>
---
 .gitignore       |   1 +
 Makefile         |  88 +++++++++++++++++---
 cache.h          |   1 +
 common-main.c    |   4 +-
 config.mak.uname |   7 ++
 exec_cmd.c       | 239 +++++++++++++++++++++++++++++++++++++++++++++++--------
 exec_cmd.h       |   4 +-
 gettext.c        |   8 +-
 git.c            |   2 +-
 9 files changed, 304 insertions(+), 50 deletions(-)

diff --git a/.gitignore b/.gitignore
index 833ef3b0b..89bd7bd8a 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 ee9d5eb11..6fddb8b8f 100644
--- a/Makefile
+++ b/Makefile
@@ -296,7 +296,8 @@ all::
 # Define PERL_PATH to the path of your Perl binary (usually /usr/bin/perl).
 #
 # Define NO_PERL_MAKEMAKER if you cannot use Makefiles generated by perl's
-# MakeMaker (e.g. using ActiveState under Cygwin).
+# MakeMaker (e.g. using ActiveState under Cygwin). NO_PERL_MAKEMAKER is
+# automatically enabled when using RUNTIME_PREFIX_PERL.
 #
 # Define NO_PERL if you do not want Perl scripts or libraries at all.
 #
@@ -416,6 +417,16 @@ all::
 #
 # Define HAVE_BSD_SYSCTL if your platform has a BSD-compatible sysctl function.
 #
+# Define HAVE_BSD_KERN_PROC_SYSCTL if your platform supports the KERN_PROC BSD
+# sysctl function.
+#
+# 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.
+#
+# Define HAVE_NS_GET_EXECUTABLE_PATH if your platform supports calling
+# _NSGetExecutablePath to retrieve the path of the running executable.
+#
 # Define HAVE_GETDELIM if your system has the getdelim() function.
 #
 # Define PAGER_ENV to a SP separated VAR=VAL pairs to define
@@ -425,6 +436,16 @@ all::
 #
 # to say "export LESS=FRX (and LV=-c) if the environment variable
 # LESS (and LV) is not set, respectively".
+#
+# 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. Users may want to enable
+# RUNTIME_PREFIX_PERL as well (see below).
+#
+# Define RUNTIME_PREFIX_PERL to configure Git's PERL commands to locate Git
+# support libraries relative to their filesystem path instead of hard-coding
+# it. RUNTIME_PREFIX_PERL also sets NO_PERL_MAKEMAKER.
 
 GIT-VERSION-FILE: FORCE
 	@$(SHELL_PATH) ./GIT-VERSION-GEN
@@ -462,6 +483,7 @@ ARFLAGS = rcs
 #   mandir
 #   infodir
 #   htmldir
+#   localedir
 # This can help installing the suite in a relocatable way.
 
 prefix = $(HOME)
@@ -485,6 +507,7 @@ pathsep = :
 mandir_relative = $(patsubst $(prefix)/%,%,$(mandir))
 infodir_relative = $(patsubst $(prefix)/%,%,$(infodir))
 htmldir_relative = $(patsubst $(prefix)/%,%,$(htmldir))
+localedir_relative = $(patsubst $(prefix)/%,%,$(localedir))
 
 export prefix bindir sharedir sysconfdir gitwebdir localedir
 
@@ -1522,9 +1545,6 @@ ifdef SHA1_MAX_BLOCK_SIZE
 	LIB_OBJS += compat/sha1-chunked.o
 	BASIC_CFLAGS += -DSHA1_MAX_BLOCK_SIZE="$(SHA1_MAX_BLOCK_SIZE)"
 endif
-ifdef NO_PERL_MAKEMAKER
-	export NO_PERL_MAKEMAKER
-endif
 ifdef NO_HSTRERROR
 	COMPAT_CFLAGS += -DNO_HSTRERROR
 	COMPAT_OBJS += compat/hstrerror.o
@@ -1549,6 +1569,15 @@ ifdef RUNTIME_PREFIX
 	COMPAT_CFLAGS += -DRUNTIME_PREFIX
 endif
 
+ifdef RUNTIME_PREFIX_PERL
+	# Control PERL library location so its paths and contents are not dependent on
+	# the host's PERL version. See perl/Makefile for more information.
+	NO_PERL_MAKEMAKER = YesPlease
+endif
+ifdef NO_PERL_MAKEMAKER
+	export NO_PERL_MAKEMAKER
+endif
+
 ifdef NO_PTHREADS
 	BASIC_CFLAGS += -DNO_PTHREADS
 else
@@ -1628,10 +1657,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
@@ -1714,6 +1756,7 @@ bindir_relative_SQ = $(subst ','\'',$(bindir_relative))
 mandir_relative_SQ = $(subst ','\'',$(mandir_relative))
 infodir_relative_SQ = $(subst ','\'',$(infodir_relative))
 localedir_SQ = $(subst ','\'',$(localedir))
+localedir_relative_SQ = $(subst ','\'',$(localedir_relative))
 gitexecdir_SQ = $(subst ','\'',$(gitexecdir))
 template_dir_SQ = $(subst ','\'',$(template_dir))
 htmldir_relative_SQ = $(subst ','\'',$(htmldir_relative))
@@ -1962,17 +2005,16 @@ perl/perl.mak: GIT-CFLAGS GIT-PREFIX perl/Makefile perl/Makefile.PL
 	$(QUIET_SUBDIR0)perl $(QUIET_SUBDIR1) PERL_PATH='$(PERL_PATH_SQ)' prefix='$(prefix_SQ)' $(@F)
 
 PERL_DEFINES = $(PERL_PATH_SQ):$(PERLLIB_EXTRA_SQ)
-$(SCRIPT_PERL_GEN): % : %.perl perl/perl.mak GIT-PERL-DEFINES GIT-VERSION-FILE
+
+$(SCRIPT_PERL_GEN): % : %.perl perl/perl.mak GIT-PERL-DEFINES GIT-PERL-HEADER GIT-VERSION-FILE
 	$(QUIET_GEN)$(RM) $@ $@+ && \
 	INSTLIBDIR=`MAKEFLAGS= $(MAKE) -C perl -s --no-print-directory instlibdir` && \
 	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' \
 	    $< >$@+ && \
@@ -1986,6 +2028,29 @@ GIT-PERL-DEFINES: FORCE
 		echo "$$FLAGS" >$@; \
 	    fi
 
+GIT-PERL-HEADER: perl/perl.mak GIT-PERL-DEFINES FORCE
+ifndef RUNTIME_PREFIX_PERL
+	# Hardcode the runtime path.
+	INSTLIBDIR=`MAKEFLAGS= $(MAKE) -C perl -s --no-print-directory instlibdir` && \
+	INSTLIBDIR_EXTRA='$(PERLLIB_EXTRA_SQ)' && \
+	echo \
+	  'use lib (split(/$(pathsep)/, $$ENV{GITPERLLIB} || "'"$$INSTLIBDIR"'"));' \
+	  >$@
+else
+	# Probe the runtime path relative to the PERL script. RUNTIME_PREFIX_PERL
+	# automatically sets NO_PERL_MAKEMAKER, causing PERL scripts to be installed
+	# to "$(prefix)/lib" (see "perl/Makefile"). This expectation is hard-coded
+	# into the generated code below.
+	GITEXECDIR='$(gitexecdir_SQ)' && \
+	echo \
+	  'sub _get_git_lib{'\
+	  'use FindBin;'\
+	  '(my $$p=$$FindBin::Bin)=~s=/'$${GITEXECDIR}'$$==;'\
+		'return File::Spec->catdir($$p,"'"lib"'");' \
+	  '};' \
+	  'use lib (split(/$(pathsep)/, $$ENV{GITPERLLIB}||_get_git_lib()));'\
+	  >$@
+endif
 
 .PHONY: gitweb
 gitweb:
@@ -2130,6 +2195,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)"'
 
@@ -2147,7 +2213,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
@@ -2704,7 +2770,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/cache.h b/cache.h
index cb7fb7c00..fb7795410 100644
--- a/cache.h
+++ b/cache.h
@@ -445,6 +445,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"
 
 /*
  * This environment variable is expected to contain a boolean indicating
diff --git a/common-main.c b/common-main.c
index 6a689007e..6516a1f89 100644
--- a/common-main.c
+++ b/common-main.c
@@ -32,12 +32,12 @@ int main(int argc, const char **argv)
 	 */
 	sanitize_stdfds();
 
+	git_resolve_executable_dir(argv[0]);
+
 	git_setup_gettext();
 
 	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 685a80d13..58fd62b4d 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
@@ -206,6 +208,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
@@ -218,6 +221,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
@@ -236,6 +241,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 ce192a2d6..c9006c4c9 100644
--- a/exec_cmd.c
+++ b/exec_cmd.c
@@ -2,53 +2,232 @@
 #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 <mach-o/dyld.h>
+#endif
+
+#if defined(HAVE_BSD_KERN_PROC_SYSCTL)
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/sysctl.h>
+#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.  "
-				"Using static fallback '%s'.\n", prefix);
+			     "but prefix computation failed.  "
+			     "Using static fallback '%s'.\n",
+			     prefix);
 	}
 	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 applicaton 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 authoratative methods over
+	 * selectively-available or non- authoratative 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 sysetems, and is not authoratative
+	 * 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, and there is
+ * nothing to resolve.
+ */
+
 static const char *system_prefix(void)
 {
 	return PREFIX;
 }
 
-void git_extract_argv0_path(const char *argv0)
+void git_resolve_executable_dir(const char *argv0)
 {
 }
 
@@ -65,32 +244,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 +278,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 +302,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 +318,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 ff0b48048..2522453cd 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 db727ea02..6b64d5c2e 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 9e96dd409..dc4cc1419 100644
--- a/git.c
+++ b/git.c
@@ -65,7 +65,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);
-- 
2.15.0.448.gf294e3d99a-goog


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* Re: [PATCH 1/1] exec_cmd: RUNTIME_PREFIX on some POSIX systems
  2017-11-19 17:31 ` [PATCH 1/1] " Dan Jacques
@ 2017-11-20  1:01   ` Junio C Hamano
  2017-11-20 21:00   ` Ævar Arnfjörð Bjarmason
  1 sibling, 0 replies; 7+ messages in thread
From: Junio C Hamano @ 2017-11-20  1:01 UTC (permalink / raw)
  To: Dan Jacques
  Cc: git, Ævar Arnfjörð Bjarmason, Johannes Schindelin

Dan Jacques <dnj@google.com> writes:

> 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.
>
> Git's PERL tooling now responds to RUNTIME_PREFIX_PERL. When configured,
> Git's generated PERL scripts resolve the Git library location relative to
> their runtime paths instead of hard-coding them. Structural changes
> were made to Makefile to support selective PERL header generation.

Ævar and Dscho Cc'ed for input on Perl and also for the following
piece from the cover letter of the previous iteration (which is not
in the cover for this iteration--because there ought to be no impact
to Windows unless the new RUNTIME_PREFIX_PERL is used):

    Please note that this patch affects Windows Git builds, since
    the Windows Git project uses RUNTIME_PREFIX to support arbitrary
    installation paths.  Notably, PERL scripts are now always
    installed without MakeMaker (if they weren't before), and
    EXEC_PATH_ENVIRONMENT is preferred by tools instead of
    re-resolving argv[0]. Chromium uses the stock redistributable
    Windows Git package, so I haven't had an opportunity to test
    this patch on that platform.

The rest of the patch kept for reference but I not yet have any
comment on it yet.

Thanks.

> Small incidental formatting cleanup of "exec_cmd.c".
>
> Signed-off-by: Dan Jacques <dnj@google.com>
> ---
>  .gitignore       |   1 +
>  Makefile         |  88 +++++++++++++++++---
>  cache.h          |   1 +
>  common-main.c    |   4 +-
>  config.mak.uname |   7 ++
>  exec_cmd.c       | 239 +++++++++++++++++++++++++++++++++++++++++++++++--------
>  exec_cmd.h       |   4 +-
>  gettext.c        |   8 +-
>  git.c            |   2 +-
>  9 files changed, 304 insertions(+), 50 deletions(-)
>
> diff --git a/.gitignore b/.gitignore
> index 833ef3b0b..89bd7bd8a 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 ee9d5eb11..6fddb8b8f 100644
> --- a/Makefile
> +++ b/Makefile
> @@ -296,7 +296,8 @@ all::
>  # Define PERL_PATH to the path of your Perl binary (usually /usr/bin/perl).
>  #
>  # Define NO_PERL_MAKEMAKER if you cannot use Makefiles generated by perl's
> -# MakeMaker (e.g. using ActiveState under Cygwin).
> +# MakeMaker (e.g. using ActiveState under Cygwin). NO_PERL_MAKEMAKER is
> +# automatically enabled when using RUNTIME_PREFIX_PERL.
>  #
>  # Define NO_PERL if you do not want Perl scripts or libraries at all.
>  #
> @@ -416,6 +417,16 @@ all::
>  #
>  # Define HAVE_BSD_SYSCTL if your platform has a BSD-compatible sysctl function.
>  #
> +# Define HAVE_BSD_KERN_PROC_SYSCTL if your platform supports the KERN_PROC BSD
> +# sysctl function.
> +#
> +# 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.
> +#
> +# Define HAVE_NS_GET_EXECUTABLE_PATH if your platform supports calling
> +# _NSGetExecutablePath to retrieve the path of the running executable.
> +#
>  # Define HAVE_GETDELIM if your system has the getdelim() function.
>  #
>  # Define PAGER_ENV to a SP separated VAR=VAL pairs to define
> @@ -425,6 +436,16 @@ all::
>  #
>  # to say "export LESS=FRX (and LV=-c) if the environment variable
>  # LESS (and LV) is not set, respectively".
> +#
> +# 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. Users may want to enable
> +# RUNTIME_PREFIX_PERL as well (see below).
> +#
> +# Define RUNTIME_PREFIX_PERL to configure Git's PERL commands to locate Git
> +# support libraries relative to their filesystem path instead of hard-coding
> +# it. RUNTIME_PREFIX_PERL also sets NO_PERL_MAKEMAKER.
>  
>  GIT-VERSION-FILE: FORCE
>  	@$(SHELL_PATH) ./GIT-VERSION-GEN
> @@ -462,6 +483,7 @@ ARFLAGS = rcs
>  #   mandir
>  #   infodir
>  #   htmldir
> +#   localedir
>  # This can help installing the suite in a relocatable way.
>  
>  prefix = $(HOME)
> @@ -485,6 +507,7 @@ pathsep = :
>  mandir_relative = $(patsubst $(prefix)/%,%,$(mandir))
>  infodir_relative = $(patsubst $(prefix)/%,%,$(infodir))
>  htmldir_relative = $(patsubst $(prefix)/%,%,$(htmldir))
> +localedir_relative = $(patsubst $(prefix)/%,%,$(localedir))
>  
>  export prefix bindir sharedir sysconfdir gitwebdir localedir
>  
> @@ -1522,9 +1545,6 @@ ifdef SHA1_MAX_BLOCK_SIZE
>  	LIB_OBJS += compat/sha1-chunked.o
>  	BASIC_CFLAGS += -DSHA1_MAX_BLOCK_SIZE="$(SHA1_MAX_BLOCK_SIZE)"
>  endif
> -ifdef NO_PERL_MAKEMAKER
> -	export NO_PERL_MAKEMAKER
> -endif
>  ifdef NO_HSTRERROR
>  	COMPAT_CFLAGS += -DNO_HSTRERROR
>  	COMPAT_OBJS += compat/hstrerror.o
> @@ -1549,6 +1569,15 @@ ifdef RUNTIME_PREFIX
>  	COMPAT_CFLAGS += -DRUNTIME_PREFIX
>  endif
>  
> +ifdef RUNTIME_PREFIX_PERL
> +	# Control PERL library location so its paths and contents are not dependent on
> +	# the host's PERL version. See perl/Makefile for more information.
> +	NO_PERL_MAKEMAKER = YesPlease
> +endif
> +ifdef NO_PERL_MAKEMAKER
> +	export NO_PERL_MAKEMAKER
> +endif
> +
>  ifdef NO_PTHREADS
>  	BASIC_CFLAGS += -DNO_PTHREADS
>  else
> @@ -1628,10 +1657,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
> @@ -1714,6 +1756,7 @@ bindir_relative_SQ = $(subst ','\'',$(bindir_relative))
>  mandir_relative_SQ = $(subst ','\'',$(mandir_relative))
>  infodir_relative_SQ = $(subst ','\'',$(infodir_relative))
>  localedir_SQ = $(subst ','\'',$(localedir))
> +localedir_relative_SQ = $(subst ','\'',$(localedir_relative))
>  gitexecdir_SQ = $(subst ','\'',$(gitexecdir))
>  template_dir_SQ = $(subst ','\'',$(template_dir))
>  htmldir_relative_SQ = $(subst ','\'',$(htmldir_relative))
> @@ -1962,17 +2005,16 @@ perl/perl.mak: GIT-CFLAGS GIT-PREFIX perl/Makefile perl/Makefile.PL
>  	$(QUIET_SUBDIR0)perl $(QUIET_SUBDIR1) PERL_PATH='$(PERL_PATH_SQ)' prefix='$(prefix_SQ)' $(@F)
>  
>  PERL_DEFINES = $(PERL_PATH_SQ):$(PERLLIB_EXTRA_SQ)
> -$(SCRIPT_PERL_GEN): % : %.perl perl/perl.mak GIT-PERL-DEFINES GIT-VERSION-FILE
> +
> +$(SCRIPT_PERL_GEN): % : %.perl perl/perl.mak GIT-PERL-DEFINES GIT-PERL-HEADER GIT-VERSION-FILE
>  	$(QUIET_GEN)$(RM) $@ $@+ && \
>  	INSTLIBDIR=`MAKEFLAGS= $(MAKE) -C perl -s --no-print-directory instlibdir` && \
>  	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' \
>  	    $< >$@+ && \
> @@ -1986,6 +2028,29 @@ GIT-PERL-DEFINES: FORCE
>  		echo "$$FLAGS" >$@; \
>  	    fi
>  
> +GIT-PERL-HEADER: perl/perl.mak GIT-PERL-DEFINES FORCE
> +ifndef RUNTIME_PREFIX_PERL
> +	# Hardcode the runtime path.
> +	INSTLIBDIR=`MAKEFLAGS= $(MAKE) -C perl -s --no-print-directory instlibdir` && \
> +	INSTLIBDIR_EXTRA='$(PERLLIB_EXTRA_SQ)' && \
> +	echo \
> +	  'use lib (split(/$(pathsep)/, $$ENV{GITPERLLIB} || "'"$$INSTLIBDIR"'"));' \
> +	  >$@
> +else
> +	# Probe the runtime path relative to the PERL script. RUNTIME_PREFIX_PERL
> +	# automatically sets NO_PERL_MAKEMAKER, causing PERL scripts to be installed
> +	# to "$(prefix)/lib" (see "perl/Makefile"). This expectation is hard-coded
> +	# into the generated code below.
> +	GITEXECDIR='$(gitexecdir_SQ)' && \
> +	echo \
> +	  'sub _get_git_lib{'\
> +	  'use FindBin;'\
> +	  '(my $$p=$$FindBin::Bin)=~s=/'$${GITEXECDIR}'$$==;'\
> +		'return File::Spec->catdir($$p,"'"lib"'");' \
> +	  '};' \
> +	  'use lib (split(/$(pathsep)/, $$ENV{GITPERLLIB}||_get_git_lib()));'\
> +	  >$@
> +endif
>  
>  .PHONY: gitweb
>  gitweb:
> @@ -2130,6 +2195,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)"'
>  
> @@ -2147,7 +2213,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
> @@ -2704,7 +2770,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/cache.h b/cache.h
> index cb7fb7c00..fb7795410 100644
> --- a/cache.h
> +++ b/cache.h
> @@ -445,6 +445,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"
>  
>  /*
>   * This environment variable is expected to contain a boolean indicating
> diff --git a/common-main.c b/common-main.c
> index 6a689007e..6516a1f89 100644
> --- a/common-main.c
> +++ b/common-main.c
> @@ -32,12 +32,12 @@ int main(int argc, const char **argv)
>  	 */
>  	sanitize_stdfds();
>  
> +	git_resolve_executable_dir(argv[0]);
> +
>  	git_setup_gettext();
>  
>  	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 685a80d13..58fd62b4d 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
> @@ -206,6 +208,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
> @@ -218,6 +221,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
> @@ -236,6 +241,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 ce192a2d6..c9006c4c9 100644
> --- a/exec_cmd.c
> +++ b/exec_cmd.c
> @@ -2,53 +2,232 @@
>  #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 <mach-o/dyld.h>
> +#endif
> +
> +#if defined(HAVE_BSD_KERN_PROC_SYSCTL)
> +#include <sys/param.h>
> +#include <sys/types.h>
> +#include <sys/sysctl.h>
> +#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.  "
> -				"Using static fallback '%s'.\n", prefix);
> +			     "but prefix computation failed.  "
> +			     "Using static fallback '%s'.\n",
> +			     prefix);
>  	}
>  	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 applicaton 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 authoratative methods over
> +	 * selectively-available or non- authoratative 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 sysetems, and is not authoratative
> +	 * 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, and there is
> + * nothing to resolve.
> + */
> +
>  static const char *system_prefix(void)
>  {
>  	return PREFIX;
>  }
>  
> -void git_extract_argv0_path(const char *argv0)
> +void git_resolve_executable_dir(const char *argv0)
>  {
>  }
>  
> @@ -65,32 +244,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 +278,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 +302,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 +318,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 ff0b48048..2522453cd 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 db727ea02..6b64d5c2e 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 9e96dd409..dc4cc1419 100644
> --- a/git.c
> +++ b/git.c
> @@ -65,7 +65,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);

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH 1/1] exec_cmd: RUNTIME_PREFIX on some POSIX systems
  2017-11-19 17:31 ` [PATCH 1/1] " Dan Jacques
  2017-11-20  1:01   ` Junio C Hamano
@ 2017-11-20 21:00   ` Ævar Arnfjörð Bjarmason
  2017-11-20 21:50     ` Ævar Arnfjörð Bjarmason
  1 sibling, 1 reply; 7+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2017-11-20 21:00 UTC (permalink / raw)
  To: Dan Jacques; +Cc: git, Junio C Hamano, Johannes Schindelin


On Sun, Nov 19 2017, Dan Jacques jotted:

> [...]

Firstly the promise of this is very neat. I'm happy to offer any help I
can give.

> 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.
>
> Git's PERL tooling now responds to RUNTIME_PREFIX_PERL. When configured,
> Git's generated PERL scripts resolve the Git library location relative to
> their runtime paths instead of hard-coding them. Structural changes
> were made to Makefile to support selective PERL header generation.
>
> Small incidental formatting cleanup of "exec_cmd.c".

> +$(SCRIPT_PERL_GEN): % : %.perl perl/perl.mak GIT-PERL-DEFINES GIT-PERL-HEADER GIT-VERSION-FILE
>  	$(QUIET_GEN)$(RM) $@ $@+ && \
>  	INSTLIBDIR=`MAKEFLAGS= $(MAKE) -C perl -s --no-print-directory instlibdir` && \
>  	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' \
>  	    $< >$@+ && \
> @@ -1986,6 +2028,29 @@ GIT-PERL-DEFINES: FORCE
>  		echo "$$FLAGS" >$@; \
>  	    fi
>
> +GIT-PERL-HEADER: perl/perl.mak GIT-PERL-DEFINES FORCE
> +ifndef RUNTIME_PREFIX_PERL
> +	# Hardcode the runtime path.
> +	INSTLIBDIR=`MAKEFLAGS= $(MAKE) -C perl -s --no-print-directory instlibdir` && \
> +	INSTLIBDIR_EXTRA='$(PERLLIB_EXTRA_SQ)' && \
> +	echo \
> +	  'use lib (split(/$(pathsep)/, $$ENV{GITPERLLIB} || "'"$$INSTLIBDIR"'"));' \
> +	  >$@
> +else
> +	# Probe the runtime path relative to the PERL script. RUNTIME_PREFIX_PERL
> +	# automatically sets NO_PERL_MAKEMAKER, causing PERL scripts to be installed
> +	# to "$(prefix)/lib" (see "perl/Makefile"). This expectation is hard-coded
> +	# into the generated code below.
> +	GITEXECDIR='$(gitexecdir_SQ)' && \
> +	echo \
> +	  'sub _get_git_lib{'\
> +	  'use FindBin;'\
> +	  '(my $$p=$$FindBin::Bin)=~s=/'$${GITEXECDIR}'$$==;'\
> +		'return File::Spec->catdir($$p,"'"lib"'");' \
> +	  '};' \
> +	  'use lib (split(/$(pathsep)/, $$ENV{GITPERLLIB}||_get_git_lib()));'\
> +	  >$@
> +endif

If you run run:

    make -j8 prefix=/tmp/git RUNTIME_PREFIX=YesPlease RUNTIME_PREFIX_PERL= CFLAGS="-O0 -g" all install

And then:

    make -j8 prefix=/tmp/git RUNTIME_PREFIX=YesPlease RUNTIME_PREFIX_PERL=YesPlease CFLAGS="-O0 -g" all install

You end up with this:

    $ tree /tmp/git/{lib,share/perl}
    /tmp/git/lib
    └── x86_64-linux-gnu
        └── perl
            └── 5.26.1
                ├── auto
                │   └── Git
                └── perllocal.pod
    /tmp/git/share/perl
    └── 5.26.1
        [...]
        └── Git.pm

You need to bust the perl/PM.stamp cache as a function of your
RUNTIME_PREFIX_PERL variable (or NO_PERL_MAKEMAKER).

Other than that, is this whole NO_PERL_MAKEMAKER workaround just because
you couldn't figure out what the target RELPERLPATH is in
$prefix/$RELPERLPATH, which in this case is share/perl/5.26.1 ?

I don't remember offhand how to extract that, but htis is built into
perl itself, see e.g.:

    $ PERL5LIB= /usr/bin/perl -E 'say for grep { m[share|lib] } @INC'
    /usr/local/lib/x86_64-linux-gnu/perl/5.26.1
    /usr/local/share/perl/5.26.1
    /usr/lib/x86_64-linux-gnu/perl5/5.26
    /usr/share/perl5
    /usr/lib/x86_64-linux-gnu/perl/5.26
    /usr/share/perl/5.26
    /usr/local/lib/site_perl
    /usr/lib/x86_64-linux-gnu/perl-base

So it's in Config.pm somewhere IIRC. But your patch doesn't discuss why
you toggled NO_PERL_MAKEMAKER, is it purely to work around this issue,
and if we had some aesy way to figure out the target $RELPERLPATH we
wouldn't need to do that?

Seems a bit of baby & bathwater there :)

Aside from that:

1. The regex match you're doing to munge the dir could be done as a
   catdir($orig, '..', '..', 'lib'), that doesn't work as discussed
   above, but *might* be more portable. I say might because I don't know
   if the path string is always normalized to be unix-like, but if not
   this won't work e.g on Windows where it'll have \ not /.

2. You are 'use'-ing FindBin there unconditionally (use is not function
   local in perl), and implicitly assuming it loads File::Spec.

   Ignoring the NO_PERL_MAKEMAKER caveats above I'd suggest something
   like this:

       #!/usr/bin/perl

       # BEGIN RUNTIME_PREFIX_PERL=YesPlease generated code.
       #
       # This finds our Git::* libraries at relative locations.
       BEGIN {
           use lib split /:/,
           (
               $ENV{GITPERLLIB}
               ||
               do {
                   require FindBin;
                   require File::Spec;
                   File::Spec->catdir($FindBin::Bin, '..', '..', 'lib');
               }
           );
       }
       # END RUNTIME_PREFIX_PERL=YesPlease generated code.

       # Copyright (C) 2006, Eric Wong <normalperson@yhbt.net>
       # License: GPL v2 or later

   It's also nice to have some whitespace / comments to note that this
   is generated code.

 3. I may be squinting at this wrong but it seems to me that between
    your v1 and v2 reading GITPERLLIB here no longer makes any sense at
    all. You used to set it in git itself, now it takes priority but
    nothing sets it, presumably you'd have some external wrapper script
    that'll set it?

    Now if I compile with RUNTIME_PREFIX=YesPlease I get magic
    auto-discovery of C program paths, right? But it'll still fallback
    to the system perl libs (if any) unless
    RUNTIME_PREFIX_PERL=YesPlease is set. Shouldn't we just make
    RUNTIME_PREFIX=YesPlease Just Work for everything?

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH 1/1] exec_cmd: RUNTIME_PREFIX on some POSIX systems
  2017-11-20 21:00   ` Ævar Arnfjörð Bjarmason
@ 2017-11-20 21:50     ` Ævar Arnfjörð Bjarmason
  0 siblings, 0 replies; 7+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2017-11-20 21:50 UTC (permalink / raw)
  To: Dan Jacques; +Cc: git, Junio C Hamano, Johannes Schindelin


On Mon, Nov 20 2017, Ævar Arnfjörð Bjarmason jotted:

> On Sun, Nov 19 2017, Dan Jacques jotted:
>
>> [...]
>
> Firstly the promise of this is very neat. I'm happy to offer any help I
> can give.
>
>> 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.
>>
>> Git's PERL tooling now responds to RUNTIME_PREFIX_PERL. When configured,
>> Git's generated PERL scripts resolve the Git library location relative to
>> their runtime paths instead of hard-coding them. Structural changes
>> were made to Makefile to support selective PERL header generation.
>>
>> Small incidental formatting cleanup of "exec_cmd.c".
>
>> +$(SCRIPT_PERL_GEN): % : %.perl perl/perl.mak GIT-PERL-DEFINES GIT-PERL-HEADER GIT-VERSION-FILE
>>  	$(QUIET_GEN)$(RM) $@ $@+ && \
>>  	INSTLIBDIR=`MAKEFLAGS= $(MAKE) -C perl -s --no-print-directory instlibdir` && \
>>  	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' \
>>  	    $< >$@+ && \
>> @@ -1986,6 +2028,29 @@ GIT-PERL-DEFINES: FORCE
>>  		echo "$$FLAGS" >$@; \
>>  	    fi
>>
>> +GIT-PERL-HEADER: perl/perl.mak GIT-PERL-DEFINES FORCE
>> +ifndef RUNTIME_PREFIX_PERL
>> +	# Hardcode the runtime path.
>> +	INSTLIBDIR=`MAKEFLAGS= $(MAKE) -C perl -s --no-print-directory instlibdir` && \
>> +	INSTLIBDIR_EXTRA='$(PERLLIB_EXTRA_SQ)' && \
>> +	echo \
>> +	  'use lib (split(/$(pathsep)/, $$ENV{GITPERLLIB} || "'"$$INSTLIBDIR"'"));' \
>> +	  >$@
>> +else
>> +	# Probe the runtime path relative to the PERL script. RUNTIME_PREFIX_PERL
>> +	# automatically sets NO_PERL_MAKEMAKER, causing PERL scripts to be installed
>> +	# to "$(prefix)/lib" (see "perl/Makefile"). This expectation is hard-coded
>> +	# into the generated code below.
>> +	GITEXECDIR='$(gitexecdir_SQ)' && \
>> +	echo \
>> +	  'sub _get_git_lib{'\
>> +	  'use FindBin;'\
>> +	  '(my $$p=$$FindBin::Bin)=~s=/'$${GITEXECDIR}'$$==;'\
>> +		'return File::Spec->catdir($$p,"'"lib"'");' \
>> +	  '};' \
>> +	  'use lib (split(/$(pathsep)/, $$ENV{GITPERLLIB}||_get_git_lib()));'\
>> +	  >$@
>> +endif
>
> If you run run:
>
>     make -j8 prefix=/tmp/git RUNTIME_PREFIX=YesPlease RUNTIME_PREFIX_PERL= CFLAGS="-O0 -g" all install
>
> And then:
>
>     make -j8 prefix=/tmp/git RUNTIME_PREFIX=YesPlease RUNTIME_PREFIX_PERL=YesPlease CFLAGS="-O0 -g" all install
>
> You end up with this:
>
>     $ tree /tmp/git/{lib,share/perl}
>     /tmp/git/lib
>     └── x86_64-linux-gnu
>         └── perl
>             └── 5.26.1
>                 ├── auto
>                 │ └── Git
>                 └── perllocal.pod
>     /tmp/git/share/perl
>     └── 5.26.1
>         [...]
>         └── Git.pm
>
> You need to bust the perl/PM.stamp cache as a function of your
> RUNTIME_PREFIX_PERL variable (or NO_PERL_MAKEMAKER).
>
> Other than that, is this whole NO_PERL_MAKEMAKER workaround just because
> you couldn't figure out what the target RELPERLPATH is in
> $prefix/$RELPERLPATH, which in this case is share/perl/5.26.1 ?
>
> I don't remember offhand how to extract that, but htis is built into
> perl itself, see e.g.:
>
>     $ PERL5LIB= /usr/bin/perl -E 'say for grep { m[share|lib] } @INC'
>     /usr/local/lib/x86_64-linux-gnu/perl/5.26.1
>     /usr/local/share/perl/5.26.1
>     /usr/lib/x86_64-linux-gnu/perl5/5.26
>     /usr/share/perl5
>     /usr/lib/x86_64-linux-gnu/perl/5.26
>     /usr/share/perl/5.26
>     /usr/local/lib/site_perl
>     /usr/lib/x86_64-linux-gnu/perl-base
>
> So it's in Config.pm somewhere IIRC. But your patch doesn't discuss why
> you toggled NO_PERL_MAKEMAKER, is it purely to work around this issue,
> and if we had some aesy way to figure out the target $RELPERLPATH we
> wouldn't need to do that?
>
> Seems a bit of baby & bathwater there :)

So LeonT over at #p5p helped me with this. He believes this'll work
(unless MakeMaker INSTALL_BASE is set, but that should break the Git
install anyway):


    /usr/bin/perl -MConfig -wE 'my ($relsite) = $Config{installsitelib} =~ m[^\Q$Config{siteprefixexp}\E/(.+)]s; say $relsite'
    share/perl/5.26.1

I.e. aside from my spiel below injecting this should work, and would
eliminate the need to toggle NO_PERL_MAKEMAKER (untested):

    BEGIN {
        use lib split /:/,
        (
            $ENV{GITPERLLIB}
            ||
            do {
                require FindBin;
                require File::Spec;
                require Config;
                Config->import;

                my ($relsite) = $Config{installsitelib} =~ m[^\Q$Config{siteprefixexp}\E/(.+)]s
                    or die "PANIC: Ohes noes $Config{siteprefixexp} doesn't match a subset of $Config{installsitelib}";
                File::Spec->catdir($FindBin::Bin, '..', '..', $relsite);
            }
        );
    }


> Aside from that:
>
> 1. The regex match you're doing to munge the dir could be done as a
>    catdir($orig, '..', '..', 'lib'), that doesn't work as discussed
>    above, but *might* be more portable. I say might because I don't know
>    if the path string is always normalized to be unix-like, but if not
>    this won't work e.g on Windows where it'll have \ not /.
>
> 2. You are 'use'-ing FindBin there unconditionally (use is not function
>    local in perl), and implicitly assuming it loads File::Spec.
>
>    Ignoring the NO_PERL_MAKEMAKER caveats above I'd suggest something
>    like this:
>
>        #!/usr/bin/perl
>
>        # BEGIN RUNTIME_PREFIX_PERL=YesPlease generated code.
>        #
>        # This finds our Git::* libraries at relative locations.
>        BEGIN {
>            use lib split /:/,
>            (
>                $ENV{GITPERLLIB}
>                ||
>                do {
>                    require FindBin;
>                    require File::Spec;
>                    File::Spec->catdir($FindBin::Bin, '..', '..', 'lib');
>                }
>            );
>        }
>        # END RUNTIME_PREFIX_PERL=YesPlease generated code.
>
>        # Copyright (C) 2006, Eric Wong <normalperson@yhbt.net>
>        # License: GPL v2 or later
>
>    It's also nice to have some whitespace / comments to note that this
>    is generated code.
>
>  3. I may be squinting at this wrong but it seems to me that between
>     your v1 and v2 reading GITPERLLIB here no longer makes any sense at
>     all. You used to set it in git itself, now it takes priority but
>     nothing sets it, presumably you'd have some external wrapper script
>     that'll set it?
>
>     Now if I compile with RUNTIME_PREFIX=YesPlease I get magic
>     auto-discovery of C program paths, right? But it'll still fallback
>     to the system perl libs (if any) unless
>     RUNTIME_PREFIX_PERL=YesPlease is set. Shouldn't we just make
>     RUNTIME_PREFIX=YesPlease Just Work for everything?

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2017-11-20 21:50 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-11-16 17:05 [PATCH 0/1] RUNTIME_PREFIX on POSIX systems Dan Jacques
2017-11-16 17:05 ` [PATCH 1/1] exec_cmd: RUNTIME_PREFIX on some " Dan Jacques
2017-11-17  5:08   ` Junio C Hamano
  -- strict thread matches above, loose matches on Subject: below --
2017-11-19 17:31 [PATCH v2 0/1] " Dan Jacques
2017-11-19 17:31 ` [PATCH 1/1] " Dan Jacques
2017-11-20  1:01   ` Junio C Hamano
2017-11-20 21:00   ` Ævar Arnfjörð Bjarmason
2017-11-20 21:50     ` Ævar Arnfjörð Bjarmason

Code repositories for project(s) associated with this public inbox

	https://80x24.org/mirrors/git.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).