Skip to content

Commit 05ba87e

Browse files
committed
Detect system with libtinfo6 and download alternate GHC bindist (commercialhaskell#2302)
1 parent 63f203d commit 05ba87e

7 files changed

Lines changed: 135 additions & 78 deletions

File tree

ChangeLog.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ Other enhancements:
2929
* Support for absolute file path in `url` field of `setup-info` or `--ghc-bindist`
3030
* `stack setup` no longer unpacks to the system temp dir on posix systems.
3131
[#996](https://github.com/commercialhaskell/stack/issues/996)
32+
* Detects system with libtinfo6 and can download alternate GHC bindists
33+
[#2302](https://github.com/commercialhaskell/stack/issues/2302).
3234

3335
Bug fixes:
3436

src/Stack/Config.hs

Lines changed: 3 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ module Stack.Config
2929
,packagesParser
3030
,resolvePackageEntry
3131
,getImplicitGlobalProjectDir
32-
,getIsGMP4
3332
,getSnapshots
3433
,makeConcreteResolver
3534
,checkOwnership
@@ -61,8 +60,7 @@ import qualified Data.Map as Map
6160
import Data.Maybe
6261
import Data.Monoid.Extra
6362
import qualified Data.Text as T
64-
import Data.Text.Encoding (encodeUtf8, decodeUtf8, decodeUtf8With)
65-
import Data.Text.Encoding.Error (lenientDecode)
63+
import Data.Text.Encoding (encodeUtf8, decodeUtf8)
6664
import qualified Data.Yaml.Extra as Yaml
6765
import Distribution.System (OS (..), Platform (..), buildPlatform)
6866
import qualified Distribution.Text
@@ -76,7 +74,6 @@ import Path.Extra (toFilePathNoTrailingSep)
7674
import Path.Find (findInParents)
7775
import Path.IO
7876
import qualified Paths_stack as Meta
79-
import Safe (headMay)
8077
import Stack.BuildPlan
8178
import Stack.Config.Build
8279
import Stack.Config.Docker
@@ -324,35 +321,6 @@ configFromConfigMonoid configStackRoot configUserConfigPath mresolver mproject C
324321

325322
return Config {..}
326323

327-
-- | Get the default 'GHCVariant'. On older Linux systems with libgmp4, returns 'GHCGMP4'.
328-
getDefaultGHCVariant
329-
:: (MonadIO m, MonadBaseControl IO m, MonadCatch m, MonadLogger m)
330-
=> EnvOverride -> Platform -> m GHCVariant
331-
getDefaultGHCVariant menv (Platform _ Linux) = do
332-
$logDebug "Checking whether stack was built with libgmp4"
333-
isGMP4 <- getIsGMP4 menv
334-
if isGMP4
335-
then $logDebug "Stack was built with libgmp4, so the default ghc-variant will be gmp4"
336-
else $logDebug "Stack was not built with libgmp4"
337-
return (if isGMP4 then GHCGMP4 else GHCStandard)
338-
getDefaultGHCVariant _ _ = return GHCStandard
339-
340-
-- Determine whether 'stack' is linked with libgmp4 (libgmp.so.3)
341-
getIsGMP4
342-
:: (MonadIO m, MonadBaseControl IO m, MonadCatch m, MonadLogger m)
343-
=> EnvOverride -> m Bool
344-
getIsGMP4 menv = do
345-
executablePath <- liftIO getExecutablePath
346-
elddOut <- tryProcessStdout Nothing menv "ldd" [executablePath]
347-
return $
348-
case elddOut of
349-
Left _ -> False
350-
Right lddOut -> hasLineWithFirstWord "libgmp.so.3" lddOut
351-
where
352-
hasLineWithFirstWord w =
353-
elem (Just w) .
354-
map (headMay . T.words) . T.lines . decodeUtf8With lenientDecode
355-
356324
-- | Get the directory on Windows where we should install extra programs. For
357325
-- more information, see discussion at:
358326
-- https://github.com/fpco/minghc/issues/43#issuecomment-99737383
@@ -380,14 +348,10 @@ instance HasGHCVariant MiniConfig where
380348

381349
-- | Load the 'MiniConfig'.
382350
loadMiniConfig
383-
:: (MonadIO m, MonadBaseControl IO m, MonadCatch m, MonadLogger m)
351+
:: (MonadIO m)
384352
=> Manager -> Config -> m MiniConfig
385353
loadMiniConfig manager config = do
386-
menv <- liftIO $ configEnvOverride config minimalEnvSettings
387-
ghcVariant <-
388-
case configGHCVariant0 config of
389-
Just ghcVariant -> return ghcVariant
390-
Nothing -> getDefaultGHCVariant menv (configPlatform config)
354+
let ghcVariant = fromMaybe GHCStandard (configGHCVariant0 config)
391355
return (MiniConfig manager ghcVariant config)
392356

393357
-- Load the configuration, using environment variables, and defaults as

src/Stack/Setup.hs

Lines changed: 80 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ import qualified Data.Text.Encoding.Error as T
5858
import Data.Time.Clock (NominalDiffTime, diffUTCTime, getCurrentTime)
5959
import Data.Typeable (Typeable)
6060
import qualified Data.Yaml as Yaml
61-
import Distribution.System (OS, Arch (..), Platform (..))
61+
import Distribution.System (OS (Linux), Arch (..), Platform (..))
6262
import qualified Distribution.System as Cabal
6363
import Distribution.Text (simpleParse)
6464
import Lens.Micro (set)
@@ -70,7 +70,7 @@ import Path.Extra (toFilePathNoTrailingSep)
7070
import Path.IO hiding (findExecutable)
7171
import qualified Paths_stack as Meta
7272
import Prelude hiding (concat, elem, any) -- Fix AMP warning
73-
import Safe (readMay)
73+
import Safe (headMay, readMay)
7474
import Stack.Build (build)
7575
import Stack.Config (resolvePackageEntry, loadConfig)
7676
import Stack.Constants (distRelativeDir, stackProgName)
@@ -215,7 +215,7 @@ setupEnv mResolveMissingGHC = do
215215
, soptsGHCBindistURL = Nothing
216216
}
217217

218-
mghcBin <- ensureCompiler sopts
218+
(mghcBin, compilerBuild) <- ensureCompiler sopts
219219

220220
-- Modify the initial environment to include the GHC path, if a local GHC
221221
-- is being used
@@ -237,6 +237,7 @@ setupEnv mResolveMissingGHC = do
237237
{ envConfigBuildConfig = bconfig
238238
, envConfigCabalVersion = cabalVer
239239
, envConfigCompilerVersion = compilerVer
240+
, envConfigCompilerBuild = compilerBuild
240241
, envConfigPackages = Map.fromList $ concat packages
241242
}
242243

@@ -315,6 +316,7 @@ setupEnv mResolveMissingGHC = do
315316
}
316317
, envConfigCabalVersion = cabalVer
317318
, envConfigCompilerVersion = compilerVer
319+
, envConfigCompilerBuild = compilerBuild
318320
, envConfigPackages = envConfigPackages envConfig0
319321
}
320322

