insane osx short library name hack
Since I'm dealing with arbitrarily short fields in which to store the library name, and would have to rebuild a bunch of stuff like git to avoid that, and I have to prefix this obnoxiously long "@executable_path" to it, it's easy to run out of space. This makes it use 1 and 2 letter long filenames for libraries in the app. Fun fun fun fun fun.
This commit is contained in:
parent
56b3dbe544
commit
01b359b4f8
1 changed files with 45 additions and 20 deletions
|
@ -23,19 +23,24 @@ import Utility.Monad
|
||||||
import Utility.SafeCommand
|
import Utility.SafeCommand
|
||||||
import Utility.Path
|
import Utility.Path
|
||||||
|
|
||||||
|
import qualified Data.Map as M
|
||||||
|
import qualified Data.Set as S
|
||||||
|
|
||||||
|
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] -> IO [FilePath]
|
mklibs :: FilePath -> [FilePath] -> LibMap -> IO [FilePath]
|
||||||
mklibs appbase libdirs = do
|
mklibs appbase libdirs libmap = do
|
||||||
new <- catMaybes <$> installLibs appbase
|
(new, libmap') <- installLibs appbase libmap
|
||||||
if null new
|
if null new
|
||||||
then return (libdirs++new)
|
then return (libdirs++new)
|
||||||
else mklibs appbase (libdirs++new)
|
else mklibs appbase (libdirs++new) libmap'
|
||||||
|
|
||||||
{- Returns directories into which new libs were installed. -}
|
{- Returns directories into which new libs were installed. -}
|
||||||
installLibs :: FilePath -> IO [Maybe FilePath]
|
installLibs :: FilePath -> LibMap -> IO ([FilePath], LibMap)
|
||||||
installLibs appbase = do
|
installLibs appbase libmap = do
|
||||||
needlibs <- otool appbase
|
(needlibs, libmap') <- otool appbase libmap
|
||||||
forM needlibs $ \lib -> do
|
libs <- forM needlibs $ \lib -> do
|
||||||
let dest = appbase </> takeFileName lib
|
let dest = appbase </> takeFileName lib
|
||||||
ifM (doesFileExist dest)
|
ifM (doesFileExist dest)
|
||||||
( return Nothing
|
( return Nothing
|
||||||
|
@ -46,19 +51,21 @@ installLibs appbase = do
|
||||||
_ <- boolSystem "chmod" [Param "644", File dest]
|
_ <- boolSystem "chmod" [Param "644", File dest]
|
||||||
return $ Just appbase
|
return $ Just appbase
|
||||||
)
|
)
|
||||||
|
return (catMaybes libs, libmap')
|
||||||
|
|
||||||
{- Returns libraries to install. -}
|
{- Returns libraries to install. -}
|
||||||
otool :: FilePath -> IO [FilePath]
|
otool :: FilePath -> LibMap -> IO ([FilePath], LibMap)
|
||||||
otool appbase = do
|
otool appbase libmap = do
|
||||||
files <- filterM doesFileExist =<< dirContentsRecursive appbase
|
files <- filterM doesFileExist =<< dirContentsRecursive appbase
|
||||||
l <- forM files $ \file -> do
|
process [] files libmap
|
||||||
libs <- filter unprocessed . parseOtool
|
|
||||||
<$> readProcess "otool" ["-L", file]
|
|
||||||
forM_ libs $ \lib -> install_name_tool file lib
|
|
||||||
return libs
|
|
||||||
return $ nub $ concat l
|
|
||||||
where
|
where
|
||||||
unprocessed s = not ("@executable_path" `isInfixOf` s)
|
unprocessed s = not ("@executable_path" `isInfixOf` s)
|
||||||
|
process c [] m = return (nub $ concat c, m)
|
||||||
|
process c (file:rest) m = do
|
||||||
|
libs <- filter unprocessed . parseOtool
|
||||||
|
<$> readProcess "otool" ["-L", file]
|
||||||
|
m' <- install_name_tool file libs m
|
||||||
|
process (libs:c) (file:rest) m'
|
||||||
|
|
||||||
parseOtool :: String -> [FilePath]
|
parseOtool :: String -> [FilePath]
|
||||||
parseOtool = catMaybes . map parse . lines
|
parseOtool = catMaybes . map parse . lines
|
||||||
|
@ -72,22 +79,40 @@ parseOtool = catMaybes . map parse . lines
|
||||||
- Assumes all executables are installed into the same directory, and
|
- Assumes all executables are installed into the same directory, and
|
||||||
- the libraries will be installed in subdirectories that match their
|
- the libraries will be installed in subdirectories that match their
|
||||||
- absolute paths. -}
|
- absolute paths. -}
|
||||||
install_name_tool :: FilePath -> FilePath -> IO ()
|
install_name_tool :: FilePath -> [FilePath] -> LibMap -> IO LibMap
|
||||||
install_name_tool binary lib = do
|
install_name_tool _ [] libmap = return libmap
|
||||||
|
install_name_tool binary (lib:rest) libmap = do
|
||||||
|
let (libname, libmap') = getLibName lib libmap
|
||||||
ok <- boolSystem "install_name_tool"
|
ok <- boolSystem "install_name_tool"
|
||||||
[ Param "-change"
|
[ Param "-change"
|
||||||
, File lib
|
, File lib
|
||||||
, Param $ "@executable_path" ++ (takeFileName lib)
|
, Param $ "@executable_path/" ++ libname
|
||||||
, File binary
|
, File binary
|
||||||
]
|
]
|
||||||
unless ok $
|
unless ok $
|
||||||
hPutStrLn stderr $ "install_name_tool failed for " ++ binary
|
hPutStrLn stderr $ "install_name_tool failed for " ++ binary
|
||||||
|
install_name_tool binary rest libmap'
|
||||||
|
|
||||||
|
{- Uses really short names for the library files it installs, because
|
||||||
|
- binaries have arbitrarily short RPATH field limits. -}
|
||||||
|
getLibName :: FilePath -> LibMap -> (FilePath, LibMap)
|
||||||
|
getLibName lib libmap = case M.lookup lib libmap of
|
||||||
|
Just n -> (n, libmap)
|
||||||
|
Nothing -> let n = nextfreename
|
||||||
|
in (n, M.insert lib n libmap)
|
||||||
|
where
|
||||||
|
names = map (\c -> [c]) letters ++
|
||||||
|
[[l1, l2] | l1 <- letters, l2 <- letters]
|
||||||
|
letters = ['a' .. 'z'] ++ ['A' .. 'Z'] ++ ['0' .. '9']
|
||||||
|
used = S.fromList $ M.elems libmap
|
||||||
|
nextfreename = fromMaybe (error "ran out of short library names!") $
|
||||||
|
headMaybe $ dropWhile (`S.member` used) names
|
||||||
|
|
||||||
main :: IO ()
|
main :: IO ()
|
||||||
main = getArgs >>= go
|
main = getArgs >>= go
|
||||||
where
|
where
|
||||||
go [] = error "specify OSXAPP_BASE"
|
go [] = error "specify OSXAPP_BASE"
|
||||||
go (appbase:_) = do
|
go (appbase:_) = do
|
||||||
libdirs <- mklibs appbase []
|
libdirs <- mklibs appbase [] M.empty
|
||||||
writeFile (appbase </> "libdirs") $
|
writeFile (appbase </> "libdirs") $
|
||||||
unlines $ nub libdirs
|
unlines $ nub libdirs
|
||||||
|
|
Loading…
Reference in a new issue