{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE CPP #-}
{-# OPTIONS_GHC -fno-warn-deprecations #-}
module Distribution.Simple.ConfigureScript (
runConfigureScript
) where
import Prelude ()
import Distribution.Compat.Prelude
import Distribution.PackageDescription
import Distribution.Simple.Program
import Distribution.Simple.Program.Db
import Distribution.Simple.Setup
import Distribution.Simple.LocalBuildInfo
import Distribution.Simple.Utils
import Distribution.Utils.NubList
import Distribution.Verbosity
import Distribution.Pretty
import Distribution.System (buildPlatform)
import System.FilePath (searchPathSeparator, takeDirectory, (</>),
splitDirectories, dropDrive)
#ifdef mingw32_HOST_OS
import System.FilePath (normalise, splitDrive)
#endif
import Distribution.Compat.Directory (makeAbsolute)
import Distribution.Compat.Environment (getEnvironment)
import Distribution.Compat.GetShortPathName (getShortPathName)
import qualified Data.List.NonEmpty as NonEmpty
import qualified Data.Map as Map
runConfigureScript :: Verbosity -> ConfigFlags -> LocalBuildInfo
-> IO ()
runConfigureScript :: Verbosity -> ConfigFlags -> LocalBuildInfo -> IO ()
runConfigureScript Verbosity
verbosity ConfigFlags
flags LocalBuildInfo
lbi = do
[(String, String)]
env <- IO [(String, String)]
getEnvironment
let programDb :: ProgramDb
programDb = LocalBuildInfo -> ProgramDb
withPrograms LocalBuildInfo
lbi
(String
ccProg, [String]
ccFlags) <- Verbosity -> ProgramDb -> IO (String, [String])
configureCCompiler Verbosity
verbosity ProgramDb
programDb
String
ccProgShort <- String -> IO String
getShortPathName String
ccProg
String
configureFile <- String -> IO String
makeAbsolute forall a b. (a -> b) -> a -> b
$
forall a. a -> Maybe a -> a
fromMaybe String
"." (String -> String
takeDirectory forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> LocalBuildInfo -> Maybe String
cabalFilePath LocalBuildInfo
lbi) String -> String -> String
</> String
"configure"
let configureFile' :: String
configureFile' = String -> String
toUnix String
configureFile
forall (t :: * -> *) (f :: * -> *) a b.
(Foldable t, Applicative f) =>
t a -> (a -> f b) -> f ()
for_ [(Char, String)]
badAutoconfCharacters forall a b. (a -> b) -> a -> b
$ \(Char
c, String
cname) ->
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Char
c forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` String -> String
dropDrive String
configureFile') forall a b. (a -> b) -> a -> b
$
Verbosity -> String -> IO ()
warn Verbosity
verbosity forall a b. (a -> b) -> a -> b
$ forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat
[ String
"The path to the './configure' script, '", String
configureFile'
, String
"', contains the character '", [Char
c], String
"' (", String
cname, String
")."
, String
" This may cause the script to fail with an obscure error, or for"
, String
" building the package to fail later."
]
let
flagEnvVar :: FlagName -> String
flagEnvVar :: FlagName -> String
flagEnvVar FlagName
flag = String
"CABAL_FLAG_" forall a. [a] -> [a] -> [a]
++ forall a b. (a -> b) -> [a] -> [b]
map Char -> Char
f (FlagName -> String
unFlagName FlagName
flag)
where f :: Char -> Char
f Char
c
| Char -> Bool
isAlphaNum Char
c = Char
c
| Bool
otherwise = Char
'_'
cabalFlagMap :: Map String (NonEmpty (FlagName, Bool))
cabalFlagMap :: Map String (NonEmpty (FlagName, Bool))
cabalFlagMap = forall k a. Ord k => (a -> a -> a) -> [(k, a)] -> Map k a
Map.fromListWith forall a. Semigroup a => a -> a -> a
(<>)
[ (FlagName -> String
flagEnvVar FlagName
flag, (FlagName
flag, Bool
bool) forall a. a -> [a] -> NonEmpty a
:| [])
| (FlagName
flag, Bool
bool) <- FlagAssignment -> [(FlagName, Bool)]
unFlagAssignment forall a b. (a -> b) -> a -> b
$ LocalBuildInfo -> FlagAssignment
flagAssignment LocalBuildInfo
lbi
]
Map String (FlagName, Bool)
cabalFlagMapDeconflicted :: Map String (FlagName, Bool) <-
forall a b c. (a -> b -> c) -> b -> a -> c
flip forall (t :: * -> *) k a b.
Applicative t =>
(k -> a -> t b) -> Map k a -> t (Map k b)
Map.traverseWithKey Map String (NonEmpty (FlagName, Bool))
cabalFlagMap forall a b. (a -> b) -> a -> b
$ \ String
envVar -> \case
(FlagName, Bool)
singleFlag :| [] -> forall (f :: * -> *) a. Applicative f => a -> f a
pure (FlagName, Bool)
singleFlag
collidingFlags :: NonEmpty (FlagName, Bool)
collidingFlags@((FlagName, Bool)
firstFlag :| (FlagName, Bool)
_ : [(FlagName, Bool)]
_) -> do
let quote :: String -> String
quote String
s = String
"'" forall a. [a] -> [a] -> [a]
++ String
s forall a. [a] -> [a] -> [a]
++ String
"'"
toName :: (FlagName, b) -> String
toName = String -> String
quote forall b c a. (b -> c) -> (a -> b) -> a -> c
. FlagName -> String
unFlagName forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. (a, b) -> a
fst
renderedList :: String
renderedList = forall a. [a] -> [[a]] -> [a]
intercalate String
", " forall a b. (a -> b) -> a -> b
$ forall a. NonEmpty a -> [a]
NonEmpty.toList forall a b. (a -> b) -> a -> b
$ forall {b}. (FlagName, b) -> String
toName forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> NonEmpty (FlagName, Bool)
collidingFlags
Verbosity -> String -> IO ()
warn Verbosity
verbosity forall a b. (a -> b) -> a -> b
$ [String] -> String
unwords
[ String
"Flags", String
renderedList, String
"all map to the same environment variable"
, String -> String
quote String
envVar, String
"causing a collision."
, String
"The value first flag", forall {b}. (FlagName, b) -> String
toName (FlagName, Bool)
firstFlag, String
"will be used."
]
forall (f :: * -> *) a. Applicative f => a -> f a
pure (FlagName, Bool)
firstFlag
let cabalFlagEnv :: [(String, Maybe String)]
cabalFlagEnv = [ (String
envVar, forall a. a -> Maybe a
Just String
val)
| (String
envVar, (FlagName
_, Bool
bool)) <- forall k a. Map k a -> [(k, a)]
Map.toList Map String (FlagName, Bool)
cabalFlagMapDeconflicted
, let val :: String
val = if Bool
bool then String
"1" else String
"0"
] forall a. [a] -> [a] -> [a]
++
[ ( String
"CABAL_FLAGS"
, forall a. a -> Maybe a
Just forall a b. (a -> b) -> a -> b
$ [String] -> String
unwords [ (FlagName, Bool) -> String
showFlagValue (FlagName, Bool)
fv | (FlagName, Bool)
fv <- FlagAssignment -> [(FlagName, Bool)]
unFlagAssignment forall a b. (a -> b) -> a -> b
$ LocalBuildInfo -> FlagAssignment
flagAssignment LocalBuildInfo
lbi ]
)
]
let extraPath :: [String]
extraPath = forall a. NubList a -> [a]
fromNubList forall a b. (a -> b) -> a -> b
$ ConfigFlags -> NubList String
configProgramPathExtra ConfigFlags
flags
let cflagsEnv :: String
cflagsEnv = forall b a. b -> (a -> b) -> Maybe a -> b
maybe ([String] -> String
unwords [String]
ccFlags) (forall a. [a] -> [a] -> [a]
++ (String
" " forall a. [a] -> [a] -> [a]
++ [String] -> String
unwords [String]
ccFlags))
forall a b. (a -> b) -> a -> b
$ forall a b. Eq a => a -> [(a, b)] -> Maybe b
lookup String
"CFLAGS" [(String, String)]
env
spSep :: String
spSep = [Char
searchPathSeparator]
pathEnv :: String
pathEnv = forall b a. b -> (a -> b) -> Maybe a -> b
maybe (forall a. [a] -> [[a]] -> [a]
intercalate String
spSep [String]
extraPath)
((forall a. [a] -> [[a]] -> [a]
intercalate String
spSep [String]
extraPath forall a. [a] -> [a] -> [a]
++ String
spSep)forall a. [a] -> [a] -> [a]
++) forall a b. (a -> b) -> a -> b
$ forall a b. Eq a => a -> [(a, b)] -> Maybe b
lookup String
"PATH" [(String, String)]
env
overEnv :: [(String, Maybe String)]
overEnv = (String
"CFLAGS", forall a. a -> Maybe a
Just String
cflagsEnv) forall a. a -> [a] -> [a]
:
[(String
"PATH", forall a. a -> Maybe a
Just String
pathEnv) | Bool -> Bool
not (forall (t :: * -> *) a. Foldable t => t a -> Bool
null [String]
extraPath)] forall a. [a] -> [a] -> [a]
++
[(String, Maybe String)]
cabalFlagEnv
hp :: Platform
hp = LocalBuildInfo -> Platform
hostPlatform LocalBuildInfo
lbi
maybeHostFlag :: [String]
maybeHostFlag = if Platform
hp forall a. Eq a => a -> a -> Bool
== Platform
buildPlatform then [] else [String
"--host=" forall a. [a] -> [a] -> [a]
++ forall a. Show a => a -> String
show (forall a. Pretty a => a -> Doc
pretty Platform
hp)]
args' :: [String]
args' = String
configureFile'forall a. a -> [a] -> [a]
:[String]
args forall a. [a] -> [a] -> [a]
++ [String
"CC=" forall a. [a] -> [a] -> [a]
++ String
ccProgShort] forall a. [a] -> [a] -> [a]
++ [String]
maybeHostFlag
shProg :: Program
shProg = String -> Program
simpleProgram String
"sh"
progDb :: ProgramDb
progDb = (ProgramSearchPath -> ProgramSearchPath) -> ProgramDb -> ProgramDb
modifyProgramSearchPath
(\ProgramSearchPath
p -> forall a b. (a -> b) -> [a] -> [b]
map String -> ProgramSearchPathEntry
ProgramSearchPathDir [String]
extraPath forall a. [a] -> [a] -> [a]
++ ProgramSearchPath
p) ProgramDb
emptyProgramDb
Maybe ConfiguredProgram
shConfiguredProg <- Program -> ProgramDb -> Maybe ConfiguredProgram
lookupProgram Program
shProg
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
`fmap` Verbosity -> Program -> ProgramDb -> IO ProgramDb
configureProgram Verbosity
verbosity Program
shProg ProgramDb
progDb
case Maybe ConfiguredProgram
shConfiguredProg of
Just ConfiguredProgram
sh -> Verbosity -> ProgramInvocation -> IO ()
runProgramInvocation Verbosity
verbosity forall a b. (a -> b) -> a -> b
$
(ConfiguredProgram -> [String] -> ProgramInvocation
programInvocation (ConfiguredProgram
sh {programOverrideEnv :: [(String, Maybe String)]
programOverrideEnv = [(String, Maybe String)]
overEnv}) [String]
args')
{ progInvokeCwd :: Maybe String
progInvokeCwd = forall a. a -> Maybe a
Just (LocalBuildInfo -> String
buildDir LocalBuildInfo
lbi) }
Maybe ConfiguredProgram
Nothing -> forall a. Verbosity -> String -> IO a
die' Verbosity
verbosity String
notFoundMsg
where
args :: [String]
args = Bool -> ConfigFlags -> [String]
configureArgs Bool
backwardsCompatHack ConfigFlags
flags
backwardsCompatHack :: Bool
backwardsCompatHack = Bool
False
notFoundMsg :: String
notFoundMsg = String
"The package has a './configure' script. "
forall a. [a] -> [a] -> [a]
++ String
"If you are on Windows, This requires a "
forall a. [a] -> [a] -> [a]
++ String
"Unix compatibility toolchain such as MinGW+MSYS or Cygwin. "
forall a. [a] -> [a] -> [a]
++ String
"If you are not on Windows, ensure that an 'sh' command "
forall a. [a] -> [a] -> [a]
++ String
"is discoverable in your path."
toUnix :: String -> String
#ifdef mingw32_HOST_OS
toUnix s = let tmp = normalise s
(l, rest) = case splitDrive tmp of
([], x) -> ("/" , x)
(h:_, x) -> ('/':h:"/", x)
parts = splitDirectories rest
in l ++ intercalate "/" parts
#else
toUnix :: String -> String
toUnix String
s = forall a. [a] -> [[a]] -> [a]
intercalate String
"/" forall a b. (a -> b) -> a -> b
$ String -> [String]
splitDirectories String
s
#endif
badAutoconfCharacters :: [(Char, String)]
badAutoconfCharacters :: [(Char, String)]
badAutoconfCharacters =
[ (Char
' ', String
"space")
, (Char
'\t', String
"tab")
, (Char
'\n', String
"newline")
, (Char
'\0', String
"null")
, (Char
'"', String
"double quote")
, (Char
'#', String
"hash")
, (Char
'$', String
"dollar sign")
, (Char
'&', String
"ampersand")
, (Char
'\'', String
"single quote")
, (Char
'(', String
"left bracket")
, (Char
')', String
"right bracket")
, (Char
'*', String
"star")
, (Char
';', String
"semicolon")
, (Char
'<', String
"less-than sign")
, (Char
'=', String
"equals sign")
, (Char
'>', String
"greater-than sign")
, (Char
'?', String
"question mark")
, (Char
'[', String
"left square bracket")
, (Char
'\\', String
"backslash")
, (Char
'`', String
"backtick")
, (Char
'|', String
"pipe")
]