@@ -332,7 +334,7 @@ addIncludeLib (ExtraDirs _bins includes libs) config = config
332334
-- | Ensure compiler (ghc or ghcjs) is installed and provide the PATHs to add if necessary
333335
ensureCompiler :: (MonadIO m, MonadMask m, MonadLogger m, MonadReader env m, HasConfig env, HasHttpManager env, HasTerminal env, HasReExec env, HasLogLevel env, HasGHCVariant env, MonadBaseControl IO m)
334336
=> SetupOpts
335-
-> m (Maybe ExtraDirs)
337+
-> m (Maybe ExtraDirs, CompilerBuild)
336338
ensureCompiler sopts = do
337339
let wc = whichCompiler (soptsWantedCompiler sopts)
338340
when (getGhcVersion (soptsWantedCompiler sopts) < $(mkVersion "7.8")) $ do
@@ -363,7 +365,7 @@ ensureCompiler sopts = do
363365

364366
-- If we need to install a GHC or MSYS, try to do so
365367
-- Return the additional directory paths of GHC & MSYS.
366-
mtools <- if needLocal
368+
(mtools, compilerBuild) <- if needLocal
367369
then do
368370
getSetupInfo' <- runOnce (getSetupInfo (soptsStackSetupYaml sopts) =<< asks getHttpManager)
369371

