Subject: [PATCH] Install C source code for for debugging help

From: Paul Eggert <[email-masked]>
Date: Thu, 26 Sep 2019 13:07:52 -0700

Without this change, on typical GNU/Linux distributions
like Debian, the first button of ‘C-h f car RET’ does not
work because the source code for ‘car’ is not installed.
Fix this by installing the (compressed) C source code along with
the (compressed) Lisp source code that is already installed.
This adds about 3 MB (about 2%) to the size of the installed files
on my platform.
* Makefile.in (install_srcdir, enable_install_srcdir):
New macros.
(epaths-force): Substitute PATH_SOURCE too.
(exp_sourcesrcdir): Install copy of C source code if
enable_install_srcdir says to.
* configure.ac (install_srcdir): New var.
Add support for --disable-install-srcdir (to disable installation
of source) and for --enable-install-srcdir='.' (to have Emacs
refer to its source dir).
* lisp/emacs-lisp/find-func.el (find-function-C-source):
Also look for gzipped source files.
* src/emacs.c (init_cmdargs): Set source-directory to
be the same as installation-directory when
running with an uninstalled Emacs.
* src/epaths.in (PATH_SOURCE): New macro.
* src/lread.c (syms_of_lread): When initializing
source-directory, use PATH_SOURCE as-is if it is absolute;
otherwise, use it relative to PATH_DUMPLOADSEARCH/.. as	before.
---
 Makefile.in                  | 25 +++++++++++++++++++++++++
 configure.ac                 | 19 +++++++++++++++++++
 etc/NEWS                     |  6 ++++++
 lisp/emacs-lisp/find-func.el |  5 ++++-
 src/emacs.c                  |  2 ++
 src/epaths.in                |  3 +++
 src/lread.c                  |  8 ++++++--
 7 files changed, 65 insertions(+), 3 deletions(-)

diff --git a/Makefile.in b/Makefile.in
index aa11e6b0b7..6ffb8eb747 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -266,6 +266,10 @@ etcdir=
 # once.
 archlibdir=@archlibdir@
 
+# Where to put installed source code, and whether to install it at all.
+install_srcdir=@install_srcdir@
+enable_install_srcdir=@enable_install_srcdir@
+
 # Where to put the etc/DOC file.
 etcdocdir=@etcdocdir@
 
@@ -374,6 +378,7 @@ epaths-force:
 	  -e 's;\(#.*PATH_BITMAPS\).*$$;\1 "${bitmapdir}";'		\
 	  -e 's;\(#.*PATH_X_DEFAULTS\).*$$;\1 "${x_default_search_path}";' \
 	  -e 's;\(#.*PATH_GAME\).*$$;\1 $(PATH_GAME);'			\
+	  -e 's;\(#.*PATH_SOURCE\).*$$;\1 "${install_srcdir}";'	\
 	  -e 's;\(#.*PATH_DOC\).*$$;\1 "${etcdocdir}";') &&		\
 	${srcdir}/build-aux/move-if-change epaths.h.$$$$ src/epaths.h
 
@@ -624,6 +629,26 @@ install-arch-indep:
 	  done; \
 	  ${GZIP_PROG} -9n "../etc/publicsuffix.txt"; \
 	}
+ifneq ($(enable_install_srcdir),no)
+	-unset CDPATH; \
+	umask 022; $(MKDIR_P) "$(DESTDIR)$(install_srcdir)/src" && \
+	exp_sourcesrcdir=`cd "$(DESTDIR)$(install_srcdir)/src" && /bin/pwd` && \
+	[ "`cd $(srcdir)/src && /bin/pwd`" = "$$exp_sourcesrcdir" ] || { \
+	  $(set_installuser); \
+	  printf 'Copying compressed C sources to %s ...\n' \
+		 "$(DESTDIR)$(install_srcdir)/src"; \
+	  for file in `cd $(srcdir) && echo src/*.[cm]`; do \
+	    installed_file="$(DESTDIR)$(install_srcdir)/$$file" && \
+	    $(INSTALL_DATA) "$$file" "$$installed_file" && \
+	    [ -z "$(GZIP_PROG)" ] || { \
+	      rm -f "$$installed_file.gz" && \
+	      $(GZIP_PROG) -9n "$$installed_file" && \
+	      installed_file=$$installed_file.gz; \
+	    } || exit; \
+	    chown $$installuser "$$installed_file" || true; \
+	  done; \
+	}
+endif
 	-chmod -R a+r "$(DESTDIR)${datadir}/emacs/${version}" ${COPYDESTS}
 
 ## The above chmods are needed because "umask 022; tar ..." is not
diff --git a/configure.ac b/configure.ac
index 7435f2e8da..ac50e6e297 100644
--- a/configure.ac
+++ b/configure.ac
@@ -194,6 +194,7 @@
 lisppath='${locallisppath}:${standardlisppath}'
 etcdir='${datadir}/emacs/${version}/etc'
 archlibdir='${libexecdir}/emacs/${version}/${configuration}'
+install_srcdir='${datadir}/emacs/${version}'
 etcdocdir='${datadir}/emacs/${version}/etc'
 gamedir='${localstatedir}/games/emacs'
 
@@ -540,6 +541,21 @@ AC_DEFUN
   locallisppath=${enableval} locallisppathset=yes
 fi)
 
