-- cabal-helper: Simple interface to Cabal's configuration state -- Copyright (C) 2019 Daniel Gröber <cabal-helper@dxld.at> -- -- SPDX-License-Identifier: Apache-2.0 -- -- Licensed under the Apache License, Version 2.0 (the "License"); -- you may not use this file except in compliance with the License. -- You may obtain a copy of the License at -- -- http://www.apache.org/licenses/LICENSE-2.0 {-# LANGUAGE GADTs, TypeFamilies, DataKinds #-} {-| Module : Distribution.Helper.Discover Description : Finding project contexts License : Apache-2.0 Maintainer : cabal-helper@dxld.at Portability : portable -} -- TODO: $ sed -e s/DistDir/BuildDir/ module Distribution.Helper.Discover ( findProjects , getDefaultDistDir , isValidDistDir ) where import Control.Monad.Writer import System.Directory import System.FilePath import CabalHelper.Compiletime.Types import CabalHelper.Compiletime.Cabal -- | @findProjects dir@. Find available project instances in @dir@. -- -- For example, if the given directory contains both a @cabal.project@ and -- a @stack.yaml@ file: -- -- >>> findProjects "." -- [ Ex (ProjLocStackYaml "./stack.yaml"), Ex (ProjLocCabalV2File "./cabal.project") ] -- -- Note that this function only looks for "default" project markers. If you -- want to for example support the common pattern of having multiple -- @stack-<GHC_VER>.yaml@ files simply fill out a 'ProjLoc' yourself. In -- this case 'ProjLocStackYaml'. findProjects :: FilePath -> IO [Ex ProjLoc] findProjects dir = execWriterT $ do let cabalProject = dir </> "cabal.project" whenM (liftIO $ doesFileExist cabalProject) $ tell [Ex $ ProjLocV2File cabalProject dir] let stackYaml = dir </> "stack.yaml" whenM (liftIO $ doesFileExist stackYaml) $ tell [Ex $ ProjLocStackYaml stackYaml] maybeCabalDir <- liftIO (fmap takeDirectory <$> findCabalFile dir) forM_ [Ex . ProjLocV2Dir, Ex . ProjLocV1Dir] $ \proj -> traverse (tell . pure . proj) maybeCabalDir -- | @getDefaultDistDir pl@. Get the default dist-dir for the given project. -- -- Note that the path in the returned dist-dir might not exist yet if the -- build-tool has never been run for this project before. This is fine as -- far as @cabal-helper@ is concerned. It will simply invoke the build-tool -- as needed to answer the requested queries. getDefaultDistDir :: ProjLoc pt -> DistDir pt getDefaultDistDir (ProjLocV1CabalFile _cabal_file pkgdir) = DistDirCabal SCV1 $ pkgdir </> "dist" getDefaultDistDir (ProjLocV1Dir pkgdir) = DistDirCabal SCV1 $ pkgdir </> "dist" getDefaultDistDir (ProjLocV2File _cabal_project projdir) = DistDirCabal SCV2 $ projdir </> "dist-newstyle" getDefaultDistDir (ProjLocV2Dir projdir) = DistDirCabal SCV2 $ projdir </> "dist-newstyle" getDefaultDistDir (ProjLocStackYaml _) = DistDirStack Nothing -- | @isValidDistDir distdir@. Check if @distdir@ looks like a valid -- build-dir for it's project type. We just check if characteristic marker -- files for the associated project type exist. -- -- If the project type does not have a way to do this (for example -- 'DistDirStack') check we return 'Nothing'. isValidDistDir :: DistDir pt -> IO (Maybe Bool) isValidDistDir (DistDirCabal cpt dir) = do fmap Just $ doesFileExist $ dir </> cabalProjTypeMarkerFile cpt isValidDistDir DistDirStack{} = return Nothing cabalProjTypeMarkerFile :: SCabalProjType pt -> FilePath cabalProjTypeMarkerFile SCV1 = "setup-config" cabalProjTypeMarkerFile SCV2 = "cache" </> "plan.json" whenM :: Monad m => m Bool -> m () -> m () whenM p x = p >>= (`when` x)