Skip to content

Commit e3aa238

Browse files
committed
'stack setup' support Ubuntu 16.10 (no-pie) and add 'ghc-build' option
fixes commercialhaskell#2542
1 parent 9b1eea6 commit e3aa238

7 files changed

Lines changed: 196 additions & 72 deletions

File tree

ChangeLog.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,12 @@ Behavior changes:
1010

1111
Other enhancements:
1212

13-
* Add`support for `system-ghc` and `install-ghc` fields to `stack config set` command.
13+
* Add support for `system-ghc` and `install-ghc` fields to `stack config set` command.
14+
* Add `ghc-build` option to override autodetected GHC build to use (e.g. gmp4,
15+
tinfo6, nopie) on Linux.
16+
* `stack setup` detects systems where gcc enables PIE by default (such as Ubuntu
17+
16.10) and adjusts the GHC `configure` options accordingly.
18+
[#2542](https://github.com/commercialhaskell/stack/issues/2542)
1419

1520
Bug fixes:
1621

doc/yaml_configuration.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -435,6 +435,14 @@ Specify a variant binary distribution of GHC to use. Known values:
435435
[setup-info](#setup-info) so `stack setup` knows where to download it, or
436436
pass the `stack setup --ghc-bindist` argument on the command-line
437437

438+
### ghc-build
439+
440+
(Since 1.2.1)
441+
442+
Specify a specialized architecture bindist to use. Normally this is
443+
determined automatically, but you can override the autodetected value here.
444+
Possible arguments include `standard`, `gmp4`, `tinfo6`, and `nopie`.
445+
438446
### setup-info
439447

440448
(Since 0.1.5)
@@ -451,7 +459,7 @@ setup-info:
451459
url: "https://example.com/ghc-7.10.2-i386-unknown-mingw32-foo.tar.xz"
452460
```
453461

454-
`url` may be either URL or (since UNRELEASED) absolute file path.
462+
`url` may be either URL or (since 1.2.0) absolute file path.
455463

456464
### pvp-bounds
457465

src/Stack/Config.hs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,7 @@ configFromConfigMonoid configStackRoot configUserConfigPath mresolver mproject C
227227
configMonoidPackageIndices
228228

229229
configGHCVariant0 = getFirst configMonoidGHCVariant
230+
configGHCBuild = getFirst configMonoidGHCBuild
230231

231232
configSystemGHC = fromFirst (isNothing configGHCVariant0) configMonoidSystemGHC
232233
configInstallGHC = fromFirst False configMonoidInstallGHC

