summaryrefslogtreecommitdiff
path: root/Build/OSXMkLibs.hs
blob: 45a86000b646a58741808ce12b881dd6a27b6484 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
{- OSX library copier
 -
 - Copyright 2012 Joey Hess <joey@kitenet.net>
 -
 - Licensed under the GNU GPL version 3 or higher.
 -}

module Build.OSXMkLibs where

import Control.Applicative
import System.Environment
import Data.Maybe
import System.FilePath
import System.Directory
import System.IO
import Control.Monad
import Data.List

import Utility.PartialPrelude
import Utility.Directory
import Utility.Process
import Utility.Monad
import Utility.SafeCommand
import Utility.Path

{- Recursively find and install libs, until nothing new to install is found. -}
mklibs :: FilePath -> [FilePath] -> IO [FilePath]
mklibs appbase libdirs = do
	new <- catMaybes <$> installLibs appbase
	if null new
		then return (libdirs++new)
		else mklibs appbase (libdirs++new)

{- Returns directories into which new libs were installed. -}
installLibs :: FilePath -> IO [Maybe FilePath]
installLibs appbase = do
	needlibs <- otool appbase
	forM needlibs $ \lib -> do
		let dest = appbase </> takeFileName lib
		ifM (doesFileExist dest)
			( return Nothing
			, do
				createDirectoryIfMissing True appbase
				putStrLn $ "installing " ++ lib
				_ <- boolSystem "cp" [File lib, File dest]
				return $ Just appbase
			)

{- Returns libraries to install. -}
otool :: FilePath -> IO [FilePath]
otool appbase = do
	files <- filterM doesFileExist =<< dirContentsRecursive appbase
	l <- forM files $ \file -> do
		libs <- filter unprocessed . parseOtool
			<$> readProcess "otool" ["-L", file]
		forM_ libs $ \lib -> install_name_tool file lib
		return libs
	return $ nub $ concat l
  where
	unprocessed s = not ("@executable_path" `isInfixOf` s)

parseOtool :: String -> [FilePath]
parseOtool = catMaybes . map parse . lines
  where
	parse l
		| "\t" `isPrefixOf` l = headMaybe $ words l
		| otherwise = Nothing

{- Adjusts binaries to use libraries in paths relative to the executable.
 -
 - Assumes all executables are installed into the same directory, and
 - the libraries will be installed in subdirectories that match their
 - absolute paths. -}
install_name_tool :: FilePath -> FilePath -> IO ()
install_name_tool binary lib = do
	ok <- boolSystem "install_name_tool"
		[ Param "-change"
		, File lib
		, Param $ "@executable_path" ++ (dropFileName lib)
		, File binary
		]
	unless ok $
		hPutStrLn stderr $ "install_name_tool failed for " ++ binary

main :: IO ()
main = getArgs >>= go
  where
	go [] = error "specify OSXAPP_BASE"
	go (appbase:_) = do
		libdirs <- mklibs appbase []
		writeFile (appbase </> "libdirs") $
			unlines $ nub libdirs