Merge branch 'dj/runtime-prefix'
authorJunio C Hamano <gitster@pobox.com>
Tue, 8 May 2018 06:59:17 +0000 (15:59 +0900)
committerJunio C Hamano <gitster@pobox.com>
Tue, 8 May 2018 06:59:17 +0000 (15:59 +0900)
A build-time option has been added to allow Git to be told to refer
to its associated files relative to the main binary, in the same
way that has been possible on Windows for quite some time, for
Linux, BSDs and Darwin.

* dj/runtime-prefix:
Makefile: quote $INSTLIBDIR when passing it to sed
Makefile: remove unused @@PERLLIBDIR@@ substitution variable
mingw/msvc: use the new-style RUNTIME_PREFIX helper
exec_cmd: provide a new-style RUNTIME_PREFIX helper for Windows
exec_cmd: RUNTIME_PREFIX on some POSIX systems
Makefile: add Perl runtime prefix support
Makefile: generate Perl header from template file

1  2 
.gitignore
Makefile
cache.h
common-main.c
exec-cmd.c
exec-cmd.h
gettext.c
git.c
diff --cc .gitignore
Simple merge
diff --cc Makefile
index b345d0eaa956e9a28be7c852e1988cc9e81b65bb,2ba24035f55d8cc5b10aaf44bddbf4c3d86bf089..46a215dc89f61ccb24ade982315251c3ed2de16a
+++ b/Makefile
@@@ -2021,10 -2102,19 +2109,23 @@@ GIT-PERL-DEFINES: FORC
                echo "$$FLAGS" >$@; \
            fi
  
+ GIT-PERL-HEADER: $(PERL_HEADER_TEMPLATE) GIT-PERL-DEFINES Makefile
+       $(QUIET_GEN)$(RM) $@ && \
+       INSTLIBDIR='$(perllibdir_SQ)' && \
+       INSTLIBDIR_EXTRA='$(PERLLIB_EXTRA_SQ)' && \
+       INSTLIBDIR="$$INSTLIBDIR$${INSTLIBDIR_EXTRA:+:$$INSTLIBDIR_EXTRA}" && \
+       sed -e 's=@@PATHSEP@@=$(pathsep)=g' \
+           -e "s=@@INSTLIBDIR@@=$$INSTLIBDIR=g" \
+           -e 's=@@PERLLIBDIR_REL@@=$(perllibdir_relative_SQ)=g' \
+           -e 's=@@GITEXECDIR_REL@@=$(gitexecdir_relative_SQ)=g' \
+           -e 's=@@LOCALEDIR_REL@@=$(localedir_relative_SQ)=g' \
+           $< >$@+ && \
+       mv $@+ $@
 +.PHONY: perllibdir
 +perllibdir:
 +      @echo '$(perllibdir_SQ)'
 +
  .PHONY: gitweb
  gitweb:
        $(QUIET_SUBDIR0)gitweb $(QUIET_SUBDIR1) all
  $(OBJECTS): $(LIB_H)
  endif
  
 -exec_cmd.sp exec_cmd.s exec_cmd.o: GIT-PREFIX
 -exec_cmd.sp exec_cmd.s exec_cmd.o: EXTRA_CPPFLAGS = \
 +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)"'
  
diff --cc cache.h
Simple merge
diff --cc common-main.c
Simple merge
diff --cc exec-cmd.c
index 8a8261746afc66441408e1a4ea3b6f98dba5f884,0000000000000000000000000000000000000000..3b0a039083a8d377325983272e4aa38ee1b5674c
mode 100644,000000..100644
--- /dev/null
@@@ -1,165 -1,0 +1,365 @@@
- #define MAX_ARGS      32
 +#include "cache.h"
 +#include "exec-cmd.h"
 +#include "quote.h"
 +#include "argv-array.h"
- static const char *argv_exec_path;
 +
