Sven Tennie pushed to branch wip/supersven/libDir-setting at Glasgow Haskell Compiler / GHC
Commits:
-
41114e51
by Sven Tennie at 2026-05-28T10:41:07+02:00
-
9bbb7658
by Sven Tennie at 2026-05-28T10:41:07+02:00
12 changed files:
- + changelog.d/libdir-setting
- compiler/GHC/Driver/Config/Interpreter.hs
- compiler/GHC/Driver/DynFlags.hs
- compiler/GHC/Driver/Session.hs
- compiler/GHC/Settings.hs
- compiler/GHC/Settings/IO.hs
- hadrian/src/Rules/BinaryDist.hs
- hadrian/src/Rules/Generate.hs
- libraries/ghc-boot/GHC/Settings/Utils.hs
- + testsuite/tests/ghc-api/settings/LibDir.hs
- + testsuite/tests/ghc-api/settings/LibDir.stdout
- + testsuite/tests/ghc-api/settings/all.T
Changes:
| 1 | +section: packaging
|
|
| 2 | +synopsis: Added a new optional configuration setting for `LibDir` to support inplace
|
|
| 3 | + stage2 cross-compilers where binaries and libraries are in different stage
|
|
| 4 | + directories.
|
|
| 5 | +issues: #19174
|
|
| 6 | +mrs: !15716
|
|
| 7 | + |
|
| 8 | +description: {
|
|
| 9 | + Previously, `libDir` was always derived from `topDir`, which does not work
|
|
| 10 | + for inplace stage2 cross-compilers where executables live in `_build/stage1/`
|
|
| 11 | + but libraries live in `_build/stage2/`. A new optional `LibDir` setting in the
|
|
| 12 | + `settings` file allows overriding this. When absent (e.g. in bindists),
|
|
| 13 | + `libDir` defaults to `topDir`, preserving relocatability.
|
|
| 14 | + |
|
| 15 | + The global package database path is now resolved relative to `libDir`
|
|
| 16 | + (which may differ from `topDir` when `LibDir` is set). This affects inplace
|
|
| 17 | + cross-compiler builds but is transparent to normal and bindist builds.
|
|
| 18 | +} |
| ... | ... | @@ -17,8 +17,8 @@ import System.Directory |
| 17 | 17 | |
| 18 | 18 | initInterpOpts :: DynFlags -> IO InterpOpts
|
| 19 | 19 | initInterpOpts dflags = do
|
| 20 | - wasm_dyld <- makeAbsolute $ topDir dflags </> "dyld.mjs"
|
|
| 21 | - js_interp <- makeAbsolute $ topDir dflags </> "ghc-interp.js"
|
|
| 20 | + wasm_dyld <- makeAbsolute $ libDir dflags </> "dyld.mjs"
|
|
| 21 | + js_interp <- makeAbsolute $ libDir dflags </> "ghc-interp.js"
|
|
| 22 | 22 | pure $ InterpOpts
|
| 23 | 23 | { interpExternal = gopt Opt_ExternalInterpreter dflags
|
| 24 | 24 | , interpProg = pgm_i dflags
|
| ... | ... | @@ -61,7 +61,7 @@ module GHC.Driver.DynFlags ( |
| 61 | 61 | |
| 62 | 62 | -- ** System tool settings and locations
|
| 63 | 63 | programName, projectVersion,
|
| 64 | - ghcUsagePath, ghciUsagePath, topDir, toolDir,
|
|
| 64 | + ghcUsagePath, ghciUsagePath, topDir, libDir, toolDir,
|
|
| 65 | 65 | versionedAppDir, versionedFilePath,
|
| 66 | 66 | extraGccViaCFlags, globalPackageDatabasePath,
|
| 67 | 67 | |
| ... | ... | @@ -1516,6 +1516,8 @@ ghciUsagePath :: DynFlags -> FilePath |
| 1516 | 1516 | ghciUsagePath dflags = fileSettings_ghciUsagePath $ fileSettings dflags
|
| 1517 | 1517 | topDir :: DynFlags -> FilePath
|
| 1518 | 1518 | topDir dflags = fileSettings_topDir $ fileSettings dflags
|
| 1519 | +libDir :: DynFlags -> FilePath
|
|
| 1520 | +libDir dflags = fileSettings_libDir $ fileSettings dflags
|
|
| 1519 | 1521 | toolDir :: DynFlags -> Maybe FilePath
|
| 1520 | 1522 | toolDir dflags = fileSettings_toolDir $ fileSettings dflags
|
| 1521 | 1523 | extraGccViaCFlags :: DynFlags -> [String]
|
| ... | ... | @@ -3551,9 +3551,9 @@ compilerInfo dflags |
| 3551 | 3551 | ("Project name", cProjectName)
|
| 3552 | 3552 | -- Next come the settings, so anything else can be overridden
|
| 3553 | 3553 | -- in the settings file (as "lookup" uses the first match for the
|
| 3554 | - -- key)
|
|
| 3554 | + -- key). We filter out LibDir from rawSettings to avoid duplication.
|
|
| 3555 | 3555 | : map (fmap expandDirectories)
|
| 3556 | - (rawSettings dflags)
|
|
| 3556 | + (filter ((/= "LibDir") . fst) (rawSettings dflags))
|
|
| 3557 | 3557 | ++
|
| 3558 | 3558 | [("C compiler command", queryCmd $ ccProgram . tgtCCompiler),
|
| 3559 | 3559 | ("C compiler flags", queryFlags $ ccProgram . tgtCCompiler),
|
| ... | ... | @@ -3656,7 +3656,7 @@ compilerInfo dflags |
| 3656 | 3656 | -- Whether or not GHC was compiled using -prof
|
| 3657 | 3657 | ("GHC Profiled", showBool hostIsProfiled),
|
| 3658 | 3658 | ("Debug on", showBool debugIsOn),
|
| 3659 | - ("LibDir", topDir dflags),
|
|
| 3659 | + ("LibDir", libDir dflags),
|
|
| 3660 | 3660 | -- This is always an absolute path, unlike "Relative Global Package DB" which is
|
| 3661 | 3661 | -- in the settings file.
|
| 3662 | 3662 | ("Global Package DB", globalPackageDatabasePath dflags)
|
| ... | ... | @@ -179,11 +179,24 @@ data ToolSettings = ToolSettings |
| 179 | 179 | -- | Paths to various files and directories used by GHC, including those that
|
| 180 | 180 | -- provide more settings.
|
| 181 | 181 | data FileSettings = FileSettings
|
| 182 | - { fileSettings_ghcUsagePath :: FilePath -- ditto
|
|
| 183 | - , fileSettings_ghciUsagePath :: FilePath -- ditto
|
|
| 184 | - , fileSettings_toolDir :: Maybe FilePath -- ditto
|
|
| 185 | - , fileSettings_topDir :: FilePath -- ditto
|
|
| 182 | + { fileSettings_ghcUsagePath :: FilePath
|
|
| 183 | + -- ^ Path to @ghc-usage.txt@, displayed by @ghc --help@
|
|
| 184 | + , fileSettings_ghciUsagePath :: FilePath
|
|
| 185 | + -- ^ Path to @ghci-usage.txt@, displayed by @ghci --help@
|
|
| 186 | + , fileSettings_toolDir :: Maybe FilePath
|
|
| 187 | + -- ^ Directory containing the mingw toolchain (Windows only);
|
|
| 188 | + -- see Note [tooldir: How GHC finds mingw on Windows] in `GHC.SysTools.BaseDir`
|
|
| 189 | + , fileSettings_topDir :: FilePath
|
|
| 190 | + -- ^ GHC's top directory: the root from which GHC locates its support files
|
|
| 191 | + -- (e.g. settings).
|
|
| 192 | + -- See Note [topdir: How GHC finds its files] in `GHC.SysTools.BaseDir`
|
|
| 186 | 193 | , fileSettings_globalPackageDatabase :: FilePath
|
| 194 | + -- ^ Path to the global package database, relative to `libDir`
|
|
| 195 | + , fileSettings_libDir :: FilePath
|
|
| 196 | + -- ^ Directory containing GHC's library packages and the global package
|
|
| 197 | + -- database. Defaults to 'fileSettings_topDir' but can differ in inplace
|
|
| 198 | + -- builds used for cross-compilation testing (the "stage2 cross-compiler"
|
|
| 199 | + -- scenario).
|
|
| 187 | 200 | }
|
| 188 | 201 | |
| 189 | 202 |
| ... | ... | @@ -28,6 +28,7 @@ import GHC.Toolchain.Program |
| 28 | 28 | import GHC.Toolchain
|
| 29 | 29 | import GHC.Data.Maybe
|
| 30 | 30 | import Data.Bifunctor (Bifunctor(second))
|
| 31 | +import Data.Either (fromRight)
|
|
| 31 | 32 | |
| 32 | 33 | data SettingsError
|
| 33 | 34 | = SettingsError_MissingData String
|
| ... | ... | @@ -117,12 +118,6 @@ initSettings top_dir = do |
| 117 | 118 | then ["-fwrapv", "-fno-builtin"]
|
| 118 | 119 | else []
|
| 119 | 120 | |
| 120 | - -- The package database is either a relative path to the location of the settings file
|
|
| 121 | - -- OR an absolute path.
|
|
| 122 | - -- In case the path is absolute then top_dir </> abs_path == abs_path
|
|
| 123 | - -- the path is relative then top_dir </> rel_path == top_dir </> rel_path
|
|
| 124 | - globalpkgdb_path <- installed <$> getSetting "Relative Global Package DB"
|
|
| 125 | - |
|
| 126 | 121 | let ghc_usage_msg_path = installed "ghc-usage.txt"
|
| 127 | 122 | ghci_usage_msg_path = installed "ghci-usage.txt"
|
| 128 | 123 | |
| ... | ... | @@ -148,6 +143,19 @@ initSettings top_dir = do |
| 148 | 143 | |
| 149 | 144 | baseUnitId <- getSetting_raw "base unit-id"
|
| 150 | 145 | |
| 146 | + -- LibDir is optional. If not set, derive it from topDir. This allows
|
|
| 147 | + -- bindists to work without explicitly setting LibDir, but gives us the
|
|
| 148 | + -- option to override it for inplace test compilers (the "stage2
|
|
| 149 | + -- cross-compiler" scenario). If LibDir is a relative path, it is
|
|
| 150 | + -- interpreted relative to topDir.
|
|
| 151 | + let lib_dir = installed $ fromRight "." $
|
|
| 152 | + getRawFilePathSetting top_dir settingsFile mySettings "LibDir"
|
|
| 153 | + |
|
| 154 | + -- The package database is either a relative path to lib_dir OR an absolute path.
|
|
| 155 | + -- In case the path is absolute then lib_dir </> abs_path == abs_path
|
|
| 156 | + -- the path is relative then lib_dir </> rel_path == lib_dir </> rel_path
|
|
| 157 | + globalpkgdb_path <- (lib_dir </>) <$> getSetting "Relative Global Package DB"
|
|
| 158 | + |
|
| 151 | 159 | return $ Settings
|
| 152 | 160 | { sGhcNameVersion = GhcNameVersion
|
| 153 | 161 | { ghcNameVersion_programName = "ghc"
|
| ... | ... | @@ -159,6 +167,7 @@ initSettings top_dir = do |
| 159 | 167 | , fileSettings_ghciUsagePath = ghci_usage_msg_path
|
| 160 | 168 | , fileSettings_toolDir = mtool_dir
|
| 161 | 169 | , fileSettings_topDir = top_dir
|
| 170 | + , fileSettings_libDir = lib_dir
|
|
| 162 | 171 | , fileSettings_globalPackageDatabase = globalpkgdb_path
|
| 163 | 172 | }
|
| 164 | 173 |
| ... | ... | @@ -14,6 +14,7 @@ import qualified System.Directory.Extra as IO |
| 14 | 14 | import Data.Either
|
| 15 | 15 | import qualified Data.Set as Set
|
| 16 | 16 | import Oracles.Flavour
|
| 17 | +import Rules.Generate (generateSettings)
|
|
| 17 | 18 | |
| 18 | 19 | {-
|
| 19 | 20 | Note [Binary distributions]
|
| ... | ... | @@ -218,6 +219,17 @@ bindistRules = do |
| 218 | 219 | IO.createFileLink version_prog versioned_runhaskell_path
|
| 219 | 220 | |
| 220 | 221 | copyDirectory (ghcBuildDir -/- "lib") bindistFilesDir
|
| 222 | + |
|
| 223 | + -- Regenerate settings file without LibDir. For bindists, LibDir should
|
|
| 224 | + -- be derived from topdir at runtime such that the GHC binary is
|
|
| 225 | + -- relocatable. The package DB is always at "package.conf.d" relative to
|
|
| 226 | + -- the lib dir, matching the known bindist layout.
|
|
| 227 | + let bindistSettings = bindistFilesDir -/- "lib" -/- "settings"
|
|
| 228 | + bindistContext = vanillaContext Stage1 compiler
|
|
| 229 | + bindistSettingsContent <- interpretInContext bindistContext $
|
|
| 230 | + generateSettings bindistSettings False "package.conf.d"
|
|
| 231 | + writeFile' bindistSettings bindistSettingsContent
|
|
| 232 | + |
|
| 221 | 233 | copyDirectory (rtsIncludeDir) bindistFilesDir
|
| 222 | 234 | when windowsHost $ createGhcii (bindistFilesDir -/- "bin")
|
| 223 | 235 |
| 1 | 1 | module Rules.Generate (
|
| 2 | 2 | isGeneratedCmmFile, compilerDependencies, generatePackageCode,
|
| 3 | 3 | generateRules, copyRules, generatedDependencies,
|
| 4 | - templateRules
|
|
| 4 | + templateRules, generateSettings
|
|
| 5 | 5 | ) where
|
| 6 | 6 | |
| 7 | 7 | import Development.Shake.FilePath
|
| ... | ... | @@ -256,8 +256,21 @@ generateRules = do |
| 256 | 256 | |
| 257 | 257 | forM_ allStages $ \stage -> do
|
| 258 | 258 | let prefix = root -/- stageString stage -/- "lib"
|
| 259 | - go gen file = generate file (semiEmptyTarget (succStage stage)) gen
|
|
| 260 | - (prefix -/- "settings") %> \out -> go (generateSettings out) out
|
|
| 259 | + -- Stage0 compiler builds Stage1, Stage1 -> Stage2, etc.
|
|
| 260 | + buildStage = succStage stage
|
|
| 261 | + go gen file = generate file (semiEmptyTarget buildStage) gen
|
|
| 262 | + (prefix -/- "settings") %> \out -> do
|
|
| 263 | + let get_pkg_db stg = packageDbPath (PackageDbLoc stg Final)
|
|
| 264 | + pkgDb <- case buildStage of
|
|
| 265 | + Stage0 {} -> error "Unable to generate settings for stage0. This should never be reached."
|
|
| 266 | + Stage1 -> get_pkg_db Stage1
|
|
| 267 | + Stage2 -> get_pkg_db Stage1
|
|
| 268 | + Stage3 -> get_pkg_db Stage2
|
|
| 269 | + -- addTrailingPathSeparator needed: makeRelativeNoSysLink uses
|
|
| 270 | + -- splitPath where "lib" and "lib/" are distinct components.
|
|
| 271 | + let lib_topDir = addTrailingPathSeparator prefix
|
|
| 272 | + relPkgDb = makeRelativeNoSysLink lib_topDir pkgDb
|
|
| 273 | + go (generateSettings out True relPkgDb) out
|
|
| 261 | 274 | (prefix -/- "targets" -/- "default.target") %> \out -> go (show <$> expr getTargetTarget) out
|
| 262 | 275 | |
| 263 | 276 | where
|
| ... | ... | @@ -460,19 +473,17 @@ ghcWrapper stage = do |
| 460 | 473 | return $ unwords $ map show $ [ ghcPath ]
|
| 461 | 474 | ++ [ "$@" ]
|
| 462 | 475 | |
| 463 | -generateSettings :: FilePath -> Expr String
|
|
| 464 | -generateSettings settingsFile = do
|
|
| 476 | +-- | Generate settings file, optionally including @LibDir@.
|
|
| 477 | +--
|
|
| 478 | +-- @rel_pkg_db@: package DB path relative to the lib dir (e.g.
|
|
| 479 | +-- "package.conf.d"). Callers supply the correct relative path. For bindists
|
|
| 480 | +-- the layout is known statically; for in-tree builds callers compute it. For
|
|
| 481 | +-- bindists, we omit @LibDir@ so it defaults to @topDir@ at runtime.
|
|
| 482 | +generateSettings :: FilePath -> Bool -> FilePath -> Expr String
|
|
| 483 | +generateSettings settingsFile includeLibDir rel_pkg_db = do
|
|
| 465 | 484 | ctx <- getContext
|
| 466 | 485 | stage <- getStage
|
| 467 | 486 | |
| 468 | - package_db_path <- expr $ do
|
|
| 469 | - let get_pkg_db stg = packageDbPath (PackageDbLoc stg Final)
|
|
| 470 | - case stage of
|
|
| 471 | - Stage0 {} -> error "Unable to generate settings for stage0"
|
|
| 472 | - Stage1 -> get_pkg_db Stage1
|
|
| 473 | - Stage2 -> get_pkg_db Stage1
|
|
| 474 | - Stage3 -> get_pkg_db Stage2
|
|
| 475 | - |
|
| 476 | 487 | -- The unit-id of the base package which is always linked against (#25382)
|
| 477 | 488 | base_unit_id <- expr $ do
|
| 478 | 489 | case stage of
|
| ... | ... | @@ -481,15 +492,24 @@ generateSettings settingsFile = do |
| 481 | 492 | Stage2 -> pkgUnitId Stage1 base
|
| 482 | 493 | Stage3 -> pkgUnitId Stage2 base
|
| 483 | 494 | |
| 484 | - let rel_pkg_db = makeRelativeNoSysLink (dropFileName settingsFile) package_db_path
|
|
| 495 | + let -- E.g. the Stage2 compiler lives in _build/stage1
|
|
| 496 | + -- So, we need to decrement the stage to get the correct directory
|
|
| 497 | + stage_dir_stage = predStage stage
|
|
| 498 | + |
|
| 499 | + -- addTrailingPathSeparator is needed because makeRelativeNoSysLink uses
|
|
| 500 | + -- splitPath internally, where "lib" and "lib/" are distinct components.
|
|
| 501 | + lib_topDir :: FilePath <- expr $ addTrailingPathSeparator <$> stageLibPath stage_dir_stage
|
|
| 502 | + let rel_lib_topDir = makeRelativeNoSysLink (dropFileName settingsFile) lib_topDir
|
|
| 485 | 503 | |
| 486 | 504 | settings <- traverse sequence $
|
| 487 | - [ ("unlit command", ("$topdir/../bin/" <>) <$> expr (programName (ctx { Context.package = unlit })))
|
|
| 488 | - , ("Use interpreter", expr $ yesNo <$> ghcWithInterpreter (predStage stage))
|
|
| 489 | - , ("RTS ways", escapeArgs . map show . Set.toList <$> getRtsWays)
|
|
| 490 | - , ("Relative Global Package DB", pure rel_pkg_db)
|
|
| 491 | - , ("base unit-id", pure base_unit_id)
|
|
| 492 | - ]
|
|
| 505 | + [ ("unlit command", ("$topdir/../bin/" <>) <$> expr (programName (ctx { Context.package = unlit })))
|
|
| 506 | + , ("Use interpreter", expr $ yesNo <$> ghcWithInterpreter (predStage stage))
|
|
| 507 | + , ("RTS ways", escapeArgs . map show . Set.toList <$> getRtsWays)
|
|
| 508 | + , ("Relative Global Package DB", pure rel_pkg_db)
|
|
| 509 | + , ("base unit-id", pure base_unit_id)
|
|
| 510 | + ]
|
|
| 511 | + ++ ([("LibDir", pure rel_lib_topDir) | includeLibDir])
|
|
| 512 | + |
|
| 493 | 513 | let showTuple (k, v) = "(" ++ show k ++ ", " ++ show v ++ ")"
|
| 494 | 514 | pure $ case settings of
|
| 495 | 515 | [] -> "[]"
|
| ... | ... | @@ -3,6 +3,7 @@ module GHC.Settings.Utils where |
| 3 | 3 | import Prelude -- See Note [Why do we import Prelude here?]
|
| 4 | 4 | |
| 5 | 5 | import Data.Char (isSpace)
|
| 6 | +import Data.Either (fromRight)
|
|
| 6 | 7 | import Data.Map (Map)
|
| 7 | 8 | import qualified Data.Map as Map
|
| 8 | 9 | |
| ... | ... | @@ -45,7 +46,10 @@ getTargetArchOS target = tgtArchOs target |
| 45 | 46 | getGlobalPackageDb :: FilePath -> RawSettings -> Either String FilePath
|
| 46 | 47 | getGlobalPackageDb settingsFile settings = do
|
| 47 | 48 | rel_db <- getRawSetting settingsFile settings "Relative Global Package DB"
|
| 48 | - return (dropFileName settingsFile </> rel_db)
|
|
| 49 | + let top_dir = dropFileName settingsFile
|
|
| 50 | + lib_dir = (top_dir </>) $ fromRight "." $
|
|
| 51 | + getRawFilePathSetting top_dir settingsFile settings "LibDir"
|
|
| 52 | + return (lib_dir </> rel_db)
|
|
| 49 | 53 | |
| 50 | 54 | --------------------------------------------------------------------------------
|
| 51 | 55 | -- lib/settings
|
| 1 | +module Main where
|
|
| 2 | + |
|
| 3 | +import Control.Monad (when)
|
|
| 4 | +import Control.Monad.IO.Class (liftIO)
|
|
| 5 | +import Data.List (intercalate)
|
|
| 6 | +import GHC
|
|
| 7 | +import GHC.Driver.DynFlags
|
|
| 8 | +import GHC.Driver.Env (hsc_dflags)
|
|
| 9 | +import GHC.Settings
|
|
| 10 | +import System.Directory
|
|
| 11 | +import System.Environment
|
|
| 12 | +import System.Exit (ExitCode (ExitFailure), exitWith)
|
|
| 13 | +import System.FilePath
|
|
| 14 | +import System.IO (hPutStrLn, stderr)
|
|
| 15 | +import System.Process (readProcess)
|
|
| 16 | +import Unsafe.Coerce (unsafeCoerce)
|
|
| 17 | + |
|
| 18 | +-- Verify that a LibDir setting in the settings file is respected:
|
|
| 19 | +-- 1. fileSettings_libDir and fileSettings_globalPackageDatabase reflect the
|
|
| 20 | +-- configured LibDir path (not topDir)
|
|
| 21 | +-- 2. GHC can still compile with a LibDir that differs from topDir
|
|
| 22 | +-- 3. --print-libdir and --print-global-package-db output the correct paths
|
|
| 23 | +--
|
|
| 24 | +-- We create a symlink to the real lib dir so that the package DB remains
|
|
| 25 | +-- findable, but use a separate topDir so that topDir ≠ libDir, proving
|
|
| 26 | +-- the LibDir setting is actually used.
|
|
| 27 | +--
|
|
| 28 | +-- Tested for both relative and absolute LibDir values.
|
|
| 29 | +main :: IO ()
|
|
| 30 | +main = do
|
|
| 31 | + libdir : ghcBin : _ <- getArgs
|
|
| 32 | + |
|
| 33 | + (rawSettingOpts, rawTargetOpts, realLibDir) <- runGhc (Just libdir) $ do
|
|
| 34 | + dflags <- hsc_dflags <$> getSession
|
|
| 35 | + pure (rawSettings dflags, rawTarget dflags, fileSettings_libDir (fileSettings dflags))
|
|
| 36 | + |
|
| 37 | + tmpDir <- getTemporaryDirectory
|
|
| 38 | + let topDir = tmpDir </> "T19174_top"
|
|
| 39 | + symlinkLib = tmpDir </> "T19174_lib"
|
|
| 40 | + -- Remove stale dirs from prior runs; createDirectoryLink fails if path exists.
|
|
| 41 | + removePathForcibly topDir
|
|
| 42 | + removePathForcibly symlinkLib
|
|
| 43 | + createDirectoryIfMissing True (topDir </> "targets")
|
|
| 44 | + createDirectoryLink realLibDir symlinkLib
|
|
| 45 | + |
|
| 46 | + let testWithLibDir libDirValue = do
|
|
| 47 | + writeTopDirFiles topDir rawSettingOpts rawTargetOpts libDirValue
|
|
| 48 | + runGhc (Just topDir) $ do
|
|
| 49 | + assertSettings topDir symlinkLib
|
|
| 50 | + compileAndRunTestExpr
|
|
| 51 | + assertGhcFlags ghcBin topDir symlinkLib
|
|
| 52 | + |
|
| 53 | + testWithLibDir (".." </> takeFileName symlinkLib)
|
|
| 54 | + testWithLibDir symlinkLib
|
|
| 55 | + |
|
| 56 | + putStrLn "OK"
|
|
| 57 | + |
|
| 58 | +writeTopDirFiles ::
|
|
| 59 | + (Show a) =>
|
|
| 60 | + FilePath ->
|
|
| 61 | + [(String, String)] ->
|
|
| 62 | + a ->
|
|
| 63 | + String ->
|
|
| 64 | + IO ()
|
|
| 65 | +writeTopDirFiles topDir rawSettingOpts rawTargetOpts libDirValue = do
|
|
| 66 | + let settings = filter ((/= "LibDir") . fst) rawSettingOpts ++ [("LibDir", libDirValue)]
|
|
| 67 | + writeFile (topDir </> "settings") $
|
|
| 68 | + "[" ++ intercalate "\n," (map show settings) ++ "]"
|
|
| 69 | + writeFile (topDir </> "targets" </> "default.target") $
|
|
| 70 | + show rawTargetOpts
|
|
| 71 | + |
|
| 72 | +assertSettings :: FilePath -> FilePath -> Ghc ()
|
|
| 73 | +assertSettings topDir expectedLib = do
|
|
| 74 | + dflags <- hsc_dflags <$> getSession
|
|
| 75 | + let fs = fileSettings dflags
|
|
| 76 | + actualLib = fileSettings_libDir fs
|
|
| 77 | + actualPkgDb = fileSettings_globalPackageDatabase fs
|
|
| 78 | + normActualLib <- liftIO $ canonicalizePath actualLib
|
|
| 79 | + normExpected <- liftIO $ canonicalizePath expectedLib
|
|
| 80 | + normTopDir <- liftIO $ canonicalizePath topDir
|
|
| 81 | + normActualPkgDb <- liftIO $ canonicalizePath actualPkgDb
|
|
| 82 | + normExpectedPkgDb <- liftIO $ canonicalizePath (expectedLib </> "package.conf.d")
|
|
| 83 | + liftIO $ do
|
|
| 84 | + when (normActualLib /= normExpected) $
|
|
| 85 | + die
|
|
| 86 | + [ "FAIL: libDir should be " ++ normExpected,
|
|
| 87 | + " got " ++ normActualLib
|
|
| 88 | + ]
|
|
| 89 | + when (normActualLib == normTopDir) $
|
|
| 90 | + die ["FAIL: libDir equals topDir — LibDir setting was ignored"]
|
|
| 91 | + when (normActualPkgDb /= normExpectedPkgDb) $
|
|
| 92 | + die
|
|
| 93 | + [ "FAIL: globalPackageDB should be " ++ normExpectedPkgDb,
|
|
| 94 | + " got " ++ normActualPkgDb
|
|
| 95 | + ]
|
|
| 96 | + |
|
| 97 | +assertGhcFlags :: FilePath -> FilePath -> FilePath -> IO ()
|
|
| 98 | +assertGhcFlags ghcBin topDir expectedLib = do
|
|
| 99 | + normExpectedLib <- canonicalizePath expectedLib
|
|
| 100 | + normExpectedPkgDb <- canonicalizePath (expectedLib </> "package.conf.d")
|
|
| 101 | + |
|
| 102 | + printedLibDir <- trim <$> readProcess ghcBin ["-B" ++ topDir, "--print-libdir"] ""
|
|
| 103 | + normPrintedLib <- canonicalizePath printedLibDir
|
|
| 104 | + when (normPrintedLib /= normExpectedLib) $
|
|
| 105 | + die
|
|
| 106 | + [ "FAIL: --print-libdir should be " ++ normExpectedLib,
|
|
| 107 | + " got " ++ normPrintedLib
|
|
| 108 | + ]
|
|
| 109 | + |
|
| 110 | + printedPkgDb <- trim <$> readProcess ghcBin ["-B" ++ topDir, "--print-global-package-db"] ""
|
|
| 111 | + normPrintedPkgDb <- canonicalizePath printedPkgDb
|
|
| 112 | + when (normPrintedPkgDb /= normExpectedPkgDb) $
|
|
| 113 | + die
|
|
| 114 | + [ "FAIL: --print-global-package-db should be " ++ normExpectedPkgDb,
|
|
| 115 | + " got " ++ normPrintedPkgDb
|
|
| 116 | + ]
|
|
| 117 | + |
|
| 118 | +compileAndRunTestExpr :: Ghc ()
|
|
| 119 | +compileAndRunTestExpr = do
|
|
| 120 | + dflags <- getSessionDynFlags
|
|
| 121 | + _ <- setSessionDynFlags dflags
|
|
| 122 | + setContext [IIDecl (simpleImportDecl (mkModuleName "Prelude"))]
|
|
| 123 | + result <- compileExpr "length [1,2,3 :: Int]"
|
|
| 124 | + liftIO $ print (unsafeCoerce result :: Int)
|
|
| 125 | + |
|
| 126 | +trim :: String -> String
|
|
| 127 | +trim = reverse . dropWhile (== '\n') . reverse
|
|
| 128 | + |
|
| 129 | +die :: [String] -> IO ()
|
|
| 130 | +die msgs = mapM_ (hPutStrLn stderr) msgs >> exitWith (ExitFailure 1) |
| 1 | +3
|
|
| 2 | +3
|
|
| 3 | +OK |
| 1 | +test('LibDir',
|
|
| 2 | + [ extra_run_opts('"' + config.libdir + '" "' + config.compiler + '"')
|
|
| 3 | + , req_interp
|
|
| 4 | + # createDirectoryLink uses CreateSymbolicLink on Windows (requires developer
|
|
| 5 | + # mode or admin); also GHC searches for mingw relative to topDir, which our
|
|
| 6 | + # artificial topDir doesn't provide.
|
|
| 7 | + , when(opsys('mingw32'), skip)
|
|
| 8 | + # TODO: wasm CI image lacks permission to create symlinks in temp dir (`/tmp`).
|
|
| 9 | + , when(arch('wasm32'), skip)
|
|
| 10 | + ]
|
|
| 11 | + , compile_and_run
|
|
| 12 | + , ['-package ghc -package directory -package filepath']) |