src/Stack/Options.hs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,7 @@ cleanOptsParser = CleanShallow <$> packages <|> doFullClean
210210
-- | Command-line arguments parser for configuration.
211211
configOptsParser :: GlobalOptsContext -> Parser ConfigMonoid
212212
configOptsParser hide0 =
213-
(\stackRoot workDir buildOpts dockerOpts nixOpts systemGHC installGHC arch ghcVariant jobs includes libs overrideGccPath skipGHCCheck skipMsys localBin modifyCodePage allowDifferentUser -> mempty
213+
(\stackRoot workDir buildOpts dockerOpts nixOpts systemGHC installGHC arch ghcVariant ghcBuild jobs includes libs overrideGccPath skipGHCCheck skipMsys localBin modifyCodePage allowDifferentUser -> mempty
214214
{ configMonoidStackRoot = stackRoot
215215
, configMonoidWorkDir = workDir
216216
, configMonoidBuildOpts = buildOpts
@@ -221,6 +221,7 @@ configOptsParser hide0 =
221221
, configMonoidSkipGHCCheck = skipGHCCheck
222222
, configMonoidArch = arch
223223
, configMonoidGHCVariant = ghcVariant
224+
, configMonoidGHCBuild = ghcBuild
224225
, configMonoidJobs = jobs
225226
, configMonoidExtraIncludeDirs = includes
226227
, configMonoidExtraLibDirs = libs
@@ -261,6 +262,7 @@ configOptsParser hide0 =
261262
<> hide
262263
))
263264
<*> optionalFirst (ghcVariantParser (hide0 /= OuterGlobalOpts))
265+
<*> optionalFirst (ghcBuildParser (hide0 /= OuterGlobalOpts))
264266
<*> optionalFirst (option auto
265267
( long "jobs"
266268
<> short 'j'
@@ -854,6 +856,23 @@ ghcVariantParser hide =
854856
Left e -> readerError (show e)
855857
Right v -> return v
856858

859+
-- | GHC build parser
860+
ghcBuildParser :: Bool -> Parser CompilerBuild
861+
ghcBuildParser hide =
862+
option
863+
readGHCBuild
864+
(long "ghc-build" <> metavar "BUILD" <>
865+
help
866+
"Specialized GHC build, e.g. 'gmp4' or 'standard' (usually auto-detected)" <>
867+
hideMods hide
868+
)
869+
where
870+
readGHCBuild = do
871+
s <- readerAsk
872+
case parseCompilerBuild s of
873+
Left e -> readerError (show e)
874+
Right v -> return v
875+
857876
-- | Parser for @solverCmd@
858877
solverOptsParser :: Parser Bool
859878
solverOptsParser = boolFlags False

src/Stack/Setup.hs

Lines changed: 88 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -476,67 +476,85 @@ ensureCompiler sopts = do
476476
-- | Determine which GHC build to use dependong on which shared libraries are available
477477
-- on the system.
478478
getGhcBuild
479-
:: (MonadIO m, MonadBaseControl IO m, MonadCatch m, MonadLogger m, HasPlatform env, MonadReader env m)
479+
:: (MonadIO m, MonadBaseControl IO m, MonadCatch m, MonadLogger m, HasPlatform env, HasConfig env, MonadReader env m)
480480
=> EnvOverride -> m CompilerBuild
481481
getGhcBuild menv = do
482482

483-
-- TODO: a more reliable, flexible, and data driven approach would be to actually download small
484-
-- "test" executables (from setup-info) that link to the same gmp/tinfo versions
485-
-- that GHC does (i.e. built in same environment as the GHC bindist). The algorithm would go
486-
-- something like this:
487-
--
488-
-- check for previous 'uname -a'/`ldconfig -p` plus compiler version/variant in cache
489-
-- if cached, then use that as suffix
490-
-- otherwise:
491-
-- download setup-info
492-
-- go through all with right prefix for os/version/variant
493-
-- first try "standard" (no extra suffix), then the rest
494-
-- download "compatibility check" exe if not already downloaded
495-
-- try running it
496-
-- if successful, then choose that
497-
-- cache compiler suffix with the uname -a and ldconfig -p output hash plus compiler version
498-
--
499-
-- Of course, could also try to make a static GHC bindist instead of all this rigamarole.
500-
501-
platform <- asks getPlatform
502-
case platform of
503-
Platform _ Linux -> do
504-
eldconfigOut <- tryProcessStdout Nothing menv "ldconfig" ["-p"]
505-
let firstWords = case eldconfigOut of
506-
Right ldconfigOut -> mapMaybe (headMay . T.words) $
507-
T.lines $ T.decodeUtf8With T.lenientDecode ldconfigOut
508-
Left _ -> []
509-
checkLib lib
510-
| libT `elem` firstWords = do
511-
$logDebug ("Found shared library " <> libT <> " in 'ldconfig -p' output")
512-
return True
483+
config <- asks getConfig
484+
case configGHCBuild config of
485+
Just ghcBuild -> return ghcBuild
486+
Nothing -> determineGhcBuild
487+
where
488+
determineGhcBuild = do
489+
-- TODO: a more reliable, flexible, and data driven approach would be to actually download small
490+
-- "test" executables (from setup-info) that link to the same gmp/tinfo versions
491+
-- that GHC does (i.e. built in same environment as the GHC bindist). The algorithm would go
492+
-- something like this:
493+
--
494+
-- check for previous 'uname -a'/`ldconfig -p` plus compiler version/variant in cache
495+
-- if cached, then use that as suffix
496+
-- otherwise:
497+
-- download setup-info
498+
-- go through all with right prefix for os/version/variant
499+
-- first try "standard" (no extra suffix), then the rest
500+
-- download "compatibility check" exe if not already downloaded
501+
-- try running it
502+
-- if successful, then choose that
503+
-- cache compiler suffix with the uname -a and ldconfig -p output hash plus compiler version
504+
--
505+
-- Of course, could also try to make a static GHC bindist instead of all this rigamarole.
506+
507+
platform <- asks getPlatform
508+
case platform of
509+
Platform _ Linux -> do
510+
eldconfigOut <- tryProcessStdout Nothing menv "ldconfig" ["-p"]
511+
egccErrOut <- tryProcessStderrStdout Nothing menv "gcc" ["-v"]
512+
let firstWords = case eldconfigOut of
513+
Right ldconfigOut -> mapMaybe (headMay . T.words) $
514+
T.lines $ T.decodeUtf8With T.lenientDecode ldconfigOut
515+
Left _ -> []
516+
checkLib lib
517+
| libT `elem` firstWords = do
518+
$logDebug ("Found shared library " <> libT <> " in 'ldconfig -p' output")
519+
return True
513520
#ifndef WINDOWS
514-
-- (mkAbsDir "/usr/lib") fails to compile on Windows, thus the CPP
515-
| otherwise = do
516-
-- This is a workaround for the fact that libtinfo.so.6 doesn't appear in
517-
-- the 'ldconfig -p' output on Arch even when it exists.
518-
-- There doesn't seem to be an easy way to get the true list of directories
519-
-- to scan for shared libs, but this works for our particular case.
520-
e <- doesFileExist ($(mkAbsDir "/usr/lib") </> lib)
521-
if e
522-
then $logDebug ("Found shared library " <> libT <> " in /usr/lib")
523-
else $logDebug ("Did not find shared library " <> libT)
524-
return e
521+
-- (mkAbsDir "/usr/lib") fails to compile on Windows, thus the CPP
522+
| otherwise = do
523+
-- This is a workaround for the fact that libtinfo.so.6 doesn't appear in
524+
-- the 'ldconfig -p' output on Arch even when it exists.
525+
-- There doesn't seem to be an easy way to get the true list of directories
526+
-- to scan for shared libs, but this works for our particular case.
527+
e <- doesFileExist ($(mkAbsDir "/usr/lib") </> lib)
528+
if e
529+
then $logDebug ("Found shared library " <> libT <> " in /usr/lib")
530+
else $logDebug ("Did not find shared library " <> libT)
531+
return e
525532
#endif
526-
where
527-
libT = T.pack (toFilePath lib)
528-
hastinfo5 <- checkLib $(mkRelFile "libtinfo.so.5")
529-
hastinfo6 <- checkLib $(mkRelFile "libtinfo.so.6")
530-
hasncurses6 <- checkLib $(mkRelFile "libncursesw.so.6")
531-
hasgmp5 <- checkLib $(mkRelFile "libgmp.so.10")
532-
hasgmp4 <- checkLib $(mkRelFile "libgmp.so.3")
533-
if | hastinfo5 && hasgmp5 -> useBuild CompilerBuildStandard
534-
| hastinfo6 && hasgmp5 -> useBuild (CompilerBuildSpecialized "tinfo6")
535-
| hasncurses6 && hasgmp5 -> useBuild (CompilerBuildSpecialized "ncurses6")
536-
| hasgmp4 && hastinfo5 -> useBuild (CompilerBuildSpecialized "gmp4")
537-
| otherwise -> useBuild CompilerBuildStandard
538-
_ -> useBuild CompilerBuildStandard
539-
where
533+
where
534+
libT = T.pack (toFilePath lib)
535+
noPie = case egccErrOut of
536+
Right (gccErr,gccOut) ->
537+
"--enable-default-pie" `elem` (S8.words (gccOut <> gccErr))
538+
Left _ -> False
539+
hastinfo5 <- checkLib $(mkRelFile "libtinfo.so.5")
540+
hastinfo6 <- checkLib $(mkRelFile "libtinfo.so.6")
541+
hasncurses6 <- checkLib $(mkRelFile "libncursesw.so.6")
542+
hasgmp5 <- checkLib $(mkRelFile "libgmp.so.10")
543+
hasgmp4 <- checkLib $(mkRelFile "libgmp.so.3")
544+
let libComponents =
545+
if | hastinfo5 && hasgmp5 -> []
546+
| hastinfo6 && hasgmp5 -> ["tinfo6"]
547+
| hasncurses6 && hasgmp5 -> ["ncurses6"]
548+
| hasgmp4 && hastinfo5 -> ["gmp4"]
549+
| otherwise -> []
550+
pieComponents =
551+
if noPie
552+
then ["nopie"]
553+
else []
554+
case (concat [libComponents, pieComponents]) of
555+
[] -> useBuild CompilerBuildStandard
556+
components -> useBuild (CompilerBuildSpecialized (intercalate "-" components))
557+
_ -> useBuild CompilerBuildStandard
540558
useBuild CompilerBuildStandard = do
541559
$logDebug "Using standard GHC build"
542560
return (CompilerBuildStandard)
@@ -774,7 +792,7 @@ downloadAndInstallCompiler ghcBuild si wanted@GhcVersion{} versionCheck mbindist
774792
_ -> throwM RequireCustomGHCVariant
775793
case wanted of
776794
GhcVersion version ->
777-
return (version, DownloadInfo (T.pack bindistURL) Nothing Nothing)
795+
return (version, GHCDownloadInfo mempty mempty (DownloadInfo (T.pack bindistURL) Nothing Nothing))
778796
_ ->
779797
throwM WantedMustBeGHC
780798
_ -> do
@@ -786,7 +804,7 @@ downloadAndInstallCompiler ghcBuild si wanted@GhcVersion{} versionCheck mbindist
786804
let installer =
787805
case configPlatform config of
788806
Platform _ Cabal.Windows -> installGHCWindows selectedVersion
789-
_ -> installGHCPosix selectedVersion
807+
_ -> installGHCPosix selectedVersion downloadInfo
790808
$logInfo $
791809
"Preparing to install GHC" <>
792810
(case ghcVariant of
@@ -799,7 +817,7 @@ downloadAndInstallCompiler ghcBuild si wanted@GhcVersion{} versionCheck mbindist
799817
$logInfo "This will not interfere with any system-level installation."
800818
ghcPkgName <- parsePackageNameFromString ("ghc" ++ ghcVariantSuffix ghcVariant ++ compilerBuildSuffix ghcBuild)
801819
let tool = Tool $ PackageIdentifier ghcPkgName selectedVersion
802-
downloadAndInstallTool (configLocalPrograms config) si downloadInfo tool installer
820+
downloadAndInstallTool (configLocalPrograms config) si (gdiDownloadInfo downloadInfo) tool installer
803821
downloadAndInstallCompiler compilerBuild si wanted versionCheck _mbindistUrl = do
804822
config <- asks getConfig
805823
ghcVariant <- asks getGHCVariant
@@ -905,13 +923,14 @@ data ArchiveType
905923

906924
installGHCPosix :: (MonadIO m, MonadMask m, MonadLogger m, MonadReader env m, HasConfig env, HasHttpManager env, MonadBaseControl IO m, HasTerminal env)
907925
=> Version
926+
-> GHCDownloadInfo
908927
-> SetupInfo
909928
-> Path Abs File
910929
-> ArchiveType
911930
-> Path Abs Dir
912931
-> Path Abs Dir
913932
-> m ()
914-
installGHCPosix version _ archiveFile archiveType tempDir destDir = do
933+
installGHCPosix version downloadInfo _ archiveFile archiveType tempDir destDir = do
915934
platform <- asks getPlatform
916935
menv0 <- getMinimalEnvOverride
917936
menv <- mkEnvOverride platform (removeHaskellEnvVars (unEnvOverride menv0))
@@ -942,8 +961,9 @@ installGHCPosix version _ archiveFile archiveType tempDir destDir = do
942961
parseRelDir $
943962
"ghc-" ++ versionString version
944963

945-
let runStep step wd cmd args = do
946-
result <- try (readProcessNull (Just wd) menv cmd args)
964+
let runStep step wd env cmd args = do
965+
menv' <- modifyEnvOverride menv (Map.union env)
966+
result <- try (readProcessNull (Just wd) menv' cmd args)
947967
case result of
948968
Right _ -> return ()
949969
Left ex -> do
@@ -962,13 +982,16 @@ installGHCPosix version _ archiveFile archiveType tempDir destDir = do
962982

963983
$logSticky $ T.concat ["Unpacking GHC into ", T.pack . toFilePath $ tempDir, " ..."]
964984
$logDebug $ "Unpacking " <> T.pack (toFilePath archiveFile)
965-
runStep "unpacking" tempDir tarTool [compOpt : "xf", toFilePath archiveFile]
985+
runStep "unpacking" tempDir mempty tarTool [compOpt : "xf", toFilePath archiveFile]
966986

967987
$logSticky "Configuring GHC ..."
968-
runStep "configuring" dir (toFilePath $ dir </> $(mkRelFile "configure")) ["--prefix=" ++ toFilePath destDir]
988+
runStep "configuring" dir
989+
(gdiConfigureEnv downloadInfo)
990+
(toFilePath $ dir </> $(mkRelFile "configure"))
991+
(("--prefix=" ++ toFilePath destDir) : map T.unpack (gdiConfigureOpts downloadInfo))
969992

970993
$logSticky "Installing GHC ..."
971-
runStep "installing" dir makeTool ["install"]
994+
runStep "installing" dir mempty makeTool ["install"]
972995

973996
$logStickyDone $ "Installed GHC."
974997
$logDebug $ "GHC installed to " <> T.pack (toFilePath destDir)

0 commit comments

Comments
 (0)