- static const char *argv0_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
-       assert(argv0_path);
-       assert(is_absolute_path(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;
 +
-           !(prefix = strip_path_suffix(argv0_path, GIT_EXEC_PATH)) &&
-           !(prefix = strip_path_suffix(argv0_path, BINDIR)) &&
-           !(prefix = strip_path_suffix(argv0_path, "git"))) {
++      assert(executable_dirname);
++      assert(is_absolute_path(executable_dirname));
 +
 +      if (!prefix &&
- void git_extract_argv0_path(const char *argv0)
++          !(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);
 +      }
 +      return prefix;
 +}
 +
-               return;
++/*
++ * 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)
-               argv0_path = xstrndup(argv0, slash - argv0);
++              return -1;
 +
 +      slash = find_last_dir_sep(argv0);
++      if (slash) {
++              trace_printf("trace: resolved executable path from argv0: %s\n",
++                           argv0);
++              strbuf_add_absolute_path(buf, argv0);
++              return 0;
++      }
++      return -1;
++}
++
++#ifdef PROCFS_EXECUTABLE_PATH
++/*
++ * Resolves the executable path by examining a procfs symlink.
++ *
++ * Returns 0 on success, -1 on failure.
++ */
++static int git_get_exec_path_procfs(struct strbuf *buf)
++{
++      if (strbuf_realpath(buf, PROCFS_EXECUTABLE_PATH, 0)) {
++              trace_printf(
++                      "trace: resolved executable path from procfs: %s\n",
++                      buf->buf);
++              return 0;
++      }
++      return -1;
++}
++#endif /* PROCFS_EXECUTABLE_PATH */
++
++#ifdef HAVE_BSD_KERN_PROC_SYSCTL
++/*
++ * Resolves the executable path using KERN_PROC_PATHNAME BSD sysctl.
++ *
++ * Returns 0 on success, -1 on failure.
++ */
++static int git_get_exec_path_bsd_sysctl(struct strbuf *buf)
++{
++      int mib[4];
++      char path[MAXPATHLEN];
++      size_t cb = sizeof(path);
++
++      mib[0] = CTL_KERN;
++      mib[1] = KERN_PROC;
++      mib[2] = KERN_PROC_PATHNAME;
++      mib[3] = -1;
++      if (!sysctl(mib, 4, path, &cb, NULL, 0)) {
++              trace_printf(
++                      "trace: resolved executable path from sysctl: %s\n",
++                      path);
++              strbuf_addstr(buf, path);
++              return 0;
++      }
++      return -1;
++}
++#endif /* HAVE_BSD_KERN_PROC_SYSCTL */
++
++#ifdef HAVE_NS_GET_EXECUTABLE_PATH
++/*
++ * Resolves the executable path by querying Darwin application stack.
++ *
++ * Returns 0 on success, -1 on failure.
++ */
++static int git_get_exec_path_darwin(struct strbuf *buf)
++{
++      char path[PATH_MAX];
++      uint32_t size = sizeof(path);
++      if (!_NSGetExecutablePath(path, &size)) {
++              trace_printf(
++                      "trace: resolved executable path from Darwin stack: %s\n",
++                      path);
++              strbuf_addstr(buf, path);
++              return 0;
++      }
++      return -1;
++}
++#endif /* HAVE_NS_GET_EXECUTABLE_PATH */
++
++#ifdef HAVE_WPGMPTR
++/*
++ * Resolves the executable path by using the global variable _wpgmptr.
++ *
++ * Returns 0 on success, -1 on failure.
++ */
++static int git_get_exec_path_wpgmptr(struct strbuf *buf)
++{
++      int len = wcslen(_wpgmptr) * 3 + 1;
++      strbuf_grow(buf, len);
++      len = xwcstoutf(buf->buf, _wpgmptr, len);
++      if (len < 0)
++              return -1;
++      buf->len += len;
++      return 0;
++}
++#endif /* HAVE_WPGMPTR */
++
++/*
++ * Resolves the absolute path of the current executable.
++ *
++ * Returns 0 on success, -1 on failure.
++ */
++static int git_get_exec_path(struct strbuf *buf, const char *argv0)
++{
++      /*
++       * Identifying the executable path is operating system specific.
++       * Selectively employ all available methods in order of preference,
++       * preferring highly-available authoritative methods over
++       * selectively-available or non-authoritative methods.
++       *
++       * All cases fall back on resolving against argv[0] if there isn't a
++       * better functional method. However, note that argv[0] can be
++       * used-supplied on many operating systems, and is not authoritative
++       * in those cases.
++       *
++       * Each of these functions returns 0 on success, so evaluation will stop
++       * after the first successful method.
++       */
++      if (
++#ifdef HAVE_BSD_KERN_PROC_SYSCTL
++              git_get_exec_path_bsd_sysctl(buf) &&
++#endif /* HAVE_BSD_KERN_PROC_SYSCTL */
++
++#ifdef HAVE_NS_GET_EXECUTABLE_PATH
++              git_get_exec_path_darwin(buf) &&
++#endif /* HAVE_NS_GET_EXECUTABLE_PATH */
++
++#ifdef PROCFS_EXECUTABLE_PATH
++              git_get_exec_path_procfs(buf) &&
++#endif /* PROCFS_EXECUTABLE_PATH */
++
++#ifdef HAVE_WPGMPTR
++              git_get_exec_path_wpgmptr(buf) &&
++#endif /* HAVE_WPGMPTR */
++
++              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)
- void git_extract_argv0_path(const char *argv0)
++              resolved[slash - resolved] = '\0';
++
++      executable_dirname = resolved;
++      trace_printf("trace: resolved executable dir: %s\n",
++                   executable_dirname);
 +}
 +
 +#else
 +
++/*
++ * When not using a runtime prefix, Git uses a hard-coded path.
++ */
 +static const char *system_prefix(void)
 +{
 +      return PREFIX;
 +}
 +
- void git_set_argv_exec_path(const char *exec_path)
++/*
++ * This is called during initialization, but No work needs to be done here when
++ * runtime prefix is not being used.
++ */
++void git_resolve_executable_dir(const char *argv0)
 +{
 +}
 +
 +#endif /* RUNTIME_PREFIX */
 +
 +char *system_path(const char *path)
 +{
 +      struct strbuf d = STRBUF_INIT;
 +
 +      if (is_absolute_path(path))
 +              return xstrdup(path);
 +
 +      strbuf_addf(&d, "%s/%s", system_prefix(), path);
 +      return strbuf_detach(&d, NULL);
 +}
 +
-       argv_exec_path = exec_path;
++static const char *exec_path_value;
++
++void git_set_exec_path(const char *exec_path)
 +{
- /* Returns the highest-priority, location to look for git programs. */
++      exec_path_value = exec_path;
 +      /*
 +       * Propagate this setting to external programs.
 +       */
 +      setenv(EXEC_PATH_ENVIRONMENT, exec_path, 1);
 +}
 +
-       static char *cached_exec_path;
-       if (argv_exec_path)
-               return argv_exec_path;
-       if (!cached_exec_path) {
++/* Returns the highest-priority location to look for git programs. */
 +const char *git_exec_path(void)
 +{
-                       cached_exec_path = xstrdup(env);
++      if (!exec_path_value) {
 +              const char *env = getenv(EXEC_PATH_ENVIRONMENT);
 +              if (env && *env)
-                       cached_exec_path = system_path(GIT_EXEC_PATH);
++                      exec_path_value = xstrdup(env);
 +              else
-       return cached_exec_path;
++                      exec_path_value = system_path(GIT_EXEC_PATH);
 +      }
-       add_path(&new_path, git_exec_path());
++      return exec_path_value;
 +}
 +
 +static void add_path(struct strbuf *out, const char *path)
 +{
 +      if (path && *path) {
 +              strbuf_add_absolute_path(out, path);
 +              strbuf_addch(out, PATH_SEP);
 +      }
 +}
 +
 +void setup_path(void)
 +{
++      const char *exec_path = git_exec_path();
 +      const char *old_path = getenv("PATH");
 +      struct strbuf new_path = STRBUF_INIT;
 +
- int execv_git_cmd(const char **argv) {
++      git_set_exec_path(exec_path);
++      add_path(&new_path, exec_path);
 +
 +      if (old_path)
 +              strbuf_addstr(&new_path, old_path);
 +      else
 +              strbuf_addstr(&new_path, _PATH_DEFPATH);
 +
 +      setenv("PATH", new_path.buf, 1);
 +
 +      strbuf_release(&new_path);
 +}
 +
 +const char **prepare_git_cmd(struct argv_array *out, const char **argv)
 +{
 +      argv_array_push(out, "git");
 +      argv_array_pushv(out, argv);
 +      return out->argv;
 +}
 +
- int execl_git_cmd(const char *cmd,...)
++int execv_git_cmd(const char **argv)
++{
 +      struct argv_array nargv = ARGV_ARRAY_INIT;
 +
 +      prepare_git_cmd(&nargv, argv);
 +      trace_argv_printf(nargv.argv, "trace: exec:");
 +
 +      /* execvp() can only ever return if it fails */
 +      sane_execvp("git", (char **)nargv.argv);
 +
 +      trace_printf("trace: exec failed: %s\n", strerror(errno));
 +
 +      argv_array_clear(&nargv);
 +      return -1;
 +}
 +
++int execl_git_cmd(const char *cmd, ...)
 +{
 +      int argc;
 +      const char *argv[MAX_ARGS + 1];
 +      const char *arg;
 +      va_list param;
 +
 +      va_start(param, cmd);
 +      argv[0] = cmd;
 +      argc = 1;
 +      while (argc < MAX_ARGS) {
 +              arg = argv[argc++] = va_arg(param, char *);
 +              if (!arg)
 +                      break;
 +      }
 +      va_end(param);
 +      if (MAX_ARGS <= argc)
 +              return error("too many args to run %s", cmd);
 +
 +      argv[argc] = NULL;
 +      return execv_git_cmd(argv);
 +}
diff --cc exec-cmd.h
index ff0b48048a6ba82827b4d441654c34c21cfb7b9a,0000000000000000000000000000000000000000..2522453cdaa74d9e9d806f20036518a394dd32e2
mode 100644,000000..100644
--- /dev/null
@@@ -1,16 -1,0 +1,16 @@@
- extern void git_set_argv_exec_path(const char *exec_path);
- extern void git_extract_argv0_path(const char *path);
 +#ifndef GIT_EXEC_CMD_H
 +#define GIT_EXEC_CMD_H
 +
 +struct argv_array;
 +
++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);
 +extern int execv_git_cmd(const char **argv); /* NULL terminated */
 +LAST_ARG_MUST_BE_NULL
 +extern int execl_git_cmd(const char *cmd, ...);
 +extern char *system_path(const char *path);
 +
 +#endif /* GIT_EXEC_CMD_H */
diff --cc gettext.c
index db727ea0204aa13acea0f189ea03e59b5cdb918f,6b64d5c2e8a5cc2a194ba1486de05437dd8051c6..baba28343c311594e048d62e190ded09e49ed5b0
+++ b/gettext.c
@@@ -2,7 -2,8 +2,8 @@@
   * Copyright (c) 2010 Ævar Arnfjörð Bjarmason
   */
  
- #include "git-compat-util.h"
+ #include "cache.h"
 -#include "exec_cmd.h"
++#include "exec-cmd.h"
  #include "gettext.h"
  #include "strbuf.h"
  #include "utf8.h"
diff --cc git.c
Simple merge