@@ -373,17 +375,20 @@ ensureCompiler sopts = do
373375
-- Install GHC
374376
ghcVariant <- asks getGHCVariant
375377
config <- asks getConfig
376-
ghcPkgName <- parsePackageNameFromString ("ghc" ++ ghcVariantSuffix ghcVariant)
377-
let installedCompiler =
378+
(installedCompiler, compilerBuild) <-
378379
case wc of
379-
Ghc -> getInstalledTool installed ghcPkgName (isWanted . GhcVersion)
380-
Ghcjs -> getInstalledGhcjs installed isWanted
380+
Ghc -> do
381+
ghcBuild <- getGhcBuild menv0
382+
ghcPkgName <- parsePackageNameFromString ("ghc" ++ ghcVariantSuffix ghcVariant ++ compilerBuildSuffix ghcBuild)
383+
return (getInstalledTool installed ghcPkgName (isWanted . GhcVersion), ghcBuild)
384+
Ghcjs -> return (getInstalledGhcjs installed isWanted, CompilerBuildStandard)
381385
compilerTool <- case installedCompiler of
382386
Just tool -> return tool
383387
Nothing
384388
| soptsInstallIfMissing sopts -> do
385389
si <- getSetupInfo'
386390
downloadAndInstallCompiler
391+
compilerBuild
387392
si
388393
(soptsWantedCompiler sopts)
389394
(soptsCompilerCheck sopts)
@@ -393,6 +398,7 @@ ensureCompiler sopts = do
393398
msystem
394399
(soptsWantedCompiler sopts, expectedArch)
395400
ghcVariant
401+
compilerBuild
396402
(soptsCompilerCheck sopts)
397403
(soptsStackYaml sopts)
398404
(fromMaybe
@@ -421,8 +427,8 @@ ensureCompiler sopts = do
421427
return Nothing
422428
_ -> return Nothing
423429

424-
return $ Just (compilerTool, mmsys2Tool)
425-
else return Nothing
430+
return (Just (compilerTool, mmsys2Tool), compilerBuild)
431+
else return (Nothing, CompilerBuildStandard)
426432

427433
mpaths <- case mtools of
428434
Nothing -> return Nothing
@@ -452,9 +458,56 @@ ensureCompiler sopts = do
452458

453459
when (soptsSanityCheck sopts) $ sanityCheck menv wc
454460

455-
return mpaths
461+
return (mpaths, compilerBuild)
462+
463+
-- | Determine which GHC build to use dependong on which shared libraries are available
464+
-- on the system.
465+
getGhcBuild
466+
:: (MonadIO m, MonadBaseControl IO m, MonadCatch m, MonadLogger m, HasPlatform env, MonadReader env m)
467+
=> EnvOverride -> m CompilerBuild
468+
getGhcBuild menv = do
469+
470+
-- TODO: a more reliable, flexible, and data driven approach would be to actually download small
471+
-- "test" executables (from setup-info) that link to the same gmp/tinfo versions
472+
-- that GHC does (i.e. built in same environment as the GHC bindist). The algorithm would go
473+
-- something like this:
474+
--
475+
-- check for previous 'uname -a' plus compiler version/variant in cache
476+
-- if cached, then use that as suffix
477+
-- otherwise:
478+
-- download setup-info
479+
-- go through all with right prefix for os/version/variant
480+
-- first try "standard" (no extra suffix), then the rest
481+
-- download "compatibility check" exe if not already downloaded
482+
-- try running it
483+
-- if successful, then choose that
484+
-- cache compiler suffix with the uname -a and ldconfig -p output hash plus compiler version
456485

457-
-- Ensure Docker container-compatible 'stack' executable is downloaded
486+
platform <- asks getPlatform
487+
case platform of
488+
Platform _ Linux -> do
489+
eldconfigOut <- tryProcessStdout Nothing menv "ldconfig" ["-p"]
490+
let efirstWords = fmap (mapMaybe (headMay . T.words) .
491+
T.lines . T.decodeUtf8With T.lenientDecode) eldconfigOut
492+
case efirstWords of
493+
Right firstWords
494+
| "libtinfo.so.6" `elem` firstWords
495+
&& not ("libtinfo.so.5" `elem` firstWords) -> do
496+
$logDebug "Found libtinfo.so.6; using tinfo6 GHC build"
497+
return (CompilerBuildSpecialized "tinfo6")
498+
| "libgmp.so.3" `elem` firstWords
499+
&& not ("libgmp.so.10" `elem` firstWords) -> do
500+
$logDebug "Found libgmp.so.3; using gmp4 GHC build"
501+
return (CompilerBuildSpecialized "gmp4")
502+
| otherwise -> do
503+
$logDebug "Did not find libtinfo.so.6 or libgmp.so.3; using standard GHC build"
504+
return CompilerBuildStandard
505+
Left _ -> do
506+
$logDebug "ldconfig -p failed; falling back to using standard GHC build"
507+
return CompilerBuildStandard
508+
_ -> return CompilerBuildStandard
509+
510+
-- | Ensure Docker container-compatible 'stack' executable is downloaded
458511
ensureDockerStackExe
459512
:: (MonadIO m, MonadMask m, MonadLogger m, MonadReader env m, HasConfig env, HasHttpManager env, MonadBaseControl IO m)
460513
=> Platform -> m (Path Abs File)
@@ -669,12 +722,13 @@ downloadAndInstallTool programsDir si downloadInfo tool installer = do
669722
return tool
670723

671724
downloadAndInstallCompiler :: (MonadIO m, MonadMask m, MonadLogger m, MonadReader env m, HasConfig env, HasGHCVariant env, HasHttpManager env, HasTerminal env, HasReExec env, HasLogLevel env, MonadBaseControl IO m)
672-
=> SetupInfo
725+
=> CompilerBuild
726+
-> SetupInfo
673727
-> CompilerVersion
674728
-> VersionCheck
675729
-> Maybe String
676730
-> m Tool
677-
downloadAndInstallCompiler si wanted@GhcVersion{} versionCheck mbindistURL = do
731+
downloadAndInstallCompiler ghcBuild si wanted@GhcVersion{} versionCheck mbindistURL = do
678732
ghcVariant <- asks getGHCVariant
679733
(selectedVersion, downloadInfo) <- case mbindistURL of
680734
Just bindistURL -> do
@@ -687,7 +741,7 @@ downloadAndInstallCompiler si wanted@GhcVersion{} versionCheck mbindistURL = do
687741
_ ->
688742
throwM WantedMustBeGHC
689743
_ -> do
690-
ghcKey <- getGhcKey
744+
ghcKey <- getGhcKey ghcBuild
691745
case Map.lookup ghcKey $ siGHCs si of
692746
Nothing -> throwM $ UnknownOSKey ghcKey
693747
Just pairs_ -> getWantedCompilerInfo ghcKey versionCheck wanted GhcVersion pairs_
@@ -701,16 +755,19 @@ downloadAndInstallCompiler si wanted@GhcVersion{} versionCheck mbindistURL = do
701755
(case ghcVariant of
702756
GHCStandard -> ""
703757
v -> " (" <> T.pack (ghcVariantName v) <> ")") <>
758+
(case ghcBuild of
759+
CompilerBuildStandard -> ""
760+
b -> " (" <> T.pack (compilerBuildName b) <> ")") <>
704761
" to an isolated location."
705762
$logInfo "This will not interfere with any system-level installation."
706-
ghcPkgName <- parsePackageNameFromString ("ghc" ++ ghcVariantSuffix ghcVariant)
763+
ghcPkgName <- parsePackageNameFromString ("ghc" ++ ghcVariantSuffix ghcVariant ++ compilerBuildSuffix ghcBuild)
707764
let tool = Tool $ PackageIdentifier ghcPkgName selectedVersion
708765
downloadAndInstallTool (configLocalPrograms config) si downloadInfo tool installer
709-
downloadAndInstallCompiler si wanted versionCheck _mbindistUrl = do
766+
downloadAndInstallCompiler compilerBuild si wanted versionCheck _mbindistUrl = do
710767
config <- asks getConfig
711768
ghcVariant <- asks getGHCVariant
712-
case ghcVariant of
713-
GHCStandard -> return ()
769+
case (ghcVariant, compilerBuild) of
770+
(GHCStandard, CompilerBuildStandard) -> return ()
714771
_ -> throwM GHCJSRequiresStandardVariant
715772
(selectedVersion, downloadInfo) <- case Map.lookup "source" $ siGHCJSs si of
716773
Nothing -> throwM $ UnknownOSKey "source"
@@ -738,12 +795,12 @@ getWantedCompilerInfo key versionCheck wanted toCV pairs_ =
738795
filter (isWantedCompiler versionCheck wanted . toCV . fst) (Map.toList pairs_)
739796

740797
getGhcKey :: (MonadReader env m, HasPlatform env, HasGHCVariant env, MonadCatch m)
741-
=> m Text
742-
getGhcKey = do
798+
=> CompilerBuild -> m Text
799+
getGhcKey ghcBuild = do
743800
ghcVariant <- asks getGHCVariant
744801
platform <- asks getPlatform
745802
osKey <- getOSKey platform
746-
return $ osKey <> T.pack (ghcVariantSuffix ghcVariant)
803+
return $ osKey <> T.pack (ghcVariantSuffix ghcVariant) <> T.pack (compilerBuildSuffix ghcBuild)
747804

748805
getOSKey :: (MonadThrow m)
749806
=> Platform -> m Text

src/Stack/SetupCmd.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ setup
8282
-> m ()
8383
setup SetupCmdOpts{..} wantedCompiler compilerCheck mstack = do
8484
Config{..} <- asks getConfig
85-
mpaths <- ensureCompiler SetupOpts
85+
mpaths <- fst <$> ensureCompiler SetupOpts
8686
{ soptsInstallIfMissing = True
8787
, soptsUseSystem = configSystemGHC && not scoForceReinstall
8888
, soptsWantedCompiler = wantedCompiler

src/Stack/Solver.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,7 @@ setupCompiler compiler = do
290290
, "compiler available on your PATH." ]
291291

292292
config <- asks getConfig
293-
mpaths <- ensureCompiler SetupOpts
293+
mpaths <- fst <$> ensureCompiler SetupOpts
294294
{ soptsInstallIfMissing = configInstallGHC config
295295
, soptsUseSystem = configSystemGHC config
296296
, soptsWantedCompiler = compiler

src/Stack/Types/Build.hs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ data StackBuildException
9696
(Maybe (CompilerVersion, Arch))
9797
(CompilerVersion, Arch)
9898
GHCVariant
99+
CompilerBuild
99100
VersionCheck
100101
(Maybe (Path Abs File))
101102
Text -- recommended resolution
@@ -145,7 +146,7 @@ instance Show StackBuildException where
145146
", the package id couldn't be found " <> "(via ghc-pkg describe " <>
146147
packageNameString name <> "). This shouldn't happen, " <>
147148
"please report as a bug")
148-
show (CompilerVersionMismatch mactual (expected, earch) ghcVariant check mstack resolution) = concat
149+
show (CompilerVersionMismatch mactual (expected, earch) ghcVariant ghcBuild check mstack resolution) = concat
149150
[ case mactual of
150151
Nothing -> "No compiler found, expected "
151152
Just (actual, arch) -> concat
@@ -164,6 +165,7 @@ instance Show StackBuildException where
164165
, " ("
165166
, C.display earch
166167
, ghcVariantSuffix ghcVariant
168+
, compilerBuildSuffix ghcBuild
167169
, ") (based on "
168170
, case mstack of
169171
Nothing -> "command line arguments"

0 commit comments

Comments
 (0)