From 278aa59dcd091d9337937bab92fe68cad1dc3e85 Mon Sep 17 00:00:00 2001
From: Michael Witten <mfwitten@gmail.com>
Date: Fri, 8 Mar 2024 19:34:15 +0000
Subject: [PATCH] build: Tell cmake to set 'rpath' so the installed 'cmark' can
 find 'libcmark.so'

Before this commit, the 'cmark' executable didn't inform the
dynamic linker where to look for the 'libcmark' shared object;
this becomes an irritation when 'libcmark' is installed in an
unorthodox location:

  $ ldd /path/to/installed/files/bin/cmark
          linux-gate.so.1 (0xb7ed7000)
          libcmark.so.0.31.0 => not found
          libc.so.6 => /usr/lib/libc.so.6 (0xb7cf3000)
          /lib/ld-linux.so.2 => /usr/lib/ld-linux.so.2 (0xb7ed8000)

Because of this commit, the 'cmark' executable's 'rpath' setting
(or equivalent) is set upon installation, thereby providing the
required search directory:

  $ ldd /path/to/installed/files/bin/cmark
          linux-gate.so.1 (0xb7ef2000)
          libcmark.so.0.31.0 => /path/to/installed/files/lib/libcmark.so.0.31.0 (0xb7e95000)
          libc.so.6 => /usr/lib/libc.so.6 (0xb7cbc000)
          /lib/ld-linux.so.2 => /usr/lib/ld-linux.so.2 (0xb7ef3000)

There is some intelligence behind whether 'rpath' is set at all:

  * If a shared object (e.g., 'libcmark') is going to be installed
    in a standard location, then such location is not added to 'rpath'.

  * A non-standard installation will cause 'rpath' to include at least
    the following path provided by cmake:

      "${CMAKE_INSTALL_PREFIX_FULL_LIBDIR}"

  * Also, cmake has been instructed to append any non-standard path
    to 'rpath' if cmake is aware of an external dependency from
    such a path.

Of course, this will only help on a system that supports an 'rpath'
feature known to cmake; for example, Windows has no such feature,
and so all of this will presumably be ignored when building under
that system.
---
 CMakeLists.txt               |  5 ++++-
 cmake/modules/SetRPATH.cmake | 15 +++++++++++++++
 2 files changed, 19 insertions(+), 1 deletion(-)
 create mode 100644 cmake/modules/SetRPATH.cmake

diff --git a/CMakeLists.txt b/CMakeLists.txt
index ce54658c8..054f8d43c 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -11,8 +11,9 @@ project(cmark
   LANGUAGES C CXX
   VERSION 0.31.0)
 
+list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake/modules)
+
 if(CMAKE_BUILD_TYPE MATCHES "asan|ubsan")
-  list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake/modules)
   include(FindAsan)
 endif()
 
@@ -69,6 +70,8 @@ set(CMAKE_VISIBILITY_INLINES_HIDDEN 1)
 
 set(CMAKE_INCLUDE_CURRENT_DIR ON)
 
+include(SetRPATH)
+
 # Check integrity of node structure when compiled as debug
 add_compile_options($<$<CONFIG:Debug>:-DCMARK_DEBUG_NODES>)
 
diff --git a/cmake/modules/SetRPATH.cmake b/cmake/modules/SetRPATH.cmake
new file mode 100644
index 000000000..818a0cd03
--- /dev/null
+++ b/cmake/modules/SetRPATH.cmake
@@ -0,0 +1,15 @@
+set(CMAKE_SKIP_BUILD_RPATH FALSE)
+set(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE)
+
+# Append non-standard external dependency directories
+set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
+
+# CMake 3.25 has a better scoping solution: 'block()'
+function(SetRPATH)
+  set(p "${CMAKE_INSTALL_FULL_LIBDIR}")
+  list(FIND CMAKE_PLATFORM_IMPLICIT_LINK_DIRECTORIES "${p}" i)
+  if("${i}" STREQUAL "-1")
+    set(CMAKE_INSTALL_RPATH "${p}" PARENT_SCOPE)
+  endif()
+endfunction()
+SetRPATH()