clarified Executable.libraries_closure;
authorwenzelm
Mon, 12 Oct 2020 15:58:37 +0200
changeset 72454 549391271e74
parent 72453 e4dde7beab39
child 72455 7bf67a58f54a
clarified Executable.libraries_closure;
src/Pure/System/executable.scala
--- a/src/Pure/System/executable.scala	Mon Oct 12 07:25:38 2020 +0000
+++ b/src/Pure/System/executable.scala	Mon Oct 12 15:58:37 2020 +0200
@@ -9,30 +9,61 @@
 
 object Executable
 {
-  def libraries(path: Path, mingw: MinGW = MinGW.none): List[Path] =
+  def libraries_closure(exe_path0: Path,
+    mingw: MinGW = MinGW.none,
+    filter: String => Boolean = _ => true): List[String] =
   {
-    val path1 = path.expand
-    val dir = path1.dir
-    val exe = path1.base
+    val exe_path = exe_path0.expand
+    val exe_dir = exe_path.dir
+    val exe = exe_path.base
 
-    def command_output(cmd: String): List[String] =
+    val ldd_lines =
     {
-      val script = mingw.bash_script(cmd + " " + File.bash_path(exe))
-      Library.split_lines(Isabelle_System.bash(script, cwd = dir.file).check.out)
+      val ldd = if (Platform.is_macos) "otool -L" else "ldd"
+      val script = mingw.bash_script(ldd + " " + File.bash_path(exe))
+      Library.split_lines(Isabelle_System.bash(script, cwd = exe_dir.file).check.out)
     }
 
-    if (Platform.is_macos) {
-      val Pattern = """^\s*(.+)\s+\(.*\)$""".r
-      for { Pattern(a) <- command_output("otool -L") } yield {
-        Library.try_unprefix("@executable_path/", a) match {
-          case None => Path.explode(a)
-          case Some(b) => dir + Path.explode(b)
-        }
+    def lib_name(lib: String): String =
+      Library.take_prefix[Char](_ != '.',
+        Library.take_suffix[Char](_ != '/', lib.toList)._2)._1.mkString
+
+    val libs =
+      if (Platform.is_macos) {
+        val Pattern = """^\s*(/.+)\s+\(.*\)$""".r
+        for {
+          Pattern(lib) <- ldd_lines
+          if !lib.startsWith("@executable_path/") && filter(lib_name(lib))
+        } yield lib
+      }
+      else {
+        val Pattern = """^.*=>\s*(/.+)\s+\(.*\)$""".r
+        val prefix =
+          mingw.root match {
+            case None => ""
+            case Some(path) => path.absolute.implode
+          }
+        for { Pattern(lib) <- ldd_lines if filter(lib_name(lib)) }
+          yield prefix + lib
+      }
+
+    if (libs.nonEmpty) {
+      libs.foreach(lib => File.copy(Path.explode(lib), exe_dir))
+
+      if (Platform.is_linux) {
+        // requires "LDFLAGS=-Wl,-rpath,_DUMMY_"
+        Isabelle_System.bash("chrpath -r '$ORIGIN' " + File.bash_path(exe_path)).check
+      }
+      else if (Platform.is_macos) {
+        val script =
+          ("install_name_tool" ::
+            libs.map(file => "-change " + Bash.string(file) + " " +
+              Bash.string("@executable_path/" + Path.explode(file).file_name) + " " +
+              File.bash_path(exe))).mkString(" ")
+        Isabelle_System.bash(script, cwd = exe_dir.file).check
       }
     }
-    else {
-      val Pattern = """^.*=>\s*(.+)\s+\(.*\)$""".r
-      for { Pattern(a) <- command_output("ldd") } yield Path.explode(a)
-    }
+
+    libs
   }
 }