Subject: Re: bug#37527: [PATCH] Install C source code for for debugging help

From: Paul Eggert <[email-masked]>
Date: Fri, 24 Jan 2020 16:47:49 -0800

On 1/23/20 10:24 AM, Eli Zaretskii wrote:

> In that case, I will repeat my suggestion to have a separate variable
> for the installed sources, because otherwise we are making an
> incompatible behavior change.

OK, I did it that way by installing the attached patch instead; the new 
separate variable is 'emacs-source-directory'.
>From a0a70d7e1fe2237c1b0f374b72aba324b7de95fa Mon Sep 17 00:00:00 2001
From: Paul Eggert <[email-masked]>
Date: Fri, 24 Jan 2020 16:41:38 -0800
Subject: [PATCH] Install C source code for C-h f etc.
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

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 (Bug#37527).
Fix this by installing the (compressed) C source code alongside
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 (emacs_srcdir): New macro.
(epaths-force): Substitute PATH_EMACS_SOURCE.
(install-c-src): New rule, that installs a copy of the C source
code if emacs_srcdir says to.
(install-arch-indep): Depend on it.
* configure.ac (emacs_srcdir): New var.
Add support for --disable-install-srcdir.
* lisp/emacs-lisp/find-func.el (find-function-C-source-directory):
Look in emacs-source-directory first.
(find-function-C-source): Also look for gzipped source files.
* lisp/startup.el (normal-top-level):
Also recode emacs-source-directory.
* src/epaths.in (PATH_EMACS_SOURCE): New macro.
* src/lread.c: Include dosname.h, for IS_ABSOLUTE_FILE_NAME.
(syms_of_lread): New var emacs-source-directory.
---
 INSTALL                      | 65 ++++++++++++++++++------------------
 Makefile.in                  | 31 +++++++++++++++--
 configure.ac                 | 14 ++++++++
 etc/NEWS                     |  9 +++++
 lisp/emacs-lisp/find-func.el | 11 ++++--
 lisp/startup.el              |  3 +-
 src/epaths.in                |  4 +++
 src/lread.c                  | 10 +++++-
 8 files changed, 108 insertions(+), 39 deletions(-)

diff --git a/INSTALL b/INSTALL
index 2d257f9ce6..cb12e4a17a 100644
--- a/INSTALL
+++ b/INSTALL
@@ -214,41 +214,42 @@ like 'apt-get build-dep emacs' (on older systems, replace 'emacs' with
 eg 'emacs25').  On Red Hat-based systems, the corresponding command is
 'dnf builddep emacs' (on older systems, use 'yum-builddep' instead).
 
+
+DEBUGGING AN INSTALLED EMACS
+
+* Installed Emacs source code
+
+Emacs installs a compressed copy of much of its source code, to make
+it easy for users to read the source code of Emacs via commands like
+M-x describe-function (C-h f) to display the definition of a function.
+This compressed copy ordinarily includes both the Elisp source code
+that Emacs is mostly written in, as well as the C source code for the
+core Emacs executable.
+
 * GNU/Linux source and debug packages
 
 Many GNU/Linux systems provide separate packages containing the
-sources and debug symbols of Emacs.  They are useful if you want to
-check the source code of Emacs primitive functions or debug Emacs on
-the C level.
-
-The names of the packages that you need vary according to the
-GNU/Linux distribution that you use.  On Debian-based systems, you can
-install a source package of Emacs with a command like 'apt-get source
-emacs' (on older systems, replace 'emacs' with eg 'emacs25').  The
-target directory for unpacking the source tree is the current
-directory.  On Red Hat-based systems, the corresponding command is
-'dnf install emacs-debugsource', with target directory /usr/src/debug
-(this requires to add the *-debuginfo repositories first, via 'dnf
-config-manager --set-enabled fedora-debuginfo updates-debuginfo').
-
-Once you have installed the source package, for example at
-/path/to/emacs-26.1, add the following line to your startup file:
-
-     (setq find-function-C-source-directory
-           "/path/to/emacs-26.1/src")
-
-The installation directory of the Emacs source package will contain
-the exact package name and version number Emacs is installed on your
-system.  If a new Emacs package is installed, the source package must
-be reinstalled as well, and the setting in your startup file must be
-updated.
-
-Emacs debugging symbols are distributed by a debug package.  It does
-not exist for every released Emacs package, this depends on the
-distribution.  On Debian-based systems, you can install a debug
-package of Emacs with a command like 'apt-get install emacs-dbg' (on
-older systems, replace 'emacs' with eg 'emacs25').  On Red Hat-based
-systems, the corresponding command is 'dnf debuginfo-install emacs'.
+sources and debug symbols of Emacs.  They can help you debug the
+installed Emacs on the C level.  The procedures for installing these
+packages depend on the GNU/Linux system that you use.
+
+Emacs debugging symbols are distributed by a debug package if one
+exists for your system.  On Debian-based systems, you can
+install a debug package of Emacs with a command like 'apt-get install
+emacs-dbg' (on older systems, replace 'emacs' with e.g. 'emacs25').
+On Red Hat-based systems, the corresponding command is 'dnf
+debuginfo-install emacs'; this may require adding the *-debuginfo
+repositories first, via 'dnf config-manager --set-enabled
+fedora-debuginfo updates-debuginfo'.
+
+Some systems also have an Emacs source package that is also helpful
+when debugging the installed Emacs.  To unpack an Emacs source package
+into the current directory on Debian-based systems, you can use a
+command like 'apt-get source emacs' (on older systems, replace 'emacs'
+with e.g. 'emacs25'); you may first need to add the appropriate
+'source' URIs to your sources.list.  On Red Hat-based systems,
+installing the debugging symbols automatically installs the
+corresponding source package in the appropriate location.

 DETAILED BUILDING AND INSTALLATION:
diff --git a/Makefile.in b/Makefile.in
index 2c82c49fba..adefa98fd0 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -266,6 +266,9 @@ etcdir=
 # once.
 archlibdir=@archlibdir@
 
+# Where to install Emacs C source code, or empty if it is not installed.
+emacs_srcdir=@emacs_srcdir@
+
 # Where to put the etc/DOC file.
 etcdocdir=@etcdocdir@
 
@@ -374,6 +377,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_EMACS_SOURCE\).*$$;\1 "${emacs_srcdir}";'	\
 	  -e 's;\(#.*PATH_DOC\).*$$;\1 "${etcdocdir}";') &&		\
 	${srcdir}/build-aux/move-if-change epaths.h.$$$$ src/epaths.h
 
@@ -461,7 +465,7 @@ $(srcdir)/configure:
 # ==================== Installation ====================
 
 .PHONY: install install-arch-dep install-arch-indep install-etcdoc install-info
-.PHONY: install-man install-etc install-strip install-$(NTDIR)
+.PHONY: install-man install-c-src install-etc install-strip install-$(NTDIR)
 .PHONY: uninstall uninstall-$(NTDIR)
 
 ## If we let lib-src do its own installation, that means we
@@ -568,7 +572,8 @@ set_installuser=
 ## work correctly, and therefore no idea when tar can be replaced.
 ## See also these comments from 2004 about cp -r working fine:
 ## https://lists.gnu.org/r/autoconf-patches/2004-11/msg00005.html
-install-arch-indep: lisp install-info install-man ${INSTALL_ARCH_INDEP_EXTRA}
+install-arch-indep: lisp install-info install-man install-c-src \
+  $(INSTALL_ARCH_INDEP_EXTRA)
 	umask 022 && $(MKDIR_P) "$(DESTDIR)$(includedir)"
 	$(INSTALL_DATA) src/emacs-module.h "$(DESTDIR)$(includedir)/emacs-module.h"
 	-set ${COPYDESTS} ; \
@@ -700,6 +705,28 @@ install-man:
 	  ${GZIP_PROG} -9n "$(DESTDIR)${man1dir}/$${dest}" || true; \
 	done
 
+install-c-src:
+ifneq (,$(emacs_srcdir))
+	-unset CDPATH; \
+	umask 022; $(MKDIR_P) "$(DESTDIR)$(emacs_srcdir)/src" && \
+	exp_sourcesrcdir=`cd "$(DESTDIR)$(emacs_srcdir)/src" && /bin/pwd` && \
+	[ "`cd $(srcdir)/src && /bin/pwd`" = "$$exp_sourcesrcdir" ] || { \
+	  $(set_installuser); \
+	  printf 'Copying compressed C sources to %s ...\n' \
+		 "$(DESTDIR)$(emacs_srcdir)/src"; \
+	  for file in `cd $(srcdir) && echo src/*.[cm]`; do \
+	    installed_file="$(DESTDIR)$(emacs_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
+
 ## Install those items from etc/ that need to end up elsewhere.
 
 ## If you prefer, choose "emacs22" at installation time.
diff --git a/configure.ac b/configure.ac
index 27e44dacfb..4de83c6b26 100644
--- a/configure.ac
+++ b/configure.ac
@@ -194,6 +194,7 @@
 lisppath='${locallisppath}:${standardlisppath}'
 etcdir='${datadir}/emacs/${version}/etc'
 archlibdir='${libexecdir}/emacs/${version}/${configuration}'
+emacs_srcdir='${datadir}/emacs/${version}'
 etcdocdir='${datadir}/emacs/${version}/etc'
 gamedir='${localstatedir}/games/emacs'
 
@@ -540,6 +541,15 @@ 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.])],
+  [case $enableval in
+     yes) ;;
+     no) emacs_srcdir=;;
+     *) AC_MSG_ERROR([invalid install-srcdir]);;
+   esac])
+
 AC_ARG_ENABLE(checking,
 [AS_HELP_STRING([--enable-checking@<:@=LIST@:>@],
 		[enable expensive checks.  With LIST,
@@ -2048,6 +2058,9 @@ AC_DEFUN
      dnl This one isn't really used, only archlibdir is.
      libexecdir="\${ns_appbindir}/libexec"
      archlibdir="\${ns_appbindir}/libexec"
+     case $emacs_srcdir in
+       ?*) emacs_srcdir="\${ns_appresdir}";;
+     esac
      etcdocdir="\${ns_appresdir}/etc"
      etcdir="\${ns_appresdir}/etc"
      dnl FIXME maybe set datarootdir instead.
@@ -5230,6 +5243,7 @@ AC_DEFUN
 AC_SUBST(x_default_search_path)
 AC_SUBST(etcdir)
 AC_SUBST(archlibdir)
+AC_SUBST([emacs_srcdir])
 AC_SUBST(etcdocdir)
 AC_SUBST(bitmapdir)
 AC_SUBST(gamedir)
diff --git a/etc/NEWS b/etc/NEWS
index 5395f2ccfb..c3a71ade8a 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -58,6 +58,12 @@ shaping, so 'configure' now recommends that combination.
 ** The ftx font backend driver has been removed.
 It was declared obsolete in Emacs 27.1.
 
+---
+** 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'.
+
 
 * Startup Changes in Emacs 28.1
 
@@ -142,6 +148,9 @@ called when the function object is garbage-collected.  Use
 ** 'parse-time-string' can now parse ISO 8601 format strings,
 such as "2020-01-15T16:12:21-08:00".
 
+** The new variable 'emacs-source-directory' gives the Emacs source
+code location.
+
 
 * Changes in Emacs 28.1 on Non-Free Operating Systems
 
diff --git a/lisp/emacs-lisp/find-func.el b/lisp/emacs-lisp/find-func.el
index 167ead3ce0..be53324f14 100644
--- a/lisp/emacs-lisp/find-func.el
+++ b/lisp/emacs-lisp/find-func.el
@@ -219,8 +219,10 @@ find-library--from-load-history
             (locate-file basename (list dir) (find-library-suffixes)))))))
 
 (defvar find-function-C-source-directory
-  (let ((dir (expand-file-name "src" source-directory)))
-    (if (file-accessible-directory-p dir) dir))
+  (let ((dir (expand-file-name "src" emacs-source-directory)))
+    (if (file-accessible-directory-p dir) dir
+      (setq dir (expand-file-name "src" source-directory))
+      (if (file-accessible-directory-p dir) dir)))
   "Directory where the C source files of Emacs can be found.
 If nil, do not try to find the source code of functions and variables
 defined in C.")
@@ -245,7 +247,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/lisp/startup.el b/lisp/startup.el
index 1f545c6692..676fb35e00 100644
--- a/lisp/startup.el
+++ b/lisp/startup.el
@@ -623,7 +623,8 @@ normal-top-level
 		(set pathsym (mapcar (lambda (dir)
 				       (decode-coding-string dir coding t))
 				     path)))))
