forked from commercialhaskell/stack
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathPackageFile.hs
More file actions
194 lines (187 loc) · 7.03 KB
/
PackageFile.hs
File metadata and controls
194 lines (187 loc) · 7.03 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE OverloadedStrings #-}
-- | A module which exports all package-level file-gathering logic.
module Stack.PackageFile
( getPackageFile
, packageDescModulesAndFiles
) where
import qualified Data.Map.Strict as M
import qualified Data.Set as S
import qualified Data.Text as T
import Distribution.CabalSpecVersion ( CabalSpecVersion )
import Distribution.ModuleName ( ModuleName )
import Distribution.PackageDescription hiding ( FlagName )
import Distribution.Simple.Glob ( matchDirFileGlob )
import qualified Distribution.Types.UnqualComponentName as Cabal
import Path ( parent, (</>) )
import Path.Extra ( forgivingResolveFile, rejectMissingFile )
import Path.IO ( doesFileExist )
import Stack.ComponentFile
( benchmarkFiles, executableFiles, libraryFiles
, resolveOrWarn, testFiles
)
import Stack.Constants
( relFileHpackPackageConfig, relFileSetupHs, relFileSetupLhs
)
import Stack.Constants.Config ( distDirFromDir )
import Stack.Prelude
import Stack.Types.BuildConfig ( HasBuildConfig (..) )
import Stack.Types.CompilerPaths ( cabalVersionL )
import Stack.Types.EnvConfig ( HasEnvConfig )
import Stack.Types.NamedComponent ( NamedComponent (..) )
import Stack.Types.PackageFile
( DotCabalPath (..), GetPackageFileContext (..)
, PackageWarning (..)
)
import qualified System.FilePath as FilePath
import System.IO.Error ( isUserError )
-- | Resolve the file, if it can't be resolved, warn for the user
-- (purely to be helpful).
resolveFileOrWarn :: FilePath.FilePath
-> RIO GetPackageFileContext (Maybe (Path Abs File))
resolveFileOrWarn = resolveOrWarn "File" f
where
f p x = forgivingResolveFile p x >>= rejectMissingFile
-- | Get all files referenced by the package.
packageDescModulesAndFiles ::
PackageDescription
-> RIO
GetPackageFileContext
( Map NamedComponent (Map ModuleName (Path Abs File))
, Map NamedComponent [DotCabalPath]
, Set (Path Abs File)
, [PackageWarning]
)
packageDescModulesAndFiles pkg = do
(libraryMods, libDotCabalFiles, libWarnings) <-
maybe
(pure (M.empty, M.empty, []))
(asModuleAndFileMap libComponent libraryFiles)
(library pkg)
(subLibrariesMods, subLibDotCabalFiles, subLibWarnings) <-
fmap
foldTuples
( mapM
(asModuleAndFileMap internalLibComponent libraryFiles)
(subLibraries pkg)
)
(executableMods, exeDotCabalFiles, exeWarnings) <-
fmap
foldTuples
( mapM
(asModuleAndFileMap exeComponent executableFiles)
(executables pkg)
)
(testMods, testDotCabalFiles, testWarnings) <-
fmap
foldTuples
(mapM (asModuleAndFileMap testComponent testFiles) (testSuites pkg))
(benchModules, benchDotCabalPaths, benchWarnings) <-
fmap
foldTuples
( mapM
(asModuleAndFileMap benchComponent benchmarkFiles)
(benchmarks pkg)
)
dfiles <- resolveGlobFiles
(specVersion pkg)
( extraSrcFiles pkg
++ map (dataDir pkg FilePath.</>) (dataFiles pkg)
)
let modules = libraryMods <> subLibrariesMods <> executableMods <> testMods <>
benchModules
files = libDotCabalFiles <> subLibDotCabalFiles <> exeDotCabalFiles <>
testDotCabalFiles <> benchDotCabalPaths
warnings = libWarnings <> subLibWarnings <> exeWarnings <> testWarnings <>
benchWarnings
pure (modules, files, dfiles, warnings)
where
libComponent = const CLib
internalLibComponent =
CInternalLib . T.pack . maybe
"" Cabal.unUnqualComponentName . libraryNameString . libName
exeComponent = CExe . T.pack . Cabal.unUnqualComponentName . exeName
testComponent = CTest . T.pack . Cabal.unUnqualComponentName . testName
benchComponent = CBench . T.pack . Cabal.unUnqualComponentName . benchmarkName
asModuleAndFileMap label f lib = do
(a, b, c) <- f (label lib) lib
pure (M.singleton (label lib) a, M.singleton (label lib) b, c)
foldTuples = foldl' (<>) (M.empty, M.empty, [])
-- | Resolve globbing of files (e.g. data files) to absolute paths.
resolveGlobFiles ::
CabalSpecVersion -- ^ Cabal file version
-> [String]
-> RIO GetPackageFileContext (Set (Path Abs File))
resolveGlobFiles cabalFileVersion =
fmap (S.fromList . catMaybes . concat) .
mapM resolve
where
resolve name =
if '*' `elem` name
then explode name
else fmap pure (resolveFileOrWarn name)
explode name = do
dir <- asks (parent . ctxFile)
names <- matchDirFileGlob' (toFilePath dir) name
mapM resolveFileOrWarn names
matchDirFileGlob' dir glob =
catch
(liftIO (matchDirFileGlob minBound cabalFileVersion dir glob))
( \(e :: IOException) ->
if isUserError e
then do
prettyWarnL
[ flow "Wildcard does not match any files:"
, style File $ fromString glob
, line <> flow "in directory:"
, style Dir $ fromString dir
]
pure []
else throwIO e
)
-- | Gets all of the modules, files, build files, and data files that constitute
-- the package. This is primarily used for dirtiness checking during build, as
-- well as use by "stack ghci"
getPackageFile ::
( HasEnvConfig s, MonadReader s m, MonadThrow m, MonadUnliftIO m )
=> PackageDescription
-> Path Abs File
-> m ( Map NamedComponent (Map ModuleName (Path Abs File))
, Map NamedComponent [DotCabalPath]
, Set (Path Abs File)
, [PackageWarning]
)
getPackageFile pkg cabalfp =
debugBracket ("getPackageFiles" <+> pretty cabalfp) $ do
let pkgDir = parent cabalfp
distDir <- distDirFromDir pkgDir
bc <- view buildConfigL
cabalVer <- view cabalVersionL
(componentModules, componentFiles, dataFiles', warnings) <-
runRIO
(GetPackageFileContext cabalfp distDir bc cabalVer)
(packageDescModulesAndFiles pkg)
setupFiles <-
if buildType pkg == Custom
then do
let setupHsPath = pkgDir </> relFileSetupHs
setupLhsPath = pkgDir </> relFileSetupLhs
setupHsExists <- doesFileExist setupHsPath
if setupHsExists
then pure (S.singleton setupHsPath)
else do
setupLhsExists <- doesFileExist setupLhsPath
if setupLhsExists
then pure (S.singleton setupLhsPath)
else pure S.empty
else pure S.empty
buildFiles <- fmap (S.insert cabalfp . S.union setupFiles) $ do
let hpackPath = pkgDir </> relFileHpackPackageConfig
hpackExists <- doesFileExist hpackPath
pure $ if hpackExists then S.singleton hpackPath else S.empty
pure
( componentModules
, componentFiles
, buildFiles <> dataFiles'
, warnings
)