{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE RecordWildCards #-}

module Ormolu.Utils.Cabal
  ( CabalSearchResult (..),
    CabalInfo (..),
    Extension (..),
    getCabalInfoForSourceFile,
    findCabalFile,
    parseCabalInfo,
  )
where

import Control.Exception
import Control.Monad.IO.Class
import Data.ByteString qualified as B
import Data.Map.Lazy (Map)
import Data.Map.Lazy qualified as M
import Data.Maybe (maybeToList)
import Data.Set (Set)
import Data.Set qualified as Set
import Distribution.ModuleName qualified as ModuleName
import Distribution.PackageDescription
import Distribution.PackageDescription.Parsec
import Distribution.Types.CondTree qualified as CT
import Distribution.Utils.Path (getSymbolicPath)
import Language.Haskell.Extension
import Ormolu.Config
import Ormolu.Exception
import Ormolu.Fixity
import Ormolu.Utils.IO (Cache, findClosestFileSatisfying, newCache, withCache)
import System.Directory
import System.FilePath
import System.IO.Unsafe (unsafePerformIO)

-- | The result of searching for a @.cabal@ file.
--
-- @since 0.5.3.0
data CabalSearchResult
  = -- | Cabal file could not be found
    CabalNotFound
  | -- | Cabal file was found, but it did not mention the source file in
    -- question
    CabalDidNotMention CabalInfo
  | -- | Cabal file was found and it mentions the source file in question
    CabalFound CabalInfo
  deriving (CabalSearchResult -> CabalSearchResult -> Bool
(CabalSearchResult -> CabalSearchResult -> Bool)
-> (CabalSearchResult -> CabalSearchResult -> Bool)
-> Eq CabalSearchResult
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: CabalSearchResult -> CabalSearchResult -> Bool
== :: CabalSearchResult -> CabalSearchResult -> Bool
$c/= :: CabalSearchResult -> CabalSearchResult -> Bool
/= :: CabalSearchResult -> CabalSearchResult -> Bool
Eq, Int -> CabalSearchResult -> ShowS
[CabalSearchResult] -> ShowS
CabalSearchResult -> FilePath
(Int -> CabalSearchResult -> ShowS)
-> (CabalSearchResult -> FilePath)
-> ([CabalSearchResult] -> ShowS)
-> Show CabalSearchResult
forall a.
(Int -> a -> ShowS) -> (a -> FilePath) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> CabalSearchResult -> ShowS
showsPrec :: Int -> CabalSearchResult -> ShowS
$cshow :: CabalSearchResult -> FilePath
show :: CabalSearchResult -> FilePath
$cshowList :: [CabalSearchResult] -> ShowS
showList :: [CabalSearchResult] -> ShowS
Show)

-- | Cabal information of interest to Ormolu.
data CabalInfo = CabalInfo
  { -- | Package name
    CabalInfo -> PackageName
ciPackageName :: !PackageName,
    -- | Extension and language settings in the form of 'DynOption's
    CabalInfo -> [DynOption]
ciDynOpts :: ![DynOption],
    -- | Direct dependencies
    CabalInfo -> Set PackageName
ciDependencies :: !(Set PackageName),
    -- | Absolute path to the cabal file
    CabalInfo -> FilePath
ciCabalFilePath :: !FilePath
  }
  deriving (CabalInfo -> CabalInfo -> Bool
(CabalInfo -> CabalInfo -> Bool)
-> (CabalInfo -> CabalInfo -> Bool) -> Eq CabalInfo
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: CabalInfo -> CabalInfo -> Bool
== :: CabalInfo -> CabalInfo -> Bool
$c/= :: CabalInfo -> CabalInfo -> Bool
/= :: CabalInfo -> CabalInfo -> Bool
Eq, Int -> CabalInfo -> ShowS
[CabalInfo] -> ShowS
CabalInfo -> FilePath
(Int -> CabalInfo -> ShowS)
-> (CabalInfo -> FilePath)
-> ([CabalInfo] -> ShowS)
-> Show CabalInfo
forall a.
(Int -> a -> ShowS) -> (a -> FilePath) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> CabalInfo -> ShowS
showsPrec :: Int -> CabalInfo -> ShowS
$cshow :: CabalInfo -> FilePath
show :: CabalInfo -> FilePath
$cshowList :: [CabalInfo] -> ShowS
showList :: [CabalInfo] -> ShowS
Show)

-- | Locate a @.cabal@ file corresponding to the given Haskell source file
-- and obtain 'CabalInfo' from it.
getCabalInfoForSourceFile ::
  (MonadIO m) =>
  -- | Haskell source file
  FilePath ->
  -- | Extracted cabal info, if any
  m CabalSearchResult
getCabalInfoForSourceFile :: forall (m :: * -> *). MonadIO m => FilePath -> m CabalSearchResult
getCabalInfoForSourceFile FilePath
sourceFile =
  IO (Maybe FilePath) -> m (Maybe FilePath)
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (FilePath -> IO (Maybe FilePath)
forall (m :: * -> *). MonadIO m => FilePath -> m (Maybe FilePath)
findCabalFile FilePath
sourceFile) m (Maybe FilePath)
-> (Maybe FilePath -> m CabalSearchResult) -> m CabalSearchResult
forall a b. m a -> (a -> m b) -> m b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
    Just FilePath
cabalFile -> do
      (mentioned, cabalInfo) <- FilePath -> FilePath -> m (Bool, CabalInfo)
forall (m :: * -> *).
MonadIO m =>
FilePath -> FilePath -> m (Bool, CabalInfo)
parseCabalInfo FilePath
cabalFile FilePath
sourceFile
      return
        ( if mentioned
            then CabalFound cabalInfo
            else CabalDidNotMention cabalInfo
        )
    Maybe FilePath
Nothing -> CabalSearchResult -> m CabalSearchResult
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return CabalSearchResult
CabalNotFound

-- | Find the path to an appropriate @.cabal@ file for a Haskell source
-- file, if available.
findCabalFile ::
  (MonadIO m) =>
  -- | Path to a Haskell source file in a project with a @.cabal@ file
  FilePath ->
  -- | Absolute path to the @.cabal@ file, if available
  m (Maybe FilePath)
findCabalFile :: forall (m :: * -> *). MonadIO m => FilePath -> m (Maybe FilePath)
findCabalFile = (FilePath -> Bool) -> FilePath -> m (Maybe FilePath)
forall (m :: * -> *).
MonadIO m =>
(FilePath -> Bool) -> FilePath -> m (Maybe FilePath)
findClosestFileSatisfying ((FilePath -> Bool) -> FilePath -> m (Maybe FilePath))
-> (FilePath -> Bool) -> FilePath -> m (Maybe FilePath)
forall a b. (a -> b) -> a -> b
$ \FilePath
x ->
  ShowS
takeExtension FilePath
x FilePath -> FilePath -> Bool
forall a. Eq a => a -> a -> Bool
== FilePath
".cabal"

-- | Parsed cabal file information to be shared across multiple source files.
data CachedCabalFile = CachedCabalFile
  { -- | Parsed generic package description.
    CachedCabalFile -> GenericPackageDescription
genericPackageDescription :: GenericPackageDescription,
    -- | Map from Haskell source file paths (without any extensions) to the
    -- corresponding 'DynOption's and dependencies.
    CachedCabalFile -> Map FilePath ([DynOption], [PackageName])
extensionsAndDeps :: Map FilePath ([DynOption], [PackageName])
  }
  deriving (Int -> CachedCabalFile -> ShowS
[CachedCabalFile] -> ShowS
CachedCabalFile -> FilePath
(Int -> CachedCabalFile -> ShowS)
-> (CachedCabalFile -> FilePath)
-> ([CachedCabalFile] -> ShowS)
-> Show CachedCabalFile
forall a.
(Int -> a -> ShowS) -> (a -> FilePath) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> CachedCabalFile -> ShowS
showsPrec :: Int -> CachedCabalFile -> ShowS
$cshow :: CachedCabalFile -> FilePath
show :: CachedCabalFile -> FilePath
$cshowList :: [CachedCabalFile] -> ShowS
showList :: [CachedCabalFile] -> ShowS
Show)

-- | Cache ref that stores 'CachedCabalFile' per Cabal file.
cacheRef :: Cache FilePath CachedCabalFile
cacheRef :: Cache FilePath CachedCabalFile
cacheRef = IO (Cache FilePath CachedCabalFile)
-> Cache FilePath CachedCabalFile
forall a. IO a -> a
unsafePerformIO IO (Cache FilePath CachedCabalFile)
forall k v. Ord k => IO (Cache k v)
newCache
{-# NOINLINE cacheRef #-}

-- | Parse 'CabalInfo' from a @.cabal@ file at the given 'FilePath'.
parseCabalInfo ::
  (MonadIO m) =>
  -- | Location of the .cabal file
  FilePath ->
  -- | Location of the source file we are formatting
  FilePath ->
  -- | Indication if the source file was mentioned in the Cabal file and the
  -- extracted 'CabalInfo'
  m (Bool, CabalInfo)
parseCabalInfo :: forall (m :: * -> *).
MonadIO m =>
FilePath -> FilePath -> m (Bool, CabalInfo)
parseCabalInfo FilePath
cabalFileAsGiven FilePath
sourceFileAsGiven = IO (Bool, CabalInfo) -> m (Bool, CabalInfo)
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO (Bool, CabalInfo) -> m (Bool, CabalInfo))
-> IO (Bool, CabalInfo) -> m (Bool, CabalInfo)
forall a b. (a -> b) -> a -> b
$ do
  cabalFile <- FilePath -> IO FilePath
makeAbsolute FilePath
cabalFileAsGiven
  sourceFileAbs <- makeAbsolute sourceFileAsGiven
  CachedCabalFile {..} <- withCache cacheRef cabalFile $ do
    cabalFileBs <- B.readFile cabalFile
    genericPackageDescription <-
      whenLeft (snd . runParseResult $ parseGenericPackageDescription cabalFileBs) $
        throwIO . OrmoluCabalFileParsingFailed cabalFile . snd
    let extensionsAndDeps =
          FilePath
-> GenericPackageDescription
-> Map FilePath ([DynOption], [PackageName])
getExtensionAndDepsMap FilePath
cabalFile GenericPackageDescription
genericPackageDescription
    pure CachedCabalFile {..}
  let (dynOpts, dependencies, mentioned) =
        case M.lookup (dropExtensions sourceFileAbs) extensionsAndDeps of
          Maybe ([DynOption], [PackageName])
Nothing -> ([], Set PackageName -> [PackageName]
forall a. Set a -> [a]
Set.toList Set PackageName
defaultDependencies, Bool
False)
          Just ([DynOption]
dynOpts', [PackageName]
dependencies') -> ([DynOption]
dynOpts', [PackageName]
dependencies', Bool
True)
      pdesc = GenericPackageDescription -> PackageDescription
packageDescription GenericPackageDescription
genericPackageDescription
  return
    ( mentioned,
      CabalInfo
        { ciPackageName = pkgName (package pdesc),
          ciDynOpts = dynOpts,
          ciDependencies = Set.fromList dependencies,
          ciCabalFilePath = cabalFile
        }
    )
  where
    whenLeft :: (Applicative f) => Either e a -> (e -> f a) -> f a
    whenLeft :: forall (f :: * -> *) e a.
Applicative f =>
Either e a -> (e -> f a) -> f a
whenLeft Either e a
eitha e -> f a
ma = (e -> f a) -> (a -> f a) -> Either e a -> f a
forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
either e -> f a
ma a -> f a
forall a. a -> f a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Either e a
eitha

-- | Get a map from Haskell source file paths (without any extensions) to
-- the corresponding 'DynOption's and dependencies.
getExtensionAndDepsMap ::
  -- | Path to the cabal file
  FilePath ->
  -- | Parsed generic package description
  GenericPackageDescription ->
  Map FilePath ([DynOption], [PackageName])
getExtensionAndDepsMap :: FilePath
-> GenericPackageDescription
-> Map FilePath ([DynOption], [PackageName])
getExtensionAndDepsMap FilePath
cabalFile GenericPackageDescription {[(UnqualComponentName, CondTree ConfVar [Dependency] Benchmark)]
[(UnqualComponentName, CondTree ConfVar [Dependency] Executable)]
[(UnqualComponentName, CondTree ConfVar [Dependency] ForeignLib)]
[(UnqualComponentName, CondTree ConfVar [Dependency] Library)]
[(UnqualComponentName, CondTree ConfVar [Dependency] TestSuite)]
[PackageFlag]
Maybe (CondTree ConfVar [Dependency] Library)
Maybe Version
PackageDescription
packageDescription :: GenericPackageDescription -> PackageDescription
packageDescription :: PackageDescription
gpdScannedVersion :: Maybe Version
genPackageFlags :: [PackageFlag]
condLibrary :: Maybe (CondTree ConfVar [Dependency] Library)
condSubLibraries :: [(UnqualComponentName, CondTree ConfVar [Dependency] Library)]
condForeignLibs :: [(UnqualComponentName, CondTree ConfVar [Dependency] ForeignLib)]
condExecutables :: [(UnqualComponentName, CondTree ConfVar [Dependency] Executable)]
condTestSuites :: [(UnqualComponentName, CondTree ConfVar [Dependency] TestSuite)]
condBenchmarks :: [(UnqualComponentName, CondTree ConfVar [Dependency] Benchmark)]
condBenchmarks :: GenericPackageDescription
-> [(UnqualComponentName, CondTree ConfVar [Dependency] Benchmark)]
condExecutables :: GenericPackageDescription
-> [(UnqualComponentName,
     CondTree ConfVar [Dependency] Executable)]
condForeignLibs :: GenericPackageDescription
-> [(UnqualComponentName,
     CondTree ConfVar [Dependency] ForeignLib)]
condLibrary :: GenericPackageDescription
-> Maybe (CondTree ConfVar [Dependency] Library)
condSubLibraries :: GenericPackageDescription
-> [(UnqualComponentName, CondTree ConfVar [Dependency] Library)]
condTestSuites :: GenericPackageDescription
-> [(UnqualComponentName, CondTree ConfVar [Dependency] TestSuite)]
genPackageFlags :: GenericPackageDescription -> [PackageFlag]
gpdScannedVersion :: GenericPackageDescription -> Maybe Version
..} =
  [Map FilePath ([DynOption], [PackageName])]
-> Map FilePath ([DynOption], [PackageName])
forall (f :: * -> *) k a.
(Foldable f, Ord k) =>
f (Map k a) -> Map k a
M.unions ([Map FilePath ([DynOption], [PackageName])]
 -> Map FilePath ([DynOption], [PackageName]))
-> ([[Map FilePath ([DynOption], [PackageName])]]
    -> [Map FilePath ([DynOption], [PackageName])])
-> [[Map FilePath ([DynOption], [PackageName])]]
-> Map FilePath ([DynOption], [PackageName])
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [[Map FilePath ([DynOption], [PackageName])]]
-> [Map FilePath ([DynOption], [PackageName])]
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat ([[Map FilePath ([DynOption], [PackageName])]]
 -> Map FilePath ([DynOption], [PackageName]))
-> [[Map FilePath ([DynOption], [PackageName])]]
-> Map FilePath ([DynOption], [PackageName])
forall a b. (a -> b) -> a -> b
$
    [ (Library -> ([FilePath], ([DynOption], [PackageName])))
-> CondTree ConfVar [Dependency] Library
-> Map FilePath ([DynOption], [PackageName])
forall {k} {a} {c} {a} {v}.
(Ord k, Semigroup a, Semigroup c) =>
(a -> ([k], a)) -> CondTree v c a -> Map k a
buildMap Library -> ([FilePath], ([DynOption], [PackageName]))
extractFromLibrary (CondTree ConfVar [Dependency] Library
 -> Map FilePath ([DynOption], [PackageName]))
-> [CondTree ConfVar [Dependency] Library]
-> [Map FilePath ([DynOption], [PackageName])]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [CondTree ConfVar [Dependency] Library]
lib [CondTree ConfVar [Dependency] Library]
-> [CondTree ConfVar [Dependency] Library]
-> [CondTree ConfVar [Dependency] Library]
forall a. [a] -> [a] -> [a]
++ [CondTree ConfVar [Dependency] Library]
sublibs,
      (Executable -> ([FilePath], ([DynOption], [PackageName])))
-> CondTree ConfVar [Dependency] Executable
-> Map FilePath ([DynOption], [PackageName])
forall {k} {a} {c} {a} {v}.
(Ord k, Semigroup a, Semigroup c) =>
(a -> ([k], a)) -> CondTree v c a -> Map k a
buildMap Executable -> ([FilePath], ([DynOption], [PackageName]))
extractFromExecutable (CondTree ConfVar [Dependency] Executable
 -> Map FilePath ([DynOption], [PackageName]))
-> ((UnqualComponentName, CondTree ConfVar [Dependency] Executable)
    -> CondTree ConfVar [Dependency] Executable)
-> (UnqualComponentName, CondTree ConfVar [Dependency] Executable)
-> Map FilePath ([DynOption], [PackageName])
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (UnqualComponentName, CondTree ConfVar [Dependency] Executable)
-> CondTree ConfVar [Dependency] Executable
forall a b. (a, b) -> b
snd ((UnqualComponentName, CondTree ConfVar [Dependency] Executable)
 -> Map FilePath ([DynOption], [PackageName]))
-> [(UnqualComponentName,
     CondTree ConfVar [Dependency] Executable)]
-> [Map FilePath ([DynOption], [PackageName])]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [(UnqualComponentName, CondTree ConfVar [Dependency] Executable)]
condExecutables,
      (TestSuite -> ([FilePath], ([DynOption], [PackageName])))
-> CondTree ConfVar [Dependency] TestSuite
-> Map FilePath ([DynOption], [PackageName])
forall {k} {a} {c} {a} {v}.
(Ord k, Semigroup a, Semigroup c) =>
(a -> ([k], a)) -> CondTree v c a -> Map k a
buildMap TestSuite -> ([FilePath], ([DynOption], [PackageName]))
extractFromTestSuite (CondTree ConfVar [Dependency] TestSuite
 -> Map FilePath ([DynOption], [PackageName]))
-> ((UnqualComponentName, CondTree ConfVar [Dependency] TestSuite)
    -> CondTree ConfVar [Dependency] TestSuite)
-> (UnqualComponentName, CondTree ConfVar [Dependency] TestSuite)
-> Map FilePath ([DynOption], [PackageName])
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (UnqualComponentName, CondTree ConfVar [Dependency] TestSuite)
-> CondTree ConfVar [Dependency] TestSuite
forall a b. (a, b) -> b
snd ((UnqualComponentName, CondTree ConfVar [Dependency] TestSuite)
 -> Map FilePath ([DynOption], [PackageName]))
-> [(UnqualComponentName, CondTree ConfVar [Dependency] TestSuite)]
-> [Map FilePath ([DynOption], [PackageName])]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [(UnqualComponentName, CondTree ConfVar [Dependency] TestSuite)]
condTestSuites,
      (Benchmark -> ([FilePath], ([DynOption], [PackageName])))
-> CondTree ConfVar [Dependency] Benchmark
-> Map FilePath ([DynOption], [PackageName])
forall {k} {a} {c} {a} {v}.
(Ord k, Semigroup a, Semigroup c) =>
(a -> ([k], a)) -> CondTree v c a -> Map k a
buildMap Benchmark -> ([FilePath], ([DynOption], [PackageName]))
extractFromBenchmark (CondTree ConfVar [Dependency] Benchmark
 -> Map FilePath ([DynOption], [PackageName]))
-> ((UnqualComponentName, CondTree ConfVar [Dependency] Benchmark)
    -> CondTree ConfVar [Dependency] Benchmark)
-> (UnqualComponentName, CondTree ConfVar [Dependency] Benchmark)
-> Map FilePath ([DynOption], [PackageName])
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (UnqualComponentName, CondTree ConfVar [Dependency] Benchmark)
-> CondTree ConfVar [Dependency] Benchmark
forall a b. (a, b) -> b
snd ((UnqualComponentName, CondTree ConfVar [Dependency] Benchmark)
 -> Map FilePath ([DynOption], [PackageName]))
-> [(UnqualComponentName, CondTree ConfVar [Dependency] Benchmark)]
-> [Map FilePath ([DynOption], [PackageName])]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [(UnqualComponentName, CondTree ConfVar [Dependency] Benchmark)]
condBenchmarks
    ]
  where
    lib :: [CondTree ConfVar [Dependency] Library]
lib = Maybe (CondTree ConfVar [Dependency] Library)
-> [CondTree ConfVar [Dependency] Library]
forall a. Maybe a -> [a]
maybeToList Maybe (CondTree ConfVar [Dependency] Library)
condLibrary
    sublibs :: [CondTree ConfVar [Dependency] Library]
sublibs = (UnqualComponentName, CondTree ConfVar [Dependency] Library)
-> CondTree ConfVar [Dependency] Library
forall a b. (a, b) -> b
snd ((UnqualComponentName, CondTree ConfVar [Dependency] Library)
 -> CondTree ConfVar [Dependency] Library)
-> [(UnqualComponentName, CondTree ConfVar [Dependency] Library)]
-> [CondTree ConfVar [Dependency] Library]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [(UnqualComponentName, CondTree ConfVar [Dependency] Library)]
condSubLibraries

    buildMap :: (a -> ([k], a)) -> CondTree v c a -> Map k a
buildMap a -> ([k], a)
f CondTree v c a
a = [(k, a)] -> Map k a
forall k a. Ord k => [(k, a)] -> Map k a
M.fromList ((,a
extsAndDeps) (k -> (k, a)) -> [k] -> [(k, a)]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [k]
files)
      where
        (a
mergedA, c
_) = CondTree v c a -> (a, c)
forall a c v.
(Semigroup a, Semigroup c) =>
CondTree v c a -> (a, c)
CT.ignoreConditions CondTree v c a
a
        ([k]
files, a
extsAndDeps) = a -> ([k], a)
f a
mergedA

    extractFromBuildInfo :: [FilePath]
-> BuildInfo -> ([FilePath], ([DynOption], [PackageName]))
extractFromBuildInfo [FilePath]
extraModules BuildInfo {Bool
[FilePath]
[(FilePath, FilePath)]
[ModuleName]
[Dependency]
[ExeDependency]
[LegacyExeDependency]
[Mixin]
[PkgconfigDependency]
[RelativePath Framework 'File]
[RelativePath Include 'File]
[SymbolicPath Include 'File]
[SymbolicPath Pkg 'File]
[SymbolicPath Pkg ('Dir Source)]
[SymbolicPath Pkg ('Dir Framework)]
[SymbolicPath Pkg ('Dir Include)]
[SymbolicPath Pkg ('Dir Lib)]
[Extension]
[Language]
Maybe Language
PerCompilerFlavor [FilePath]
buildable :: Bool
buildTools :: [LegacyExeDependency]
buildToolDepends :: [ExeDependency]
cppOptions :: [FilePath]
asmOptions :: [FilePath]
cmmOptions :: [FilePath]
ccOptions :: [FilePath]
cxxOptions :: [FilePath]
ldOptions :: [FilePath]
hsc2hsOptions :: [FilePath]
pkgconfigDepends :: [PkgconfigDependency]
frameworks :: [RelativePath Framework 'File]
extraFrameworkDirs :: [SymbolicPath Pkg ('Dir Framework)]
asmSources :: [SymbolicPath Pkg 'File]
cmmSources :: [SymbolicPath Pkg 'File]
cSources :: [SymbolicPath Pkg 'File]
cxxSources :: [SymbolicPath Pkg 'File]
jsSources :: [SymbolicPath Pkg 'File]
hsSourceDirs :: [SymbolicPath Pkg ('Dir Source)]
otherModules :: [ModuleName]
virtualModules :: [ModuleName]
autogenModules :: [ModuleName]
defaultLanguage :: Maybe Language
otherLanguages :: [Language]
defaultExtensions :: [Extension]
otherExtensions :: [Extension]
oldExtensions :: [Extension]
extraLibs :: [FilePath]
extraLibsStatic :: [FilePath]
extraGHCiLibs :: [FilePath]
extraBundledLibs :: [FilePath]
extraLibFlavours :: [FilePath]
extraDynLibFlavours :: [FilePath]
extraLibDirs :: [SymbolicPath Pkg ('Dir Lib)]
extraLibDirsStatic :: [SymbolicPath Pkg ('Dir Lib)]
includeDirs :: [SymbolicPath Pkg ('Dir Include)]
includes :: [SymbolicPath Include 'File]
autogenIncludes :: [RelativePath Include 'File]
installIncludes :: [RelativePath Include 'File]
options :: PerCompilerFlavor [FilePath]
profOptions :: PerCompilerFlavor [FilePath]
sharedOptions :: PerCompilerFlavor [FilePath]
profSharedOptions :: PerCompilerFlavor [FilePath]
staticOptions :: PerCompilerFlavor [FilePath]
customFieldsBI :: [(FilePath, FilePath)]
targetBuildDepends :: [Dependency]
mixins :: [Mixin]
asmOptions :: BuildInfo -> [FilePath]
asmSources :: BuildInfo -> [SymbolicPath Pkg 'File]
autogenIncludes :: BuildInfo -> [RelativePath Include 'File]
autogenModules :: BuildInfo -> [ModuleName]
buildToolDepends :: BuildInfo -> [ExeDependency]
buildTools :: BuildInfo -> [LegacyExeDependency]
buildable :: BuildInfo -> Bool
cSources :: BuildInfo -> [SymbolicPath Pkg 'File]
ccOptions :: BuildInfo -> [FilePath]
cmmOptions :: BuildInfo -> [FilePath]
cmmSources :: BuildInfo -> [SymbolicPath Pkg 'File]
cppOptions :: BuildInfo -> [FilePath]
customFieldsBI :: BuildInfo -> [(FilePath, FilePath)]
cxxOptions :: BuildInfo -> [FilePath]
cxxSources :: BuildInfo -> [SymbolicPath Pkg 'File]
defaultExtensions :: BuildInfo -> [Extension]
defaultLanguage :: BuildInfo -> Maybe Language
extraBundledLibs :: BuildInfo -> [FilePath]
extraDynLibFlavours :: BuildInfo -> [FilePath]
extraFrameworkDirs :: BuildInfo -> [SymbolicPath Pkg ('Dir Framework)]
extraGHCiLibs :: BuildInfo -> [FilePath]
extraLibDirs :: BuildInfo -> [SymbolicPath Pkg ('Dir Lib)]
extraLibDirsStatic :: BuildInfo -> [SymbolicPath Pkg ('Dir Lib)]
extraLibFlavours :: BuildInfo -> [FilePath]
extraLibs :: BuildInfo -> [FilePath]
extraLibsStatic :: BuildInfo -> [FilePath]
frameworks :: BuildInfo -> [RelativePath Framework 'File]
hsSourceDirs :: BuildInfo -> [SymbolicPath Pkg ('Dir Source)]
hsc2hsOptions :: BuildInfo -> [FilePath]
includeDirs :: BuildInfo -> [SymbolicPath Pkg ('Dir Include)]
includes :: BuildInfo -> [SymbolicPath Include 'File]
installIncludes :: BuildInfo -> [RelativePath Include 'File]
jsSources :: BuildInfo -> [SymbolicPath Pkg 'File]
ldOptions :: BuildInfo -> [FilePath]
mixins :: BuildInfo -> [Mixin]
oldExtensions :: BuildInfo -> [Extension]
options :: BuildInfo -> PerCompilerFlavor [FilePath]
otherExtensions :: BuildInfo -> [Extension]
otherLanguages :: BuildInfo -> [Language]
otherModules :: BuildInfo -> [ModuleName]
pkgconfigDepends :: BuildInfo -> [PkgconfigDependency]
profOptions :: BuildInfo -> PerCompilerFlavor [FilePath]
profSharedOptions :: BuildInfo -> PerCompilerFlavor [FilePath]
sharedOptions :: BuildInfo -> PerCompilerFlavor [FilePath]
staticOptions :: BuildInfo -> PerCompilerFlavor [FilePath]
targetBuildDepends :: BuildInfo -> [Dependency]
virtualModules :: BuildInfo -> [ModuleName]
..} = (,([DynOption]
exts, [PackageName]
deps)) ([FilePath] -> ([FilePath], ([DynOption], [PackageName])))
-> [FilePath] -> ([FilePath], ([DynOption], [PackageName]))
forall a b. (a -> b) -> a -> b
$ do
      m <- [FilePath]
extraModules [FilePath] -> [FilePath] -> [FilePath]
forall a. [a] -> [a] -> [a]
++ (ModuleName -> FilePath
ModuleName.toFilePath (ModuleName -> FilePath) -> [ModuleName] -> [FilePath]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [ModuleName]
otherModules)
      normalise . (takeDirectory cabalFile </>) <$> prependSrcDirs (dropExtensions m)
      where
        prependSrcDirs :: FilePath -> [FilePath]
prependSrcDirs FilePath
f
          | [SymbolicPath Pkg ('Dir Source)] -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [SymbolicPath Pkg ('Dir Source)]
hsSourceDirs = [FilePath
f]
          | Bool
otherwise = (FilePath -> ShowS
</> FilePath
f) ShowS
-> (SymbolicPath Pkg ('Dir Source) -> FilePath)
-> SymbolicPath Pkg ('Dir Source)
-> FilePath
forall b c a. (b -> c) -> (a -> b) -> a -> c
. SymbolicPath Pkg ('Dir Source) -> FilePath
forall (allowAbsolute :: AllowAbsolute) from (to :: FileOrDir).
SymbolicPathX allowAbsolute from to -> FilePath
getSymbolicPath (SymbolicPath Pkg ('Dir Source) -> FilePath)
-> [SymbolicPath Pkg ('Dir Source)] -> [FilePath]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [SymbolicPath Pkg ('Dir Source)]
hsSourceDirs
        deps :: [PackageName]
deps = Dependency -> PackageName
depPkgName (Dependency -> PackageName) -> [Dependency] -> [PackageName]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [Dependency]
targetBuildDepends
        exts :: [DynOption]
exts = [DynOption]
-> (Language -> [DynOption]) -> Maybe Language -> [DynOption]
forall b a. b -> (a -> b) -> Maybe a -> b
maybe [] Language -> [DynOption]
langExt Maybe Language
defaultLanguage [DynOption] -> [DynOption] -> [DynOption]
forall a. [a] -> [a] -> [a]
++ (Extension -> DynOption) -> [Extension] -> [DynOption]
forall a b. (a -> b) -> [a] -> [b]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap Extension -> DynOption
extToDynOption [Extension]
defaultExtensions
        langExt :: Language -> [DynOption]
langExt =
          DynOption -> [DynOption]
forall a. a -> [a]
forall (f :: * -> *) a. Applicative f => a -> f a
pure (DynOption -> [DynOption])
-> (Language -> DynOption) -> Language -> [DynOption]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. FilePath -> DynOption
DynOption (FilePath -> DynOption)
-> (Language -> FilePath) -> Language -> DynOption
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (FilePath
"-X" FilePath -> ShowS
forall a. Semigroup a => a -> a -> a
<>) ShowS -> (Language -> FilePath) -> Language -> FilePath
forall b c a. (b -> c) -> (a -> b) -> a -> c
. \case
            UnknownLanguage FilePath
lan -> FilePath
lan
            Language
lan -> Language -> FilePath
forall a. Show a => a -> FilePath
show Language
lan
        extToDynOption :: Extension -> DynOption
extToDynOption =
          FilePath -> DynOption
DynOption (FilePath -> DynOption)
-> (Extension -> FilePath) -> Extension -> DynOption
forall b c a. (b -> c) -> (a -> b) -> a -> c
. \case
            EnableExtension KnownExtension
e -> FilePath
"-X" FilePath -> ShowS
forall a. [a] -> [a] -> [a]
++ KnownExtension -> FilePath
forall a. Show a => a -> FilePath
show KnownExtension
e
            DisableExtension KnownExtension
e -> FilePath
"-XNo" FilePath -> ShowS
forall a. [a] -> [a] -> [a]
++ KnownExtension -> FilePath
forall a. Show a => a -> FilePath
show KnownExtension
e
            UnknownExtension FilePath
e -> FilePath
"-X" FilePath -> ShowS
forall a. [a] -> [a] -> [a]
++ FilePath
e

    extractFromLibrary :: Library -> ([FilePath], ([DynOption], [PackageName]))
extractFromLibrary Library {Bool
[ModuleName]
[ModuleReexport]
BuildInfo
LibraryName
LibraryVisibility
libName :: LibraryName
exposedModules :: [ModuleName]
reexportedModules :: [ModuleReexport]
signatures :: [ModuleName]
libExposed :: Bool
libVisibility :: LibraryVisibility
libBuildInfo :: BuildInfo
exposedModules :: Library -> [ModuleName]
libBuildInfo :: Library -> BuildInfo
libExposed :: Library -> Bool
libName :: Library -> LibraryName
libVisibility :: Library -> LibraryVisibility
reexportedModules :: Library -> [ModuleReexport]
signatures :: Library -> [ModuleName]
..} =
      [FilePath]
-> BuildInfo -> ([FilePath], ([DynOption], [PackageName]))
extractFromBuildInfo (ModuleName -> FilePath
ModuleName.toFilePath (ModuleName -> FilePath) -> [ModuleName] -> [FilePath]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [ModuleName]
exposedModules) BuildInfo
libBuildInfo
    extractFromExecutable :: Executable -> ([FilePath], ([DynOption], [PackageName]))
extractFromExecutable Executable {BuildInfo
ExecutableScope
UnqualComponentName
RelativePath Source 'File
exeName :: UnqualComponentName
modulePath :: RelativePath Source 'File
exeScope :: ExecutableScope
buildInfo :: BuildInfo
buildInfo :: Executable -> BuildInfo
exeName :: Executable -> UnqualComponentName
exeScope :: Executable -> ExecutableScope
modulePath :: Executable -> RelativePath Source 'File
..} =
      [FilePath]
-> BuildInfo -> ([FilePath], ([DynOption], [PackageName]))
extractFromBuildInfo [RelativePath Source 'File -> FilePath
forall (allowAbsolute :: AllowAbsolute) from (to :: FileOrDir).
SymbolicPathX allowAbsolute from to -> FilePath
getSymbolicPath RelativePath Source 'File
modulePath] BuildInfo
buildInfo
    extractFromTestSuite :: TestSuite -> ([FilePath], ([DynOption], [PackageName]))
extractFromTestSuite TestSuite {[FilePath]
BuildInfo
TestSuiteInterface
UnqualComponentName
testName :: UnqualComponentName
testInterface :: TestSuiteInterface
testBuildInfo :: BuildInfo
testCodeGenerators :: [FilePath]
testBuildInfo :: TestSuite -> BuildInfo
testCodeGenerators :: TestSuite -> [FilePath]
testInterface :: TestSuite -> TestSuiteInterface
testName :: TestSuite -> UnqualComponentName
..} =
      [FilePath]
-> BuildInfo -> ([FilePath], ([DynOption], [PackageName]))
extractFromBuildInfo [FilePath]
mainPath BuildInfo
testBuildInfo
      where
        mainPath :: [FilePath]
mainPath = case TestSuiteInterface
testInterface of
          TestSuiteExeV10 Version
_ RelativePath Source 'File
p -> [RelativePath Source 'File -> FilePath
forall (allowAbsolute :: AllowAbsolute) from (to :: FileOrDir).
SymbolicPathX allowAbsolute from to -> FilePath
getSymbolicPath RelativePath Source 'File
p]
          TestSuiteLibV09 Version
_ ModuleName
p -> [ModuleName -> FilePath
ModuleName.toFilePath ModuleName
p]
          TestSuiteUnsupported {} -> []
    extractFromBenchmark :: Benchmark -> ([FilePath], ([DynOption], [PackageName]))
extractFromBenchmark Benchmark {BenchmarkInterface
BuildInfo
UnqualComponentName
benchmarkName :: UnqualComponentName
benchmarkInterface :: BenchmarkInterface
benchmarkBuildInfo :: BuildInfo
benchmarkBuildInfo :: Benchmark -> BuildInfo
benchmarkInterface :: Benchmark -> BenchmarkInterface
benchmarkName :: Benchmark -> UnqualComponentName
..} =
      [FilePath]
-> BuildInfo -> ([FilePath], ([DynOption], [PackageName]))
extractFromBuildInfo [FilePath]
mainPath BuildInfo
benchmarkBuildInfo
      where
        mainPath :: [FilePath]
mainPath = case BenchmarkInterface
benchmarkInterface of
          BenchmarkExeV10 Version
_ RelativePath Source 'File
p -> [RelativePath Source 'File -> FilePath
forall (allowAbsolute :: AllowAbsolute) from (to :: FileOrDir).
SymbolicPathX allowAbsolute from to -> FilePath
getSymbolicPath RelativePath Source 'File
p]
          BenchmarkUnsupported {} -> []