packages feed

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 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