+AC_ARG_ENABLE([install-srcdir],
+  [AS_HELP_STRING([--disable-install-srcdir],
+     [do not install low-level Emacs source code useful for debugging.
+      Use '--enable-install-srcdir=.' to have Emacs refer to the
+      source directory it was configured from.])],
+  [],
+  [enable_install_srcdir=yes])
+case $enable_install_srcdir in
+  yes | no) ;;
+  .) install_srcdir=`cd "$srcdir" && /bin/pwd` ||
+       AC_MSG_ERROR([cannot get srcdir name])
+     enable_install_srcdir=no;;
+  *) AC_MSG_ERROR([invalid install-srcdir]);;
+esac
+
 AC_ARG_ENABLE(checking,
 [AS_HELP_STRING([--enable-checking@<:@=LIST@:>@],
 		[enable expensive checks.  With LIST,
@@ -2061,6 +2077,7 @@ AC_DEFUN
      dnl This one isn't really used, only archlibdir is.
      libexecdir="\${ns_appbindir}/libexec"
      archlibdir="\${ns_appbindir}/libexec"
+     install_srcdir="\${ns_appresdir}"
      etcdocdir="\${ns_appresdir}/etc"
      etcdir="\${ns_appresdir}/etc"
      dnl FIXME maybe set datarootdir instead.
@@ -5211,6 +5228,8 @@ AC_DEFUN
 AC_SUBST(x_default_search_path)
 AC_SUBST(etcdir)
 AC_SUBST(archlibdir)
+AC_SUBST([enable_install_srcdir])
+AC_SUBST([install_srcdir])
 AC_SUBST(etcdocdir)
 AC_SUBST(bitmapdir)
 AC_SUBST(gamedir)
diff --git a/etc/NEWS b/etc/NEWS
index 0a4ada3cc6..7643905256 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -125,6 +125,12 @@ removed configure options.)
 ** The distribution tarball now has test cases; 'make check' runs them.
 This is intended mostly to help developers.
 
+---
+** Emacs now installs a copy of its C source code, used for debugging help.
+For example, pressing the first button in the *Help* buffer generated
+by 'C-h f car RET' now takes you to a copy of the C-language
+implementation of the function 'car'.
+
 ---
 ** Emacs now requires GTK 2.24 and GTK 3.10 for the GTK 2 and GTK 3
 builds respectively.
diff --git a/lisp/emacs-lisp/find-func.el b/lisp/emacs-lisp/find-func.el
index 142c99edd4..2812ae6f22 100644
--- a/lisp/emacs-lisp/find-func.el
+++ b/lisp/emacs-lisp/find-func.el
@@ -245,7 +245,10 @@ find-function-C-source
   (let ((dir (or find-function-C-source-directory
                  (read-directory-name "Emacs C source dir: " nil nil t))))
     (setq file (expand-file-name file dir))
-    (if (file-readable-p file)
+    (if (or (file-readable-p file)
+	    (let ((file-gz (concat file ".gz")))
+	      (and (file-readable-p file-gz)
+		   (setq file file-gz))))
         (if (null find-function-C-source-directory)
             (setq find-function-C-source-directory dir))
       (error "The C source file %s is not available"
diff --git a/src/emacs.c b/src/emacs.c
index 21a05d337e..fa547b59f5 100644
--- a/src/emacs.c
+++ b/src/emacs.c
@@ -522,6 +522,7 @@ init_cmdargs (int argc, char **argv, int skip_args, char const *original_pwd)
 	      if (!NILP (etc_exists))
 		{
                   Vinstallation_directory = Ffile_name_as_directory (dir);
+		  Vsource_directory = Vinstallation_directory;
 		  break;
 		}
 	    }
@@ -547,6 +548,7 @@ init_cmdargs (int argc, char **argv, int skip_args, char const *original_pwd)
 		{
 		  tem = Fexpand_file_name (build_string (".."), dir);
                   Vinstallation_directory = Ffile_name_as_directory (tem);
+		  Vsource_directory = Vinstallation_directory;
 		  break;
 		}
 	    }
diff --git a/src/epaths.in b/src/epaths.in
index 5fe35b64c8..4e15c3a3d6 100644
--- a/src/epaths.in
+++ b/src/epaths.in
@@ -73,5 +73,8 @@ along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.  */
 /* Where Emacs should store game score files.  */
 #define PATH_GAME "/usr/local/var/games/emacs"
 
+/* Where Emacs should look for installed sources.  */
+#define PATH_SOURCE "/usr/local/share/emacs"
+
 /* Where Emacs should look for the application default file. */
 #define PATH_X_DEFAULTS "/usr/lib/X11/%L/%T/%N%C%S:/usr/lib/X11/%l/%T/%N%C%S:/usr/lib/X11/%T/%N%C%S:/usr/lib/X11/%L/%T/%N%S:/usr/lib/X11/%l/%T/%N%S:/usr/lib/X11/%T/%N%S"
diff --git a/src/lread.c b/src/lread.c
index 5000b38a01..849632a7ec 100644
--- a/src/lread.c
+++ b/src/lread.c
@@ -4979,9 +4979,13 @@ syms_of_lread (void)
   DEFVAR_LISP ("source-directory", Vsource_directory,
 	       doc: /* Directory in which Emacs sources were found when Emacs was built.
 You cannot count on them to still be there!  */);
+  char const *path_source = PATH_SOURCE;
   Vsource_directory
-    = Fexpand_file_name (build_string ("../"),
-			 Fcar (decode_env_path (0, PATH_DUMPLOADSEARCH, 0)));
+    = (file_name_absolute_p (path_source)
+       ? build_string (path_source)
+       : Fexpand_file_name (build_string ("../" PATH_SOURCE),
+			    Fcar (decode_env_path (0, PATH_DUMPLOADSEARCH,
+						   false))));
 
   DEFVAR_LISP ("preloaded-file-list", Vpreloaded_file_list,
 	       doc: /* List of files that were preloaded (when dumping Emacs).  */);
-- 
2.21.0