Skip to content

Commit 027bddf

Browse files
committed
Introduce AbsolutePath to symbolic path abstraction
There are a few places where paths are known to be absolute. This enforces that in the type system by introducing a simple wrapper to `Distribution.Utils.Path`. ``` newtype AbsolutePath (to :: FileOrDir) = AbsolutePath (forall from . SymbolicPath from to) ``` The nice thing about this abstraction is when when a path is unwrapped, due to the universally quantified `from` type, the resulting `SymbolicPath` can be used with any API which expects a path to point `from` a specific directory.
1 parent 90fbf08 commit 027bddf

File tree

7 files changed

+40
-19
lines changed

7 files changed

+40
-19
lines changed

Cabal-syntax/src/Distribution/Utils/Path.hs

+29-5
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,12 @@ module Distribution.Utils.Path
3535
-- * Symbolic paths
3636
, RelativePath
3737
, SymbolicPath
38+
, AbsolutePath (..)
3839
, SymbolicPathX -- NB: constructor not exposed, to retain type safety.
3940

4041
-- ** Symbolic path API
4142
, getSymbolicPath
43+
, getAbsolutePath
4244
, sameDirectory
4345
, makeRelativePathEx
4446
, makeSymbolicPath
@@ -48,6 +50,7 @@ module Distribution.Utils.Path
4850
, relativeSymbolicPath
4951
, symbolicPathRelative_maybe
5052
, interpretSymbolicPath
53+
, interpretSymbolicPathAbsolute
5154

5255
-- ** General filepath API
5356
, (</>)
@@ -215,6 +218,11 @@ type RelativePath = SymbolicPathX 'OnlyRelative
215218
-- until we interpret them (using e.g. 'interpretSymbolicPath').
216219
type SymbolicPath = SymbolicPathX 'AllowAbsolute
217220

221+
newtype AbsolutePath (to :: FileOrDir) = AbsolutePath (forall from. SymbolicPath from to)
222+
223+
unsafeMakeAbsolutePath :: FilePath -> AbsolutePath to
224+
unsafeMakeAbsolutePath fp = AbsolutePath (makeSymbolicPath fp)
225+
218226
instance Binary (SymbolicPathX allowAbsolute from to)
219227
instance
220228
(Typeable allowAbsolute, Typeable from, Typeable to)
@@ -320,6 +328,12 @@ interpretSymbolicPath mbWorkDir (SymbolicPath p) =
320328
interpretSymbolicPathCWD :: SymbolicPathX allowAbsolute from to -> FilePath
321329
interpretSymbolicPathCWD (SymbolicPath p) = p
322330

331+
getAbsolutePath :: AbsolutePath to -> FilePath
332+
getAbsolutePath (AbsolutePath p) = getSymbolicPath p
333+
334+
interpretSymbolicPathAbsolute :: AbsolutePath (Dir Pkg) -> SymbolicPathX allowAbsolute Pkg to -> FilePath
335+
interpretSymbolicPathAbsolute (AbsolutePath p) sym = interpretSymbolicPath (Just p) sym
336+
323337
-- | Change what a symbolic path is pointing to.
324338
coerceSymbolicPath :: SymbolicPathX allowAbsolute from to1 -> SymbolicPathX allowAbsolute from to2
325339
coerceSymbolicPath = coerce
@@ -343,9 +357,9 @@ symbolicPathRelative_maybe (SymbolicPath fp) =
343357
else Just $ SymbolicPath fp
344358

345359
-- | Absolute path to the current working directory.
346-
absoluteWorkingDir :: Maybe (SymbolicPath CWD to) -> IO FilePath
347-
absoluteWorkingDir Nothing = Directory.getCurrentDirectory
348-
absoluteWorkingDir (Just wd) = Directory.makeAbsolute $ getSymbolicPath wd
360+
absoluteWorkingDir :: Maybe (SymbolicPath CWD to) -> IO (AbsolutePath to)
361+
absoluteWorkingDir Nothing = unsafeMakeAbsolutePath <$> Directory.getCurrentDirectory
362+
absoluteWorkingDir (Just wd) = unsafeMakeAbsolutePath <$> Directory.makeAbsolute (getSymbolicPath wd)
349363

