packages feed

kit (empty) → 0.2

raw patch · 17 files changed

+1014/−0 lines, 17 filesdep +HTTPdep +MissingHdep +QuickChecksetup-changed

Dependencies added: HTTP, MissingH, QuickCheck, base, bytestring, cmdargs, containers, directory, filepath, json, mtl, network, process

Files

+ Kit/JSON.hs view
@@ -0,0 +1,31 @@+module Kit.JSON where+  import Kit.Kit+  import Kit.Spec+  import Kit.Util+  import Control.Applicative+  import Text.JSON+  +  instance JSON Kit where+      showJSON kit = makeObj+          [ ("name", showJSON $ kitName kit)+          , ("version", showJSON $ kitVersion kit)+          ]++      readJSON (JSObject obj) = Kit <$> f "name" <*> f "version"+        where f x = mLookup x jsonObjAssoc >>= readJSON+              jsonObjAssoc = fromJSObject obj+  +  instance JSON KitSpec where+      showJSON spec = makeObj+          [ ("name", showJSON $ kitName kit)+          , ("version", showJSON $ kitVersion kit)+          , ("dependencies", showJSON $ specDependencies spec)+          ]+          where kit = specKit spec++      readJSON (JSObject obj) = +          let myKit = Kit <$> f "name" <*> f "version"+              myConfig = (KitConfiguration <$> f "dependencies" <*> (f "sourceDir" <|> pure "src"))+           in KitSpec <$> myKit <*> myConfig+        where f x = mLookup x jsonObjAssoc >>= readJSON+              jsonObjAssoc = fromJSObject obj
+ Kit/Kit.hs view
@@ -0,0 +1,13 @@+module Kit.Kit where+  import Kit.Util+    +  data Kit = Kit {+    kitName :: String,+    kitVersion :: String+  } deriving (Eq, Show, Ord, Read)++  kitFileName :: Kit -> String+  kitFileName k = kitName k ++ "-" ++ kitVersion k++  kitConfigFile :: Kit -> String+  kitConfigFile kit = kitFileName kit </> (kitName kit ++ ".xcconfig")
+ Kit/Main.hs view
@@ -0,0 +1,126 @@+module Kit.Main where+  import qualified Data.ByteString as BS++  import Control.Monad.Trans+  import Data.List+  import Data.Maybe+  import Data.Monoid+  import Kit.Kit+  import Kit.Package+  import Kit.Project+  import Kit.Repository+  import Kit.Spec+  import Kit.Util  +  import Kit.XCode.Builder+  import qualified Data.Traversable as T+  import System.Cmd+  import System.Environment+  import Text.JSON++  defaultLocalRepoPath = do+      home <- getHomeDirectory+      return $ home </> ".kit" </> "repository"+  defaultLocalRepository = fmap fileRepo defaultLocalRepoPath+  +  handleFails (Left e) = do+    print e+    return ()+  handleFails (Right _) = return ()+  +  update :: IO ()+  update = unKitIO g >>= handleFails+      where g = do+              repo <- liftIO defaultLocalRepository+              deps <- getMyDeps repo+              puts $ "Dependencies: " ++ (stringJoin ", " $ map kitFileName deps)+              liftIO $ mapM (installKit repo) deps+              puts " -> Generating XCode project..."+              liftIO $ generateXCodeProject $ deps |> kitFileName+              liftIO $ generateXCodeConfig $ deps |> kitFileName+              puts "Kit complete."+                where p x = liftIO $ print x+                      puts x = liftIO $ putStrLn x+  +  packageKit :: IO ()+  packageKit = do+      mySpec <- unKitIO myKitSpec+      T.for mySpec package+      return ()+  +  deployLocal :: IO ()+  deployLocal = do+    mySpec <- unKitIO myKitSpec+    T.for mySpec package+    T.for mySpec x+    handleFails mySpec+      where+        x :: KitSpec -> IO ()+        x spec = let +              k = specKit spec+              kf = kitFileName . specKit $ spec+              pkg = (kf ++ ".tar.gz")+            in do+              repo <- defaultLocalRepoPath+              let thisKitDir = repo </> "kits" </> kitName k </> kitVersion k+              mkdir_p thisKitDir+              copyFile pkg $ thisKitDir </> pkg+              copyFile "KitSpec" $ thisKitDir </> "KitSpec"+  +  verify :: Maybe String -> IO ()+  verify sdk = (unKitIO f >>= handleFails)+    where+      f = do+        mySpec <- myKitSpec+        puts "Checking that the kit can be depended upon..."+        puts " #> Deploying locally"+        liftIO deployLocal+        puts " #> Building temporary parent project"+        tmp <- liftIO getTemporaryDirectory+        liftIO $ inDirectory tmp $ do+          let kitVerifyDir = "kit-verify"+          cleanOrCreate kitVerifyDir+          inDirectory kitVerifyDir $ do+            writeFile "KitSpec" $ encode (KitSpec (Kit "verify-kit" "1.0") $ defaultConfiguration{kitConfigDependencies=[specKit mySpec]})+            update+            inDirectory "Kits" $ do+              system $ "xcodebuild -sdk " ++ (fromMaybe "iphonesimulator4.0" sdk)+          putStrLn "OK."+        puts "End checks."+      puts = liftIO . putStrLn+      +  createSpec :: String -> String -> IO ()+  createSpec name version = do+    let kit =(Kit name version)+    let spec = KitSpec kit defaultConfiguration+    writeFile "KitSpec" $ encode spec+    putStrLn $ "Created KitSpec for " ++ kitFileName kit+    return ()+  +  handleArgs :: [String] -> IO ()+  handleArgs ["update"] = update+  handleArgs ["package"] = packageKit+  handleArgs ["publish-local"] = deployLocal+  handleArgs ["verify"] = verify Nothing+  handleArgs ["verify", sdk] = verify $ Just sdk+  handleArgs ["create-spec", name, version] = createSpec name version+    +  handleArgs _ = putStrLn f where+    f = "Usage: kit command [arguments]" `nl`+        "Commands:" `nl`+        "\tupdate\t\t\t\tCreate an Xcode project to wrap the dependencies defined in ./KitSpec" `nl`+        "\tcreate-spec NAME VERSION\tWrite out a KitSpec file using the given name and version." `nl`+        "\tpackage\t\t\t\tCreate a tar.gz wrapping this kit" `nl`+        "\tverify [SDK]\t\t\tPackage this kit, then attempt to use it as a dependency in an empty project." `nl`+        "\tpublish-local\t\t\tPackage this kit, then deploy it to the local repository (~/.kit/repository)" +    nl a b = a ++ "\n" ++ b+    +  kitMain :: IO ()+  kitMain = do+      localRepo <- defaultLocalRepository+      path <- defaultLocalRepoPath+      mkdir_p path+      args <- getArgs+      handleArgs args++      +          
+ Kit/Package.hs view
@@ -0,0 +1,47 @@+module Kit.Package (package) where++  import Kit.Kit+  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/+     KitSpec+     *.codeproj+  -}+  +  fileBelongsInPackage :: KitConfiguration -> FilePath -> Bool+  fileBelongsInPackage config fp = let+    a = elem fp [sourceDir config, "test", "KitSpec"]+    b = "xcodeproj" `isSuffixOf` fp+    c = "xcconfig" `isSuffixOf` fp+    d = ".pch" `isSuffixOf` fp+      in a || b || c || d+  +  package :: KitSpec -> IO ()+  package spec = do+      tempDir <- getTemporaryDirectory+      current <- getCurrentDirectory+      let kd = tempDir </> kitPath+      cleanOrCreate kd+      contents <- getDirectoryContents "."+      mapM_ (cp_r_to kd) (filter (fileBelongsInPackage . specConfiguration $ spec) contents)+      inDirectory tempDir $ sh $ "tar czf " ++ (current </> (kitPath ++ ".tar.gz")) ++ " " ++ kitPath+      return ()+    where+      kitPath = kitFileName . specKit $ 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 ++ "/"+  +  
+ Kit/Project.hs view
@@ -0,0 +1,108 @@+module Kit.Project (+  getDeps,+  getMyDeps,+  installKit,+  generateXCodeConfig,+  generateXCodeProject,+  myKitSpec)+    where+      +  import Control.Monad.Trans+  import Control.Monad+  import Control.Applicative+  import Data.Foldable+  import Data.Maybe+  import Data.List+  import Data.Monoid+  import System.Cmd+  import Kit.Kit+  import Kit.Repository+  import Kit.Util+  import Kit.Spec+  import Kit.XCode.Builder+  import Kit.XCode.XCConfig+  import Kit.XCode.Prefix+  import Text.JSON+  import Kit.JSON+  import qualified Data.Traversable as T+  +  kitDir = "." </> "Kits"+  projectDir = "KitDeps.xcodeproj"+  prefixFile = "KitDeps_Prefix.pch"+  projectFile = projectDir </> "project.pbxproj"+  prefixDefault = "#ifdef __OBJC__\n" ++ "    #import <Foundation/Foundation.h>\n    #import <UIKit/UIKit.h>\n" ++ "#endif\n"+      +  generateXCodeProject :: [FilePath] -> IO ()+  generateXCodeProject kitFileNames = do+    mkdir_p kitDir+    inDirectory kitDir $ do+      let find tpe inDir = glob (inDir ++ "/src/**/*" ++ tpe)+      let find' tpe = fmap join . T.for kitFileNames $ find tpe+      headers <- find' ".h"+      sources <- (\a b c -> a ++ b ++ c) <$> glob "**/src/**/*.m" <*> glob "**/src/**/*.mm" <*> glob "**/src/**/*.c"+      mkdir_p projectDir+      writeFile projectFile $ buildXCodeProject headers sources+      combinedHeader <- generatePrefixHeader kitFileNames+      writeFile prefixFile $ prefixDefault ++ combinedHeader ++ "\n"++  readConfig' :: Kit -> IO (Maybe XCConfig)+  readConfig' kit = do+    exists <- doesFileExist fp+    contents <- T.sequence (fmap readFile $ justTrue exists fp)+    return $ fmap (fileContentsToXCC $ kitName kit) contents+        where+          fp = kitConfigFile kit++  generateXCodeConfig :: [FilePath] -> IO ()+  generateXCodeConfig kitFileNames = do+    inDirectory kitDir $ unKitIO $ do+      ca <- readMany kitFileNames "KitSpec" (\x -> readSpec x |> specKit >>= (liftIO . readConfig'))+      let combinedConfig = multiConfig "KitConfig" ca+      let contents = configToString combinedConfig+      let xcconfig = "HEADER_SEARCH_PATHS = $(HEADER_SEARCH_PATHS) " ++ (stringJoin " "  $ kitFileNames >>= (\x -> [kitDir </> x </> "src", x </> "src"])) ++ "\n" ++ "GCC_PRECOMPILE_PREFIX_HEADER = YES\nGCC_PREFIX_HEADER = $(SRCROOT)/KitDeps_Prefix.pch\n"+      liftIO $ writeFile "Kit.xcconfig" $ xcconfig ++ "\n" ++ contents+    return ()+      +  depsForSpec :: KitRepository -> KitSpec -> KitIO [Kit]+  depsForSpec kr spec = do+      deps <- mapM (getDeps kr) (specDependencies spec)+      return $ specDependencies spec ++ join deps      +  +  getDeps :: KitRepository -> Kit -> KitIO [Kit]+  getDeps kr kit = getKitSpec kr kit >>= depsForSpec kr++  getMyDeps :: KitRepository -> KitIO [Kit]+  getMyDeps kr = myKitSpec >>= depsForSpec kr++  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++  readSpec :: FilePath -> KitIO KitSpec+  readSpec kitSpecPath = readSpecContents kitSpecPath >>= KitIO . return . parses+  +  myKitSpec :: KitIO KitSpec+  myKitSpec = readSpec "KitSpec"++  -- private!+  checkExists :: FilePath -> KitIO FilePath+  checkExists kitSpecPath = do+    doesExist <- liftIO $ doesFileExist kitSpecPath+    maybeToKitIO ("Couldn't find the spec at " ++ kitSpecPath) (justTrue doesExist kitSpecPath)+  +  readSpecContents :: FilePath -> KitIO String+  readSpecContents kitSpecPath = checkExists kitSpecPath >>= liftIO . readFile+  +  parses :: String -> Either [KitError] KitSpec+  parses contents = case (decode contents) of +                      Ok a -> Right a+                      Error a -> Left [a]+  
+ Kit/Repository.hs view
@@ -0,0 +1,84 @@+{-++  Exposes the repositories.+  Allows you to:+    * copy down kit packages (getKit)+    * read kit specs (getKitSpec)+-}++module Kit.Repository (+    getKit,+    getKitSpec,+    webRepo,+    fileRepo,+    KitRepository+  ) where+    +  import Kit.Spec+  import Kit.Kit+  import Kit.Util+  import Data.List+  import Network.HTTP+  import Network.URI+  import Network.BufferType+  import Data.Maybe+  import Data.Traversable+  import Control.Monad+  import Control.Monad.Trans+  import Debug.Trace+  import Text.JSON+  import Kit.JSON+  import qualified Data.ByteString as BS++  data KitRepository = KitRepository {+    repoSave :: String -> FilePath -> IO (Maybe ()),+    repoRead :: String -> IO (Maybe String)+  }+  +  getKit :: KitRepository -> Kit -> FilePath -> IO (Maybe ())+  getKit kr k = repoSave kr (kitPackagePath k)+  +  getKitSpec :: KitRepository -> Kit -> KitIO KitSpec+  getKitSpec kr k = do+    mbKitStuff <- liftIO $ repoRead kr (kitSpecPath k)+    maybe (kitError $ "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+  +  webRepo :: String -> KitRepository+  webRepo baseUrl = KitRepository save read where+    save = download +    read = getBody++  fileRepo :: String -> KitRepository+  fileRepo baseDir = KitRepository save read where+    save src destPath = Just <$> copyFile (baseDir </> src) destPath+    read path = let file = (baseDir </> path) in do+      exists <- doesFileExist file+      sequenceA $ justTrue exists $ readFile file+      +  -- private!+  baseKitPath :: Kit -> String+  baseKitPath k = joinS ["kits", kitName k, kitVersion k] "/"+    where joinS xs x = foldl1 (++) $ intersperse x xs +  +  kitPackagePath k = baseKitPath k ++ "/" ++ kitFileName k ++ ".tar.gz"+  kitSpecPath k = baseKitPath k ++ "/" ++ "KitSpec"+  +  getBody :: BufferType a => HStream a => String -> IO (Maybe a)+  getBody path = 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+      return $ fmap rspBody $ leftMaybe rr+  +  download :: String -> FilePath -> IO (Maybe ())+  download url destination = do+      body <- getBody url+      sequenceA $ fmap (BS.writeFile destination) body+   +   +   
+ Kit/Spec.hs view
@@ -0,0 +1,18 @@+module Kit.Spec where+  import Kit.Kit+  +  data KitSpec = KitSpec {+    specKit :: Kit,+    specConfiguration :: KitConfiguration+  } deriving (Show, Read)+  +  data KitConfiguration = KitConfiguration {+    kitConfigDependencies :: [Kit],+    sourceDir :: FilePath+  } deriving (Show, Read)+  +  specDependencies :: KitSpec -> [Kit]+  specDependencies = kitConfigDependencies . specConfiguration+  +  defaultConfiguration :: KitConfiguration+  defaultConfiguration = KitConfiguration [] "src"
+ Kit/Util.hs view
@@ -0,0 +1,98 @@+{-# LANGUAGE TypeSynonymInstances #-}++module Kit.Util(+  module Kit.Util, +  module Control.Applicative, +  module System.FilePath.Posix,+  module System.Directory+  ) 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 Control.Monad+  import Data.Monoid+  import Control.Monad.Trans+      +  (|>) ma f = fmap f ma+        +  maybeRead :: Read a => String -> Maybe a+  maybeRead = fmap fst . listToMaybe . reads+  +  justTrue :: Bool -> a -> Maybe a+  justTrue True a = Just a+  justTrue False _ = Nothing+  +  maybeToRight :: b -> Maybe a -> Either b a+  maybeToRight _ (Just a) = Right a+  maybeToRight b Nothing = Left b+  +  maybeToLeft :: b -> Maybe a -> Either a b+  maybeToLeft _ (Just a) = Left a+  maybeToLeft b Nothing = Right b+  +  instance Foldable (Either c) where+    foldr f z (Left c) = z+    foldr f z (Right a) = f a z+    +  instance T.Traversable (Either c) where+    sequenceA = either (pure . Left) (Right <$>)+    +  type KitError = String+  +  data KitIO a = KitIO { unKitIO :: (IO (Either [KitError] a)) }+  +  kitError :: KitError -> KitIO a+  kitError e = KitIO . return $ Left [e]+  +  maybeToKitIO msg = maybe (kitError msg) return+  +  instance Monad KitIO where+    (KitIO ioE) >>= f = KitIO $ ioE >>= g+      where g l@(Left v) = return (Left v)+            g (Right v) = unKitIO $ f v+            +    return = KitIO . return . Right +    +  instance Functor KitIO where+    fmap f kio = kio >>= (return . f)+    +  instance MonadIO KitIO where+    liftIO v = KitIO (fmap Right v)++  mLookup a as = maybe (fail $ "No such element: " ++ a) return (lookup a as)  +  +  mkdir_p :: FilePath -> IO ()+  mkdir_p = createDirectoryIfMissing True+  +  cleanOrCreate :: FilePath -> IO ()+  cleanOrCreate directory = do+    exists <- doesDirectoryExist directory+    when exists $ removeDirectoryRecursive directory+    mkdir_p directory+    +  inDirectory :: FilePath -> IO a -> IO a+  inDirectory dir actions = do+    cwd <- getCurrentDirectory+    setCurrentDirectory dir+    v <- actions+    setCurrentDirectory cwd+    return v+  +  glob :: String -> IO [String]+  glob pattern = fmap lines (readProcess "ruby" ["-e", "puts Dir.glob(\"" ++ pattern ++ "\")"] [])+    +  stringJoin :: Monoid a => a -> [a] -> a+  stringJoin x = mconcat . intersperse x+  +  -- For each directory that exists, execute the function with the given filepath+  readMany :: MonadIO m => [FilePath] -> FilePath -> (String -> m (Maybe a)) -> m [a]+  readMany dirs fileInDir f = do+    directories <- liftIO $ filterM doesDirectoryExist dirs+    let kitSpecFiles = map (</> fileInDir) directories+    kitContents <- mapM f kitSpecFiles+    return $ catMaybes kitContents
+ Kit/XCode/Builder.hs view
@@ -0,0 +1,148 @@++module Kit.XCode.Builder (buildXCodeProject) where++  import Data.Monoid+  import Data.List+  import Kit.XCode.Common+  import Kit.XCode.ProjectFile+  import Kit.Util+  +  createBuildFile :: Integer -> FilePath -> PBXBuildFile+  createBuildFile i path = PBXBuildFile uuid1 $ PBXFileReference uuid2 path+    where uuid1 = uuid i+          uuid2 = uuid $ i + 10000000+  +  buildXCodeProject :: [FilePath] -> [FilePath] -> String+  buildXCodeProject headers sources = +      projectPbxProj bfs frs classes hs srcs +    where+      sourceStart = toInteger (length headers + 1)+      headerBuildFiles = zipWith createBuildFile [1..] headers+      sourceBuildFiles = zipWith createBuildFile [sourceStart..] sources+      allBuildFiles = (sourceBuildFiles ++ headerBuildFiles)+      bfs = buildFileSection allBuildFiles+      frs = fileReferenceSection $ map buildFileReference allBuildFiles+      classes = classesSection $ map buildFileReference (sourceBuildFiles ++ headerBuildFiles)+      hs = headersSection headerBuildFiles+      srcs = sourcesSection sourceBuildFiles+  +  xxx :: FilePath -> UUID -> UUID -> PBXBuildFile+  xxx fp buildId fileId = PBXBuildFile buildId (PBXFileReference fileId fp)++  -- 47021EC1117A7776003DB5B7 /* motive.m in Sources */ = {isa = PBXBuildFile; fileRef = 47021EBF117A7776003DB5B7 /* motive.m */; };+  buildFileItem :: PBXBuildFile -> String+  buildFileItem bf = lineItem i comment dict+    where fr = buildFileReference bf+          i = buildFileId bf+          comment = let ft Unknown = "Unknown"+                        ft Header = "Headers"+                        ft Source = "Sources"+                        bit = ft . fileType . fileReferencePath $ fr+                    in fileReferenceName fr ++ " in " ++ bit+          dict = [+              "isa" ~> "PBXBuildFile",+              "fileRef" ~> (fileReferenceId fr ++ " /* " ++ fileReferenceName fr ++ " */")+            ]++  fileTypeBit :: FileType -> String+  fileTypeBit Header = "sourcecode.c.h"+  fileTypeBit Source = "sourcecode.c.objc"+  fileTypeBit Unknown = "sourcecode.unknown"++  -- 47021EBE117A7776003DB5B7 /* motive.h */ = {isa = PBXFileReference; fileEncoding = 4;+  -- lastKnownFileType = sourcecode.c.h; name = motive.h; path = "kits/motive-0.1/src/motive.h"; sourceTree = "<group>"; };+  fileReferenceItem :: PBXFileReference -> String+  fileReferenceItem fr = lineItem fid fileName dict+    where+      fid = fileReferenceId fr+      path = fileReferencePath fr+      fileName = fileReferenceName fr+      dict = [+          "isa" ~> "PBXFileReference",+          "fileEncoding" ~> "4",+          "lastKnownFileType" ~> (fileTypeBit . fileType $ fileName),+          "name" ~> show fileName,+          "path" ~> show path,+          "sourceTree" ~> show "<group>"+        ]+      +  layoutSection :: String -> [String] -> String+  layoutSection name body = let +      front  = "/* Begin " ++ name ++ " section */"+      middle = map ("    " ++) body+      back   = "/* End " ++ name ++ " section */"+      in mconcat $ intersperse "\n" ((front : middle) ++ [back])++  buildFileSection :: [PBXBuildFile] -> String+  buildFileSection bfs = layoutSection "PBXBuildFile" (map buildFileItem bfs ++ [+      "4728C530117C02B10027D7D1 /* Kit.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 4728C52F117C02B10027D7D1 /* Kit.xcconfig */; };",+      "AA747D9F0F9514B9006C5449 /* KitDeps_Prefix.pch in Headers */ = {isa = PBXBuildFile; fileRef = AA747D9E0F9514B9006C5449 /* KitDeps_Prefix.pch */; };",+      "AACBBE4A0F95108600F1A2B1 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AACBBE490F95108600F1A2B1 /* Foundation.framework */; };"+    ])  +    +  fileReferenceSection :: [PBXFileReference] -> String+  fileReferenceSection refs = layoutSection "PBXFileReference" (map fileReferenceItem refs ++ [+  		"4728C52F117C02B10027D7D1 /* Kit.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Kit.xcconfig; sourceTree = \"<group>\"; };",+      "AA747D9E0F9514B9006C5449 /* KitDeps_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KitDeps_Prefix.pch; sourceTree = SOURCE_ROOT; };",+	    "AACBBE490F95108600F1A2B1 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };",+	    "D2AAC07E0554694100DB518D /* libKitDeps.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libKitDeps.a; sourceTree = BUILT_PRODUCTS_DIR; };"+    ])++  classesSection :: [PBXFileReference] -> String+  classesSection files = lineItem "08FB77AEFE84172EC02AAC07" "Classes" dict+	    where dict = [+            	    "isa" ~> "PBXGroup",+            	    "children" ~> ("(" ++ (mconcat (intersperse "," (map fileReferenceId files))) ++ ",)"),+            	    "name" ~> "Classes",+            	    "sourceTree" ~> show "<group>"+	                ]+	+	{-/* Begin PBXHeadersBuildPhase section */+  		D2AAC07A0554694100DB518D /* Headers */ = {+  			isa = PBXHeadersBuildPhase;+  			buildActionMask = 2147483647;+  			files = (+  				AA747D9F0F9514B9006C5449 /* KitDeps_Prefix.pch in Headers */,+  				4791C244117A7893001EB278 /* motive.h in Headers */,+  			);+  			runOnlyForDeploymentPostprocessing = 0;+  		};+  /* End PBXHeadersBuildPhase section */-}                +  headersSection :: [PBXBuildFile] -> String+  headersSection bfs = layoutSection "PBXHeadersBuildPhase" [lineItem "D2AAC07A0554694100DB518D" "Headers" [+    "isa" ~> "PBXHeadersBuildPhase",+    "buildActionMask" ~> "2147483647",+    "files" ~> ("(" ++ (stringJoin "," $ prefixPchUUID : map buildFileId bfs) ++ ",)"),+    "runOnlyForDeploymentPostprocessing" ~> "0"+	  ]]+	  where prefixPchUUID = "AA747D9F0F9514B9006C5449"+	  +	{-+    /* Begin PBXSourcesBuildPhase section */+    		D2AAC07B0554694100DB518D /* Sources */ = {+    			isa = PBXSourcesBuildPhase;+    			buildActionMask = 2147483647;+    			files = (+    				47021EC1117A7776003DB5B7 /* motive.m in Sources */,+    			);+    			runOnlyForDeploymentPostprocessing = 0;+    		};+    /* End PBXSourcesBuildPhase section */+	-}+  sourcesSection :: [PBXBuildFile] -> String+  sourcesSection bfs = layoutSection "PBXSourcesBuildPhase" [lineItem "D2AAC07B0554694100DB518D" "Sources" [+    "isa" ~> "PBXSourcesBuildPhase",+    "buildActionMask" ~> "2147483647",+    "files" ~> ("(" ++ (stringJoin "," $ map buildFileId bfs) ++ ",)"),+    "runOnlyForDeploymentPostprocessing" ~> "0"+    ]]+  +  testBuilder = let+    header = PBXFileReference "1" "fk/fk.h"+    source = PBXFileReference "2" "fk/fk.m"+    headerBF = PBXBuildFile "10" header+    sourceBF = PBXBuildFile "20" source+      in do+          putStrLn . buildFileSection $  [headerBF, sourceBF]+          putStrLn . fileReferenceSection $ [header, source]+
+ Kit/XCode/Common.hs view
@@ -0,0 +1,45 @@+module Kit.XCode.Common where+  import Data.Monoid+  import Data.List+  import System.FilePath.Posix+          +  type UUID = String+  type Dict = [(String, String)]+  +  data FileType = Header | Source | 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 _ = Unknown+  +  (~>) = (,)+  +  data PBXBuildFile = PBXBuildFile {+    buildFileId :: String,+    buildFileReference :: PBXFileReference+  }+  +  data PBXFileReference = PBXFileReference {+    fileReferenceId :: String,+    fileReferencePath :: String+  }+  +  fileReferenceName = takeFileName . fileReferencePath+  +  lineItem :: UUID -> String -> Dict -> String+  lineItem uuid comment dict = uuid ++ " /* " ++ comment ++ " */ = " ++ buildDict dict ++ ";"+  +  buildDict :: Dict -> String+  buildDict ps = g . mconcat . map (\x -> fst x ++ " = " ++ snd x ++ "; ") $ ps+    where g s = "{"  ++ s ++ "}"+  +  uuid :: Integer -> UUID+  uuid i = let+      s = show i+      lengthS = length s+      pad = 24 - lengthS+    in+      replicate pad '0' ++ s 
+ Kit/XCode/Prefix.hs view
@@ -0,0 +1,13 @@+module Kit.XCode.Prefix where+  +  import Kit.Util+  import qualified Data.Traversable as T+  +  kitPrefixFile = "Prefix.pch"+  +  generatePrefixHeader :: [FilePath] -> IO String+  generatePrefixHeader kitFileNames = do+      ca <- readMany kitFileNames kitPrefixFile $ \s -> do+        exists <- doesFileExist s+        T.for (justTrue exists s) readFile+      return $ stringJoin "\n" ca
+ Kit/XCode/ProjectFile.hs view
@@ -0,0 +1,198 @@+{-++  Constant sections of the project file+-}+module Kit.XCode.ProjectFile (projectPbxProj) where+  +  nl a b = a ++ "\n" ++ b +  +  projectPbxProj bfs fileRefsSection classes headers sources = +      top `nl` bfs `nl` fileRefsSection `nl` next1 `nl` classes `nl` next2 `nl` headers `nl` next3 `nl` sources `nl` bottom+  +  top = "// !$*UTF8*$!" `nl` +    "{" `nl`+    " archiveVersion = 1;" `nl`+    " classes = {" `nl`+    " };" `nl`+    " objectVersion = 45;" `nl`+    " objects = {"+  +  next1 = "/* Begin PBXFrameworksBuildPhase section */" `nl`+    		"D2AAC07C0554694100DB518D /* Frameworks */ = {" `nl`+    			"isa = PBXFrameworksBuildPhase;" `nl`+    			"buildActionMask = 2147483647;" `nl`+    			"files = (" `nl`+    				"AACBBE4A0F95108600F1A2B1 /* Foundation.framework in Frameworks */," `nl`+    			");" `nl`+    			"runOnlyForDeploymentPostprocessing = 0;" `nl`+    		"};" `nl`+    "/* End PBXFrameworksBuildPhase section */" `nl`+    "" `nl`+    "/* Begin PBXGroup section */" `nl`+    		"034768DFFF38A50411DB9C8B /* Products */ = {" `nl`+    			"isa = PBXGroup;" `nl`+    			"children = (" `nl`+    				"D2AAC07E0554694100DB518D /* libKitDeps.a */," `nl`+    			");" `nl`+    			"name = Products;" `nl`+    			"sourceTree = \"<group>\";" `nl`+    		"};" `nl`+    		"0867D691FE84028FC02AAC07 /* KitDeps */ = {" `nl`+    			"isa = PBXGroup;" `nl`+    			"children = (" `nl`+    				"08FB77AEFE84172EC02AAC07 /* Classes */," `nl`+    				"32C88DFF0371C24200C91783 /* Other Sources */," `nl`+    				"0867D69AFE84028FC02AAC07 /* Frameworks */," `nl`+    				"034768DFFF38A50411DB9C8B /* Products */," `nl`+    				"4728C52F117C02B10027D7D1 /* Kit.xcconfig */," `nl`+    			");" `nl`+    			"name = KitDeps;" `nl`+    			"sourceTree = \"<group>\";" `nl`+    		"};" `nl`+    		"0867D69AFE84028FC02AAC07 /* Frameworks */ = {" `nl`+    			"isa = PBXGroup;" `nl`+    			"children = (" `nl`+    				"AACBBE490F95108600F1A2B1 /* Foundation.framework */," `nl`+    			");" `nl`+    			"name = Frameworks;" `nl`+    			"sourceTree = \"<group>\";" `nl`+    		"};"+    		+  next2 = "32C88DFF0371C24200C91783 /* Other Sources */ = {" `nl`+        			"isa = PBXGroup;" `nl`+        			"children = (" `nl`+        				"AA747D9E0F9514B9006C5449 /* KitDeps_Prefix.pch */," `nl`+        			");" `nl`+        			"name = \"Other Sources\";" `nl`+        			"sourceTree = \"<group>\";" `nl`+        		"};" `nl`+        "/* End PBXGroup section */"+        +  next3 = "/* Begin PBXNativeTarget section */" `nl`+        		"D2AAC07D0554694100DB518D /* KitDeps */ = {" `nl`+        			"isa = PBXNativeTarget;" `nl`+        			"buildConfigurationList = 1DEB921E08733DC00010E9CD /* Build configuration list for PBXNativeTarget \"KitDeps\" */;" `nl`+        			"buildPhases = (" `nl`+        				"D2AAC07A0554694100DB518D /* Headers */," `nl`+        				"D2AAC07B0554694100DB518D /* Sources */," `nl`+        				"D2AAC07C0554694100DB518D /* Frameworks */," `nl`+        			");" `nl`+              "buildRules = (" `nl`+        			");" `nl`+        			"dependencies = (" `nl`+        			");" `nl`+        			"name = KitDeps;" `nl`+        			"productName = KitDeps;" `nl`+        			"productReference = D2AAC07E0554694100DB518D /* libKitDeps.a */;" `nl`+        			"productType = \"com.apple.product-type.library.static\";" `nl`+        		"};" `nl`+        "/* End PBXNativeTarget section */" `nl`+        "" `nl`+        "/* Begin PBXProject section */" `nl`+        		"0867D690FE84028FC02AAC07 /* Project object */ = {" `nl`+        			"isa = PBXProject;" `nl`+        			"buildConfigurationList = 1DEB922208733DC00010E9CD /* Build configuration list for PBXProject \"KitDepsCustom\" */;" `nl`+        			"compatibilityVersion = \"Xcode 3.1\";" `nl`+        			"hasScannedForEncodings = 1;" `nl`+        			"mainGroup = 0867D691FE84028FC02AAC07 /* KitDeps */;" `nl`+        			"productRefGroup = 034768DFFF38A50411DB9C8B /* Products */;" `nl`+        			"projectDirPath = \"\";" `nl`+        			"projectRoot = \"\";" `nl`+        			"targets = (" `nl`+        				"D2AAC07D0554694100DB518D /* KitDeps */," `nl`+        			");" `nl`+        		"};" `nl`+        "/* End PBXProject section */"+  +  bottom = "/* Begin XCBuildConfiguration section */" `nl`+        		"1DEB921F08733DC00010E9CD /* Debug */ = {" `nl`+        			"isa = XCBuildConfiguration;" `nl`+        			"buildSettings = {" `nl`+        				"ALWAYS_SEARCH_USER_PATHS = NO;" `nl`+        				"ARCHS = \"$(ARCHS_STANDARD_32_BIT)\";" `nl`+        				"COPY_PHASE_STRIP = NO;" `nl`+        				"DSTROOT = /tmp/KitDeps.dst;" `nl`+        				"GCC_DYNAMIC_NO_PIC = NO;" `nl`+        				"GCC_ENABLE_FIX_AND_CONTINUE = YES;" `nl`+        				"GCC_MODEL_TUNING = G5;" `nl`+        				"GCC_OPTIMIZATION_LEVEL = 0;" `nl`+        				"GCC_PRECOMPILE_PREFIX_HEADER = YES;" `nl`+        				"GCC_PREFIX_HEADER = KitDeps_Prefix.pch;" `nl`+        				"INSTALL_PATH = /usr/local/lib;" `nl`+        				"PRODUCT_NAME = KitDeps;" `nl`+        			"};" `nl`+        			"name = Debug;" `nl`+        		"};" `nl`+        		"1DEB922008733DC00010E9CD /* Release */ = {" `nl`+        			"isa = XCBuildConfiguration;" `nl`+        			"buildSettings = {" `nl`+        				"ALWAYS_SEARCH_USER_PATHS = NO;" `nl`+        				"ARCHS = \"$(ARCHS_STANDARD_32_BIT)\";" `nl`+        				"DSTROOT = /tmp/KitDeps.dst;" `nl`+        				"GCC_MODEL_TUNING = G5;" `nl`+        				"GCC_PRECOMPILE_PREFIX_HEADER = YES;" `nl`+        				"GCC_PREFIX_HEADER = KitDeps_Prefix.pch;" `nl`+        				"INSTALL_PATH = /usr/local/lib;" `nl`+        				"PRODUCT_NAME = KitDeps;" `nl`+        			"};" `nl`+        			"name = Release;" `nl`+        		"};" `nl`+        		"1DEB922308733DC00010E9CD /* Debug */ = {" `nl`+        			"isa = XCBuildConfiguration;" `nl`+        			"baseConfigurationReference = 4728C52F117C02B10027D7D1 /* Kit.xcconfig */;" `nl`+        			"buildSettings = {" `nl`+        				"ARCHS = \"$(ARCHS_STANDARD_32_BIT)\";" `nl`+        				"GCC_C_LANGUAGE_STANDARD = c99;" `nl`+        				"GCC_OPTIMIZATION_LEVEL = 0;" `nl`+        				"GCC_WARN_ABOUT_RETURN_TYPE = YES;" `nl`+        				"GCC_WARN_UNUSED_VARIABLE = YES;" `nl`+        				"OTHER_LDFLAGS = \"-ObjC\";" `nl`+        				"PREBINDING = NO;" `nl`+        				"SDKROOT = iphoneos4.0;" `nl`+        			"};" `nl`+        			"name = Debug;" `nl`+        		"};" `nl`+        		"1DEB922408733DC00010E9CD /* Release */ = {" `nl`+        			"isa = XCBuildConfiguration;" `nl`+        			"baseConfigurationReference = 4728C52F117C02B10027D7D1 /* Kit.xcconfig */;" `nl`+        			"buildSettings = {" `nl`+        				"ARCHS = \"$(ARCHS_STANDARD_32_BIT)\";" `nl`+        				"GCC_C_LANGUAGE_STANDARD = c99;" `nl`+        				"GCC_WARN_ABOUT_RETURN_TYPE = YES;" `nl`+        				"GCC_WARN_UNUSED_VARIABLE = YES;" `nl`+        				"OTHER_LDFLAGS = \"-ObjC\";" `nl`+        				"PREBINDING = NO;" `nl`+        				"SDKROOT = iphoneos4.0;" `nl`+        			"};" `nl`+        			"name = Release;" `nl`+        		"};" `nl`+        "/* End XCBuildConfiguration section */" `nl`+        "" `nl`+        "/* Begin XCConfigurationList section */" `nl`+        		"1DEB921E08733DC00010E9CD /* Build configuration list for PBXNativeTarget \"KitDeps\" */ = {" `nl`+        			"isa = XCConfigurationList;" `nl`+        			"buildConfigurations = (" `nl`+        				"1DEB921F08733DC00010E9CD /* Debug */," `nl`+        				"1DEB922008733DC00010E9CD /* Release */," `nl`+        			");" `nl`+        			"defaultConfigurationIsVisible = 0;" `nl`+        			"defaultConfigurationName = Release;" `nl`+        		"};" `nl`+        		"1DEB922208733DC00010E9CD /* Build configuration list for PBXProject \"KitDepsCustom\" */ = {" `nl`+        			"isa = XCConfigurationList;" `nl`+        			"buildConfigurations = (" `nl`+        				"1DEB922308733DC00010E9CD /* Debug */," `nl`+        				"1DEB922408733DC00010E9CD /* Release */," `nl`+        			");" `nl`+        			"defaultConfigurationIsVisible = 0;" `nl`+        			"defaultConfigurationName = Release;" `nl`+        		"};" `nl`+        "/* End XCConfigurationList section */" `nl`+        	"};" `nl`+        	"rootObject = 0867D690FE84028FC02AAC07 /* Project object */;" `nl`+        "}"+  +  +  main = do+    putStrLn $ projectPbxProj "" "" "" "" ""+    
+ Kit/XCode/XCConfig.hs view
@@ -0,0 +1,56 @@+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.String.Utils++  type XCCSetting = (String, String)+  type XCCInclude = String+                    +  data XCConfig = XCC { configName :: String, configSettings :: M.Map String String, configIncludes :: [XCCInclude] } deriving (Eq, Show)+  +  includeStart = "#include "+  +  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+  +  -- TODO incorporate the config name+  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+  
+ LICENSE view
@@ -0,0 +1,1 @@+TODO
+ Main.hs view
@@ -0,0 +1,10 @@+module Main where+  import Kit.Main+  import qualified Data.Map as M+  import Kit.XCode.XCConfig+  +  conf1 = XCC "conf1" (M.fromList [("A", "1")]) []+  conf2 = XCC "conf2" (M.fromList [("A", "2")]) []+  +  --main = putStrLn $ show $ multiConfig "combo" [conf1, conf2]+  main = kitMain
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ kit.cabal view
@@ -0,0 +1,16 @@+Name:                kit+Version:             0.2+Synopsis:            A dependency manager for XCode (Objective-C) projects+Description:         A dependency manager for XCode (Objective-C) projects+Category:            Development+License:             BSD3+License-file:        LICENSE+Author:              Nick Partridge+Maintainer:          nkpart@gmail.com+Build-Type:          Simple+Cabal-Version:       >=1.2+Executable kit+  Main-is:           Main.hs+  Other-Modules:     Kit.JSON,  Kit.Kit,  Kit.Main,  Kit.Package,  Kit.Project,  Kit.Repository,  Kit.Spec,  Kit.Util,  Kit.XCode.Builder,  Kit.XCode.Common,  Kit.XCode.Prefix,  Kit.XCode.ProjectFile, Kit.XCode.XCConfig+  Build-Depends:     base >= 3 && < 5, HTTP >= 4000.0.9, network, bytestring, filepath, directory, process, json, mtl, cmdargs, MissingH, containers, QuickCheck+