@@ -20,12 +20,12 @@ import Data.Default (def)
2020import Data.Either (partitionEithers)
2121import Data.Foldable (fold, for_, toList)
2222import Data.Functor ((<&>))
23−import Data.Functor.Identity (runIdentity)
2423import Data.List.NonEmpty (NonEmpty)
2524import qualified Data.List.NonEmpty as NE
2625import Data.Map (Map)
2726import qualified Data.Map as Map
2827import Data.Maybe
28+import Data.Semigroup (Last (..))
2929import Data.Set (Set)
3030import qualified Data.Set as Set
3131import Data.String.Here.Interpolated (i)
@@ -73,6 +73,9 @@ import Obelisk.Command.Project (findProjectAssets, nixShellWithoutPkgs, obeliskD
7373import Obelisk.Command.Thunk (attrCacheFileName)
7474import Obelisk.Command.Utils (ghcidExePath)
7575
76+hpackFileName :: Path Relative
77+hpackFileName = rel Hpack.packageConfig
78+
7679data CabalPackageInfo = CabalPackageInfo
7780 { _cabalPackageInfo_packageFile :: FilePath
7881 , _cabalPackageInfo_packageName :: T.Text
@@ -121,10 +124,10 @@ profile profileBasePattern rtsFlags = withProjectRoot "." $ \root -> do
121124 putLog Debug $ "Assets impurely loaded from: " <> assets
122125 time <- liftIO getCurrentTime
123126 let profileBaseName = formatTime defaultTimeLocale profileBasePattern time
124− createDirectoryIfMissing True $ takeDirectory $ Path $ root </> profileBaseName
127+ createDirectoryIfMissing True $ takeDirectory $ mkPath root </> rel profileBaseName
125128 putLog Debug [i|Storing profiled data under base name of ${root </> profileBaseName}|]
126129 freePort <- getFreePort
127− (_, _, _, ph) <- createProcess_ "runProfExe" $ setCwd (Just root) $ setDelegateCtlc True $ proc (outPath </> rel "bin" </> rel "ob-run") $
130+ (_, _, _, ph) <- createProcess_ "runProfExe" $ setCwd (Just root) $ setDelegateCtlc True $ proc (toFilePath $ outPath </> rel "bin" </> rel "ob-run") $
128131 [ show freePort
129132 , T.unpack assets
130133 , profileBaseName
@@ -134,7 +137,7 @@ profile profileBasePattern rtsFlags = withProjectRoot "." $ \root -> do
134137 <> [ "-RTS" ]
135138 void $ waitForProcess ph
136139
137−run :: MonadObelisk m => FilePath -> PathTree Interpret -> m ()
140+run :: MonadObelisk m => FilePath -> Map (Path Canonical) Interpret -> m ()
138141run root interpretPaths = do
139142 pkgs <- getParsedLocalPkgs root interpretPaths
140143 withGhciScript pkgs $ \dotGhciPath -> do
@@ -150,32 +153,32 @@ run root interpretPaths = do
150153 , "Frontend.frontend"
151154 ]
152155
153−runRepl :: MonadObelisk m => FilePath -> PathTree Interpret -> m ()
156+runRepl :: MonadObelisk m => FilePath -> Map (Path Canonical) Interpret -> m ()
154157runRepl root interpretPaths = do
155158 pkgs <- getParsedLocalPkgs root interpretPaths
156− ghciArgs <- getGhciSessionSettings pkgs (rel ".") True
159+ ghciArgs <- getGhciSessionSettings pkgs (mkPath ".") True
157160 withGhciScript pkgs $ \dotGhciPath ->
158161 runGhciRepl root pkgs (ghciArgs <> mkGhciScriptArg dotGhciPath)
159162
160−runWatch :: MonadObelisk m => FilePath -> PathTree Interpret -> m ()
163+runWatch :: MonadObelisk m => FilePath -> Map (Path Canonical) Interpret -> m ()
161164runWatch root interpretPaths = do
162165 pkgs <- getParsedLocalPkgs root interpretPaths
163166 ghciArgs <- getGhciSessionSettings pkgs root True
164167 withGhciScript pkgs $ \dotGhciPath ->
165168 runGhcid root True (ghciArgs <> mkGhciScriptArg dotGhciPath) pkgs Nothing
166169
167−exportGhciConfig :: MonadObelisk m => Bool -> FilePath -> PathTree Interpret -> m [String]
170+exportGhciConfig :: MonadObelisk m => Bool -> FilePath -> Map (Path Canonical) Interpret -> m [String]
168171exportGhciConfig useRelativePaths root interpretPaths = do
169172 pkgs <- getParsedLocalPkgs root interpretPaths
170173 getGhciSessionSettings pkgs (rel ".") useRelativePaths
171174
172−nixShellForInterpretPaths :: MonadObelisk m => Bool -> String -> FilePath -> PathTree Interpret -> Maybe String -> m ()
175+nixShellForInterpretPaths :: MonadObelisk m => Bool -> String -> FilePath -> Map (Path Canonical) Interpret -> Maybe String -> m ()
173176nixShellForInterpretPaths isPure shell root interpretPaths cmd = do
174177 pkgs <- getParsedLocalPkgs root interpretPaths
175178 nixShellWithoutPkgs root isPure False (packageInfoToNamePathMap pkgs) shell cmd
176179
177180-- | Like 'getLocalPkgs' but also parses them and fails if any of them can't be parsed.
178−getParsedLocalPkgs :: MonadObelisk m => FilePath -> PathTree Interpret -> m (NonEmpty CabalPackageInfo)
181+getParsedLocalPkgs :: MonadObelisk m => FilePath -> Map (Path Canonical) Interpret -> m (NonEmpty CabalPackageInfo)
179182getParsedLocalPkgs root interpretPaths = parsePackagesOrFail =<< getLocalPkgs root interpretPaths
180183
181184-- | Relative paths to local packages of an obelisk project.
@@ -183,15 +186,16 @@ getParsedLocalPkgs root interpretPaths = parsePackagesOrFail =<< getLocalPkgs ro
183186-- These are a combination of the obelisk predefined local packages,
184187-- and any packages that the user has set with the @packages@ argument
185188-- to the Nix @project@ function.
186−getLocalPkgs :: forall m. MonadObelisk m => FilePath -> PathTree Interpret -> m (Set (Path Canonical))
189+getLocalPkgs :: forall m. MonadObelisk m => FilePath -> Map (Path Canonical) Interpret -> m (Set (Path Canonical))
187190getLocalPkgs root interpretPaths = do
188− putLog Debug $ [i|Finding packages with root ${root} and interpret paths:|] <> "\n" <> drawPathTree textInterpret interpretPaths
191+ let pathTree :: PathTree Interpret = coerce $ foldMap (\(p, x) -> pathToTree (Last x) p) $ Map.toList interpretPaths
192+ putLog Debug $ [i|Finding packages with root ${root} and interpret paths:|] <> "\n" <> drawPathTree textInterpret pathTree
189193
190194 -- We do not want to find packages that are embedded inside other obelisk projects, unless that
191195 -- obelisk project is our own.
192196 canonicalRoot <- canonicalizePath root
193197 obeliskPackageExclusions <- findFiles'
194− (\_ p -> if takeFileName p == Path obeliskDirName && p /= canonicalRoot
198+ (\_ p -> if takeFileName p == rel obeliskDirName && p /= canonicalRoot
195199 then liftIO (doesDirectoryExist p) <&> \case
196200 True -> FindMatch_Match $ Set.singleton p
197201 False -> FindMatch_SearchTree
@@ -199,18 +203,16 @@ getLocalPkgs root interpretPaths = do
199203 )
200204 root
201205
202− putLog Debug [i|Excluding obelisk packages: ${T.pack $ unwords $ map show $ Set.toList obeliskPackageExclusions}|]
203−
204206 let
205− findRoots = runIdentity $ foldPathTreeFor (== Interpret_Interpret) "" interpretPaths (\p _ -> pure $ Set.singleton $ Path p)
206− exclusions = runIdentity $ foldPathTreeFor (== Interpret_NoInterpret) "" interpretPaths (\p _ -> pure $ Set.singleton $ Path p)
207+ findRoots = Map.keysSet $ Map.filter (== Interpret_Interpret) interpretPaths
208+ exclusions = Map.keysSet $ Map.filter (== Interpret_NoInterpret) interpretPaths
209+ <> Map.fromSet (const Interpret_NoInterpret) obeliskPackageExclusions -- Add these last because we want left side to override
207210
208211 fmap fold $ for (toList findRoots) $ \interpretPathRoot -> do
209− let allExclusions = obeliskPackageExclusions <> exclusions
210212 findFiles'
211213 (\_ p -> pure $ case () of
212− _ | p `Set.member` allExclusions || attrCacheFileName `elem` splitDirectories p -> FindMatch_SkipTree
213− | takeExtension p == ".cabal" || takeFileName p == rel Hpack.packageConfig -> FindMatch_Match $ Set.singleton p
214+ _ | p `Set.member` exclusions || attrCacheFileName `elem` splitDirectories p -> FindMatch_SkipTree
215+ | takeExtension p == ".cabal" || takeFileName p == hpackFileName -> FindMatch_Match $ Set.singleton p
214216 | otherwise -> FindMatch_SearchTree
215217 )
216218 interpretPathRoot
@@ -227,21 +229,21 @@ guessCabalPackageFile
227229 :: (MonadIO m, IsPath path)
228230 => path -- ^ Directory or path to search for cabal package
229231 -> m (Either GuessPackageFileError (Either CabalFilePath HPackFilePath))
230−guessCabalPackageFile (coerce -> pkg) = do
232+guessCabalPackageFile (toFilePath -> pkg) = do
231233 liftIO (doesDirectoryExist pkg) >>= \case
232234 False -> case cabalOrHpackFile pkg of
233235 (Just hpack@(Right _)) -> pure $ Right hpack
234236 (Just cabal@(Left (CabalFilePath cabalFilePath))) -> do
235237 -- If the cabal file has a sibling hpack file, we use that instead
236238 -- since running hpack often generates a sibling cabal file
237− let possibleHpackSibling = takeDirectory (Path cabalFilePath) </> Hpack.packageConfig
239+ let possibleHpackSibling = takeDirectory $ mkPath cabalFilePath </> hpackFileName
238240 hasHpackSibling <- liftIO $ doesFileExist possibleHpackSibling
239− pure $ Right $ if hasHpackSibling then Right (HPackFilePath possibleHpackSibling) else cabal
241+ pure $ Right $ if hasHpackSibling then Right (HPackFilePath $ toFilePath possibleHpackSibling) else cabal
240242 Nothing -> pure $ Left GuessPackageFileError_NotFound
241243 True -> do
242244 candidates <- liftIO $
243245 filterM (doesFileExist . either unCabalFilePath unHPackFilePath)
244− =<< mapMaybe (cabalOrHpackFile . (pkg </>)) <$> listDirectory pkg
246+ =<< mapMaybe (cabalOrHpackFile . toFilePath . (pkg </>)) <$> listDirectory pkg
245247 pure $ case partitionEithers candidates of
246248 ([hpack], _) -> Right $ Left hpack
247249 ([], [cabal]) -> Right $ Right cabal
@@ -251,7 +253,7 @@ guessCabalPackageFile (coerce -> pkg) = do
251253cabalOrHpackFile :: FilePath -> Maybe (Either CabalFilePath HPackFilePath)
252254cabalOrHpackFile = \case
253255 x | takeExtension x == ".cabal" -> Just (Left $ CabalFilePath x)
254− | takeFileName x == rel Hpack.packageConfig -> Just (Right $ HPackFilePath x)
256+ | takeFileName x == hpackFileName -> Just (Right $ HPackFilePath x)
255257 | otherwise -> Nothing
256258
257259-- | Parses the cabal package in a given directory.
@@ -271,10 +273,10 @@ parseCabalPackage'
271273 :: (MonadIO m, IsPath path)
272274 => path -- ^ Package directory
273275 -> m (Either T.Text ([Dist.PWarning], CabalPackageInfo))
274−parseCabalPackage' (coerce -> pkg) = runExceptT $ do
276+parseCabalPackage' (toFilePath -> pkg) = runExceptT $ do
275277 (cabalContents, packageFile, packageName) <- guessCabalPackageFile pkg >>= \case
276− Left GuessPackageFileError_NotFound -> throwError $ "No .cabal or package.yaml file found in " <> T.pack pkg
277− Left (GuessPackageFileError_Ambiguous _) -> throwError $ "Unable to determine which .cabal file to use in " <> T.pack pkg
278+ Left GuessPackageFileError_NotFound -> throwError [i|No .cabal or package.yaml file found in ${pkg}|]
279+ Left (GuessPackageFileError_Ambiguous _) -> throwError [i|Unable to determine which .cabal file to use in ${pkg}|]
278280 Right (Left (CabalFilePath file)) -> (, file, takeBaseName file) <$> liftIO (readUTF8File file)
279281 Right (Right (HPackFilePath file)) -> do
280282 let
@@ -301,7 +303,7 @@ parseCabalPackage' (coerce -> pkg) = runExceptT $ do
301303 pure $ (warnings,) $ CabalPackageInfo
302304 { _cabalPackageInfo_packageName = T.pack packageName
303305 , _cabalPackageInfo_packageFile = packageFile
304− , _cabalPackageInfo_packageRoot = unPath $ takeDirectory (Path packageFile)
306+ , _cabalPackageInfo_packageRoot = unPath $ takeDirectory (mkPath packageFile)
305307 , _cabalPackageInfo_buildable = buildable $ libBuildInfo lib
306308 , _cabalPackageInfo_sourceDirs =
307309 fromMaybe (pure ".") $ NE.nonEmpty $ hsSourceDirs $ libBuildInfo lib
@@ -340,11 +342,11 @@ parsePackagesOrFail dirs' = do
340342
341343 packageInfos <- case NE.nonEmpty $ toList unambiguous of
342344 Nothing -> failWith $ T.pack $
343− "No valid, buildable packages found" <> (if null dirs then "" else " in " <> intercalate ", " (coerce dirs))
345+ "No valid, buildable packages found" <> (if null dirs then "" else " in " <> intercalate ", " (map toFilePath dirs))
344346 Just xs -> pure xs
345347
346348 unless (null pkgDirErrs) $
347− putLog Warning $ T.pack $ "Failed to find buildable packages in " <> intercalate ", " (coerce pkgDirErrs)
349+ putLog Warning $ T.pack $ "Failed to find buildable packages in " <> intercalate ", " (map toFilePath pkgDirErrs)
348350
349351 pure packageInfos
350352 where
@@ -361,7 +363,7 @@ withGhciScript
361363 -> m ()
362364withGhciScript (toList -> packageInfos) f =
363365 withSystemTempDirectory "ob-ghci" $ \fp -> do
364− let dotGhciPath = fp </> rel ".ghci"
366+ let dotGhciPath = toFilePath $ fp </> rel ".ghci"
365367 liftIO $ writeFile dotGhciPath dotGhci
366368 f dotGhciPath
367369 where
@@ -400,11 +402,12 @@ getGhciSessionSettings (toList -> packageInfos) pathBase useRelativePaths = do
400402
401403 pure
402404 $ baseGhciOptions
403− <> ["-F", "-pgmF", unPath selfExe, "-optF", preprocessorIdentifier]
405+ <> ["-F", "-pgmF", toFilePath selfExe, "-optF", preprocessorIdentifier]
404406 <> concatMap (\p -> ["-optF", p]) pkgFiles
405407 <> [ "-i" <> intercalate ":" (concatMap toList pkgSrcPaths) ]
406408 where
407− relativeTo' = if useRelativePaths then relativeTo else const . unPath
409+ relativeTo' :: Path Canonical -> Path Canonical -> FilePath
410+ relativeTo' a b = if useRelativePaths then toFilePath $ relativeTo a b else toFilePath a
408411
409412baseGhciOptions :: [String]
410413baseGhciOptions =
@@ -449,11 +452,11 @@ runGhcid root chdirToRoot ghciArgs (toList -> packages) mcmd =
449452 testCmd = maybeToList (flip fmap mcmd $ \cmd -> "--test='" <> cmd <> "'") -- TODO: Shell escape
450453
451454 adjustRoot x = if chdirToRoot then makeRelative root x else x
452− reloadFiles = map adjustRoot [root </> rel "config"]
455+ reloadFiles = map adjustRoot [toFilePath $ root </> rel "config"]
453456 restartFiles = map (adjustRoot . _cabalPackageInfo_packageFile) packages
454457
455458mkGhciScriptArg :: IsPath path => path -> [String]
456−mkGhciScriptArg dotGhci = ["-ghci-script", coerce dotGhci]
459+mkGhciScriptArg dotGhci = ["-ghci-script", toFilePath dotGhci]
457460
458461getFreePort :: MonadIO m => m PortNumber
459462getFreePort = liftIO $ withSocketsDo $ do