module Distribution.MacOSX (
appBundleBuildHook,
MacApp(..),
ChaseDeps(..),
Exclusions,
defaultExclusions
) where
import Control.Monad (forM_)
import Data.String.Utils (replace)
import Distribution.PackageDescription (PackageDescription(..),
Executable(..))
import Distribution.Simple
import Distribution.Simple.LocalBuildInfo (LocalBuildInfo(..))
import Distribution.Simple.Setup (BuildFlags)
import System.Cmd (system)
import System.FilePath
import System.Info (os)
import System.Directory (copyFile, createDirectoryIfMissing)
import Distribution.MacOSX.Common
import Distribution.MacOSX.Dependencies
appBundleBuildHook ::
[MacApp]
-> Args
-> BuildFlags -> PackageDescription -> LocalBuildInfo -> IO ()
appBundleBuildHook apps _ _ pkg localb =
case os of
"darwin" -> forM_ apps' $ makeAppBundle localb
where apps' = case apps of
[] -> map mkDefault $ executables pkg
xs -> xs
mkDefault x = MacApp (exeName x) Nothing Nothing [] [] DoNotChase
_ -> putStrLn "Not OS X, so not building an application bundle."
makeAppBundle ::
LocalBuildInfo -> MacApp -> IO ()
makeAppBundle localb app =
do appPath <- createAppDir localb app
maybeCopyPlist appPath app
maybeCopyIcon appPath app
`catch` \err -> putStrLn ("Warning: could not set up icon for " ++
appName app ++ ": " ++ show err)
includeResources appPath app
includeDependencies appPath app
osxIncantations appPath app
createAppDir :: LocalBuildInfo -> MacApp -> IO FilePath
createAppDir localb app =
do putStrLn $ "Creating application bundle directory " ++ appPath
createDirectoryIfMissing False appPath
createDirectoryIfMissing True $ takeDirectory exeDest
createDirectoryIfMissing True $ appPath </> "Contents/Resources"
putStrLn $ "Copying executable " ++ appName app ++ " into place"
copyFile exeSrc exeDest
return appPath
where appPath = buildDir localb </> appName app <.> "app"
exeDest = appPath </> pathInApp app (appName app)
exeSrc = buildDir localb </> appName app </> appName app
includeResources ::
FilePath
-> MacApp -> IO ()
includeResources appPath app = mapM_ includeResource $ resources app
where includeResource :: FilePath -> IO ()
includeResource p =
do let pDest = appPath </> pathInApp app p
putStrLn $ "Copying resource " ++ p ++ " to " ++ pDest
createDirectoryIfMissing True $ takeDirectory pDest
copyFile p $ pDest
return ()
maybeCopyPlist ::
FilePath
-> MacApp -> IO ()
maybeCopyPlist appPath app =
case appPlist app of
Just plPath -> do
putStrLn $ "Copying " ++ plPath ++ " to " ++ plDest
copyFile plPath plDest
Nothing -> case appIcon app of
Just icPath ->
do
let pl = replace "$program" (appName app) plistTemplate
pl' = replace "$iconPath" (takeFileName icPath) pl
writeFile plDest pl'
return ()
Nothing -> return ()
where plDest = appPath </> "Contents/Info.plist"
maybeCopyIcon ::
FilePath
-> MacApp -> IO ()
maybeCopyIcon appPath app =
case appIcon app of
Just icPath ->
do putStrLn $ "Copying " ++ icPath ++ " to app's icon"
copyFile icPath $
appPath </> "Contents/Resources" </> takeFileName icPath
Nothing -> return ()
osxIncantations ::
FilePath
-> MacApp -> IO ()
osxIncantations appPath app =
do putStrLn "Running Rez, etc."
system $ rez ++ " Carbon.r -o " ++ appPath </> pathInApp app (appName app)
writeFile (appPath </> "PkgInfo") "APPL????"
system $ setFile ++ " -a C " ++ appPath </> "Contents"
return ()
rez :: FilePath
rez = "/Developer/Tools/Rez"
setFile :: FilePath
setFile = "/Developer/Tools/SetFile"
plistTemplate :: String
plistTemplate = "\
\<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\
\<!DOCTYPE plist SYSTEM \"file://localhost/System/Library/DTDs/PropertyList.dtd\">\n\
\<plist version=\"0.9\">\n\
\<dict>\n\
\<key>CFBundleInfoDictionaryVersion</key>\n\
\<string>6.0</string>\n\
\<key>CFBundleIdentifier</key>\n\
\<string>org.haskell.$program</string>\n\
\<key>CFBundleDevelopmentRegion</key>\n\
\<string>English</string>\n\
\<key>CFBundleExecutable</key>\n\
\<string>$program</string>\n\
\<key>CFBundleIconFile</key>\n\
\<string>$iconPath</string>\n\
\<key>CFBundleName</key>\n\
\<string>$program</string>\n\
\<key>CFBundlePackageType</key>\n\
\<string>APPL</string>\n\
\<key>CFBundleSignature</key>\n\
\<string>????</string>\n\
\<key>CFBundleVersion</key>\n\
\<string>1.0</string>\n\
\<key>CFBundleShortVersionString</key>\n\
\<string>1.0</string>\n\
\<key>CFBundleGetInfoString</key>\n\
\<string>$program, bundled by cabal-macosx</string>\n\
\<key>LSRequiresCarbon</key>\n\
\<true/>\n\
\<key>CSResourcesFileMapped</key>\n\
\<true/>\n\
\</dict>\n\
\</plist>"