{-# LANGUAGE TupleSections #-} module Kit.Project ( totalSpecDependencies, installKit, generateXcodeProject, readSpec ) where import Kit.Spec import Kit.Contents import Kit.Repository import Kit.Util import Kit.Xcode.Builder import Kit.Xcode.XCConfig import Control.Monad.Error import Data.Maybe import Data.List import Data.Tree import System.Cmd import qualified Data.ByteString as BS -- Paths kitDir, projectDir, prefixFile, projectFile, xcodeConfigFile, depsConfigFile, kitUpdateMakeFilePath :: FilePath kitDir = "." "Kits" projectDir = "KitDeps.xcodeproj" prefixFile = "Prefix.pch" projectFile = projectDir "project.pbxproj" xcodeConfigFile = "Kit.xcconfig" depsConfigFile = "DepsOnly.xcconfig" kitUpdateMakeFilePath = "Makefile" kitUpdateMakeFile :: String kitUpdateMakeFile = "kit: Kit.xcconfig\n" ++ "Kit.xcconfig: ../KitSpec\n" ++ "\tcd .. && kit update && exit 1\n" prefixDefault :: String prefixDefault = "#ifdef __OBJC__\n" ++ " #import \n" ++ " #import \n" ++ "#endif\n" generateXcodeProject :: [Kit] -> Maybe String -> KitIO () generateXcodeProject deps depsOnlyConfig = do specs <- forM deps $ \kit -> readSpec (kitDir packageFileName kit "KitSpec") liftIO $ inDirectory kitDir $ do kitsContents <- forM specs readKitContents createProjectFile kitsContents createHeader kitsContents createConfig kitsContents specs writeFile depsConfigFile $ "#include \"" ++ xcodeConfigFile ++ "\"\n" ++ fromMaybe "" depsOnlyConfig where sourceDirs specs = specs >>= (\spec -> [ kitDir packageFileName spec specSourceDirectory spec, packageFileName spec specSourceDirectory spec ]) createProjectFile cs = do let headers = cs >>= contentHeaders let sources = cs >>= contentSources let libs = cs >>= contentLibs mkdirP projectDir writeFile projectFile $ renderXcodeProject headers sources libs "libKitDeps.a" createHeader cs = do let headers = mapMaybe namedPrefix cs let combinedHeader = stringJoin "\n" headers writeFile prefixFile $ prefixDefault ++ combinedHeader ++ "\n" createConfig cs specs = do let configs = mapMaybe contentConfig cs let combinedConfig = multiConfig "KitConfig" configs let kitHeaders = "HEADER_SEARCH_PATHS = $(HEADER_SEARCH_PATHS) " ++ stringJoin " " (sourceDirs specs) let prefixHeaders = "GCC_PRECOMPILE_PREFIX_HEADER = YES\nGCC_PREFIX_HEADER = $(SRCROOT)/Prefix.pch\n" writeFile xcodeConfigFile $ kitHeaders ++ "\n" ++ prefixHeaders ++ "\n" ++ configToString combinedConfig ++ "\n" writeFile kitUpdateMakeFilePath kitUpdateMakeFile -- | Return all the (unique) children of this tree (except the top node), in reverse depth order. refineDeps :: Eq a => Tree a -> [a] refineDeps = nub . concat . reverse . drop 1 . levels totalSpecDependencies :: KitRepository -> KitSpec -> KitIO [Kit] totalSpecDependencies kr spec = refineDeps <$> unfoldTreeM (unfoldDeps kr) spec -- todo: check for conflicts -- todo: check for version ranges :) unfoldDeps :: KitRepository -> KitSpec -> KitIO (Kit, [KitSpec]) unfoldDeps kr ks = (specKit ks,) <$> mapM (getKitSpec kr) (specDependencies ks) -- s/mapM/traverse ? installKit :: KitRepository -> Kit -> IO () installKit kr kit = do tmpDir <- getTemporaryDirectory let fp = tmpDir (packageFileName kit ++ ".tar.gz") putStrLn $ " -> Installing " ++ packageFileName kit fmap fromJust $ getKit kr kit fp mkdirP kitDir inDirectory kitDir $ system ("tar zxf " ++ fp) return () readSpec :: FilePath -> KitIO KitSpec readSpec path = checkExists path >>= liftIO . BS.readFile >>= ErrorT . return . parses where checkExists pathToSpec = do doesExist <- liftIO $ doesFileExist pathToSpec if doesExist then return pathToSpec else throwError $ "Couldn't find the spec at " ++ pathToSpec parses = maybeToRight "Parse error in KitSpec file" . decodeSpec