350364
-- | Try to make a symbolic path relative.
351365
--
@@ -354,8 +368,8 @@ absoluteWorkingDir (Just wd) = Directory.makeAbsolute $ getSymbolicPath wd
354368
-- NB: this function may fail to make the path relative.
355369
tryMakeRelative :: Maybe (SymbolicPath CWD (Dir dir)) -> SymbolicPath dir to -> IO (SymbolicPath dir to)
356370
tryMakeRelative mbWorkDir (SymbolicPath fp) = do
357-
wd <- absoluteWorkingDir mbWorkDir
358-
return $ SymbolicPath (FilePath.makeRelative wd fp)
371+
AbsolutePath wd <- absoluteWorkingDir mbWorkDir
372+
return $ SymbolicPath (FilePath.makeRelative (getSymbolicPath wd) fp)
359373

360374
-------------------------------------------------------------------------------
361375

@@ -425,6 +439,16 @@ instance
425439
where
426440
SymbolicPath p1 </> SymbolicPath p2 = SymbolicPath (p1 </> p2)
427441

442+
instance
443+
(b1 ~ 'Dir b2, c2 ~ c3, midAbsolute ~ OnlyRelative)
444+
=> PathLike
445+
(AbsolutePath b1)
446+
(SymbolicPathX midAbsolute b2 c2)
447+
(AbsolutePath c3)
448+
where
449+
AbsolutePath (SymbolicPath p1) </> SymbolicPath p2 =
450+
unsafeMakeAbsolutePath (p1 </> p2)
451+
428452
--------------------------------------------------------------------------------
429453
-- Abstract directory locations.
430454

Cabal/src/Distribution/PackageDescription/Check/Warning.hs

+1-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ import Distribution.Types.PackageName (PackageName)
4747
import Distribution.Types.TestType (TestType, knownTestTypes)
4848
import Distribution.Types.UnqualComponentName
4949
import Distribution.Types.Version (Version)
50-
import Distribution.Utils.Path
50+
import Distribution.Utils.Path (FileOrDir (..), Pkg, RelativePath, getSymbolicPath)
5151
import Language.Haskell.Extension (Extension)
5252

5353
import qualified Data.Either as Either

Cabal/src/Distribution/Simple/Build.hs

+2-4
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,6 @@ dumpBuildInfo
275275
-- ^ Flags that the user passed to build
276276
-> IO ()
277277
dumpBuildInfo verbosity distPref dumpBuildInfoFlag pkg_descr lbi flags = do
278-
let mbWorkDir = flagToMaybe $ buildWorkingDir flags
279278
when shouldDumpBuildInfo $ do
280279
-- Changing this line might break consumers of the dumped build info.
281280
-- Announce changes on mailing lists!
@@ -289,13 +288,12 @@ dumpBuildInfo verbosity distPref dumpBuildInfoFlag pkg_descr lbi flags = do
289288
activeTargets
290289
)
291290

292-
wdir <- absoluteWorkingDir mbWorkDir
293-
294291
(compilerProg, _) <- case flavorToProgram (compilerFlavor (compiler lbi)) of
295292
Nothing ->
296293
dieWithException verbosity $ UnknownCompilerFlavor (compilerFlavor (compiler lbi))
297294
Just program -> requireProgram verbosity program (withPrograms lbi)
298295

