handle rpath in OSXMkLibs
Now oberon has some binaries and libraries that use rpath, so I had to put in this ugly hack to replace the @rapth/lib with the lib in the app. This was particularly tricky for libraries that use @rpath because I could not find a way to extract the rpath from the library. (Only from the executable, by running it.. ugh!) The hack I put in place may fail if multiple different libraries use rpath to refer to other libraries, and the "@rpath/lib" string is the same, but actually refers to different files.
This commit is contained in:
parent
78868866de
commit
b9341fd4c0
2 changed files with 59 additions and 25 deletions
|
@ -22,6 +22,7 @@ import Utility.Process
|
||||||
import Utility.Monad
|
import Utility.Monad
|
||||||
import Utility.SafeCommand
|
import Utility.SafeCommand
|
||||||
import Utility.Path
|
import Utility.Path
|
||||||
|
import Utility.Exception
|
||||||
|
|
||||||
import qualified Data.Map as M
|
import qualified Data.Map as M
|
||||||
import qualified Data.Set as S
|
import qualified Data.Set as S
|
||||||
|
@ -29,16 +30,16 @@ import qualified Data.Set as S
|
||||||
type LibMap = M.Map FilePath String
|
type LibMap = M.Map FilePath String
|
||||||
|
|
||||||
{- Recursively find and install libs, until nothing new to install is found. -}
|
{- Recursively find and install libs, until nothing new to install is found. -}
|
||||||
mklibs :: FilePath -> [FilePath] -> LibMap -> IO ()
|
mklibs :: FilePath -> [FilePath] -> [(FilePath, FilePath)] -> LibMap -> IO ()
|
||||||
mklibs appbase libdirs libmap = do
|
mklibs appbase libdirs replacement_libs libmap = do
|
||||||
(new, libmap') <- installLibs appbase libmap
|
(new, replacement_libs', libmap') <- installLibs appbase replacement_libs libmap
|
||||||
unless (null new) $
|
unless (null new) $
|
||||||
mklibs appbase (libdirs++new) libmap'
|
mklibs appbase (libdirs++new) replacement_libs' libmap'
|
||||||
|
|
||||||
{- Returns directories into which new libs were installed. -}
|
{- Returns directories into which new libs were installed. -}
|
||||||
installLibs :: FilePath -> LibMap -> IO ([FilePath], LibMap)
|
installLibs :: FilePath -> [(FilePath, FilePath)] -> LibMap -> IO ([FilePath], [(FilePath, FilePath)], LibMap)
|
||||||
installLibs appbase libmap = do
|
installLibs appbase replacement_libs libmap = do
|
||||||
(needlibs, libmap') <- otool appbase libmap
|
(needlibs, replacement_libs', libmap') <- otool appbase replacement_libs libmap
|
||||||
libs <- forM needlibs $ \lib -> do
|
libs <- forM needlibs $ \lib -> do
|
||||||
let shortlib = fromMaybe (error "internal") (M.lookup lib libmap')
|
let shortlib = fromMaybe (error "internal") (M.lookup lib libmap')
|
||||||
let fulllib = dropWhile (== '/') lib
|
let fulllib = dropWhile (== '/') lib
|
||||||
|
@ -54,24 +55,54 @@ installLibs appbase libmap = do
|
||||||
_ <- boolSystem "ln" [Param "-s", File fulllib, File symdest]
|
_ <- boolSystem "ln" [Param "-s", File fulllib, File symdest]
|
||||||
return $ Just appbase
|
return $ Just appbase
|
||||||
)
|
)
|
||||||
return (catMaybes libs, libmap')
|
return (catMaybes libs, replacement_libs', libmap')
|
||||||
|
|
||||||
{- Returns libraries to install. -}
|
{- Returns libraries to install. -}
|
||||||
otool :: FilePath -> LibMap -> IO ([FilePath], LibMap)
|
otool :: FilePath -> [(FilePath, FilePath)] -> LibMap -> IO ([FilePath], [(FilePath, FilePath)], LibMap)
|
||||||
otool appbase libmap = do
|
otool appbase replacement_libs libmap = do
|
||||||
files <- filterM doesFileExist =<< dirContentsRecursive appbase
|
files <- filterM doesFileExist =<< dirContentsRecursive appbase
|
||||||
process [] files libmap
|
process [] files replacement_libs libmap
|
||||||
where
|
where
|
||||||
want s = not ("@executable_path" `isInfixOf` s)
|
want s = not ("@executable_path" `isInfixOf` s)
|
||||||
&& not (".framework" `isInfixOf` s)
|
&& not (".framework" `isInfixOf` s)
|
||||||
&& not ("libSystem.B" `isInfixOf` s)
|
&& not ("libSystem.B" `isInfixOf` s)
|
||||||
process c [] m = return (nub $ concat c, m)
|
process c [] rls m = return (nub $ concat c, rls, m)
|
||||||
process c (file:rest) m = do
|
process c (file:rest) rls m = do
|
||||||
_ <- boolSystem "chmod" [Param "755", File file]
|
_ <- boolSystem "chmod" [Param "755", File file]
|
||||||
libs <- filter want . parseOtool
|
libs <- filter want . parseOtool
|
||||||
<$> readProcess "otool" ["-L", file]
|
<$> readProcess "otool" ["-L", file]
|
||||||
m' <- install_name_tool file libs m
|
expanded_libs <- expand_rpath libs replacement_libs file
|
||||||
process (libs:c) rest m'
|
let rls' = nub $ rls ++ (zip libs expanded_libs)
|
||||||
|
m' <- install_name_tool file libs expanded_libs m
|
||||||
|
process (expanded_libs:c) rest rls' m'
|
||||||
|
|
||||||
|
{- Expands any @rpath in the list of libraries.
|
||||||
|
-
|
||||||
|
- This is done by the nasty method of running the command with a dummy
|
||||||
|
- option (so it doesn't do anything.. hopefully!) and asking the dynamic
|
||||||
|
- linker to print expanded rpaths.
|
||||||
|
-}
|
||||||
|
expand_rpath :: [String] -> [(FilePath, FilePath)] -> FilePath -> IO [String]
|
||||||
|
expand_rpath libs replacement_libs cmd
|
||||||
|
| any ("@rpath" `isInfixOf`) libs = do
|
||||||
|
installed <- M.fromList . Prelude.read
|
||||||
|
<$> readFile "tmp/standalone-installed"
|
||||||
|
let origcmd = case M.lookup cmd installed of
|
||||||
|
Nothing -> cmd
|
||||||
|
Just cmd' -> cmd'
|
||||||
|
s <- catchDefaultIO "" $ readProcess "sh" ["-c", probe origcmd]
|
||||||
|
let m = if (null s)
|
||||||
|
then M.fromList replacement_libs
|
||||||
|
else M.fromList $ mapMaybe parse $ lines s
|
||||||
|
return $ map (replace m) libs
|
||||||
|
| otherwise = return libs
|
||||||
|
where
|
||||||
|
probe c = "DYLD_PRINT_RPATHS=1 " ++ c ++ " --getting-rpath-dummy-option 2>&1 | grep RPATH"
|
||||||
|
parse s = case words s of
|
||||||
|
("RPATH":"successful":"expansion":"of":old:"to:":new:[]) ->
|
||||||
|
Just (old, new)
|
||||||
|
_ -> Nothing
|
||||||
|
replace m l = fromMaybe l $ M.lookup l m
|
||||||
|
|
||||||
parseOtool :: String -> [FilePath]
|
parseOtool :: String -> [FilePath]
|
||||||
parseOtool = catMaybes . map parse . lines
|
parseOtool = catMaybes . map parse . lines
|
||||||
|
@ -82,10 +113,10 @@ parseOtool = catMaybes . map parse . lines
|
||||||
|
|
||||||
{- Adjusts binaries to use libraries bundled with it, rather than the
|
{- Adjusts binaries to use libraries bundled with it, rather than the
|
||||||
- system libraries. -}
|
- system libraries. -}
|
||||||
install_name_tool :: FilePath -> [FilePath] -> LibMap -> IO LibMap
|
install_name_tool :: FilePath -> [FilePath] -> [FilePath] -> LibMap -> IO LibMap
|
||||||
install_name_tool _ [] libmap = return libmap
|
install_name_tool _ [] _ libmap = return libmap
|
||||||
install_name_tool binary libs libmap = do
|
install_name_tool binary libs expanded_libs libmap = do
|
||||||
let (libnames, libmap') = getLibNames libs libmap
|
let (libnames, libmap') = getLibNames expanded_libs libmap
|
||||||
let params = concatMap change $ zip libs libnames
|
let params = concatMap change $ zip libs libnames
|
||||||
ok <- boolSystem "install_name_tool" $ params ++ [File binary]
|
ok <- boolSystem "install_name_tool" $ params ++ [File binary]
|
||||||
unless ok $
|
unless ok $
|
||||||
|
@ -123,4 +154,4 @@ main :: IO ()
|
||||||
main = getArgs >>= go
|
main = getArgs >>= go
|
||||||
where
|
where
|
||||||
go [] = error "specify OSXAPP_BASE"
|
go [] = error "specify OSXAPP_BASE"
|
||||||
go (appbase:_) = mklibs appbase [] M.empty
|
go (appbase:_) = mklibs appbase [] [] M.empty
|
||||||
|
|
|
@ -60,12 +60,15 @@ progDir topdir = topdir
|
||||||
progDir topdir = topdir </> "bin"
|
progDir topdir = topdir </> "bin"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
installProg :: FilePath -> FilePath -> IO ()
|
installProg :: FilePath -> FilePath -> IO (FilePath, FilePath)
|
||||||
installProg dir prog = searchPath prog >>= go
|
installProg dir prog = searchPath prog >>= go
|
||||||
where
|
where
|
||||||
go Nothing = error $ "cannot find " ++ prog ++ " in PATH"
|
go Nothing = error $ "cannot find " ++ prog ++ " in PATH"
|
||||||
go (Just f) = unlessM (boolSystem "install" [File f, File dir]) $
|
go (Just f) = do
|
||||||
|
let dest = dir </> takeFileName f
|
||||||
|
unlessM (boolSystem "install" [File f, File dest]) $
|
||||||
error $ "install failed for " ++ prog
|
error $ "install failed for " ++ prog
|
||||||
|
return (dest, f)
|
||||||
|
|
||||||
main = getArgs >>= go
|
main = getArgs >>= go
|
||||||
where
|
where
|
||||||
|
@ -73,5 +76,5 @@ main = getArgs >>= go
|
||||||
go (topdir:_) = do
|
go (topdir:_) = do
|
||||||
let dir = progDir topdir
|
let dir = progDir topdir
|
||||||
createDirectoryIfMissing True dir
|
createDirectoryIfMissing True dir
|
||||||
forM_ thirdpartyProgs $ installProg dir
|
installed <- forM thirdpartyProgs $ installProg dir
|
||||||
|
writeFile "tmp/standalone-installed" (show installed)
|
||||||
|
|
Loading…
Reference in a new issue