Skip to content

Commit

Permalink
'stack setup' support Ubuntu 16.10 (no-pie) and add 'ghc-build' option
Browse files Browse the repository at this point in the history
fixes #2542
  • Loading branch information
borsboom committed Sep 11, 2016
1 parent 9b1eea6 commit e3aa238
Show file tree
Hide file tree
Showing 7 changed files with 196 additions and 72 deletions.
7 changes: 6 additions & 1 deletion ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,12 @@ Behavior changes:

Other enhancements:

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

Bug fixes:

Expand Down
10 changes: 9 additions & 1 deletion doc/yaml_configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -435,6 +435,14 @@ Specify a variant binary distribution of GHC to use. Known values:
[setup-info](#setup-info) so `stack setup` knows where to download it, or
pass the `stack setup --ghc-bindist` argument on the command-line

### ghc-build

(Since 1.2.1)

Specify a specialized architecture bindist to use. Normally this is
determined automatically, but you can override the autodetected value here.
Possible arguments include `standard`, `gmp4`, `tinfo6`, and `nopie`.

### setup-info

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

`url` may be either URL or (since UNRELEASED) absolute file path.
`url` may be either URL or (since 1.2.0) absolute file path.

### pvp-bounds

Expand Down
1 change: 1 addition & 0 deletions src/Stack/Config.hs
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,7 @@ configFromConfigMonoid configStackRoot configUserConfigPath mresolver mproject C
configMonoidPackageIndices

configGHCVariant0 = getFirst configMonoidGHCVariant
configGHCBuild = getFirst configMonoidGHCBuild

configSystemGHC = fromFirst (isNothing configGHCVariant0) configMonoidSystemGHC
configInstallGHC = fromFirst False configMonoidInstallGHC
Expand Down
21 changes: 20 additions & 1 deletion src/Stack/Options.hs
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ cleanOptsParser = CleanShallow <$> packages <|> doFullClean
-- | Command-line arguments parser for configuration.
configOptsParser :: GlobalOptsContext -> Parser ConfigMonoid
configOptsParser hide0 =
(\stackRoot workDir buildOpts dockerOpts nixOpts systemGHC installGHC arch ghcVariant jobs includes libs overrideGccPath skipGHCCheck skipMsys localBin modifyCodePage allowDifferentUser -> mempty
(\stackRoot workDir buildOpts dockerOpts nixOpts systemGHC installGHC arch ghcVariant ghcBuild jobs includes libs overrideGccPath skipGHCCheck skipMsys localBin modifyCodePage allowDifferentUser -> mempty
{ configMonoidStackRoot = stackRoot
, configMonoidWorkDir = workDir
, configMonoidBuildOpts = buildOpts
Expand All @@ -221,6 +221,7 @@ configOptsParser hide0 =
, configMonoidSkipGHCCheck = skipGHCCheck
, configMonoidArch = arch
, configMonoidGHCVariant = ghcVariant
, configMonoidGHCBuild = ghcBuild
, configMonoidJobs = jobs
, configMonoidExtraIncludeDirs = includes
, configMonoidExtraLibDirs = libs
Expand Down Expand Up @@ -261,6 +262,7 @@ configOptsParser hide0 =
<> hide
))
<*> optionalFirst (ghcVariantParser (hide0 /= OuterGlobalOpts))
<*> optionalFirst (ghcBuildParser (hide0 /= OuterGlobalOpts))
<*> optionalFirst (option auto
( long "jobs"
<> short 'j'
Expand Down Expand Up @@ -854,6 +856,23 @@ ghcVariantParser hide =
Left e -> readerError (show e)
Right v -> return v

-- | GHC build parser
ghcBuildParser :: Bool -> Parser CompilerBuild
ghcBuildParser hide =
option
readGHCBuild
(long "ghc-build" <> metavar "BUILD" <>
help
"Specialized GHC build, e.g. 'gmp4' or 'standard' (usually auto-detected)" <>
hideMods hide
)
where
readGHCBuild = do
s <- readerAsk
case parseCompilerBuild s of
Left e -> readerError (show e)
Right v -> return v

-- | Parser for @solverCmd@
solverOptsParser :: Parser Bool
solverOptsParser = boolFlags False
Expand Down
153 changes: 88 additions & 65 deletions src/Stack/Setup.hs
Original file line number Diff line number Diff line change
Expand Up @@ -476,67 +476,85 @@ ensureCompiler sopts = do
-- | Determine which GHC build to use dependong on which shared libraries are available
-- on the system.
getGhcBuild
:: (MonadIO m, MonadBaseControl IO m, MonadCatch m, MonadLogger m, HasPlatform env, MonadReader env m)
:: (MonadIO m, MonadBaseControl IO m, MonadCatch m, MonadLogger m, HasPlatform env, HasConfig env, MonadReader env m)
=> EnvOverride -> m CompilerBuild
getGhcBuild menv = do

-- TODO: a more reliable, flexible, and data driven approach would be to actually download small
-- "test" executables (from setup-info) that link to the same gmp/tinfo versions
-- that GHC does (i.e. built in same environment as the GHC bindist). The algorithm would go
-- something like this:
--
-- check for previous 'uname -a'/`ldconfig -p` plus compiler version/variant in cache
-- if cached, then use that as suffix
-- otherwise:
-- download setup-info
-- go through all with right prefix for os/version/variant
-- first try "standard" (no extra suffix), then the rest
-- download "compatibility check" exe if not already downloaded
-- try running it
-- if successful, then choose that
-- cache compiler suffix with the uname -a and ldconfig -p output hash plus compiler version
--
-- Of course, could also try to make a static GHC bindist instead of all this rigamarole.

platform <- asks getPlatform
case platform of
Platform _ Linux -> do
eldconfigOut <- tryProcessStdout Nothing menv "ldconfig" ["-p"]
let firstWords = case eldconfigOut of
Right ldconfigOut -> mapMaybe (headMay . T.words) $
T.lines $ T.decodeUtf8With T.lenientDecode ldconfigOut
Left _ -> []
checkLib lib
| libT `elem` firstWords = do
$logDebug ("Found shared library " <> libT <> " in 'ldconfig -p' output")
return True
config <- asks getConfig
case configGHCBuild config of
Just ghcBuild -> return ghcBuild
Nothing -> determineGhcBuild
where
determineGhcBuild = do
-- TODO: a more reliable, flexible, and data driven approach would be to actually download small
-- "test" executables (from setup-info) that link to the same gmp/tinfo versions
-- that GHC does (i.e. built in same environment as the GHC bindist). The algorithm would go
-- something like this:
--
-- check for previous 'uname -a'/`ldconfig -p` plus compiler version/variant in cache
-- if cached, then use that as suffix
-- otherwise:
-- download setup-info
-- go through all with right prefix for os/version/variant
-- first try "standard" (no extra suffix), then the rest
-- download "compatibility check" exe if not already downloaded
-- try running it
-- if successful, then choose that
-- cache compiler suffix with the uname -a and ldconfig -p output hash plus compiler version
--
-- Of course, could also try to make a static GHC bindist instead of all this rigamarole.

platform <- asks getPlatform
case platform of
Platform _ Linux -> do
eldconfigOut <- tryProcessStdout Nothing menv "ldconfig" ["-p"]
egccErrOut <- tryProcessStderrStdout Nothing menv "gcc" ["-v"]
let firstWords = case eldconfigOut of
Right ldconfigOut -> mapMaybe (headMay . T.words) $
T.lines $ T.decodeUtf8With T.lenientDecode ldconfigOut
Left _ -> []
checkLib lib
| libT `elem` firstWords = do
$logDebug ("Found shared library " <> libT <> " in 'ldconfig -p' output")
return True
#ifndef WINDOWS
-- (mkAbsDir "/usr/lib") fails to compile on Windows, thus the CPP
| otherwise = do
-- This is a workaround for the fact that libtinfo.so.6 doesn't appear in
-- the 'ldconfig -p' output on Arch even when it exists.
-- There doesn't seem to be an easy way to get the true list of directories
-- to scan for shared libs, but this works for our particular case.
e <- doesFileExist ($(mkAbsDir "/usr/lib") </> lib)
if e
then $logDebug ("Found shared library " <> libT <> " in /usr/lib")
else $logDebug ("Did not find shared library " <> libT)
return e
-- (mkAbsDir "/usr/lib") fails to compile on Windows, thus the CPP
| otherwise = do
-- This is a workaround for the fact that libtinfo.so.6 doesn't appear in
-- the 'ldconfig -p' output on Arch even when it exists.
-- There doesn't seem to be an easy way to get the true list of directories
-- to scan for shared libs, but this works for our particular case.
e <- doesFileExist ($(mkAbsDir "/usr/lib") </> lib)
if e
then $logDebug ("Found shared library " <> libT <> " in /usr/lib")
else $logDebug ("Did not find shared library " <> libT)
return e
#endif
where
libT = T.pack (toFilePath lib)
hastinfo5 <- checkLib $(mkRelFile "libtinfo.so.5")
hastinfo6 <- checkLib $(mkRelFile "libtinfo.so.6")
hasncurses6 <- checkLib $(mkRelFile "libncursesw.so.6")
hasgmp5 <- checkLib $(mkRelFile "libgmp.so.10")
hasgmp4 <- checkLib $(mkRelFile "libgmp.so.3")
if | hastinfo5 && hasgmp5 -> useBuild CompilerBuildStandard
| hastinfo6 && hasgmp5 -> useBuild (CompilerBuildSpecialized "tinfo6")
| hasncurses6 && hasgmp5 -> useBuild (CompilerBuildSpecialized "ncurses6")
| hasgmp4 && hastinfo5 -> useBuild (CompilerBuildSpecialized "gmp4")
| otherwise -> useBuild CompilerBuildStandard
_ -> useBuild CompilerBuildStandard
where
where
libT = T.pack (toFilePath lib)
noPie = case egccErrOut of
Right (gccErr,gccOut) ->
"--enable-default-pie" `elem` (S8.words (gccOut <> gccErr))
Left _ -> False
hastinfo5 <- checkLib $(mkRelFile "libtinfo.so.5")
hastinfo6 <- checkLib $(mkRelFile "libtinfo.so.6")
hasncurses6 <- checkLib $(mkRelFile "libncursesw.so.6")
hasgmp5 <- checkLib $(mkRelFile "libgmp.so.10")
hasgmp4 <- checkLib $(mkRelFile "libgmp.so.3")
let libComponents =
if | hastinfo5 && hasgmp5 -> []
| hastinfo6 && hasgmp5 -> ["tinfo6"]
| hasncurses6 && hasgmp5 -> ["ncurses6"]
| hasgmp4 && hastinfo5 -> ["gmp4"]
| otherwise -> []
pieComponents =
if noPie
then ["nopie"]
else []
case (concat [libComponents, pieComponents]) of
[] -> useBuild CompilerBuildStandard
components -> useBuild (CompilerBuildSpecialized (intercalate "-" components))
_ -> useBuild CompilerBuildStandard
useBuild CompilerBuildStandard = do
$logDebug "Using standard GHC build"
return (CompilerBuildStandard)
Expand Down Expand Up @@ -774,7 +792,7 @@ downloadAndInstallCompiler ghcBuild si wanted@GhcVersion{} versionCheck mbindist
_ -> throwM RequireCustomGHCVariant
case wanted of
GhcVersion version ->
return (version, DownloadInfo (T.pack bindistURL) Nothing Nothing)
return (version, GHCDownloadInfo mempty mempty (DownloadInfo (T.pack bindistURL) Nothing Nothing))
_ ->
throwM WantedMustBeGHC
_ -> do
Expand All @@ -786,7 +804,7 @@ downloadAndInstallCompiler ghcBuild si wanted@GhcVersion{} versionCheck mbindist
let installer =
case configPlatform config of
Platform _ Cabal.Windows -> installGHCWindows selectedVersion
_ -> installGHCPosix selectedVersion
_ -> installGHCPosix selectedVersion downloadInfo
$logInfo $
"Preparing to install GHC" <>
(case ghcVariant of
Expand All @@ -799,7 +817,7 @@ downloadAndInstallCompiler ghcBuild si wanted@GhcVersion{} versionCheck mbindist
$logInfo "This will not interfere with any system-level installation."
ghcPkgName <- parsePackageNameFromString ("ghc" ++ ghcVariantSuffix ghcVariant ++ compilerBuildSuffix ghcBuild)
let tool = Tool $ PackageIdentifier ghcPkgName selectedVersion
downloadAndInstallTool (configLocalPrograms config) si downloadInfo tool installer
downloadAndInstallTool (configLocalPrograms config) si (gdiDownloadInfo downloadInfo) tool installer
downloadAndInstallCompiler compilerBuild si wanted versionCheck _mbindistUrl = do
config <- asks getConfig
ghcVariant <- asks getGHCVariant
Expand Down Expand Up @@ -905,13 +923,14 @@ data ArchiveType

installGHCPosix :: (MonadIO m, MonadMask m, MonadLogger m, MonadReader env m, HasConfig env, HasHttpManager env, MonadBaseControl IO m, HasTerminal env)
=> Version
-> GHCDownloadInfo
-> SetupInfo
-> Path Abs File
-> ArchiveType
-> Path Abs Dir
-> Path Abs Dir
-> m ()
installGHCPosix version _ archiveFile archiveType tempDir destDir = do
installGHCPosix version downloadInfo _ archiveFile archiveType tempDir destDir = do
platform <- asks getPlatform
menv0 <- getMinimalEnvOverride
menv <- mkEnvOverride platform (removeHaskellEnvVars (unEnvOverride menv0))
Expand Down Expand Up @@ -942,8 +961,9 @@ installGHCPosix version _ archiveFile archiveType tempDir destDir = do
parseRelDir $
"ghc-" ++ versionString version

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

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

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

$logSticky "Installing GHC ..."
runStep "installing" dir makeTool ["install"]
runStep "installing" dir mempty makeTool ["install"]

$logStickyDone $ "Installed GHC."
$logDebug $ "GHC installed to " <> T.pack (toFilePath destDir)
Expand Down
Loading

0 comments on commit e3aa238

Please sign in to comment.