kit 0.5.2 → 0.6
raw patch · 21 files changed
+909/−838 lines, 21 filesdep +data-objectdep +data-object-yamldep −json
Dependencies added: data-object, data-object-yaml
Dependencies removed: json
Files
- Kit/CmdArgs.hs +33/−0
- Kit/Commands.hs +51/−0
- Kit/Contents.hs +53/−0
- Kit/Main.hs +50/−114
- Kit/Model.hs +0/−64
- Kit/Package.hs +10/−28
- Kit/Project.hs +89/−135
- Kit/Repository.hs +18/−21
- Kit/Spec.hs +110/−0
- Kit/Util.hs +28/−25
- Kit/XCode/Builder.hs +0/−106
- Kit/XCode/Common.hs +0/−69
- Kit/XCode/ProjectFileTemplate.hs +0/−202
- Kit/XCode/XCConfig.hs +0/−61
- Kit/Xcode/Builder.hs +113/−0
- Kit/Xcode/Common.hs +71/−0
- Kit/Xcode/ProjectFileTemplate.hs +190/−0
- Kit/Xcode/XCConfig.hs +66/−0
- Main.hs +5/−2
- Text/PList.hs +13/−4
- kit.cabal +9/−7
+ Kit/CmdArgs.hs view
@@ -0,0 +1,33 @@+{-# LANGUAGE DeriveDataTypeable #-}+module Kit.CmdArgs (parseArgs, KitCmdArgs(..)) where++ import System.Console.CmdArgs as CA++ appVersion :: String+ appVersion = "0.6" -- TODO how to keep this up to date with kit.cabal?++ data KitCmdArgs = Update+ | Package+ | PublishLocal+ | Verify { sdk :: String }+ | CreateSpec { name :: String, version :: String } deriving (Show, Data, Typeable)++ parseMode :: KitCmdArgs+ parseMode = modes [+ Update &= help "Create an Xcode project to wrap the dependencies defined in `KitSpec`"+ &= auto -- The default mode to run. This is most common usage.+ , CreateSpec { Kit.CmdArgs.name = (def &= typ "NAME") &= argPos 0, version = (def &= typ "VERSION") &= argPos 1 } &=+ explicit &=+ CA.name "create-spec" &=+ help "Write out a KitSpec file using the given name and version."+ , Package &=+ help "Create a tar.gz wrapping this kit"+ , PublishLocal &= explicit &= CA.name "publish-local" &=+ help "Package this kit, then deploy it to the local repository (~/.kit/repository)"+ , Verify { sdk = "iphonesimulator4.0" &= typ "SDK" &= help "iphoneos, iphonesimulator4.0, etc."} &=+ help "Package this kit, then attempt to use it as a dependency in an empty project. This will assert that the kit and all its dependencies can be compiled together."+ ]++ parseArgs :: IO KitCmdArgs+ parseArgs = cmdArgs $ parseMode &= program "kit" &= summary ("Kit v" ++ appVersion ++ ". It's a dependency manager for Objective-C projects built with Xcode.")+
+ Kit/Commands.hs view
@@ -0,0 +1,51 @@+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+module Kit.Commands (+ Command,+ liftKit,+ mySpec,+ myKitSpec,+ myRepository,+ runCommand,+ defaultLocalRepoPath,+ defaultLocalRepository+) where++import Kit.Util+import Kit.Spec+import Kit.Repository+import Kit.Project++import Control.Monad.Error+import Control.Monad.Reader++newtype Command a = Command (ReaderT (KitSpec, KitRepository) (ErrorT String IO) a) deriving Monad++instance MonadIO Command where+ liftIO a = Command $ liftIO a++liftKit :: KitIO a -> Command a+liftKit = Command . ReaderT . const++mySpec :: Command KitSpec+mySpec = Command $ ask >>= (return . fst) ++myRepository :: Command KitRepository+myRepository = Command $ ask >>= (return . snd)++runCommand :: Command a -> IO ()+runCommand (Command cmd) = run $ do+ spec <- readSpec "KitSpec"+ repository <- liftIO defaultLocalRepository+ runReaderT cmd (spec, repository)+ where run = (handleFails =<<) . runErrorT+ handleFails = either (putStrLn . ("kit error: " ++)) (const $ return ())++myKitSpec :: KitIO KitSpec+myKitSpec = readSpec "KitSpec"++defaultLocalRepoPath :: IO FilePath+defaultLocalRepoPath = getHomeDirectory >>= \h -> return $ h </> ".kit" </> "repository"++defaultLocalRepository :: IO KitRepository+defaultLocalRepository = fileRepo <$> defaultLocalRepoPath+
+ Kit/Contents.hs view
@@ -0,0 +1,53 @@+module Kit.Contents (+ KitContents(..),+ readKitContents,+ namedPrefix+ ) where++import Kit.Spec+import Kit.Util+import Kit.Xcode.XCConfig++import qualified Data.Traversable as T++-- | The determined contents of a particular Kit+data KitContents = KitContents { + contentKit :: Kit,+ contentHeaders :: [FilePath], -- ^ Paths to headers+ contentSources :: [FilePath], -- ^ Paths to source files+ contentLibs :: [FilePath], -- ^ Paths to static libs+ contentConfig :: Maybe XCConfig, -- ^ Contents of the xcconfig base file+ contentPrefix :: Maybe String -- ^ Contents of the prefix header+}++namedPrefix :: KitContents -> Maybe String+namedPrefix kc = fmap (\s -> "//" ++ (packageFileName . contentKit $ kc) ++ "\n" ++ s) $ contentPrefix kc++-- | Determine the contents for a Kit, assumes that we're in a projects 'Kits' folder.+readKitContents :: KitSpec -> IO KitContents+readKitContents spec =+ let kitDir = packageFileName spec+ find dir tpe = glob ((kitDir </> dir </> "**/*") ++ tpe)+ findSrc = find $ specSourceDirectory spec+ headers = findSrc ".h"+ sources = join <$> mapM findSrc [".m", ".mm", ".c"]+ libs = find (specLibDirectory spec) ".a" + config = readConfig spec+ prefix = readHeader spec+ in KitContents (specKit spec) <$> headers <*> sources <*> libs <*> config <*> prefix++-- Report missing file+readHeader :: KitSpec -> IO (Maybe String)+readHeader spec = do+ let fp = packageFileName spec </> specPrefixFile spec+ exists <- doesFileExist fp+ T.sequence (fmap readFile $ justTrue exists fp)++-- TODO make this report missing file+readConfig :: KitSpec -> IO (Maybe XCConfig)+readConfig spec = do+ let fp = packageFileName spec </> specConfigFile spec+ exists <- doesFileExist fp+ contents <- T.sequence (fmap readFile $ justTrue exists fp)+ return $ fmap (fileContentsToXCC $ packageName spec) contents+
Kit/Main.hs view
@@ -1,143 +1,79 @@-{-# LANGUAGE DeriveDataTypeable #-} module Kit.Main where- import qualified Data.ByteString as BS import Control.Monad.Trans- import Control.Monad.Error- import Data.List- import Data.Maybe- import Data.Monoid- import Kit.Model+ import qualified Kit.CmdArgs as KA + import Kit.Commands+ import Kit.Spec import Kit.Package import Kit.Project- import Kit.Repository import Kit.Util- import Kit.XCode.Builder- import qualified Data.Traversable as T import System.Cmd- import System.Environment- import Text.JSON- import System.Console.CmdArgs- import System.Console.CmdArgs as CA - import Data.Data- import Data.Typeable-- appVersion = "0.5"-- defaultLocalRepoPath = do- home <- getHomeDirectory- return $ home </> ".kit" </> "repository"- defaultLocalRepository = fmap fileRepo defaultLocalRepoPath-- handleFails :: Either KitError a -> IO ()- handleFails = either (putStrLn . ("kit error: " ++)) (const $ return ())-- run f = runErrorT f >>= handleFails-- doUpdate :: IO ()- doUpdate = run $ do- repo <- liftIO defaultLocalRepository- spec <- myKitSpec- deps <- getMyDeps repo- puts $ "Dependencies: " ++ (stringJoin ", " $ map kitFileName deps)- liftIO $ mapM (installKit repo) deps- puts " -> Generating XCode project..."- generateXCodeProject deps $ specKitDepsXcodeFlags spec- puts "Kit complete. You may need to restart XCode for it to pick up any changes."- where p x = liftIO $ print x- puts x = liftIO $ putStrLn x+ doUpdate :: Command ()+ doUpdate = do+ repo <- myRepository+ spec <- mySpec+ deps <- liftKit $ totalSpecDependencies repo spec+ puts $ "Dependencies: " ++ stringJoin ", " (map packageFileName deps)+ liftIO $ mapM_ (installKit repo) deps+ puts " -> Generating Xcode project..."+ liftKit $ generateXcodeProject deps (specKitDepsXcodeFlags spec)+ puts "Kit complete. You may need to restart Xcode for it to pick up any changes." - doPackageKit :: IO ()- doPackageKit = do- mySpec <- runErrorT myKitSpec- T.for mySpec package- return ()+ doPackageKit :: Command ()+ doPackageKit = mySpec >>= liftIO . package - doDeployLocal :: IO ()- doDeployLocal = do- mySpec <- runErrorT myKitSpec- T.for mySpec package- T.for mySpec x- handleFails mySpec+ doDeployLocal :: Command ()+ doDeployLocal = mySpec >>= \spec -> liftIO $ do + package spec+ publishLocal spec + return () where- x :: KitSpec -> IO ()- x spec = let- k = specKit spec- pkg = (kitFileName k ++ ".tar.gz")- in do- repo <- defaultLocalRepoPath- let thisKitDir = repo </> "kits" </> kitName k </> kitVersion k- mkdir_p thisKitDir- copyFile ("dist" </> pkg) $ thisKitDir </> pkg- copyFile "KitSpec" $ thisKitDir </> "KitSpec"+ publishLocal :: KitSpec -> IO ()+ publishLocal spec = let pkg = (packageFileName spec ++ ".tar.gz")+ in do+ repo <- defaultLocalRepoPath+ let thisKitDir = repo </> "kits" </> packageName spec </> packageVersion spec+ mkdir_p thisKitDir+ copyFile ("dist" </> pkg) (thisKitDir </> pkg)+ copyFile "KitSpec" (thisKitDir </> "KitSpec") - doVerify :: String -> IO ()- doVerify sdk = run $ do- mySpec <- myKitSpec+ doVerify :: String -> Command ()+ doVerify sdk = do+ spec <- mySpec puts "Checking that the kit can be depended upon..." puts " #> Deploying locally"- liftIO doDeployLocal+ doDeployLocal puts " #> Building temporary parent project" tmp <- liftIO getTemporaryDirectory- liftIO $ inDirectory tmp $ do+ inDirectory tmp $ do let kitVerifyDir = "kit-verify" cleanOrCreate kitVerifyDir inDirectory kitVerifyDir $ do- let verifySpec = (defaultSpecForKit $ Kit "verify-kit" "1.0"){ specDependencies = [specKit mySpec] }- writeFile "KitSpec" $ encode verifySpec+ writeSpec "KitSpec" (defaultSpec "verify-kit" "1.0") { specDependencies = [specKit spec] } doUpdate inDirectory "Kits" $ do- system $ "open KitDeps.xcodeproj"- system $ "xcodebuild -sdk " ++ sdk- putStrLn "OK."+ liftIO $ system "open KitDeps.xcodeproj"+ liftIO $ system $ "xcodebuild -sdk " ++ sdk+ puts "OK." puts "End checks."- where puts = liftIO . putStrLn - doCreateSpec :: String -> String -> IO ()+ doCreateSpec :: String -> String -> Command () doCreateSpec name version = do- let kit =(Kit name version)- let spec = defaultSpecForKit kit- writeFile "KitSpec" $ encode spec- putStrLn $ "Created KitSpec for " ++ kitFileName kit- return ()-- data KitCmdArgs = Update- | Package- | PublishLocal- | Verify { sdk :: String }- | CreateSpec { name :: String, version :: String } deriving (Show, Data, Typeable)-- parseArgs = cmdArgs $ modes [- Update &= help "Create an Xcode project to wrap the dependencies defined in `KitSpec`"- &= auto -- The default mode to run. This is most common usage.- , (CreateSpec { Kit.Main.name = (def &= typ "NAME") &= argPos 0, version = (def &= typ "VERSION") &= argPos 1 }) &=- explicit &=- CA.name "create-spec" &=- help "Write out a KitSpec file using the given name and version."- , Package &=- help "Create a tar.gz wrapping this kit"- , PublishLocal &= explicit &= CA.name "publish-local" &=- help "Package this kit, then deploy it to the local repository (~/.kit/repository)"- , Verify { sdk = "iphonesimulator4.0" &= typ "SDK" &= help "iphoneos, iphonesimulator4.0, etc."} &=- help "Package this kit, then attempt to use it as a dependency in an empty project. This will assert that the kit and all its dependencies can be compiled together."- ] &= program "kit"- &= summary ("Kit v" ++ appVersion ++ ". It's a dependency manager for Objective-C projects built with XCode.")+ let spec = defaultSpec name version + writeSpec "KitSpec" spec+ puts $ "Created KitSpec for " ++ packageFileName spec - handleArgs :: KitCmdArgs -> IO ()- handleArgs Update = doUpdate- handleArgs Package = doPackageKit- handleArgs PublishLocal = doDeployLocal- handleArgs (Verify sdk) = doVerify sdk- handleArgs (CreateSpec name version) = doCreateSpec name version+ handleArgs :: KA.KitCmdArgs -> Command ()+ handleArgs KA.Update = doUpdate+ handleArgs KA.Package = doPackageKit+ handleArgs KA.PublishLocal = doDeployLocal+ handleArgs (KA.Verify sdkName) = doVerify sdkName+ handleArgs (KA.CreateSpec name version) = doCreateSpec name version kitMain :: IO () kitMain = do- localRepo <- defaultLocalRepository- path <- defaultLocalRepoPath- mkdir_p path- handleArgs =<< parseArgs -- =<< getArgs---+ mkdir_p =<< defaultLocalRepoPath + args <- KA.parseArgs+ runCommand $ handleArgs args
− Kit/Model.hs
@@ -1,64 +0,0 @@-module Kit.Model where-- import Text.JSON- import Control.Applicative- import System.FilePath.Posix-- data KitSpec = KitSpec {- specKit :: Kit,- specDependencies :: [Kit],- specSourceDirectory :: FilePath,- specTestDirectory :: FilePath,- specLibDirectory :: FilePath,- specPrefixFile :: FilePath,- specConfigFile :: FilePath,- specKitDepsXcodeFlags :: Maybe String- } deriving (Show, Read)-- type Version = String-- data Kit = Kit {- kitName :: String,- kitVersion :: Version- } deriving (Eq, Show, Ord, Read)-- kitFileName :: Kit -> String- kitFileName k = kitName k ++ "-" ++ kitVersion k-- defaultSpecForKit :: Kit -> KitSpec- defaultSpecForKit kit = KitSpec kit [] "src" "test" "lib" "Prefix.pch" "Config.xcconfig" Nothing- -- TODO make this and the json reading use the same defaults- -- I suspect that to do this I'll need update functions for each of- -- fields in the KitSpec record.- -- Look at the 'lenses' package on haskell.-- instance JSON Kit where- showJSON kit = makeObj [ ("name", w kitName) , ("version", w kitVersion) ] where w f = showJSON . f $ kit-- readJSON (JSObject obj) = Kit <$> f "name" <*> f "version"- where f x = f' obj x -- instance JSON KitSpec where- showJSON spec = makeObj [- ("name", showJSON $ kitName kit)- , ("version", showJSON $ kitVersion kit)- , ("dependencies", showJSON $ specDependencies spec)- ]- where kit = specKit spec-- readJSON js@(JSObject obj) =- KitSpec <$> readJSON js - <*> (f "dependencies" <|> pure []) - <*> (f "source-directory" <|> pure "src")- <*> (f "test-directory" <|> pure "test")- <*> (f "lib-directory" <|> pure "lib")- <*> (f "prefix-header" <|> pure "Prefix.pch")- <*> (f "xcconfig" <|> pure "Config.xcconfig")- <*> (Just <$> f "kitdeps-xcode-flags" <|> pure Nothing)- where f x = f' obj x-- f' obj x = mLookup x (fromJSObject obj) >>= readJSON-- mLookup :: Monad m => String -> [(String, b)] -> m b- mLookup a as = maybe (fail $ "No such element: " ++ a) return (lookup a as)-
Kit/Package.hs view
@@ -1,51 +1,33 @@ module Kit.Package (package) where - import Kit.Model+ import Kit.Spec import Kit.Util- import Kit.Project import System.Cmd import Control.Monad.Trans- import Control.Monad import Data.List- import Data.Maybe import Debug.Trace - {-- Package format- src/ | configurable source dir- test/ | test directory- lib/ | lib directory- *.xcconfig- *.pch- KitSpec- *.codeproj- -}- fileBelongsInPackage :: KitSpec -> FilePath -> Bool fileBelongsInPackage config fp = let- a = elem fp [specSourceDirectory config, specTestDirectory config, specLibDirectory config, "KitSpec"]- b = "xcodeproj" `isSuffixOf` fp- c = "xcconfig" `isSuffixOf` fp- d = ".pch" `isSuffixOf` fp- in a || b || c || d+ isCore = elem fp [specSourceDirectory config, specTestDirectory config, specLibDirectory config, "KitSpec"]+ isProject = or $ map (`isSuffixOf` fp) ["xcodeproj", "xcconfig", ".pch"]+ in isCore || isProject package :: KitSpec -> IO () package spec = do tempDir <- getTemporaryDirectory- current <- getCurrentDirectory- distDir <- fmap (</> "dist") getCurrentDirectory+ distDir <- (</> "dist") <$> getCurrentDirectory let kd = tempDir </> kitPath cleanOrCreate kd contents <- getDirectoryContents "."- mapM_ (cp_r_to kd) (filter (fileBelongsInPackage spec) contents)+ mapM_ (copyAllTo kd) (filter (fileBelongsInPackage spec) contents) mkdir_p distDir- inDirectory tempDir $ sh $ "tar czf " ++ (distDir </> (kitPath ++ ".tar.gz")) ++ " " ++ kitPath+ inDirectory tempDir $ do+ sh $ "tar czf " ++ (distDir </> (kitPath ++ ".tar.gz")) ++ " " ++ kitPath return () where- kitPath = kitFileName . specKit $ spec+ kitPath = packageFileName spec sh c = liftIO $ system (trace c c)- puts c = liftIO $ putStrLn c- p c = liftIO $ print c- cp_r_to kd c = sh $ "cp -r " ++ c ++ " " ++ kd ++ "/"+ copyAllTo kd c = sh $ "cp -r " ++ c ++ " " ++ kd ++ "/"
Kit/Project.hs view
@@ -1,153 +1,107 @@+{-# LANGUAGE TupleSections #-} module Kit.Project (- getMyDeps,+ totalSpecDependencies, installKit,- generateXCodeProject,- myKitSpec)+ generateXcodeProject,+ readSpec+ ) where - import Control.Monad.Trans- import Control.Monad- import Control.Monad.Error- import Control.Applicative- import qualified Data.Foldable as F- import Data.Maybe- import Data.List- import Data.Tree- import Data.Monoid- import System.Cmd- import Kit.Model- import Kit.Repository- import Kit.Util- import Kit.XCode.Builder- import Kit.XCode.XCConfig- import Text.JSON- import qualified Data.Traversable as T-- -- Paths- kitDir = "." </> "Kits"- projectDir = "KitDeps.xcodeproj"- prefixFile = "KitDeps_Prefix.pch"- projectFile = projectDir </> "project.pbxproj"- xcodeConfigFile = "Kit.xcconfig"- depsConfigFile = "DepsOnly.xcconfig"- kitUpdateMakeFilePath = "Makefile"- kitUpdateMakeFile = "kit: Kit.xcconfig\nKit.xcconfig: ../KitSpec\n\tcd .. && kit update && exit 1\n"-- prefixDefault = "#ifdef __OBJC__\n" ++ " #import <Foundation/Foundation.h>\n #import <UIKit/UIKit.h>\n" ++ "#endif\n"-- -- Represents an extracted project- -- Headers, Sources, Config, Prefix content- data KitContents = KitContents { contentHeaders :: [FilePath], contentSources :: [FilePath], contentLibs :: [FilePath], contentConfig :: Maybe XCConfig, contentPrefix :: Maybe String }-- readKitContents :: KitSpec -> IO KitContents- readKitContents spec =- let kit = specKit spec- kitDir = kitFileName kit- find dir tpe = glob ((kitDir </> dir </> "**/*") ++ tpe)- findSrc = find $ specSourceDirectory spec- headers = findSrc ".h"- sources = fmap join $ T.sequence [findSrc ".m", findSrc ".mm", findSrc ".c"]- libs = find (specLibDirectory spec) ".a" - config = readConfig spec- prefix = readHeader spec- in KitContents <$> headers <*> sources <*> libs <*> config <*> prefix+import Kit.Spec+import Kit.Contents+import Kit.Repository+import Kit.Util+import Kit.Xcode.Builder+import Kit.Xcode.XCConfig - generateXCodeProject :: [Kit] -> Maybe String -> KitIO ()- generateXCodeProject deps depsOnlyConfig = do- liftIO $ mkdir_p kitDir- specs <- forM deps $ \kit -> readSpec (kitDir </> kitFileName 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 kitFileNames = map kitFileName deps- sourceDirs specs = specs >>= (\spec -> [- kitDir </> (kitFileName . specKit $ spec) </> specSourceDirectory spec, - (kitFileName . specKit $ spec) </> specSourceDirectory spec- ])- createProjectFile cs = do- let headers = cs >>= contentHeaders- let sources = cs >>= contentSources- let libs = cs >>= contentLibs- mkdir_p projectDir- writeFile projectFile $ buildXCodeProject headers sources libs- createHeader cs = do- let headers = mapMaybe contentPrefix 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)/KitDeps_Prefix.pch\n"- writeFile xcodeConfigFile $ kitHeaders ++ "\n" ++ prefixHeaders ++ "\n" ++ configToString combinedConfig- writeFile kitUpdateMakeFilePath kitUpdateMakeFile+import Control.Monad.Error+import Data.Maybe+import Data.List+import Data.Tree+import System.Cmd - -- Report missing file- readHeader :: KitSpec -> IO (Maybe String)- readHeader spec = do- let fp = (kitFileName . specKit $ spec) </> specPrefixFile spec- exists <- doesFileExist fp- contents <- T.sequence (fmap readFile $ justTrue exists fp)- return contents+import qualified Data.ByteString as BS - -- TODO make this report missing file- readConfig :: KitSpec -> IO (Maybe XCConfig)- readConfig spec = do- let kit = specKit spec- let fp = kitFileName kit </> specConfigFile spec- exists <- doesFileExist fp- contents <- T.sequence (fmap readFile $ justTrue exists fp)- return $ fmap (fileContentsToXCC $ kitName kit) contents+-- Paths - refineDeps :: Tree Kit -> [Kit]- refineDeps = nub . concat . reverse . drop 1 . levels+kitDir, projectDir, prefixFile, projectFile, xcodeConfigFile, depsConfigFile, kitUpdateMakeFilePath :: FilePath - depsForSpec' :: KitRepository -> KitSpec -> KitIO [Kit]- depsForSpec' kr spec = do- tree <- unfoldTreeM (unfoldDeps kr) spec- -- todo: check for conflicts- return $ refineDeps tree+kitDir = "." </> "Kits"+projectDir = "KitDeps.xcodeproj"+prefixFile = "Prefix.pch"+projectFile = projectDir </> "project.pbxproj"+xcodeConfigFile = "Kit.xcconfig"+depsConfigFile = "DepsOnly.xcconfig"+kitUpdateMakeFilePath = "Makefile" - unfoldDeps :: KitRepository -> KitSpec -> KitIO (Kit, [KitSpec])- unfoldDeps kr ks = do- let kit = specKit ks- let depKits = specDependencies ks- specs <- mapM (getKitSpec kr) depKits- return (kit, specs)+kitUpdateMakeFile :: String+kitUpdateMakeFile = "kit: Kit.xcconfig\n" +++ "Kit.xcconfig: ../KitSpec\n" +++ "\tcd .. && kit update && exit 1\n" - getMyDeps :: KitRepository -> KitIO [Kit]- getMyDeps kr = myKitSpec >>= depsForSpec' kr+prefixDefault :: String+prefixDefault = "#ifdef __OBJC__\n" ++ + " #import <Foundation/Foundation.h>\n" ++ + " #import <UIKit/UIKit.h>\n" ++ + "#endif\n" - installKit :: KitRepository -> Kit -> IO ()- installKit kr kit = do- tmpDir <- getTemporaryDirectory- let fp = tmpDir </> (kitFileName kit ++ ".tar.gz")- putStrLn $ " -> Installing " ++ kitFileName kit- fmap fromJust $ getKit kr kit fp- let dest = kitDir- mkdir_p dest- inDirectory dest $ sh ("tar zxf " ++ fp)- return ()- where sh = system+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+ mkdir_p 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 - readSpec :: FilePath -> KitIO KitSpec- readSpec kitSpecPath = readSpecContents kitSpecPath >>= ErrorT . return . parses+-- | 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 - myKitSpec :: KitIO KitSpec- myKitSpec = readSpec "KitSpec"+totalSpecDependencies :: KitRepository -> KitSpec -> KitIO [Kit]+totalSpecDependencies kr spec = refineDeps <$> unfoldTreeM (unfoldDeps kr) spec+ -- todo: check for conflicts+ -- todo: check for version ranges :) - -- private!- checkExists :: FilePath -> KitIO FilePath- checkExists kitSpecPath = do- doesExist <- liftIO $ doesFileExist kitSpecPath- maybeToKitIO ("Couldn't find the spec at " ++ kitSpecPath) (justTrue doesExist kitSpecPath)+unfoldDeps :: KitRepository -> KitSpec -> KitIO (Kit, [KitSpec])+unfoldDeps kr ks = (specKit ks,) <$> mapM (getKitSpec kr) (specDependencies ks) -- s/mapM/traverse ? - readSpecContents :: FilePath -> KitIO String- readSpecContents kitSpecPath = checkExists kitSpecPath >>= liftIO . readFile+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+ mkdir_p kitDir + inDirectory kitDir $ system ("tar zxf " ++ fp)+ return () - parses :: String -> Either KitError KitSpec- parses contents = resultToEither (decode contents)+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
Kit/Repository.hs view
@@ -6,24 +6,21 @@ KitRepository ) where - import Kit.Model+ import Kit.Spec import Kit.Util + import Network.BufferType import Network.HTTP import Network.URI- import Network.BufferType- import qualified Data.List as L+ import Control.Monad.Error import Data.Maybe+ import qualified Data.List as L import qualified Data.Traversable as T- import Control.Monad- import Control.Monad.Trans- import Control.Monad.Error- import Text.JSON import qualified Data.ByteString as BS data KitRepository = KitRepository { repoSave :: String -> FilePath -> IO (Maybe ()),- repoRead :: String -> IO (Maybe String)+ repoRead :: String -> IO (Maybe BS.ByteString) } getKit :: KitRepository -> Kit -> FilePath -> IO (Maybe ())@@ -32,38 +29,38 @@ getKitSpec :: KitRepository -> Kit -> KitIO KitSpec getKitSpec kr k = do mbKitStuff <- liftIO $ repoRead kr (kitSpecPath k)- maybe (throwError $ "Missing " ++ kitFileName k) f mbKitStuff- where f contents = maybeToKitIO ("Invalid KitSpec file for " ++ kitFileName k) $ case (decode contents) of- Ok a -> Just a- Error _ -> Nothing+ maybe (throwError $ "Missing " ++ packageFileName k) f mbKitStuff+ where f contents = maybeToKitIO ("Invalid KitSpec file for " ++ packageFileName k) $ decodeSpec contents + -- TODO: Use the base url! webRepo :: String -> KitRepository- webRepo baseUrl = KitRepository save read where+ webRepo _ = KitRepository save doRead where save = download- read = getBody+ doRead = getBody fileRepo :: String -> KitRepository- fileRepo baseDir = KitRepository save read where+ fileRepo baseDir = KitRepository save doRead where save src destPath = Just <$> copyFile (baseDir </> src) destPath- read path = let file = (baseDir </> path) in do+ doRead fp = let file = (baseDir </> fp) in do exists <- doesFileExist file- T.sequenceA $ justTrue exists $ readFile file+ T.sequenceA $ justTrue exists $ BS.readFile file -- private! baseKitPath :: Kit -> String baseKitPath k = joinS ["kits", kitName k, kitVersion k] "/" where joinS xs x = foldl1 (++) $ L.intersperse x xs - kitPackagePath k = baseKitPath k ++ "/" ++ kitFileName k ++ ".tar.gz"+ kitPackagePath, kitSpecPath :: Kit -> String++ kitPackagePath k = baseKitPath k ++ "/" ++ packageFileName k ++ ".tar.gz" kitSpecPath k = baseKitPath k ++ "/" ++ "KitSpec" getBody :: BufferType a => HStream a => String -> IO (Maybe a)- getBody path = let+ getBody p = let request = defaultGETRequest_ . fromJust . parseURI- checkResponse r = justTrue (rspCode r == (2,0,0)) r leftMaybe = either (const Nothing) Just in do- rr <- Network.HTTP.simpleHTTP $ request path+ rr <- Network.HTTP.simpleHTTP $ request p return $ fmap rspBody $ leftMaybe rr download :: String -> FilePath -> IO (Maybe ())
+ Kit/Spec.hs view
@@ -0,0 +1,110 @@+{-# LANGUAGE KindSignatures, TypeSynonymInstances, NoMonomorphismRestriction, OverlappingInstances #-}+module Kit.Spec (+ -- | The Core Kit types+ KitSpec(..),+ Kit(..),+ -- | Duck typing the name/version of a Kit/Spec+ Packageable(..),+ packageFileName,+ -- | Utils+ defaultSpec,+ -- | Serialisation+ decodeSpec,+ encodeSpec,+ writeSpec+ ) where++ import Control.Applicative+ import Control.Monad.Trans+ + import Data.Object+ import qualified Data.ByteString as BS + import qualified Data.Object.Yaml as Y++ data KitSpec = KitSpec {+ specKit :: Kit,+ specDependencies :: [Kit],+ specSourceDirectory :: FilePath,+ specTestDirectory :: FilePath,+ specLibDirectory :: FilePath,+ specPrefixFile :: FilePath,+ specConfigFile :: FilePath,+ specKitDepsXcodeFlags :: Maybe String+ } deriving (Show, Read)++ data Kit = Kit {+ kitName :: String,+ kitVersion :: String+ } deriving (Eq, Show, Ord, Read)++ class Packageable a where+ packageName :: a -> String+ packageVersion :: a -> String++ packageFileName :: Packageable a => a -> String+ packageFileName a = packageName a ++ "-" ++ packageVersion a++ instance Packageable Kit where+ packageName = kitName+ packageVersion = kitVersion++ instance Packageable KitSpec where+ packageName = kitName . specKit+ packageVersion = kitVersion . specKit++ defaultSpec :: String -> String -> KitSpec+ defaultSpec name version = KitSpec (Kit name version) [] "src" "test" "lib" "Prefix.pch" "Config.xcconfig" Nothing+ -- TODO make this and the json reading use the same defaults+ -- I suspect that to do this I'll need update functions for each of+ -- fields in the KitSpec record.+ -- Look at the 'lenses' package on hackage.++ decodeSpec :: BS.ByteString -> Maybe KitSpec+ decodeSpec s = Y.decode s >>= readObject ++ encodeSpec :: KitSpec -> BS.ByteString+ encodeSpec = Y.encode . showObject++ writeSpec :: MonadIO m => FilePath -> KitSpec -> m ()+ writeSpec fp spec = liftIO $ BS.writeFile fp $ encodeSpec spec++ class IsObject x where+ showObject :: x -> StringObject+ readObject :: StringObject -> Maybe x+ + + (#>) :: IsObject b => [(String, Object String String)] -> String -> Maybe b+ obj #> key = lookupObject key obj >>= readObject++ instance IsObject a => IsObject [a] where+ showObject xs = Sequence $ map showObject xs+ readObject x = fromSequence x >>= mapM readObject ++ instance IsObject String where+ showObject = Scalar+ readObject = fromScalar++ instance IsObject Kit where+ showObject kit = Mapping [("name", w kitName), ("version", w kitVersion)] where w f = showObject . f $ kit++ readObject x = fromMapping x >>= \obj -> Kit <$> obj #> "name" <*> obj #> "version" ++ instance IsObject KitSpec where+ showObject spec = Mapping [+ "name" ~> (val $ kitName . specKit),+ "version" ~> (val $ kitVersion . specKit),+ "dependencies" ~> showObject (specDependencies spec)+ ] where a ~> b = (a, b)+ val f = Scalar . f $ spec++ readObject x = fromMapping x >>= parser+ where or' a b = a <|> pure b+ parser obj = KitSpec <$> readObject x+ <*> (obj #> "dependencies" `or'` [])+ <*> (obj #> "source-directory" `or'` "src")+ <*> (obj #> "test-directory" `or'` "test")+ <*> (obj #> "lib-directory" `or'` "lib")+ <*> (obj #> "prefix-header" `or'` "Prefix.pch")+ <*> (obj #> "xcconfig" `or'` "Config.xcconfig")+ <*> (Just <$> obj #> "kitdeps-xcode-flags") `or'` Nothing+
Kit/Util.hs view
@@ -4,22 +4,32 @@ module Kit.Util, module Control.Applicative, module System.FilePath.Posix,- module System.Directory+ module System.Directory,+ module Control.Monad ) where- import System.FilePath.Posix- import System.Process import System.Directory- import Data.Maybe- import Data.List- import qualified Data.Traversable as T- import Data.Foldable- import Control.Applicative+ import System.FilePath.Posix+ import System.FilePath.Glob + import Data.List+ import Data.Maybe import Data.Monoid++ import Control.Applicative+ import Control.Monad import Control.Monad.Error - import System.FilePath.Glob+ import qualified Control.Monad.State as S + popS :: S.State [a] a+ popS = do+ (x:t) <- S.get+ S.put t+ return x+ + puts :: MonadIO m => String -> m ()+ puts a = liftIO $ putStrLn a+ maybeRead :: Read a => String -> Maybe a maybeRead = fmap fst . listToMaybe . reads @@ -32,30 +42,23 @@ maybeToLeft :: b -> Maybe a -> Either a b maybeToLeft v = maybe (Right v) Left - instance Foldable (Either c) where- foldr f z = either (const z) (\v -> f v z)-- instance T.Traversable (Either c) where- sequenceA = either (pure . Left) (Right <$>)-- type KitError = String-- type KitIO a = ErrorT KitError IO a-+ type KitIO a = ErrorT String IO a+ + maybeToKitIO :: String -> Maybe a -> KitIO a maybeToKitIO msg = maybe (throwError msg) return - mkdir_p :: FilePath -> IO ()- mkdir_p = createDirectoryIfMissing True+ mkdir_p :: MonadIO m => FilePath -> m ()+ mkdir_p = liftIO . createDirectoryIfMissing True - cleanOrCreate :: FilePath -> IO ()- cleanOrCreate directory = do+ cleanOrCreate :: MonadIO m => FilePath -> m ()+ cleanOrCreate directory = liftIO $ do exists <- doesDirectoryExist directory when exists $ removeDirectoryRecursive directory mkdir_p directory inDirectory :: MonadIO m => FilePath -> m a -> m a inDirectory dir actions = do- cwd <- liftIO $ getCurrentDirectory+ cwd <- liftIO getCurrentDirectory liftIO $ setCurrentDirectory dir v <- actions liftIO $ setCurrentDirectory cwd@@ -66,4 +69,4 @@ stringJoin :: Monoid a => a -> [a] -> a stringJoin x = mconcat . intersperse x-+
− Kit/XCode/Builder.hs
@@ -1,106 +0,0 @@--module Kit.XCode.Builder (buildXCodeProject) where- import Data.Monoid- import Kit.XCode.Common- import Kit.XCode.ProjectFileTemplate- import Text.PList- import Kit.Util-- import Debug.Trace-- import System.FilePath.Posix (dropFileName)- import Data.List (nub)-- createBuildFile :: Integer -> FilePath -> PBXBuildFile- createBuildFile i path = PBXBuildFile uuid1 $ PBXFileReference uuid2 path- where uuid1 = uuid i- uuid2 = uuid $ i + 10000000-- buildXCodeProject :: [FilePath] -> [FilePath] -> [FilePath] -> String- buildXCodeProject headers sources libs = show $ makeProjectPList bfs frs classes hs srcs xxx fg libDirs- where- libDirs = nub $ map dropFileName libs - sourceStart = toInteger (length headers + 1)- libStart = toInteger (length headers + length sources + 1)- headerBuildFiles = zipWith createBuildFile [1..] headers- sourceBuildFiles = zipWith createBuildFile [sourceStart..] sources- libBuildFiles = zipWith createBuildFile [libStart..] libs- allBuildFiles = (sourceBuildFiles ++ headerBuildFiles ++ libBuildFiles)- bfs = buildFileSection allBuildFiles- frs = fileReferenceSection $ map buildFileReference allBuildFiles- classes = classesGroup $ map buildFileReference (sourceBuildFiles ++ headerBuildFiles)- hs = headersBuildPhase headerBuildFiles- srcs = sourcesBuildPhase sourceBuildFiles- xxx = frameworksBuildPhase $ traceShow libBuildFiles libBuildFiles- fg = frameworksGroup $ map buildFileReference libBuildFiles-- buildFileSection :: [PBXBuildFile] -> [PListObjectItem]- buildFileSection bfs = (map buildFileItem bfs ++ [- "4728C530117C02B10027D7D1" ~> buildFile kitConfigRefUUID,- "AA747D9F0F9514B9006C5449" ~> buildFile "AA747D9E0F9514B9006C5449",- "AACBBE4A0F95108600F1A2B1" ~> buildFile "AACBBE490F95108600F1A2B1"- ])-- fileReferenceSection :: [PBXFileReference] -> [PListObjectItem]- fileReferenceSection refs = map fileReferenceItem refs ++ [- kitConfigRefUUID ~> obj [ - "isa" ~> val "PBXFileReference",- "fileEncoding" ~> val "4",- "lastKnownFileType" ~> val "text.xcconfig",- "path" ~> val "DepsOnly.xcconfig",- "sourceTree" ~> val "<group>"- ],- "AA747D9E0F9514B9006C5449" ~> obj [- "isa" ~> val "PBXFileReference",- "fileEncoding" ~> val "4",- "lastKnownFileType" ~> val "sourcecode.c.h",- "path" ~> val "KitDeps_Prefix.pch",- "sourceTree" ~> val "SOURCE_ROOT"- ],- "AACBBE490F95108600F1A2B1" ~> obj [- "isa" ~> val "PBXFileReference",- "lastKnownFileType" ~> val "wrapper.framework",- "name" ~> val "Foundation.framework", - "path" ~> val "System/Library/Frameworks/Foundation.framework",- "sourceTree" ~> val "SDKROOT"- ],- productRefUUID ~> obj [- "isa" ~> val "PBXFileReference",- "explicitFileType" ~> val "archive.ar",- "includeInIndex" ~> val "0",- "path" ~> val "libKitDeps.a",- "sourceTree" ~> val "BUILT_PRODUCTS_DIR"- ]- ]-- classesGroup :: [PBXFileReference] -> PListObjectItem - classesGroup files = classesGroupUUID ~> group "Classes" (map (val . fileReferenceId) files)- - frameworksGroup :: [PBXFileReference] -> PListObjectItem- frameworksGroup files = frameworksGroupUUID ~> group "Frameworks" (val "AACBBE490F95108600F1A2B1" : (map (val . fileReferenceId) files))-- frameworksBuildPhase :: [PBXBuildFile] -> PListObjectItem - frameworksBuildPhase libs = frameworksBuildPhaseUUID ~> obj [- "isa" ~> val "PBXFrameworksBuildPhase",- "buildActionMask" ~> val "2147483647",- "files" ~> arr (val "AACBBE4A0F95108600F1A2B1" : map (val . buildFileId) libs),- "runOnlyForDeploymentPostprocessing" ~> val "0"- ]-- headersBuildPhase :: [PBXBuildFile] -> PListObjectItem- headersBuildPhase bfs = headersBuildPhaseUUID ~> obj [- "isa" ~> val "PBXHeadersBuildPhase",- "buildActionMask" ~> val "2147483647",- "files" ~> arr (val "AA747D9F0F9514B9006C5449" : map (val . buildFileId) bfs),- "runOnlyForDeploymentPostprocessing" ~> val "0"- ]- - sourcesBuildPhase :: [PBXBuildFile] -> PListObjectItem- sourcesBuildPhase bfs = sourcesBuildPhaseUUID ~> obj [- "isa" ~> val "PBXSourcesBuildPhase",- "buildActionMask" ~> val "22147483647147483647",- "files" ~> arr (map (val . buildFileId) bfs),- "runOnlyForDeploymentPostprocessing" ~> val "0"- ]--
− Kit/XCode/Common.hs
@@ -1,69 +0,0 @@-module Kit.XCode.Common where- import Data.Monoid- import Data.List- import System.FilePath.Posix- - import Text.PList-- - type UUID = String- - data FileType = Header | Source | Archive | Unknown- - fileType :: String -> FileType- fileType x | ".h" `isSuffixOf` x = Header- fileType x | ".m" `isSuffixOf` x = Source- fileType x | ".mm" `isSuffixOf` x = Source- fileType x | ".c" `isSuffixOf` x = Source- fileType x | ".a" `isSuffixOf` x = Archive- fileType _ = Unknown-- fileTypeBit :: FileType -> String- fileTypeBit Header = "sourcecode.c.h"- fileTypeBit Source = "sourcecode.c.objc"- fileTypeBit Unknown = "sourcecode.unknown"- fileTypeBit Archive = "archive.ar"-- data PBXBuildFile = PBXBuildFile {- buildFileId :: UUID,- buildFileReference :: PBXFileReference- } deriving (Eq, Show)- - data PBXFileReference = PBXFileReference {- fileReferenceId :: UUID,- fileReferencePath :: String- } deriving (Eq, Show)- - group name children = obj [- "isa" ~> val "PBXGroup",- "name" ~> val name,- "sourceTree" ~> val "<group>",- "children" ~> arr children- ]-- buildFile refUUID = obj [ "isa" ~> val "PBXBuildFile", "fileRef" ~> val refUUID ]-- buildFileItem :: PBXBuildFile -> PListObjectItem - buildFileItem bf = buildFileId bf ~> buildFile (fileReferenceId . buildFileReference $ bf) -- fileReferenceItem :: PBXFileReference -> PListObjectItem- fileReferenceItem fr = (fileReferenceId fr) ~> dict- where- fileName = fileReferenceName fr- dict = obj [- "isa" ~> val "PBXFileReference",- "fileEncoding" ~> val "4",- "lastKnownFileType" ~> val (fileTypeBit . fileType $ fileName),- "name" ~> val fileName,- "path" ~> (val $ fileReferencePath fr),- "sourceTree" ~> val "<group>"- ]--- fileReferenceName = takeFileName . fileReferencePath- - uuid :: Integer -> UUID- uuid i = let- s = show i- pad = 24 - length s- in replicate pad '0' ++ s
− Kit/XCode/ProjectFileTemplate.hs
@@ -1,202 +0,0 @@-{--- Constant sections of the project file--}-module Kit.XCode.ProjectFileTemplate where-- import Kit.XCode.Common- import Text.PList-- import qualified Data.List as L-- projectFile objects uuid = PListFile "!$*UTF8*$!" $ obj [- "archiveVersion" ~> val "1",- "classes" ~> obj [],- "objectVersion" ~> val "45",- "objects" ~> obj objects, - "rootObject" ~> val uuid- ]-- groupProductsUUID = "034768DFFF38A50411DB9C8B"- mainGroupUUID = "0867D691FE84028FC02AAC07"- classesGroupUUID = "08FB77AEFE84172EC02AAC07"- otherSourcesGroupUUID = "32C88DFF0371C24200C91783"- frameworksGroupUUID = "0867D69AFE84028FC02AAC07"-- productRefUUID = "D2AAC07E0554694100DB518D"- kitConfigRefUUID = "4728C52F117C02B10027D7D1"-- headersBuildPhaseUUID = "D2AAC07A0554694100DB518D"- sourcesBuildPhaseUUID = "D2AAC07B0554694100DB518D"- frameworksBuildPhaseUUID = "D2AAC07C0554694100DB518D"-- makeProjectPList :: - [PListObjectItem] -> -- build file section- [PListObjectItem] -> -- file refs section- PListObjectItem -> -- classes item- PListObjectItem -> -- headers section- PListObjectItem -> -- sources section- PListObjectItem -> -- frameworks section- PListObjectItem -> -- frameworks group- [FilePath] -> -- lib directories- PListFile - makeProjectPList bfs fileRefsSection classes headers sources frameworks fg libDirs = projectFile objs "0867D690FE84028FC02AAC07" where- objs = bfs ++ fileRefsSection ++ [frameworks] ++ [fg] ++ next1 ++ [classes] ++ [next2] ++ [headers] ++ next3 ++ [sources] ++ buildConfigurations libDirs-- next1 = [ groupProductsUUID ~> group "Products" [ val productRefUUID ],- mainGroupUUID ~> group "KitDeps" [- val classesGroupUUID,- val otherSourcesGroupUUID,- val frameworksGroupUUID,- val groupProductsUUID,- val kitConfigRefUUID- ]- ]- - next2 = otherSourcesGroupUUID ~> group "Other Sources" [val "AA747D9E0F9514B9006C5449"]-- next3 = ("D2AAC07D0554694100DB518D" ~> obj [- "isa" ~> val "PBXNativeTarget",- "buildConfigurationList" ~> val "1DEB921E08733DC00010E9CD",- "buildPhases" ~> arr [ val headersBuildPhaseUUID, val sourcesBuildPhaseUUID, val frameworksBuildPhaseUUID ],- "buildRules" ~> arr [],- "dependencies" ~> arr [],- "name" ~> val "KitDeps",- "productName" ~> val "KitDeps",- "productReference" ~> val productRefUUID,- "productType" ~> val "com.apple.product-type.library.static"- ]) : kitUpdateTarget ++ ["0867D690FE84028FC02AAC07" ~> obj [- "isa" ~> val "PBXProject",- "buildConfigurationList" ~> val "1DEB922208733DC00010E9CD",- "compatibilityVersion" ~> val "Xcode 3.1",- "hasScannedForEncodings" ~> val "1",- "mainGroup" ~> val mainGroupUUID ,- "productRefGroup" ~> val groupProductsUUID,- "projectDirPath" ~> val "",- "projectRoot" ~> val "",- "targets" ~> arr [ val "D2AAC07D0554694100DB518D", val "470E2D641287730A0084AE6F" ] - ]]-- librarySearchPaths libDirs = "LIBRARY_SEARCH_PATHS" ~> val ("$(inherited) " ++ (L.intersperse " " libDirs >>= id))-- buildConfigurations libDirs = let libSearch = librarySearchPaths libDirs in ["1DEB921F08733DC00010E9CD" ~> obj [- "isa" ~> val "XCBuildConfiguration",- "buildSettings" ~> obj [- "ALWAYS_SEARCH_USER_PATHS" ~> val "NO",- "ARCHS" ~> val "$(ARCHS_STANDARD_32_BIT)",- "COPY_PHASE_STRIP" ~> val "NO",- "DSTROOT" ~> val "/tmp/KitDeps.dst",- "GCC_DYNAMIC_NO_PIC" ~> val "NO",- "GCC_ENABLE_FIX_AND_CONTINUE" ~> val "YES",- "GCC_MODEL_TUNING" ~> val "G5",- "GCC_OPTIMIZATION_LEVEL" ~> val "0",- "GCC_PRECOMPILE_PREFIX_HEADER" ~> val "YES",- "GCC_PREFIX_HEADER" ~> val "KitDeps_Prefix.pch",- "INSTALL_PATH" ~> val "/usr/local/lib",- "PRODUCT_NAME" ~> val "KitDeps",- libSearch - ],- "name" ~> val "Debug"- ],- "1DEB922008733DC00010E9CD" ~> obj [- "isa" ~> val "XCBuildConfiguration",- "buildSettings" ~> obj [- "ALWAYS_SEARCH_USER_PATHS" ~> val "NO",- "ARCHS" ~> val "$(ARCHS_STANDARD_32_BIT)",- "DSTROOT" ~> val "/tmp/KitDeps.dst",- "GCC_MODEL_TUNING" ~> val "G5",- "GCC_PRECOMPILE_PREFIX_HEADER" ~> val "YES",- "GCC_PREFIX_HEADER" ~> val "KitDeps_Prefix.pch",- "INSTALL_PATH" ~> val "/usr/local/lib",- "PRODUCT_NAME" ~> val "KitDeps",- libSearch- ],- "name" ~> val "Release"- ],- "1DEB922308733DC00010E9CD" ~> obj [- "isa" ~> val "XCBuildConfiguration",- "baseConfigurationReference" ~> val kitConfigRefUUID,- "buildSettings" ~> obj [- "ARCHS" ~> val "$(ARCHS_STANDARD_32_BIT)",- "GCC_C_LANGUAGE_STANDARD" ~> val "c99",- "GCC_OPTIMIZATION_LEVEL" ~> val "0",- "GCC_WARN_ABOUT_RETURN_TYPE" ~> val "YES",- "GCC_WARN_UNUSED_VARIABLE" ~> val "YES",- "OTHER_LDFLAGS" ~> val "-ObjC",- "PREBINDING" ~> val "NO",- "SDKROOT" ~> val "iphoneos",- libSearch- ],- "name" ~> val "Debug"- ],- "1DEB922408733DC00010E9CD" ~> obj [- "isa" ~> val "XCBuildConfiguration",- "baseConfigurationReference" ~> val kitConfigRefUUID,- "buildSettings" ~> obj [- "ARCHS" ~> val "$(ARCHS_STANDARD_32_BIT)",- "GCC_C_LANGUAGE_STANDARD" ~> val "c99",- "GCC_WARN_ABOUT_RETURN_TYPE" ~> val "YES",- "GCC_WARN_UNUSED_VARIABLE" ~> val "YES",- "OTHER_LDFLAGS" ~> val "-ObjC",- "PREBINDING" ~> val "NO",- "SDKROOT" ~> val "iphoneos",- libSearch- ],- "name" ~> val "Release"- ],- "1DEB921E08733DC00010E9CD" ~> obj [- "isa" ~> val "XCConfigurationList",- "buildConfigurations" ~> arr [- val "1DEB921F08733DC00010E9CD",- val "1DEB922008733DC00010E9CD"- ],- "defaultConfigurationIsVisible" ~> val "0",- "defaultConfigurationName" ~> val "Release"- ],- "1DEB922208733DC00010E9CD" ~> obj [- "isa" ~> val "XCConfigurationList",- "buildConfigurations" ~> arr [- val "1DEB922308733DC00010E9CD",- val "1DEB922408733DC00010E9CD"- ],- "defaultConfigurationIsVisible" ~> val "0",- "defaultConfigurationName" ~> val "Release"- ]- ]-- kitUpdateTarget = [- "470E2D641287730A0084AE6F" ~> obj [ - "isa" ~> val "PBXLegacyTarget",- "buildArgumentsString" ~> val "",- "buildConfigurationList" ~> val "470E2D721287731E0084AE6F",- "buildPhases" ~> arr [],- "buildToolPath" ~> val "/usr/bin/make",- "buildWorkingDirectory" ~> val "",- "dependencies" ~> arr [],- "name" ~> val "KitUpdate",- "passBuildSettingsInEnvironment" ~> val "1",- "productName" ~> val "KitUpdate"- ],- "470E2D721287731E0084AE6F" ~> obj [- "isa" ~> val "XCConfigurationList",- "buildConfigurations" ~> arr [ val "470E2D651287730B0084AE6F", val "470E2D661287730B0084AE6F" ],- "defaultConfigurationIsVisible" ~> val "0",- "defaultConfigurationName" ~> val "Debug"- ],- "470E2D651287730B0084AE6F" ~> obj [- "isa" ~> val "XCBuildConfiguration",- "buildSettings" ~> obj [- "COPY_PHASE_STRIP" ~> val "NO",- "GCC_DYNAMIC_NO_PIC" ~> val "NO",- "GCC_OPTIMIZATION_LEVEL" ~> val "0", - "PRODUCT_NAME" ~> val "KitUpdate" - ],- "name" ~> val "Debug"- ], - "470E2D661287730B0084AE6F" ~> obj [- "isa" ~> val "XCBuildConfiguration",- "buildSettings" ~> obj [ "PRODUCT_NAME" ~> val "KitUpdate" ],- "name" ~> val "Release"- ]]- -
− Kit/XCode/XCConfig.hs
@@ -1,61 +0,0 @@-module Kit.XCode.XCConfig where-- import Kit.Util-- import qualified Data.Map as M- import Data.Maybe- import Data.Either- import Data.List- import Data.Char (isSpace)-- type XCCSetting = (String, String)- type XCCInclude = String- - data XCConfig = XCC { - configName :: String, - configSettings :: M.Map String String, - configIncludes :: [XCCInclude]- } deriving (Eq, Show)- - includeStart = "#include "- - strip :: String -> String- strip = f . f where f = reverse . dropWhile isSpace- - parseLine :: String -> Maybe (Either XCCSetting XCCInclude)- parseLine l | includeStart `isPrefixOf` l = Just . Right . fromJust $ stripPrefix includeStart l- parseLine l | "=" `isInfixOf` l = fmap (Left) $ breakAround '=' l- where breakAround a xs = let (q,r) = break (a ==) xs in nonEmptyPair (strip q, strip $ drop 1 r)- nonEmptyPair ([], _) = Nothing- nonEmptyPair (_, []) = Nothing- nonEmptyPair a@_ = Just a- parseLine _ = Nothing- - settingToString :: XCCSetting -> String- settingToString (a, b) = a ++ " = " ++ b- - includeToString :: XCCInclude -> String- includeToString a = includeStart ++ a- - configToString :: XCConfig -> String- configToString (XCC _ settings includes) = stringJoin "\n" $ (map includeToString includes) ++ (map settingToString $ M.toList settings)- - fileContentsToXCC :: String -> String -> XCConfig- fileContentsToXCC name content = let ls = lines content- (settings, includes) = partitionEithers (ls >>= (maybeToList . parseLine))- in XCC name (M.fromList settings) includes-- configAsMap :: XCConfig -> M.Map String [(String, String)]- configAsMap (XCC name settings _) = M.map (return . (,) name) settings- - multiConfig :: String -> [XCConfig] -> XCConfig- multiConfig name configs = XCC name settings includes- where includes = configs >>= configIncludes- settingsWithName = foldl (M.unionWith (++)) M.empty (map configAsMap configs)- aggregateSettings = M.mapWithKey f settingsWithName- where f key namesAndValues = stringJoin " " $ map ((\x -> "$(" ++ x ++ "_" ++ key ++ ")") . fst) namesAndValues- individualSettings = M.fromList $ concatMap f $ M.toList settingsWithName- where f (key, namesAndValues) = map (g key) namesAndValues- g key (n, v) = (n ++ "_" ++ key, v)- settings = M.union aggregateSettings individualSettings-
+ Kit/Xcode/Builder.hs view
@@ -0,0 +1,113 @@++module Kit.Xcode.Builder (renderXcodeProject) where+ import Kit.Xcode.Common+ import Kit.Xcode.ProjectFileTemplate+ import Text.PList+ import Kit.Util+ import Data.List (nub)+ import Control.Monad.State++ createBuildFile :: Integer -> FilePath -> PBXBuildFile+ createBuildFile i path = PBXBuildFile uuid1 $ PBXFileReference uuid2 path+ where uuid1 = uuid i+ uuid2 = uuid $ i + 10000000++ buildFileFromState :: FilePath -> State [Integer] PBXBuildFile+ buildFileFromState filePath = flip createBuildFile filePath <$> popS ++ -- | Render an Xcode project!+ renderXcodeProject :: + [FilePath] -- ^ Headers + -> [FilePath] -- ^ Sources+ -> [FilePath] -- ^ Static Libs+ -> String -- ^ Output lib name+ -> String + renderXcodeProject headers sources libs outputLibName = fst . flip runState [1..] $ do+ headerBuildFiles <- mapM buildFileFromState headers+ sourceBuildFiles <- mapM buildFileFromState sources+ libBuildFiles <- mapM buildFileFromState libs -- Build File Items+ let allBuildFiles = (sourceBuildFiles ++ headerBuildFiles ++ libBuildFiles)+ -- Build And FileRef sections+ bfs = buildFileSection allBuildFiles+ frs = fileReferenceSection (map buildFileReference allBuildFiles) outputLibName + -- Groups+ classes = classesGroup $ map buildFileReference (sourceBuildFiles ++ headerBuildFiles)+ fg = frameworksGroup $ map buildFileReference libBuildFiles+ -- Phases+ headersPhase = headersBuildPhase headerBuildFiles+ srcsPhase = sourcesBuildPhase sourceBuildFiles+ frameworksPhase = frameworksBuildPhase libBuildFiles+ -- UUID indices+ libDirs = nub $ map dropFileName libs + return . show $ makeProjectPList (bfs ++ frs ++ [classes, headersPhase, srcsPhase, frameworksPhase, fg]) libDirs++ buildFileSection :: [PBXBuildFile] -> [PListObjectItem]+ buildFileSection bfs = map buildFileItem bfs ++ [+ "4728C530117C02B10027D7D1" ~> buildFile kitConfigRefUUID,+ "AA747D9F0F9514B9006C5449" ~> buildFile "AA747D9E0F9514B9006C5449",+ "AACBBE4A0F95108600F1A2B1" ~> buildFile "AACBBE490F95108600F1A2B1"+ ]++ fileReferenceSection :: [PBXFileReference] -> String -> [PListObjectItem]+ fileReferenceSection refs archiveName = map fileReferenceItem refs ++ [+ kitConfigRefUUID ~> obj [ + "isa" ~> val "PBXFileReference",+ "fileEncoding" ~> val "4",+ "lastKnownFileType" ~> val "text.xcconfig",+ "path" ~> val "DepsOnly.xcconfig",+ "sourceTree" ~> val "<group>"+ ],+ "AA747D9E0F9514B9006C5449" ~> obj [+ "isa" ~> val "PBXFileReference",+ "fileEncoding" ~> val "4",+ "lastKnownFileType" ~> val "sourcecode.c.h",+ "path" ~> val "Prefix.pch",+ "sourceTree" ~> val "SOURCE_ROOT"+ ],+ "AACBBE490F95108600F1A2B1" ~> obj [+ "isa" ~> val "PBXFileReference",+ "lastKnownFileType" ~> val "wrapper.framework",+ "name" ~> val "Foundation.framework", + "path" ~> val "System/Library/Frameworks/Foundation.framework",+ "sourceTree" ~> val "SDKROOT"+ ],+ productRefUUID ~> obj [+ "isa" ~> val "PBXFileReference",+ "explicitFileType" ~> val "archive.ar",+ "includeInIndex" ~> val "0",+ "path" ~> val archiveName,+ "sourceTree" ~> val "BUILT_PRODUCTS_DIR"+ ]+ ]++ classesGroup :: [PBXFileReference] -> PListObjectItem + classesGroup files = classesGroupUUID ~> group "Classes" (map (val . fileReferenceId) files)+ + frameworksGroup :: [PBXFileReference] -> PListObjectItem+ frameworksGroup files = frameworksGroupUUID ~> group "Frameworks" (val "AACBBE490F95108600F1A2B1" : map (val . fileReferenceId) files)++ frameworksBuildPhase :: [PBXBuildFile] -> PListObjectItem + frameworksBuildPhase libs = frameworksBuildPhaseUUID ~> obj [+ "isa" ~> val "PBXFrameworksBuildPhase",+ "buildActionMask" ~> val "2147483647",+ "files" ~> arr (val "AACBBE4A0F95108600F1A2B1" : map (val . buildFileId) libs),+ "runOnlyForDeploymentPostprocessing" ~> val "0"+ ]++ headersBuildPhase :: [PBXBuildFile] -> PListObjectItem+ headersBuildPhase bfs = headersBuildPhaseUUID ~> obj [+ "isa" ~> val "PBXHeadersBuildPhase",+ "buildActionMask" ~> val "2147483647",+ "files" ~> arr (val "AA747D9F0F9514B9006C5449" : map (val . buildFileId) bfs),+ "runOnlyForDeploymentPostprocessing" ~> val "0"+ ]+ + sourcesBuildPhase :: [PBXBuildFile] -> PListObjectItem+ sourcesBuildPhase bfs = sourcesBuildPhaseUUID ~> obj [+ "isa" ~> val "PBXSourcesBuildPhase",+ "buildActionMask" ~> val "22147483647147483647",+ "files" ~> arr (map (val . buildFileId) bfs),+ "runOnlyForDeploymentPostprocessing" ~> val "0"+ ]++
+ Kit/Xcode/Common.hs view
@@ -0,0 +1,71 @@+module Kit.Xcode.Common where+ import Data.List+ import System.FilePath.Posix+ + import Text.PList++ + type UUID = String+ + data FileType = Header | Source | Archive | Unknown+ + fileType :: String -> FileType+ fileType x | ".h" `isSuffixOf` x = Header+ fileType x | ".m" `isSuffixOf` x = Source+ fileType x | ".mm" `isSuffixOf` x = Source+ fileType x | ".c" `isSuffixOf` x = Source+ fileType x | ".a" `isSuffixOf` x = Archive+ fileType _ = Unknown++ fileTypeBit :: FileType -> String+ fileTypeBit Header = "sourcecode.c.h"+ fileTypeBit Source = "sourcecode.c.objc"+ fileTypeBit Unknown = "sourcecode.unknown"+ fileTypeBit Archive = "archive.ar"++ data PBXBuildFile = PBXBuildFile {+ buildFileId :: UUID,+ buildFileReference :: PBXFileReference+ } deriving (Eq, Show)+ + data PBXFileReference = PBXFileReference {+ fileReferenceId :: UUID,+ fileReferencePath :: String+ } deriving (Eq, Show)+ + group :: String -> [PListType] -> PListType + group name children = obj [+ "isa" ~> val "PBXGroup",+ "name" ~> val name,+ "sourceTree" ~> val "<group>",+ "children" ~> arr children+ ]++ buildFile :: String -> PListType+ buildFile refUUID = obj [ "isa" ~> val "PBXBuildFile", "fileRef" ~> val refUUID ]++ buildFileItem :: PBXBuildFile -> PListObjectItem + buildFileItem bf = buildFileId bf ~> buildFile (fileReferenceId . buildFileReference $ bf) ++ fileReferenceItem :: PBXFileReference -> PListObjectItem+ fileReferenceItem fr = fileReferenceId fr ~> dict+ where+ fileName = fileReferenceName fr+ dict = obj [+ "isa" ~> val "PBXFileReference",+ "fileEncoding" ~> val "4",+ "lastKnownFileType" ~> val (fileTypeBit . fileType $ fileName),+ "name" ~> val fileName,+ "path" ~> val (fileReferencePath fr),+ "sourceTree" ~> val "<group>"+ ]+++ fileReferenceName :: PBXFileReference -> String+ fileReferenceName = takeFileName . fileReferencePath+ + uuid :: Integer -> UUID+ uuid i = let+ s = show i+ pad = 24 - length s+ in replicate pad '0' ++ s
+ Kit/Xcode/ProjectFileTemplate.hs view
@@ -0,0 +1,190 @@+{-# OPTIONS_GHC -fno-warn-missing-signatures #-}+module Kit.Xcode.ProjectFileTemplate where++import Kit.Xcode.Common+import Text.PList+import Control.Monad (join)++import qualified Data.List as L++projectFile objects rootId = PListFile "!$*UTF8*$!" $ obj [+ "archiveVersion" ~> val "1",+ "classes" ~> obj [],+ "objectVersion" ~> val "45",+ "objects" ~> obj objects, + "rootObject" ~> val rootId + ]++groupProductsUUID = "034768DFFF38A50411DB9C8B"+mainGroupUUID = "0867D691FE84028FC02AAC07"+classesGroupUUID = "08FB77AEFE84172EC02AAC07"+otherSourcesGroupUUID = "32C88DFF0371C24200C91783"+frameworksGroupUUID = "0867D69AFE84028FC02AAC07"++productRefUUID = "D2AAC07E0554694100DB518D"+kitConfigRefUUID = "4728C52F117C02B10027D7D1"++headersBuildPhaseUUID = "D2AAC07A0554694100DB518D"+sourcesBuildPhaseUUID = "D2AAC07B0554694100DB518D"+frameworksBuildPhaseUUID = "D2AAC07C0554694100DB518D"++makeProjectPList :: [PListObjectItem] -> [FilePath] -> PListFile +makeProjectPList objects libDirs = projectFile objs "0867D690FE84028FC02AAC07" where+ objs = objects ++ groups ++ targets ++ buildConfigurations libDirs++groups = [ groupProductsUUID ~> group "Products" [ val productRefUUID ],+ mainGroupUUID ~> group "KitDeps" [+ val classesGroupUUID,+ val otherSourcesGroupUUID,+ val frameworksGroupUUID,+ val groupProductsUUID,+ val kitConfigRefUUID+ ],+ otherSourcesGroupUUID ~> group "Other Sources" [val "AA747D9E0F9514B9006C5449"]+ ]+ +targets = ("D2AAC07D0554694100DB518D" ~> obj [+ "isa" ~> val "PBXNativeTarget",+ "buildConfigurationList" ~> val "1DEB921E08733DC00010E9CD",+ "buildPhases" ~> arr [ val headersBuildPhaseUUID, val sourcesBuildPhaseUUID, val frameworksBuildPhaseUUID ],+ "buildRules" ~> arr [],+ "dependencies" ~> arr [],+ "name" ~> val "KitDeps",+ "productName" ~> val "KitDeps",+ "productReference" ~> val productRefUUID,+ "productType" ~> val "com.apple.product-type.library.static"+ ]) : kitUpdateTarget ++ ["0867D690FE84028FC02AAC07" ~> obj [+ "isa" ~> val "PBXProject",+ "buildConfigurationList" ~> val "1DEB922208733DC00010E9CD",+ "compatibilityVersion" ~> val "Xcode 3.1",+ "hasScannedForEncodings" ~> val "1",+ "mainGroup" ~> val mainGroupUUID ,+ "productRefGroup" ~> val groupProductsUUID,+ "projectDirPath" ~> val "",+ "projectRoot" ~> val "",+ "targets" ~> arr [ val "D2AAC07D0554694100DB518D", val "470E2D641287730A0084AE6F" ] + ]]++librarySearchPaths libDirs = "LIBRARY_SEARCH_PATHS" ~> val ("$(inherited) " ++ join (L.intersperse " " libDirs))++buildConfigurations libDirs = let libSearch = librarySearchPaths libDirs in ["1DEB921F08733DC00010E9CD" ~> obj [+ "isa" ~> val "XCBuildConfiguration",+ "buildSettings" ~> obj [+ "ALWAYS_SEARCH_USER_PATHS" ~> val "NO",+ "ARCHS" ~> val "$(ARCHS_STANDARD_32_BIT)",+ "COPY_PHASE_STRIP" ~> val "NO",+ "DSTROOT" ~> val "/tmp/KitDeps.dst",+ "GCC_DYNAMIC_NO_PIC" ~> val "NO",+ "GCC_ENABLE_FIX_AND_CONTINUE" ~> val "YES",+ "GCC_MODEL_TUNING" ~> val "G5",+ "GCC_OPTIMIZATION_LEVEL" ~> val "0",+ "GCC_PRECOMPILE_PREFIX_HEADER" ~> val "YES",+ "GCC_PREFIX_HEADER" ~> val "Prefix.pch",+ "INSTALL_PATH" ~> val "/usr/local/lib",+ "PRODUCT_NAME" ~> val "KitDeps",+ libSearch + ],+ "name" ~> val "Debug"+ ],+ "1DEB922008733DC00010E9CD" ~> obj [+ "isa" ~> val "XCBuildConfiguration",+ "buildSettings" ~> obj [+ "ALWAYS_SEARCH_USER_PATHS" ~> val "NO",+ "ARCHS" ~> val "$(ARCHS_STANDARD_32_BIT)",+ "DSTROOT" ~> val "/tmp/KitDeps.dst",+ "GCC_MODEL_TUNING" ~> val "G5",+ "GCC_PRECOMPILE_PREFIX_HEADER" ~> val "YES",+ "GCC_PREFIX_HEADER" ~> val "Prefix.pch",+ "INSTALL_PATH" ~> val "/usr/local/lib",+ "PRODUCT_NAME" ~> val "KitDeps",+ libSearch+ ],+ "name" ~> val "Release"+ ],+ "1DEB922308733DC00010E9CD" ~> obj [+ "isa" ~> val "XCBuildConfiguration",+ "baseConfigurationReference" ~> val kitConfigRefUUID,+ "buildSettings" ~> obj [+ "ARCHS" ~> val "$(ARCHS_STANDARD_32_BIT)",+ "GCC_C_LANGUAGE_STANDARD" ~> val "c99",+ "GCC_OPTIMIZATION_LEVEL" ~> val "0",+ "GCC_WARN_ABOUT_RETURN_TYPE" ~> val "YES",+ "GCC_WARN_UNUSED_VARIABLE" ~> val "YES",+ "OTHER_LDFLAGS" ~> val "-ObjC",+ "PREBINDING" ~> val "NO",+ "SDKROOT" ~> val "iphoneos",+ libSearch+ ],+ "name" ~> val "Debug"+ ],+ "1DEB922408733DC00010E9CD" ~> obj [+ "isa" ~> val "XCBuildConfiguration",+ "baseConfigurationReference" ~> val kitConfigRefUUID,+ "buildSettings" ~> obj [+ "ARCHS" ~> val "$(ARCHS_STANDARD_32_BIT)",+ "GCC_C_LANGUAGE_STANDARD" ~> val "c99",+ "GCC_WARN_ABOUT_RETURN_TYPE" ~> val "YES",+ "GCC_WARN_UNUSED_VARIABLE" ~> val "YES",+ "OTHER_LDFLAGS" ~> val "-ObjC",+ "PREBINDING" ~> val "NO",+ "SDKROOT" ~> val "iphoneos",+ libSearch+ ],+ "name" ~> val "Release"+ ],+ "1DEB921E08733DC00010E9CD" ~> obj [+ "isa" ~> val "XCConfigurationList",+ "buildConfigurations" ~> arr [+ val "1DEB921F08733DC00010E9CD",+ val "1DEB922008733DC00010E9CD"+ ],+ "defaultConfigurationIsVisible" ~> val "0",+ "defaultConfigurationName" ~> val "Release"+ ],+ "1DEB922208733DC00010E9CD" ~> obj [+ "isa" ~> val "XCConfigurationList",+ "buildConfigurations" ~> arr [+ val "1DEB922308733DC00010E9CD",+ val "1DEB922408733DC00010E9CD"+ ],+ "defaultConfigurationIsVisible" ~> val "0",+ "defaultConfigurationName" ~> val "Release"+ ]+ ]++kitUpdateTarget = [+ "470E2D641287730A0084AE6F" ~> obj [ + "isa" ~> val "PBXLegacyTarget",+ "buildArgumentsString" ~> val "",+ "buildConfigurationList" ~> val "470E2D721287731E0084AE6F",+ "buildPhases" ~> arr [],+ "buildToolPath" ~> val "/usr/bin/make",+ "buildWorkingDirectory" ~> val "",+ "dependencies" ~> arr [],+ "name" ~> val "KitUpdate",+ "passBuildSettingsInEnvironment" ~> val "1",+ "productName" ~> val "KitUpdate"+ ],+ "470E2D721287731E0084AE6F" ~> obj [+ "isa" ~> val "XCConfigurationList",+ "buildConfigurations" ~> arr [ val "470E2D651287730B0084AE6F", val "470E2D661287730B0084AE6F" ],+ "defaultConfigurationIsVisible" ~> val "0",+ "defaultConfigurationName" ~> val "Debug"+ ],+ "470E2D651287730B0084AE6F" ~> obj [+ "isa" ~> val "XCBuildConfiguration",+ "buildSettings" ~> obj [+ "COPY_PHASE_STRIP" ~> val "NO",+ "GCC_DYNAMIC_NO_PIC" ~> val "NO",+ "GCC_OPTIMIZATION_LEVEL" ~> val "0", + "PRODUCT_NAME" ~> val "KitUpdate" + ],+ "name" ~> val "Debug"+ ], + "470E2D661287730B0084AE6F" ~> obj [+ "isa" ~> val "XCBuildConfiguration",+ "buildSettings" ~> obj [ "PRODUCT_NAME" ~> val "KitUpdate" ],+ "name" ~> val "Release"+ ]]++
+ Kit/Xcode/XCConfig.hs view
@@ -0,0 +1,66 @@+module Kit.Xcode.XCConfig where++ import Kit.Util++ import qualified Data.Map as M+ import Data.Maybe+ import Data.Either+ import Data.List+ import Data.Char (isSpace)++ type XCCSetting = (String, String)+ type XCCInclude = String+ + data XCConfig = XCC { + configName :: String, + configSettings :: M.Map String String, + configIncludes :: [XCCInclude]+ } deriving (Eq, Show)+ ++ includeStart :: String+ includeStart = "#include "+ + strip :: String -> String+ strip = f . f where f = reverse . dropWhile isSpace+ + parseLine :: String -> Maybe (Either XCCSetting XCCInclude)+ parseLine l | includeStart `isPrefixOf` l = Just . Right . fromJust $ stripPrefix includeStart l+ parseLine l | "=" `isInfixOf` l = fmap Left $ breakAround '=' l+ where breakAround a xs = let (q,r) = break (a ==) xs in nonEmptyPair (strip q, strip $ drop 1 r)+ nonEmptyPair ([], _) = Nothing+ nonEmptyPair (_, []) = Nothing+ nonEmptyPair a@_ = Just a+ parseLine _ = Nothing+ + settingToString :: XCCSetting -> String+ settingToString (a, b) = a ++ " = " ++ b+ + includeToString :: XCCInclude -> String+ includeToString a = includeStart ++ a+ + configToString :: XCConfig -> String+ configToString (XCC _ settings includes) = stringJoin "\n" $ map includeToString includes ++ map settingToString (M.toList settings)+ + cleanName :: String -> String+ cleanName = map (\a -> if a == '-' then '_' else a)++ fileContentsToXCC :: String -> String -> XCConfig+ fileContentsToXCC name content = let ls = lines content+ (settings, includes) = partitionEithers (ls >>= (maybeToList . parseLine))+ in XCC (cleanName name) (M.fromList settings) includes++ configAsMap :: XCConfig -> M.Map String [(String, String)]+ configAsMap (XCC name settings _) = M.map (return . (,) name) settings+ + multiConfig :: String -> [XCConfig] -> XCConfig+ multiConfig name configs = XCC name settings includes+ where includes = configs >>= configIncludes+ settingsWithName = foldl (M.unionWith (++)) M.empty (map configAsMap configs)+ aggregateSettings = M.mapWithKey f settingsWithName+ where f key namesAndValues = stringJoin " " $ map ((\x -> "$(" ++ x ++ "_" ++ key ++ ")") . fst) namesAndValues+ individualSettings = M.fromList $ concatMap f $ M.toList settingsWithName+ where f (key, namesAndValues) = map (g key) namesAndValues+ g key (n, v) = (n ++ "_" ++ key, v)+ settings = aggregateSettings `M.union` individualSettings+
Main.hs view
@@ -1,3 +1,6 @@ module Main where- import Kit.Main- main = kitMain++import Kit.Main++main :: IO ()+main = kitMain
Text/PList.hs view
@@ -2,6 +2,7 @@ module Text.PList where import Data.List (isInfixOf, isPrefixOf, intersperse)+import Control.Monad ------------------------------------------------------------------------------- -- | A Key/Value pair in a PList Object@@ -15,24 +16,32 @@ | PListObject [PListObjectItem] deriving Eq -val x = PListValue x +val :: String -> PListType+arr :: [PListType] -> PListType+obj :: [PListObjectItem] -> PListType++val = PListValue arr = PListArray obj = PListObject infixl 1 ~> +(~>) :: String -> PListType -> PListObjectItem a ~> b = PListObjectItem a b +quote :: String -> String quote s = "\"" ++ s ++ "\""++quotable :: String -> Bool quotable "" = True-quotable s = or $ map (\x -> x `isInfixOf` s && (not $ "sourcecode" `isPrefixOf` s)) quote_triggers+quotable s = any (\x -> x `isInfixOf` s && not ("sourcecode" `isPrefixOf` s)) quote_triggers where quote_triggers = ["-", "<", ">", " ", ".m", ".h", "_", "$"] instance Show PListObjectItem where show (PListObjectItem k v) = k ++ " = " ++ show v ++ ";\n" instance Show PListType where- show (PListValue a) = (if (quotable a) then quote a else a) - show (PListArray xs) = "(" ++ ((intersperse ", " $ map show xs) >>= id) ++ ")"+ show (PListValue a) = if quotable a then quote a else a + show (PListArray xs) = "(" ++ join (intersperse ", " $ map show xs) ++ ")" show (PListObject kvs) = "{\n" ++ (kvs >>= (\x -> " " ++ show x)) ++ "}\n" data PListFile = PListFile { pListFileCharset :: String, pListFileValue :: PListType }
kit.cabal view
@@ -1,24 +1,26 @@ name: kit-version: 0.5.2+version: 0.6 cabal-version: >=1.2 build-type: Simple license: BSD3 license-file: LICENSE maintainer: nkpart@gmail.com -synopsis: A dependency manager for XCode (Objective-C) projects-description: A dependency manager for XCode (Objective-C) projects+synopsis: A dependency manager for Xcode (Objective-C) projects+description: A dependency manager for Xcode (Objective-C) projects category: Development author: Nick Partridge Executable kit main-is: Main.hs buildable: True- build-depends: process -any, network -any, mtl -any, json -any,+ build-depends: process -any, network -any, mtl -any, filepath -any, directory -any, containers -any, cmdargs >=0.3, bytestring -any, base >=3 && <5, QuickCheck -any,- HTTP >=4000.0.9, Glob >= 0.5+ HTTP >=4000.0.9, Glob >= 0.5, data-object -any, data-object-yaml -any hs-source-dirs: .- other-modules: Kit.Model Kit.Main Kit.Package Kit.Project Kit.Repository Kit.Util Kit.XCode.Builder- Kit.XCode.Common Kit.XCode.ProjectFileTemplate Kit.XCode.XCConfig Text.PList+ other-modules: Kit.Spec Kit.Main Kit.Package Kit.Project Kit.Repository Kit.Util Kit.Xcode.Builder+ Kit.Xcode.Common Kit.Xcode.ProjectFileTemplate Kit.Xcode.XCConfig Text.PList+ Kit.Contents Kit.Commands Kit.CmdArgs+ Ghc-options: -Wall -fno-warn-unused-do-bind