@@ -41,8 +41,8 @@ import qualified Distribution.Utils.Path as Cabal
4141import GHC.Records ( HasField )
4242import qualified HiFileParser as Iface
4343import Path
44- ( (</>) , filename , isProperPrefixOf , parent , parseRelDir
45- , stripProperPrefix
44+ ( (</>) , fileExtension , filename , isProperPrefixOf , parent
45+ , parseRelDir , stripProperPrefix
4646 )
4747import Path.Extra
4848 ( forgivingResolveDir , forgivingResolveFile
@@ -84,7 +84,7 @@ stackBenchmarkFiles ::
8484 StackBenchmark
8585 -> RIO GetPackageFileContext (NamedComponent , ComponentFile )
8686stackBenchmarkFiles bench =
87- resolveComponentFiles (CBench bench. name) build names
87+ resolveComponentFiles (CBench bench. name) build names []
8888 where
8989 names :: [DotCabalDescriptor ]
9090 names = bnames <> exposed
@@ -106,7 +106,7 @@ stackTestSuiteFiles ::
106106 StackTestSuite
107107 -> RIO GetPackageFileContext (NamedComponent , ComponentFile )
108108stackTestSuiteFiles test =
109- resolveComponentFiles (CTest test. name) build names
109+ resolveComponentFiles (CTest test. name) build names []
110110 where
111111 names :: [DotCabalDescriptor ]
112112 names = bnames <> exposed
@@ -129,7 +129,7 @@ stackExecutableFiles ::
129129 StackExecutable
130130 -> RIO GetPackageFileContext (NamedComponent , ComponentFile )
131131stackExecutableFiles exe =
132- resolveComponentFiles (CExe exe. name) build names
132+ resolveComponentFiles (CExe exe. name) build names []
133133 where
134134 build :: StackBuildInfo
135135 build = exe. buildInfo
@@ -144,7 +144,7 @@ stackLibraryFiles ::
144144 StackLibrary
145145 -> RIO GetPackageFileContext (NamedComponent , ComponentFile )
146146stackLibraryFiles lib =
147- resolveComponentFiles componentName build names
147+ resolveComponentFiles componentName build names lib . signatures
148148 where
149149 componentRawName :: StackUnqualCompName
150150 componentRawName = lib. name
@@ -174,18 +174,22 @@ resolveComponentFiles ::
174174 => NamedComponent
175175 -> rec
176176 -> [DotCabalDescriptor ]
177+ -> [ModuleName ]
177178 -> RIO GetPackageFileContext (NamedComponent , ComponentFile )
178- resolveComponentFiles component build names = do
179+ resolveComponentFiles component build names signatureNames = do
179180 dirs <- mapMaybeM (resolveDirOrWarn . getSymbolicPath) build. hsSourceDirs
180181 dir <- asks (parent . (. file))
181182 agdirs <- autogenDirs
183+ let allDirs = (if null dirs then [dir] else dirs) ++ agdirs
182184 (modules,files,warnings) <-
183185 resolveFilesAndDeps
184186 component
185- (( if null dirs then [dir] else dirs) ++ agdirs)
187+ allDirs
186188 names
189+ (S. fromList signatureNames)
190+ sigFiles <- resolveSignatureFiles allDirs signatureNames
187191 cfiles <- buildOtherSources build
188- pure (component, ComponentFile modules (files <> cfiles) warnings)
192+ pure (component, ComponentFile modules (files <> sigFiles <> cfiles) warnings)
189193 where
190194 autogenDirs :: RIO GetPackageFileContext [Path Abs Dir ]
191195 autogenDirs = do
@@ -201,11 +205,13 @@ resolveFilesAndDeps ::
201205 NamedComponent -- ^ Package component name
202206 -> [Path Abs Dir ] -- ^ Directories to look in.
203207 -> [DotCabalDescriptor ] -- ^ Base names.
208+ -> Set ModuleName -- ^ Backpack signatures already accounted for.
204209 -> RIO
205210 GetPackageFileContext
206211 (Map ModuleName (Path Abs File ), [DotCabalPath ], [PackageWarning ])
207- resolveFilesAndDeps component dirs names0 = do
208- (dotCabalPaths, foundModules, missingModules, _) <- loop names0 S. empty M. empty
212+ resolveFilesAndDeps component dirs names0 signatureModules = do
213+ (dotCabalPaths, foundModules, missingModules, _) <-
214+ loop names0 signatureModules M. empty
209215 warnings <-
210216 liftM2 (++) (warnUnlisted foundModules) (warnMissing missingModules)
211217 pure (foundModules, dotCabalPaths, warnings)
@@ -224,7 +230,7 @@ resolveFilesAndDeps component dirs names0 = do
224230 )
225231 loop [] _ _ = pure ([] , M. empty, [] , M. empty)
226232 loop names doneModules0 knownUsages = do
227- resolved <- resolveFiles dirs names
233+ resolved <- resolveFiles dirs signatureModules names
228234 let foundFiles = mapMaybe snd resolved
229235 foundModules = mapMaybe toResolvedModule resolved
230236 missingModules = mapMaybe toMissingModule resolved
@@ -289,6 +295,22 @@ resolveFilesAndDeps component dirs names0 = do
289295 toMissingModule _ =
290296 Nothing
291297
298+ -- | Resolve Backpack signature files declared by a library component. Signature
299+ -- files are tracked for rebuilds, but they are not ordinary implementation
300+ -- modules and should not feed unlisted-module warnings.
301+ resolveSignatureFiles ::
302+ [Path Abs Dir ]
303+ -> [ModuleName ]
304+ -> RIO GetPackageFileContext [DotCabalPath ]
305+ resolveSignatureFiles dirs =
306+ fmap concat . mapM resolveSignatureFile
307+ where
308+ resolveSignatureFile mn = do
309+ let relFile = Cabal. toFilePath mn ++ " .hsig"
310+ matches <- fmap (nubOrd . catMaybes) $
311+ mapM (\ dir -> resolveDirFile dir relFile) dirs
312+ pure $ map DotCabalFilePath matches
313+
292314-- | Get the dependencies of a Haskell module file.
293315getDependencies ::
294316 Map FilePath (Path Abs File )
@@ -385,28 +407,33 @@ componentOutputDir namedComponent distDir =
385407-- extensions.
386408resolveFiles ::
387409 [Path Abs Dir ] -- ^ Directories to look in.
410+ -> Set ModuleName -- ^ Backpack signatures declared by the component.
388411 -> [DotCabalDescriptor ] -- ^ Base names.
389412 -> RIO GetPackageFileContext [(DotCabalDescriptor , Maybe DotCabalPath )]
390- resolveFiles dirs names =
391- forM names (\ name -> fmap (name, ) (findCandidate dirs name))
413+ resolveFiles dirs signatureModules names =
414+ forM names (\ name -> fmap (name, ) (findCandidate dirs signatureModules name))
392415
393416-- | Find a candidate for the given module-or-filename from the list
394417-- of directories and given extensions.
395418findCandidate ::
396419 [Path Abs Dir ]
420+ -> Set ModuleName
397421 -> DotCabalDescriptor
398422 -> RIO GetPackageFileContext (Maybe DotCabalPath )
399- findCandidate dirs name = do
423+ findCandidate dirs signatureModules name = do
400424 pkg <- asks (. file) >>= parsePackageNameFromFilePath
401425 customPreprocessorExts <- view $ configL . to (. customPreprocessorExts)
402426 let haskellPreprocessorExts =
403- haskellDefaultPreprocessorExts ++ customPreprocessorExts
427+ filter
428+ (not . isBackpackSignatureExt)
429+ (haskellDefaultPreprocessorExts ++ customPreprocessorExts)
404430 liftIO (makeNameCandidates haskellPreprocessorExts) >>= \ case
405431 [candidate] -> pure (Just (cons candidate))
406432 [] -> do
407433 case name of
408434 DotCabalModule mn
409- | display mn /= paths_pkg pkg -> logPossibilities dirs mn
435+ | display mn /= paths_pkg pkg ->
436+ logPossibilities dirs signatureModules mn
410437 _ -> pure ()
411438 pure Nothing
412439 (candidate: rest) -> do
@@ -451,22 +478,39 @@ findCandidate dirs name = do
451478 (xs, ys) -> xs ++ ys
452479 resolveCandidate dir = fmap maybeToList . resolveDirFile dir
453480
454- -- | Log that we couldn't find a candidate, but there are
455- -- possibilities for custom preprocessor extensions.
481+ isBackpackSignatureExt :: Text -> Bool
482+ isBackpackSignatureExt ext =
483+ T. toLower (fromMaybe ext $ T. stripPrefix " ." ext) == " hsig"
484+
485+ isBackpackSignatureFile :: Path b File -> Bool
486+ isBackpackSignatureFile file =
487+ maybe False (isBackpackSignatureExt . T. pack) $ fileExtension file
488+
489+ -- | Log that we couldn't find a candidate, but there are possibilities for
490+ -- custom preprocessor extensions or an undeclared Backpack signature.
456491--
457492-- For example: .erb for a Ruby file might exist in one of the
458493-- directories.
459- logPossibilities :: HasTerm env => [Path Abs Dir ] -> ModuleName -> RIO env ()
460- logPossibilities dirs mn = do
494+ logPossibilities ::
495+ HasTerm env
496+ => [Path Abs Dir ]
497+ -> Set ModuleName
498+ -> ModuleName
499+ -> RIO env ()
500+ logPossibilities dirs signatureModules mn = do
461501 possibilities <- concat <$> makePossibilities
462- unless (null possibilities) $ prettyWarn $
502+ let nonSignaturePossibilities =
503+ filter (not . isBackpackSignatureFile) possibilities
504+ signaturePossibilities =
505+ filter isBackpackSignatureFile possibilities
506+ unless (null nonSignaturePossibilities) $ prettyWarn $
463507 fillSep
464508 [ flow " Unable to find a known candidate for the Cabal entry"
465509 , (style Module . fromString $ display mn) <> " ,"
466510 , flow " but did find:"
467511 ]
468512 <> line
469- <> bulletedList (map pretty possibilities )
513+ <> bulletedList (map pretty nonSignaturePossibilities )
470514 <> blankLine
471515 <> fillSep
472516 [ flow " If you are using a custom preprocessor for this module with \
@@ -476,6 +520,26 @@ logPossibilities dirs mn = do
476520 , flow " key in Stack's project-level configuration file"
477521 , " (" <> style File " stack.yaml" <> " )."
478522 ]
523+ when
524+ (mn `S.notMember` signatureModules && not (null signaturePossibilities))
525+ $ prettyWarn
526+ $ fillSep
527+ [ flow " Found Backpack signature file for Cabal entry"
528+ , (style Module . fromString $ display mn) <> " ,"
529+ , flow " but that module is not listed in the component's"
530+ , style Shell " signatures"
531+ , flow " field:"
532+ ]
533+ <> line
534+ <> bulletedList (map pretty signaturePossibilities)
535+ <> blankLine
536+ <> fillSep
537+ [ flow " If this file is meant to be a Backpack signature, add"
538+ , style Module (fromString $ display mn)
539+ , flow " to the"
540+ , style Shell " signatures"
541+ , flow " field in the package description."
542+ ]
479543 where
480544 makePossibilities = mapM makePossibility dirs
481545
0 commit comments