296+
wdir <- absoluteWorkingDirLBI lbi
299297
let (warns, json) = mkBuildInfo wdir pkg_descr lbi flags (compilerProg, compiler lbi) activeTargets
300298
buildInfoText = renderJson json
301299
unless (null warns) $
@@ -791,7 +789,7 @@ testSuiteLibV09AsLibAndExe
791789
-> TestSuite
792790
-> ComponentLocalBuildInfo
793791
-> LocalBuildInfo
794-
-> FilePath
792+
-> AbsolutePath (Dir Pkg)
795793
-- ^ absolute inplace dir
796794
-> SymbolicPath Pkg (Dir Dist)
797795
-> ( PackageDescription

Cabal/src/Distribution/Simple/LocalBuildInfo.hs

+1-1
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ mbWorkDirLBI =
162162
flagToMaybe . setupWorkingDir . configCommonFlags . configFlags
163163

164164
-- | Absolute path to the current working directory.
165-
absoluteWorkingDirLBI :: LocalBuildInfo -> IO FilePath
165+
absoluteWorkingDirLBI :: LocalBuildInfo -> IO (AbsolutePath (Dir Pkg))
166166
absoluteWorkingDirLBI lbi = absoluteWorkingDir (mbWorkDirLBI lbi)
167167

168168
-- | Perform the action on each enabled 'library' in the package

Cabal/src/Distribution/Simple/PreProcess.hs

+2-2
Original file line numberDiff line numberDiff line change
@@ -347,8 +347,8 @@ preprocessFile mbWorkDir searchLoc buildLoc forSDist baseFile verbosity builtinS
347347
createDirectoryIfMissingVerbose verbosity True destDir
348348
runPreProcessorWithHsBootHack
349349
pp
350-
(i psrcLoc, getSymbolicPath $ psrcRelFile)
351-
(i buildLoc, srcStem <.> "hs")
350+
(getSymbolicPath $ psrcLoc, getSymbolicPath $ psrcRelFile)
351+
(getSymbolicPath $ buildLoc, srcStem <.> "hs")
352352
where
353353
i = interpretSymbolicPath mbWorkDir -- See Note [Symbolic paths] in Distribution.Utils.Path
354354
buildAsSrcLoc :: SymbolicPath Pkg (Dir Source)

Cabal/src/Distribution/Simple/Register.hs

+2-3
Original file line numberDiff line numberDiff line change
@@ -609,8 +609,7 @@ generalInstalledPackageInfo adjustRelIncDirs pkg abi_hash lib lbi clbi installDi
609609
--
610610
-- This function knows about the layout of in place packages.
611611
inplaceInstalledPackageInfo
612-
:: FilePath
613-
-- ^ top of the build tree (absolute path)
612+
:: AbsolutePath (Dir Pkg)
614613
-> SymbolicPath Pkg (Dir Dist)
615614
-- ^ location of the dist tree
616615
-> PackageDescription
@@ -629,7 +628,7 @@ inplaceInstalledPackageInfo inplaceDir distPref pkg abi_hash lib lbi clbi =
629628
clbi
630629
installDirs
631630
where
632-
i = interpretSymbolicPath (Just $ makeSymbolicPath inplaceDir) -- See Note [Symbolic paths] in Distribution.Utils.Path
631+
i = interpretSymbolicPathAbsolute inplaceDir -- See Note [Symbolic paths] in Distribution.Utils.Path
633632
adjustRelativeIncludeDirs = concatMap $ \d ->
634633
[ i $ makeRelativePathEx d -- local include-dir
635634
, i $ libTargetDir </> makeRelativePathEx d -- autogen include-dir

Cabal/src/Distribution/Simple/ShowBuildInfo.hs

+3-3
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ import Distribution.Verbosity
9191
-- | Construct a JSON document describing the build information for a
9292
-- package.
9393
mkBuildInfo
94-
:: FilePath
94+
:: AbsolutePath (Dir Pkg)
9595
-- ^ The source directory of the package
9696
-> PackageDescription
9797
-- ^ Mostly information from the .cabal file
@@ -139,7 +139,7 @@ mkCompilerInfo compilerProgram compilerInfo =
139139
, "path" .= JsonString (programPath compilerProgram)
140140
]
141141

142-
mkComponentInfo :: FilePath -> PackageDescription -> LocalBuildInfo -> ComponentLocalBuildInfo -> ([String], Json)
142+
mkComponentInfo :: AbsolutePath (Dir Pkg) -> PackageDescription -> LocalBuildInfo -> ComponentLocalBuildInfo -> ([String], Json)
143143
mkComponentInfo wdir pkg_descr lbi clbi =
144144
( warnings
145145
, JsonObject $
@@ -150,7 +150,7 @@ mkComponentInfo wdir pkg_descr lbi clbi =
150150
, "modules" .= JsonArray (map (JsonString . display) modules)
151151
, "src-files" .= JsonArray (map (JsonString . getSymbolicPath) sourceFiles)
152152
, "hs-src-dirs" .= JsonArray (map (JsonString . prettyShow) $ hsSourceDirs bi)
153-
, "src-dir" .= JsonString (addTrailingPathSeparator wdir)
153+
, "src-dir" .= JsonString (addTrailingPathSeparator (getAbsolutePath wdir))
154154
]
155155
<> cabalFile
156156
)

0 commit comments

Comments
 (0)