packages feed

kit-0.7.10: Kit/Main.hs

module Kit.Main where

  import qualified Kit.CmdArgs as KA 
  import Kit.Commands
  import Kit.Spec
  import Kit.Package
  import Kit.Project
  import Kit.Util
  import Kit.Contents
  import Kit.Dependency
  import Kit.WorkingCopy
  import Data.Yaml (encodeFile)
  import Kit.Util.FSAction
  import System.Exit
  import Data.Tree (drawTree)
  import Data.List (partition, groupBy, sortBy, maximumBy, nub, intercalate)
  import Data.Function (on)
  import Kit.Repository (KitRepository, unpackKit, packagesDirectory, publishLocally)
  import Control.Monad.State
  import Kit.AbsolutePath

  kitMain :: IO ()
  kitMain = do
    args <- KA.parseArgs
    runCommand (KA.repositoryDir args) . handleArgs $ args

  handleArgs :: KA.KitCmdArgs -> Command ()
  handleArgs (KA.Update _) = doUpdate
  handleArgs (KA.Package _) = doPackageKit
  handleArgs (KA.PublishLocal _ versionTag) = doPublishLocal versionTag 
  handleArgs (KA.Verify sdkName _) = doVerify sdkName
  handleArgs (KA.CreateSpec name version _) = doCreateSpec name version
  handleArgs (KA.ShowTree _) = doShowTree

  doUpdate :: Command ()
  doUpdate = do
              repo <- myRepository
              workingCopy <- myWorkingCopy
              liftKit $ do
                rawDeps <- totalSpecDependencies repo workingCopy
                (deps, didResolveConflict) <- unconflict rawDeps
                let (devPackages, repoPackages) = partition isDevDep deps
                mapM_ (liftIO . unpackKit repo) repoPackages
                mapM_ (sayError . ("Using dev-package: " ++) . packageName) devPackages
                project <- buildProject repo workingCopy deps
                reportResources project
                writeProject project
                (if didResolveConflict then sayWarn else say Green) "Kit complete. You may need to restart Xcode for it to pick up any changes."
                liftIO . when didResolveConflict . exitWith . ExitFailure $ 1
      where buildProject repo workingCopy deps = do
                allContents <- mapM (dependencyContents repo) deps
                return $ makeKitProject allContents (specKitDepsXcodeFlags (workingKitSpec workingCopy))
            reportResources project = let resourceDirs = kitProjectResourceDirs project
                                       in unless (null resourceDirs) $ puts $ "Resources: " ++ stringJoin ", " (map fst resourceDirs)
            writeProject = liftIO . runActions . kitProjectActions

  unconflict :: (Packageable b, MonadIO m) => [b] -> m ([b], Bool)
  unconflict deps = let byName = groupBy ((==) `on` (packageName . snd)) . sortBy (compare `on` (packageName . snd)) $ zip [(1::Int)..] deps
                        stripIndex (xs, hadConflict) = (map snd . sortBy (compare `on` fst) $ xs, hadConflict)
                in liftM stripIndex $ flip runStateT False $ forM byName $ \allDeps@(b:bs) ->
                    if null bs
                      then return b
                      else do
                        let versions = nub $ map (packageVersion . snd) allDeps
                        let maxVersion = maximumBy (compare `on` (packageVersion . snd)) allDeps
                        sayWarn $ "Dependency conflict: " ++ packageName (snd b) ++ " => " ++ intercalate ", " versions
                        sayWarn $ "Selected maximum version: " ++ (packageVersion . snd $ maxVersion)
                        put True
                        return maxVersion

  dependencyContents :: (Applicative m, MonadIO m) => KitRepository -> Dependency -> m KitContents
  dependencyContents repo dep = readKitContents' baseDir (depSpec dep) where
    baseDir = dependency ((packagesDirectory repo </>) . packageFileName) (\fp _ -> devKitDir </> fp) dep
    readKitContents' base spec = do
      absoluteBase <- liftIO $ absolutePath base
      readKitContents absoluteBase spec

  doPackageKit :: Command ()
  doPackageKit = mySpec >>= liftIO . package 

  doPublishLocal :: Maybe String -> Command ()
  doPublishLocal versionTag = do
    spec <- mySpec 
    specFile <- workingSpecFile <$> myWorkingCopy
    repo <- myRepository
    let updatedSpec = maybe spec (\tag -> updateVersion spec (++tag)) versionTag
    liftIO $ do
      package updatedSpec 
      publishLocally repo updatedSpec specFile $ "dist" </> packageFileName updatedSpec ++ ".tar.gz"
    return ()

  doShowTree :: Command ()
  doShowTree = do
    repo <- myRepository
    wc <- myWorkingCopy
    tree <- liftKit $ dependencyTree repo wc
    puts $ drawTree $ fmap packageFileName tree

  doVerify :: String -> Command ()
  doVerify sdk = do
        spec <- mySpec
        puts "Checking that the kit can be depended upon..."
        puts " #> Deploying locally"
        doPublishLocal Nothing -- TODO publish with a verify tag
        puts " #> Building temporary parent project"
        liftIO $ inDirectoryM getTemporaryDirectory $ do
          let kitVerifyDir = "kit-verify"
          cleanOrCreate kitVerifyDir
          inDirectory kitVerifyDir $ do
            encodeFile "KitSpec" (defaultSpec "verify-kit" "1.0") { specDependencies = [specKit spec] }
            runCommand Nothing doUpdate 
            inDirectory "Kits" $ do
              shell "open ."
              shell $ "xcodebuild -sdk " ++ sdk
          puts "OK."
        puts "End checks."

  doCreateSpec :: String -> String -> Command ()
  doCreateSpec name version = do
    let spec = defaultSpec name version 
    liftIO $ encodeFile "KitSpec" spec
    puts $ "Created KitSpec for " ++ packageFileName spec