pkgsrc-Changes archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
CVS commit: pkgsrc/lang/ghc910
Module Name: pkgsrc
Committed By: pho
Date: Sun Dec 28 05:16:51 UTC 2025
Modified Files:
pkgsrc/lang/ghc910: bootstrap.mk distinfo
pkgsrc/lang/ghc910/patches: patch-hadrian_src_Rules_BinaryDist.hs
Log Message:
lang/ghc910: Refine how "make bootstrap" builds a bootkit
"make bootstrap" now bundles non-system shared libraries, including ones
from pkgsrc, with the resulting bootkit. It copies libraries and injects
relative rpaths into binaries, similarly to what lang/rust does but without
hard-coding library names:
https://github.com/NetBSD/pkgsrc/blob/pkgsrc-2024Q4/lang/rust/Makefile#L642-L684
No revision rebump is needed because this change does not affect the actual
package to be built. It just affects bootkits.
It's not that we are going to rebuild our bootkits right away. But when we
upgrade our GHC to >9.14 we will need to do it, and things will be easier
at that time.
Tested on NetBSD, FreeBSD, Darwin, and SunOS.
To generate a diff of this commit:
cvs rdiff -u -r1.3 -r1.4 pkgsrc/lang/ghc910/bootstrap.mk
cvs rdiff -u -r1.11 -r1.12 pkgsrc/lang/ghc910/distinfo
cvs rdiff -u -r1.1 -r1.2 \
pkgsrc/lang/ghc910/patches/patch-hadrian_src_Rules_BinaryDist.hs
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
Modified files:
Index: pkgsrc/lang/ghc910/bootstrap.mk
diff -u pkgsrc/lang/ghc910/bootstrap.mk:1.3 pkgsrc/lang/ghc910/bootstrap.mk:1.4
--- pkgsrc/lang/ghc910/bootstrap.mk:1.3 Thu Aug 28 12:14:54 2025
+++ pkgsrc/lang/ghc910/bootstrap.mk Sun Dec 28 05:16:50 2025
@@ -1,4 +1,4 @@
-# $NetBSD: bootstrap.mk,v 1.3 2025/08/28 12:14:54 pho Exp $
+# $NetBSD: bootstrap.mk,v 1.4 2025/12/28 05:16:50 pho Exp $
# -----------------------------------------------------------------------------
# Select a bindist of bootstrapping compiler on a per-platform basis. See
# ./files/BOOTSTRAP.md for details.
@@ -78,7 +78,7 @@ PKG_FAIL_REASON+= "internal error: unsup
.endif
# For package developers, please do not upload any bootkits unsafely
-# built. That is, machines shared with someone or on a cloud hosting
+# built. That is, machines shared with someone else or on a cloud hosting
# service should be avoided for building bootkits.
.for i in ${DISTFILES:M*-boot-*}
SITES.${i}?= ${MASTER_SITE_LOCAL}
@@ -174,22 +174,9 @@ HADRIAN_CMD= ${PKGSRC_SET_ENV} ${ALL_ENV
# An unusual target "bootstrap"
#
# Build a bootstrapping compiler using an already installed GHC. This is
-# certainly impossible if you don't have one. It's absolutely important to
-# build it with the fewest possible run-time dependencies, otherwise the
-# resulting binary can easily get unusable.
-
-# We don't want our bootkits to have a run-time dependency on libgcc. In
-# fact GHC's implementation of Haskell exception handling does not depend
-# on libgcc's facilities so it is attractive to do the same for "normal"
-# build... but we can't. This is because Haskell programs may call C
-# functions via FFI, and those C functions may call C++ functions in turn,
-# possibly in a different shared library.
-.include "../../mk/compiler.mk"
-.if make(bootstrap) && ${CC_VERSION:Mgcc-*}
-# But on some platforms gcc automagically inserts a dependency on a shared
-# libgcc when -lpthread is given, which is seemingly unavoidable.
-LDFLAGS+= -static-libgcc
-.endif
+# certainly impossible if you don't have one. A bootkit built with this
+# target will have required shared libraries bundled with it, except for
+# certain system libraries such as libc, libm, and libpthread.
# MacOS X 10.7 is the oldest macOS version supporting __thread. Although
# ${WRKSRC}/configure has a check for it, the actual build will fail
@@ -200,32 +187,6 @@ LDFLAGS+= -static-libgcc
MACOSX_DEPLOYMENT_TARGET:= 10.9
.endif
-# Gather information about packages on which bootkit depends. It will be
-# used in the post-bootstrap phase.
-BOOT_GHC_DEPS:= curses iconv
-BOOT_GHC_PKGSRC_DEPS:= # empty
-.for pkg in ${BOOT_GHC_DEPS}
-
-# NOTE: pkglint(1) complains for including these builtin.mk files, telling
-# that we must include buildlink3.mk instead. But then how do we get
-# variables like USE_BUILTIN.${pkg} defined before including
-# ../../mk/bsd.pkg.mk, given that ../../mk/bsd.buildlink3.mk isn't
-# protected against multiple inclusion?
-CHECK_BUILTIN.${pkg}:= yes
-. if ${pkg} == "curses"
-. include "../../mk/curses.builtin.mk"
-. elif ${pkg} == "iconv"
-. include "../../converters/libiconv/builtin.mk"
-. endif
-CHECK_BUILTIN.${pkg}:= no
-
-# BOOT_GHC_PKGSRC_DEPS is a list of packages whose pkgsrc version is
-# preferred over native one, either by user or ../../mk/platform
-. if ${PREFER.${pkg}} == "pkgsrc"
-BOOT_GHC_PKGSRC_DEPS+= ${pkg}
-. endif
-.endfor
-
# Compiler wrappers must not remove -I/-L flags for the installed GHC's
# libdir, otherwise the stage-0 GHC (which we are going to use for building
# our bootstraping kit) will not work. Ideally it should be added to
@@ -240,8 +201,8 @@ MAKEVARS+= BOOT_GHC_LIBDIR
BUILDLINK_PASSTHRU_DIRS+= ${BOOT_GHC_LIBDIR}
# Default values for BUILDLINK_INCDIRS.<pkg> are only generated in the
-# barrier. See ../../mk/buildlink3/bsd.buildlink3.mk and
-# ../../mk/bsd.pkg.barrier.mk
+# barrier, which we use in CONFIGURE_ARGS. See
+# ../../mk/buildlink3/bsd.buildlink3.mk and ../../mk/bsd.pkg.barrier.mk
.PHONY: bootstrap
BOOT_ARCHIVE.new= ${BOOT_ARCHIVE:S/-${BOOT_VERSION}-/-${PKGVERSION_NOREV}-/}
.if make(bootstrap)
@@ -253,34 +214,43 @@ bootstrap: barrier
bootstrap: pre-bootstrap .WAIT ${WRKDIR}/stamp-dist-boot .WAIT post-bootstrap
.endif
-# For normal build we use pkgsrc libffi.so, but for bootkits we can't do
-# that because that would mean bootkits have run-time dependency on
-# it. However, building the bundled one isn't a solution either, because
-# pkgsrc libffi tends to be heavily patched to support our exotic
-# platforms. So we remove ${BUILDLINK_DIR}/lib/libffi.so just before we
-# build our bootkit so that the resulting executables link with the static
-# one.
+# --with-system-libffi is necessary, otherwise GHC tries to build its own
+# copy of libffi, which is bad because the bundled libffi tends not to work
+# flawlessly on exotic platforms we intend to support.
CONFIGURE_ARGS.boot= ${CONFIGURE_ARGS.common}
CONFIGURE_ARGS.boot+= --with-bindist-prefix="ghc-boot-" --with-system-libffi
# Hadrian arguments to use while building a bootkit.
HADRIAN_ARGS.boot= ${HADRIAN_ARGS.common}
+HADRIAN_ARGS.boot+= --prefix=${PREFIX:Q} # Needed because of our patch to Rules.BinaryDist
HADRIAN_ARGS.boot+= --docs=none
.if ${OPSYS} == "FreeBSD"
# -fsplit-sections appears to corrupt the symbol table of stage 1
# libHSghc-*.a(Instances.o) and cause a linkage failure. Either Clang or
-# LLD is doing something wrong, probably the former.
+# LLD is doing something wrong, probably the former. TODO: Check and see
+# if the problem has gone. This workaround bloats the binary size.
HADRIAN_ARGS.boot+= --flavour=bootkit
.else
HADRIAN_ARGS.boot+= --flavour=bootkit+split_sections
.endif
+.if make(bootstrap)
# Determine the version of GHC being used to build the bootkit. We will
# need this to bootstrap Hadrian.
-.if make(bootstrap)
BOOT_GHC_VERSION_CMD= ghc --numeric-version
BOOT_GHC_VERSION!= (${BOOT_GHC_VERSION_CMD}) 2>/dev/null || ${ECHO}
HADRIAN_BOOT_SOURCE:= ${HADRIAN_BOOT_SOURCE:S/${BOOT_VERSION}/${BOOT_GHC_VERSION}/}
+
+# Needed because of our patch to Rules.BinaryDist
+ALL_ENV+= SYSTEM_DEFAULT_RPATH=${SYSTEM_DEFAULT_RPATH:Q}
+
+# On ELF platforms we use readelf and patchelf to embed relative rpaths
+# into binaries. We cannot use devel/chrpath because it cannot embed rpaths
+# longer than existing ones.
+. if ${OBJECT_FMT} == "ELF"
+USE_TOOLS+= readelf
+TOOL_DEPENDS+= patchelf-[0-9]*:../../devel/patchelf
+. endif
.endif
.PHONY: pre-bootstrap
@@ -302,6 +272,25 @@ pre-bootstrap: wrapper
that building bootstrapping compiler is impossible."; \
${FAIL_MSG} "Please run \"${MAKE} clean\" first."; \
fi
+# Fail early if we don't have tools we're going to use.
+.if ${OBJECT_FMT} == "ELF"
+ ${RUN}for prog in patchelf readelf; do \
+ if ! ${TYPE} $$prog >/dev/null 2>&1; then \
+ ${ERROR_MSG} "You don't have $$prog in your PATH."; \
+ ${FAIL_MSG} "Perhaps you need to run \"${MAKE} clean\" first?"; \
+ fi; \
+ done
+.elif ${OBJECT_FMT} == "Mach-O"
+ ${RUN}for prog in install_name_tool otool; do \
+ if ! ${TYPE} $$prog >/dev/null 2>&1; then \
+ ${FAIL_MSG} "You don't have $$prog in your PATH, which is\
+ necessary to build a bootkit."; \
+ fi; \
+ done
+.else
+ ${FAIL_MSG} "Sorry but we don't know how to build a bootkit on\
+ platforms whose object format is ${OBJECT_FMT}."
+.endif
${WRKDIR}/stamp-configure-boot:
@${PHASE_MSG} "Configuring bootstrapping compiler ${PKGNAME_NOREV}"
@@ -344,12 +333,6 @@ ${WRKDIR}/stamp-build-boot: ${WRKDIR}/st
-s ${DISTDIR}/${DIST_SUBDIR}/${HADRIAN_BOOT_SOURCE}
@${PHASE_MSG} "Building bootstrapping compiler ${PKGNAME_NOREV}"
- for f in ${BUILDLINK_DIR:Q}/lib/libffi.*; do \
- case "$$f" in \
- *.a) :;; \
- *) ${RM} -f "$$f";; \
- esac; \
- done
cd ${WRKSRC} && ${HADRIAN_CMD} ${HADRIAN_ARGS.boot}
${TOUCH} ${.TARGET}
@@ -367,27 +350,4 @@ post-bootstrap:
@${ECHO} "Now you can copy it into ${DISTDIR}/${DIST_SUBDIR} to use as your"
@${ECHO} "bootstrap kit. You may want to take a backup in case \"lintpkgsrc -r\""
@${ECHO} "removes it."
- @${ECHO}
- @${ECHO} "Your bootstrap kit has the following run-time dependencies:"
-.for pkg in ${BOOT_GHC_DEPS}
- @${PRINTF} " * %-8s" "${pkg}:"
-. if ${USE_BUILTIN.${pkg}:tl} == no
- @${ECHO_N} " pkgsrc ${BUILDLINK_PKGNAME.${pkg}}"
-. else
- @${ECHO_N} " native"
-. if empty(BUILTIN_PKG.${pkg})
- @${ECHO_N} " (version/variant unknown)"
-. else
- @${ECHO_N} " ${BUILTIN_PKG.${pkg}}"
-. endif
-. endif
- @${ECHO}
-.endfor
-.if !empty(BOOT_GHC_PKGSRC_DEPS)
- @${ECHO}
- @${ECHO} "Please note that it is generally not a good idea for a bootkit to depend"
- @${ECHO} "on pkgsrc packages, as pkgsrc tends to move faster than operating systems"
- @${ECHO} "so your bootkit will bitrot more quickly. You may want to rebuild it"
- @${ECHO} "without setting PREFER_PKGSRC to \"yes\"."
-.endif
@${ECHO} "=========================================================================="
Index: pkgsrc/lang/ghc910/distinfo
diff -u pkgsrc/lang/ghc910/distinfo:1.11 pkgsrc/lang/ghc910/distinfo:1.12
--- pkgsrc/lang/ghc910/distinfo:1.11 Mon Dec 22 11:12:21 2025
+++ pkgsrc/lang/ghc910/distinfo Sun Dec 28 05:16:50 2025
@@ -1,4 +1,4 @@
-$NetBSD: distinfo,v 1.11 2025/12/22 11:12:21 wiz Exp $
+$NetBSD: distinfo,v 1.12 2025/12/28 05:16:50 pho Exp $
BLAKE2s (ghc-9.10.1-src.tar.xz) = 7e4433ead6349bd073d31803b63e6c39fb3833ad691e985e25bc5b027da7fb85
SHA512 (ghc-9.10.1-src.tar.xz) = 46d47e7811a19dcce501002ab674d84ab2fcb842309b5094af52dc5ad26bf5b309b160c1b689f3342666013bf4b0587425c60fbd6c637e739839d41a6a96d990
@@ -12,9 +12,6 @@ Size (ghc-9.8.2-boot-aarch64-unknown-net
BLAKE2s (ghc-9.8.2-boot-hadrian-ghc910.tar.gz) = 1197d07b5523870c2811df41120a7730cecbd5edd02f7bed13c7c0d463e88a5b
SHA512 (ghc-9.8.2-boot-hadrian-ghc910.tar.gz) = 41241ca7b09ecc7935f9893983039d1ed0809998387367860543b81f3c9d3350bfe312a6b11170085d62ca4a1a723ae4fdf3efca0195822f641c758a2893c3bb
Size (ghc-9.8.2-boot-hadrian-ghc910.tar.gz) = 1446610 bytes
-BLAKE2s (ghc-9.8.2-boot-i386-unknown-freebsd.tar.xz) = 7e67d0277b2375024c59243533e9abd05fed5f291bb1e650c6b51ad15882b6e3
-SHA512 (ghc-9.8.2-boot-i386-unknown-freebsd.tar.xz) = eb8fbf004ddd43ee503ae7987be66afe79a15954733bb6d2aae16b281f1436b200d392eefc57571463d0a90f5ae80505b8bb2841cc28b69309d9b8653f3ab4a7
-Size (ghc-9.8.2-boot-i386-unknown-freebsd.tar.xz) = 83193744 bytes
BLAKE2s (ghc-9.8.2-boot-x86_64-apple-darwin.tar.xz) = fce50342032195058f3d873dabf6ca1a19a3ae0f7f24895af0d07edbfbb1c724
SHA512 (ghc-9.8.2-boot-x86_64-apple-darwin.tar.xz) = 3c18ddb7217da9158b7623bc7360cbb12326de3c94301a989cbf866801cbad7058002a2c6769c533e56293cd798e9e8cdfe337472af8f4c7ce039d4d1610721d
Size (ghc-9.8.2-boot-x86_64-apple-darwin.tar.xz) = 94933928 bytes
@@ -38,7 +35,7 @@ SHA1 (patch-hadrian_bootstrap_bootstrap.
SHA1 (patch-hadrian_bootstrap_src_Main.hs) = 2e0a7ae2ef195013ca07313c150a7e48b6985e32
SHA1 (patch-hadrian_src_Builder.hs) = 848aae01ed5e7095ea9e5864d6b6afe23f653b90
SHA1 (patch-hadrian_src_Hadrian_Haskell_Cabal_Parse.hs) = 6b2d7a4c7b2d63a8a8bf6b089bbb275201d27081
-SHA1 (patch-hadrian_src_Rules_BinaryDist.hs) = 8ea388ce1f380c854be871ee8361d5aace67e9a6
+SHA1 (patch-hadrian_src_Rules_BinaryDist.hs) = ad2d30b42a663d80407cc396f43fffb1e68e4322
SHA1 (patch-hadrian_src_Rules_Documentation.hs) = 29cef344130c616d9eb0cc9723d06414186d30dd
SHA1 (patch-hadrian_src_Settings_Builders_Cabal.hs) = 16f2b1fa9d5259ec86773449b9e936b95a2f3219
SHA1 (patch-hadrian_src_Settings_Builders_Ghc.hs) = e2913e9f83bb36bb0b432a24518c9b6ca2990c6a
Index: pkgsrc/lang/ghc910/patches/patch-hadrian_src_Rules_BinaryDist.hs
diff -u pkgsrc/lang/ghc910/patches/patch-hadrian_src_Rules_BinaryDist.hs:1.1 pkgsrc/lang/ghc910/patches/patch-hadrian_src_Rules_BinaryDist.hs:1.2
--- pkgsrc/lang/ghc910/patches/patch-hadrian_src_Rules_BinaryDist.hs:1.1 Wed Jan 29 13:21:53 2025
+++ pkgsrc/lang/ghc910/patches/patch-hadrian_src_Rules_BinaryDist.hs Sun Dec 28 05:16:51 2025
@@ -1,24 +1,44 @@
-$NetBSD: patch-hadrian_src_Rules_BinaryDist.hs,v 1.1 2025/01/29 13:21:53 pho Exp $
+$NetBSD: patch-hadrian_src_Rules_BinaryDist.hs,v 1.2 2025/12/28 05:16:51 pho Exp $
-Hunk #0, #1, #3:
+Hunk #1, #2, #4:
Distinguish bootstrapping bindists from regular ones. This is
pkgsrc-specific.
-Hunk #2:
+Hunk #3:
Do not run `ghc-pkg recache' while creating a binary distribution. We
don't want a package cache to be installed because we do it in our
INSTALL script. Also it's not possible to run ghc-pkg in the first
place without setting LD_LIBRARY_PATH since we don't build relocatable
executables. This is pkgsrc-specific.
-Hunk #4:
+Hunk #5:
We want our bootkits to be as small as possible, even though `xz -9e'
is very slow and consumes about 680 MiB of memory. This is
pkgsrc-specific.
+Hunk #0, #6:
+ Bundle pkgsrc-installed dependencies with bootkits. This is
+ pkgsrc-specific.
+
--- hadrian/src/Rules/BinaryDist.hs.orig 2024-05-10 05:05:49.000000000 +0000
+++ hadrian/src/Rules/BinaryDist.hs
-@@ -115,7 +115,7 @@ installTo relocatable prefix = do
+@@ -13,10 +13,15 @@ import Settings
+ import Settings.Program (programContext)
+ import Target
+ import Utilities
++import qualified System.Directory as IO
+ import qualified System.Directory.Extra as IO
++import Control.Monad.IO.Class (MonadIO)
+ import Data.Either
++import Data.Set (Set)
+ import qualified Data.Set as Set
+ import Oracles.Flavour
++import qualified System.Environment as IO
++import System.Exit (ExitCode(..))
+
+ {-
+ Note [Binary distributions]
+@@ -115,7 +120,7 @@ installTo relocatable prefix = do
root <- buildRoot
version <- setting ProjectVersion
targetPlatform <- setting TargetPlatformFull
@@ -27,7 +47,7 @@ Hunk #4:
bindistFilesDir = root -/- "bindist" -/- ghcVersionPretty
runBuilder (Configure bindistFilesDir) ["--prefix="++prefix] [] []
let env = case relocatable of
-@@ -154,7 +154,7 @@ bindistRules = do
+@@ -154,7 +159,7 @@ bindistRules = do
let ghcBuildDir = root -/- stageString Stage1
bindistFilesDir = root -/- "bindist" -/- ghcVersionPretty
@@ -36,7 +56,7 @@ Hunk #4:
rtsIncludeDir = ghcBuildDir -/- "lib" -/- distDir -/- rtsDir
-/- "include"
-@@ -232,17 +232,6 @@ bindistRules = do
+@@ -232,16 +237,7 @@ bindistRules = do
copyDirectory (rtsIncludeDir) bindistFilesDir
when windowsHost $ createGhcii (bindistFilesDir -/- "bin")
@@ -50,11 +70,11 @@ Hunk #4:
- cmd_ (bindistFilesDir -/- "bin" -/- ghcPkgName) ["recache"]
-
-
--
++ bundleVendorLibs bindistFilesDir
+
-- TODO: we should only embed the docs that have been generated
-- depending on the current settings (flavours' "ghcDocs" field and
- -- "--docs=.." command-line flag)
-@@ -319,7 +308,7 @@ bindistRules = do
+@@ -319,7 +315,7 @@ bindistRules = do
version <- setting ProjectVersion
targetPlatform <- setting TargetPlatformFull
@@ -63,7 +83,7 @@ Hunk #4:
-- Finally, we create the archive <root>/bindist/ghc-X.Y.Z-platform.tar.xz
tarPath <- builderPath (Tar Create)
-@@ -388,7 +377,7 @@ generateBuildMk = do
+@@ -388,7 +384,7 @@ generateBuildMk = do
-- | Flag to pass to tar to use the given 'Compressor'.
compressorTarFlag :: Compressor -> String
compressorTarFlag Gzip = "--gzip"
@@ -72,3 +92,252 @@ Hunk #4:
compressorTarFlag Bzip2 = "--bzip"
-- | File extension to use for archives compressed with the given 'Compressor'.
+@@ -556,3 +552,248 @@ createGhcii outDir = do
+ , "exec \"$(dirname \"$0\")\"/ghc --interactive \"$@\""
+ ]
+
++data ObjectFmt = ELF | MachO deriving (Show, Eq)
++data ObjectType = ObjExec | ObjLib deriving (Show, Eq)
++
++bundleVendorLibs :: FilePath -> Action ()
++bundleVendorLibs bindistDir =
++ -- For each native object in "bin" and "lib", iterate the list of library
++ -- dependencies. Copy any libraries not in a blocklist (including their
++ -- transitive dependencies) to lib/vendor/ and inject a relative RPATH.
++ do isELF <- isElfTarget
++ isMachO <- isOsxTarget -- Strictly speaking this is wrong, as macOS
++ -- isn't the only platform using Mach-O. But we
++ -- can't do anything better at the moment.
++ fmt <- case () of
++ _ | isELF -> pure ELF
++ _ | isMachO -> pure MachO
++ _ | otherwise -> fail "Sorry but we don't know how to bundle vendor libraries for this target platform"
++ let prefixErr = "You must specify a path with --prefix when using the"
++ ++ " 'install' rule"
++ prefix <- addTrailingPathSeparator . fromMaybe (error prefixErr) <$> cmdPrefix
++ -- Bundled libraries go into "${ghclibdir}/lib/vendor" where ghclibdir
++ -- is "${prefix}/lib/ghc-${version}". The bindist Makefile installs
++ -- everything under "${ghclibdir}/lib" preserving the directory
++ -- structure so we don't need to modify it.
++ let destDir = bindistDir -/- "lib/vendor"
++ liftIO $ IO.removePathForcibly destDir
++ liftIO $ IO.createDirectory destDir
++ mapM_ (go fmt ObjExec prefix destDir)
++ =<< filterM isRegular
++ =<< liftIO (IO.listFiles (bindistDir -/- "bin"))
++ mapM_ (go fmt ObjLib prefix destDir)
++ =<< filterM isRegular
++ =<< liftIO (IO.listFilesInside (pure . (/= destDir)) (bindistDir -/- "lib"))
++ where
++ isRegular :: FilePath -> Action Bool
++ isRegular = (not <$>) . liftIO . IO.pathIsSymbolicLink
++
++ go :: ObjectFmt -> ObjectType -> FilePath -> FilePath -> FilePath -> Action ()
++ go fmt typ prefix destDir path =
++ do bi <- getBinInfo fmt typ path
++ deps <- biToDeps fmt bi
++ mapM_ (copyAndExamine fmt prefix destDir) deps
++ unless (all isBlockedDynDep deps) $
++ -- The file is not necessarily writable. Make sure it is, before
++ -- trying to patch it.
++ do perms <- liftIO $ IO.getPermissions path
++ let perms' = IO.setOwnerWritable True perms
++ liftIO $ IO.setPermissions path perms'
++ embedRPath fmt typ prefix destDir bi path
++
++ copyAndExamine :: ObjectFmt -> FilePath -> FilePath -> FilePath -> Action ()
++ copyAndExamine fmt prefix destDir depPath =
++ do copied <- copyDynDep destDir depPath
++ when copied $
++ -- Recursively bundle dependencies of this library.
++ do let copiedPath = replaceDirectory depPath destDir
++ go fmt ObjLib prefix destDir copiedPath
++
++data BinInfo =
++ BinInfo
++ { biNeeded :: !(Set FilePath)
++ , biRPaths :: ![FilePath]
++ }
++ deriving Show
++
++instance Semigroup BinInfo where
++ a <> b = BinInfo (biNeeded a <> biNeeded b) (biRPaths a <> biRPaths b)
++
++instance Monoid BinInfo where
++ mempty = BinInfo mempty mempty
++
++data MachOLoadCommand = LCLoadDylib | LCRPath
++ deriving (Show, Eq)
++
++getBinInfo :: ObjectFmt -> ObjectType -> FilePath -> Action BinInfo
++getBinInfo ELF typ path
++ | typ == ObjExec || "lib" `isPrefixOf` takeFileName path =
++ do (Exit c, Stdout out) <- cmd (EchoStderr False) "readelf" ["-Wd", path]
++ case c of
++ ExitFailure _ -> pure mempty -- Ignore the error; it's probably not an ELF object.
++ ExitSuccess -> pure . foldl' go mempty $ lines out
++ | otherwise =
++ pure mempty -- This file isn't worth readelf-ing. Don't waste time.
++ where
++ go :: BinInfo -> String -> BinInfo
++ go bi line =
++ case words line of
++ (_ : ("(NEEDED)" : _)) ->
++ let dep = takeWhile (/= ']') . drop 1 . dropWhile (/= '[') $ line
++ in
++ bi { biNeeded = Set.insert dep (biNeeded bi) }
++
++ (_ : ("(RPATH)" : _)) ->
++ let paths = splitSearchPath . takeWhile (/= ']') . drop 1 . dropWhile (/= '[') $ line
++ in
++ bi { biRPaths = biRPaths bi <> paths }
++
++ _ -> bi -- Ignore this line.
++
++getBinInfo MachO typ path
++ | typ == ObjExec || "lib" `isPrefixOf` takeFileName path =
++ do (Exit c, Stdout out) <- cmd (EchoStderr False) "otool" ["-l", path]
++ case c of
++ ExitFailure _ -> pure mempty -- Ignore the error; it's probably not a Mach-O object.
++ ExitSuccess -> pure . fst . foldl' go (mempty, Nothing) $ lines out
++ | otherwise =
++ pure mempty -- This file isn't worth otool-ing. Don't waste time.
++ where
++ go :: (BinInfo, Maybe MachOLoadCommand) -> String -> (BinInfo, Maybe MachOLoadCommand)
++ go (bi, cmd) line =
++ case words line of
++ ["cmd" , "LC_LOAD_DYLIB"] -> (bi, Just LCLoadDylib)
++ ["cmd" , "LC_RPATH" ] -> (bi, Just LCRPath)
++ ("name":_)
++ | cmd == Just LCLoadDylib -> -- "name /usr/lib/libSystem.B.dylib (offset 24)"
++ let dep = extractValue "name" line
++ in
++ (bi { biNeeded = Set.insert dep (biNeeded bi) }, Nothing)
++ ("path":_)
++ | cmd == Just LCRPath -> -- "path /opt/pkg/lib (offset 12)"
++ let rpath = extractValue "path" line
++ in
++ (bi { biRPaths = biRPaths bi <> pure rpath }, Nothing)
++ _ -> -- Ignore everything else.
++ (bi, cmd)
++
++ extractValue :: String -> String -> String
++ extractValue key line =
++ let isSpace = (== ' ')
++ keyLen = length key
++ value' = dropWhile isSpace . drop keyLen . dropWhile isSpace $ line
++ in
++ -- Delete anything after the last occurence of '('.
++ reverse . dropWhile isSpace . drop 1 . dropWhile (/= '(') . reverse . dropWhile isSpace $ value'
++
++biToDeps :: (MonadFail m, MonadIO m) => ObjectFmt -> BinInfo -> m [FilePath]
++biToDeps ELF bi =
++ do def <- liftIO $ IO.getEnv "SYSTEM_DEFAULT_RPATH" -- Expected to be set by bootstrap.mk
++ mapM (findDep $ splitSearchPath def) (Set.toList (biNeeded bi))
++ where
++ findDep :: (MonadFail m, MonadIO m) => [FilePath] -> FilePath -> m FilePath
++ findDep defPaths depName =
++ do let rpaths = biRPaths bi <> defPaths
++ r <- liftIO $ IO.findFile rpaths depName
++ case r of
++ Nothing ->
++ -- This is bad enough for aborting with an error.
++ fail $ "Library " <> depName <> " not found in any of rpaths " <>
++ intercalate [searchPathSeparator] rpaths
++ Just found ->
++ pure found
++
++-- macOS is weird. System libraries don't actually exist in the
++-- filesystem. And rpaths work very differently from ELF.
++biToDeps MachO bi =
++ do def <- liftIO $ IO.getEnv "SYSTEM_DEFAULT_RPATH" -- Expected to be set by bootstrap.mk
++ mapM (findDep $ splitSearchPath def) (Set.toList (biNeeded bi))
++ where
++ findDep :: (MonadFail m, MonadIO m) => [FilePath] -> FilePath -> m FilePath
++ findDep _ depPath@('@':_) =
++ fail $ "Relative dependency path (" <> depPath <>
++ ") is currently not supported. Poke pho@ when this error actually happens."
++ findDep defPaths depPath
++ | any ((`isPrefixOf` depPath) . addTrailingPathSeparator) defPaths =
++ -- This library most likely doesn't exist as an actual file.
++ pure depPath
++ | otherwise =
++ do e <- liftIO $ IO.doesFileExist depPath
++ if e
++ then pure depPath
++ -- This is bad enough for aborting with an error.
++ else fail $ "Library " <> depPath <> " not found"
++
++isBlockedDynDep :: FilePath -> Bool
++isBlockedDynDep = flip Set.member blockList . dropExtensions . takeFileName
++ where
++ -- These libraries are exempted from being bundled with the resulting
++ -- bootkit.
++ {-# NOINLINE blockList #-}
++ blockList :: Set FilePath
++ blockList =
++ Set.fromList
++ [ "libSystem" -- No versions of macOS lack this.
++ , "libc" -- No reasonable OSes lack this.
++ , "libdl" -- This tends to interact with system dynamic
++ -- linker. Bundling this may cause version
++ -- inconsistencies.
++ , "libm" -- No reasonable OSes lack this.
++ , "libpthread" -- No reasonable OSes lack this.
++ , "librt" -- This tends to contain syscall wrappers.
++ ]
++
++-- Return @true@ iff the copy has actually happened.
++copyDynDep :: MonadIO m => FilePath -> FilePath -> m Bool
++copyDynDep destDir depPath =
++ if isBlockedDynDep depPath then
++ pure False
++ else
++ do let destPath = destDir -/- takeFileName depPath
++ exists <- liftIO $ IO.doesFileExist destPath
++ if exists
++ then pure False
++ else do canonSrc <- liftIO $ IO.canonicalizePath depPath -- Resolve symlinks
++ liftIO $ IO.copyFile canonSrc destPath
++ pure True
++
++embedRPath :: ObjectFmt -> ObjectType -> FilePath -> FilePath -> BinInfo -> FilePath -> Action ()
++embedRPath ELF _ prefix destDir bi path =
++ -- Delete any rpaths starting with $PREFIX/ and add a relative rpath to
++ -- destDir (i.e. "lib/vendor").
++ do let original = biRPaths bi
++ filtered = filter (not . (prefix `isPrefixOf`)) original
++ absDestDir <- liftIO $ IO.makeAbsolute destDir
++ absPath <- liftIO $ IO.makeAbsolute path
++ relRPath <- fromJust <$> liftIO (makeRelativeEx (takeDirectory absPath) absDestDir)
++ -- We must use $ORIGIN, not ${ORIGIN}, because SunOS doesn't support
++ -- the latter.
++ let normRel = dropTrailingPathSeparator . normalise $ "$ORIGIN" -/- relRPath
++ final = intercalate [searchPathSeparator] (normRel : filtered)
++ cmd_ "patchelf" ["--set-rpath", final, path]
++
++embedRPath MachO ObjExec _ destDir bi path =
++ -- This is a Mach-O executable. Add an rpath to destDir, and replace any
++ -- references to vendored libraries with relative paths.
++ do absDestDir <- liftIO $ IO.makeAbsolute destDir
++ absPath <- liftIO $ IO.makeAbsolute path
++ relRPath <- fromJust <$> liftIO (makeRelativeEx (takeDirectory absPath) absDestDir)
++ let args = ["-add_rpath", "@executable_path" -/- relRPath] <> dylibChanges bi <> [path]
++ cmd_ "install_name_tool" args
++
++embedRPath MachO ObjLib _ _ bi path =
++ -- This is a Mach-O library. Change its installation path to a relative
++ -- one, and replace any references to vendored libraries with relative
++ -- paths.
++ do let args = ["-id", "@rpath" -/- takeFileName path] <> dylibChanges bi <> [path]
++ cmd_ "install_name_tool" args
++
++dylibChanges :: BinInfo -> [String]
++dylibChanges bi = concatMap change . Set.toList . biNeeded $ bi
++ where
++ change :: FilePath -> [String]
++ change depPath
++ | isBlockedDynDep depPath =
++ []
++ | otherwise =
++ ["-change", depPath, "@rpath" -/- takeFileName depPath]
Home |
Main Index |
Thread Index |
Old Index