-	(dolist (filesym '(data-directory doc-directory exec-directory
+	(dolist (filesym '(data-directory doc-directory emacs-source-directory
+					  exec-directory
 					  installation-directory
 					  invocation-directory invocation-name
 					  source-directory
diff --git a/src/epaths.in b/src/epaths.in
index 3cadd160ec..554fdd7228 100644
--- a/src/epaths.in
+++ b/src/epaths.in
@@ -73,5 +73,9 @@ 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 its own installed source code,
+   or the empty string if the source code is not installed.  */
+#define PATH_EMACS_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 69dd73912b..274491f022 100644
--- a/src/lread.c
+++ b/src/lread.c
@@ -44,6 +44,7 @@ #define DEFINE_SYMBOLS
 #include "blockinput.h"
 #include "pdumper.h"
 #include <c-ctype.h>
+#include <dosname.h>
 #include <vla.h>
 
 #ifdef MSDOS
@@ -4992,11 +4993,18 @@ 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!  */);
+You cannot count on them to still be there!  Also see
+`emacs-source-directory'.  */);
   Vsource_directory
     = Fexpand_file_name (build_string ("../"),
 			 Fcar (decode_env_path (0, PATH_DUMPLOADSEARCH, 0)));
 
+  DEFVAR_LISP ("emacs-source-directory", Vemacs_source_directory,
+	       doc: /* Directory where Emacs sources can be found.  */);
+  Vemacs_source_directory = (IS_ABSOLUTE_FILE_NAME (PATH_EMACS_SOURCE)
+			     ? build_string (PATH_EMACS_SOURCE)
+			     : Vsource_directory);
+
   DEFVAR_LISP ("preloaded-file-list", Vpreloaded_file_list,
 	       doc: /* List of files that were preloaded (when dumping Emacs).  */);
   Vpreloaded_file_list = Qnil;
-- 
2.24.1