2013-12-06 16:27:21 +00:00
|
|
|
|
{- Allows linking haskell programs too big for all the files to fit in a
|
|
|
|
|
- command line.
|
|
|
|
|
-
|
2013-12-06 19:08:30 +00:00
|
|
|
|
- See https://ghc.haskell.org/trac/ghc/ticket/8596
|
|
|
|
|
-
|
2013-12-06 16:27:21 +00:00
|
|
|
|
- Copyright 2013 Joey Hess <joey@kitenet.net>
|
|
|
|
|
-
|
|
|
|
|
- Licensed under the GNU GPL version 3 or higher.
|
|
|
|
|
-}
|
|
|
|
|
|
|
|
|
|
module Main where
|
|
|
|
|
|
2013-12-06 17:14:01 +00:00
|
|
|
|
import Data.List.Utils
|
2013-12-06 16:27:21 +00:00
|
|
|
|
import Text.Parsec
|
|
|
|
|
import Text.Parsec.String
|
|
|
|
|
import Control.Applicative ((<$>))
|
|
|
|
|
import Control.Monad
|
2013-12-10 18:21:22 +00:00
|
|
|
|
import System.Directory
|
|
|
|
|
import Data.Maybe
|
2013-12-06 16:27:21 +00:00
|
|
|
|
|
|
|
|
|
import Utility.Monad
|
|
|
|
|
import Utility.Process
|
2013-12-10 18:21:22 +00:00
|
|
|
|
import Utility.Env
|
2013-12-06 16:27:21 +00:00
|
|
|
|
|
2013-12-06 17:53:58 +00:00
|
|
|
|
data CmdParams = CmdParams
|
|
|
|
|
{ cmd :: String
|
|
|
|
|
, opts :: String
|
|
|
|
|
, env :: Maybe [(String, String)]
|
|
|
|
|
} deriving (Show)
|
2013-12-06 16:27:21 +00:00
|
|
|
|
|
2013-12-06 16:43:30 +00:00
|
|
|
|
{- Find where ghc calls gcc to link the executable. -}
|
2013-12-06 16:27:21 +00:00
|
|
|
|
parseGhcLink :: Parser CmdParams
|
|
|
|
|
parseGhcLink = do
|
2013-12-09 19:28:33 +00:00
|
|
|
|
void $ many prelinkline
|
|
|
|
|
void linkheaderline
|
|
|
|
|
void $ char '"'
|
2013-12-06 16:27:21 +00:00
|
|
|
|
gcccmd <- many1 (noneOf "\"")
|
2013-12-09 19:28:33 +00:00
|
|
|
|
void $ string "\" "
|
2013-12-06 16:27:21 +00:00
|
|
|
|
gccparams <- restOfLine
|
2013-12-06 17:53:58 +00:00
|
|
|
|
return $ CmdParams gcccmd (manglepaths gccparams) Nothing
|
2013-12-06 16:27:21 +00:00
|
|
|
|
where
|
|
|
|
|
linkheaderline = do
|
2013-12-09 19:28:33 +00:00
|
|
|
|
void $ string "*** Linker"
|
2013-12-06 16:27:21 +00:00
|
|
|
|
restOfLine
|
2013-12-06 17:14:01 +00:00
|
|
|
|
prelinkline = do
|
2013-12-09 19:28:33 +00:00
|
|
|
|
void $ notFollowedBy linkheaderline
|
2013-12-06 16:27:21 +00:00
|
|
|
|
restOfLine
|
2013-12-06 16:55:32 +00:00
|
|
|
|
manglepaths = replace "\\" "/"
|
2013-12-06 16:27:21 +00:00
|
|
|
|
|
2013-12-06 17:27:29 +00:00
|
|
|
|
{- Find where gcc calls collect2. -}
|
2013-12-06 16:48:39 +00:00
|
|
|
|
parseGccLink :: Parser CmdParams
|
2013-12-06 17:14:01 +00:00
|
|
|
|
parseGccLink = do
|
2013-12-09 19:28:33 +00:00
|
|
|
|
cenv <- collectenv
|
|
|
|
|
void $ try $ char ' '
|
2013-12-06 17:27:29 +00:00
|
|
|
|
path <- manyTill anyChar (try $ string collectcmd)
|
2013-12-09 19:28:33 +00:00
|
|
|
|
void $ char ' '
|
2013-12-06 17:27:29 +00:00
|
|
|
|
collect2params <- restOfLine
|
2013-12-09 19:28:33 +00:00
|
|
|
|
return $ CmdParams (path ++ collectcmd) (escapeDosPaths collect2params) cenv
|
2013-12-06 17:14:01 +00:00
|
|
|
|
where
|
2013-12-06 17:27:29 +00:00
|
|
|
|
collectcmd = "collect2.exe"
|
2013-12-10 17:51:52 +00:00
|
|
|
|
collectgccenv = "COLLECT_GCC"
|
|
|
|
|
collectltoenv = "COLLECT_LTO_WRAPPER"
|
2013-12-06 18:20:44 +00:00
|
|
|
|
pathenv = "COMPILER_PATH"
|
|
|
|
|
libpathenv = "LIBRARY_PATH"
|
|
|
|
|
optenv = "COLLECT_GCC_OPTIONS"
|
|
|
|
|
collectenv = do
|
2013-12-10 17:51:52 +00:00
|
|
|
|
void $ many1 $ do
|
|
|
|
|
notFollowedBy $ string collectgccenv
|
|
|
|
|
restOfLine
|
|
|
|
|
void $ string collectgccenv
|
|
|
|
|
void $ char '='
|
|
|
|
|
g <- restOfLine
|
|
|
|
|
void $ string collectltoenv
|
|
|
|
|
void $ char '='
|
|
|
|
|
lt <- restOfLine
|
|
|
|
|
void $ many1 $ do
|
|
|
|
|
notFollowedBy $ string pathenv
|
|
|
|
|
restOfLine
|
2013-12-09 19:28:33 +00:00
|
|
|
|
void $ string pathenv
|
|
|
|
|
void $ char '='
|
2013-12-06 18:20:44 +00:00
|
|
|
|
p <- restOfLine
|
2013-12-09 19:28:33 +00:00
|
|
|
|
void $ string libpathenv
|
|
|
|
|
void $ char '='
|
2013-12-06 18:20:44 +00:00
|
|
|
|
lp <- restOfLine
|
2013-12-09 19:28:33 +00:00
|
|
|
|
void $ string optenv
|
|
|
|
|
void $ char '='
|
2013-12-06 18:20:44 +00:00
|
|
|
|
o <- restOfLine
|
2013-12-10 17:51:52 +00:00
|
|
|
|
return $ Just [(collectgccenv, g), (collectltoenv, lt), (pathenv, p), (libpathenv, lp), (optenv, o)]
|
2013-12-06 18:20:44 +00:00
|
|
|
|
|
|
|
|
|
{- Find where collect2 calls ld. -}
|
|
|
|
|
parseCollect2 :: Parser CmdParams
|
|
|
|
|
parseCollect2 = do
|
2013-12-09 19:28:33 +00:00
|
|
|
|
void $ manyTill restOfLine (try versionline)
|
2013-12-06 18:20:44 +00:00
|
|
|
|
path <- manyTill anyChar (try $ string ldcmd)
|
2013-12-09 19:28:33 +00:00
|
|
|
|
void $ char ' '
|
2013-12-06 18:20:44 +00:00
|
|
|
|
params <- restOfLine
|
2013-12-06 18:46:32 +00:00
|
|
|
|
return $ CmdParams (path ++ ldcmd) (escapeDosPaths params) Nothing
|
2013-12-06 18:20:44 +00:00
|
|
|
|
where
|
|
|
|
|
ldcmd = "ld.exe"
|
2013-12-06 22:26:52 +00:00
|
|
|
|
versionline = do
|
2013-12-09 19:28:33 +00:00
|
|
|
|
void $ string "collect2 version"
|
2013-12-06 22:26:52 +00:00
|
|
|
|
restOfLine
|
2013-12-06 16:43:30 +00:00
|
|
|
|
|
2013-12-06 17:39:35 +00:00
|
|
|
|
{- Input contains something like
|
|
|
|
|
- c:/program files/haskell platform/foo -LC:/Program Files/Haskell Platform/ -L...
|
|
|
|
|
- and the *right* spaces must be escaped with \
|
|
|
|
|
-
|
|
|
|
|
- Argh.
|
|
|
|
|
-}
|
|
|
|
|
escapeDosPaths :: String -> String
|
|
|
|
|
escapeDosPaths = replace "Program Files" "Program\\ Files"
|
|
|
|
|
. replace "program files" "program\\ files"
|
|
|
|
|
. replace "Haskell Platform" "Haskell\\ Platform"
|
|
|
|
|
. replace "haskell platform" "haskell\\ platform"
|
2013-12-06 18:58:26 +00:00
|
|
|
|
. replace "Application Data" "Application\\ Data"
|
2013-12-08 19:42:27 +00:00
|
|
|
|
. replace "Documents and Settings" "Documents\\ and\\ Settings"
|
2013-12-06 19:24:39 +00:00
|
|
|
|
. replace "Files (x86)" "Files\\ (x86)"
|
|
|
|
|
. replace "files (x86)" "files\\ (x86)"
|
2013-12-06 17:39:35 +00:00
|
|
|
|
|
2013-12-06 16:27:21 +00:00
|
|
|
|
restOfLine :: Parser String
|
|
|
|
|
restOfLine = newline `after` many (noneOf "\n")
|
|
|
|
|
|
2013-12-06 18:29:58 +00:00
|
|
|
|
getOutput :: String -> [String] -> Maybe [(String, String)] -> IO (String, Bool)
|
2013-12-09 19:28:33 +00:00
|
|
|
|
getOutput c ps environ = do
|
|
|
|
|
putStrLn $ unwords [c, show ps]
|
2013-12-10 18:21:22 +00:00
|
|
|
|
systemenviron <- getEnvironment
|
|
|
|
|
let environ' = fromMaybe [] environ ++ systemenviron
|
|
|
|
|
out@(s, ok) <- processTranscript' c ps (Just environ') Nothing
|
2013-12-09 19:28:33 +00:00
|
|
|
|
putStrLn $ unwords [c, "finished", show ok, "output size:", show (length s)]
|
2013-12-07 15:14:54 +00:00
|
|
|
|
return out
|
2013-12-06 16:27:21 +00:00
|
|
|
|
|
2013-12-06 16:43:30 +00:00
|
|
|
|
atFile :: FilePath -> String
|
|
|
|
|
atFile f = '@':f
|
|
|
|
|
|
2013-12-06 18:29:58 +00:00
|
|
|
|
runAtFile :: Parser CmdParams -> String -> FilePath -> [String] -> IO (String, Bool)
|
2013-12-06 16:43:30 +00:00
|
|
|
|
runAtFile p s f extraparams = do
|
2013-12-09 19:17:55 +00:00
|
|
|
|
when (null $ opts c) $
|
|
|
|
|
error $ "failed to find any options for " ++ f ++ " in >>>" ++ s ++ "<<<"
|
2013-12-06 16:43:30 +00:00
|
|
|
|
writeFile f (opts c)
|
2013-12-06 17:53:58 +00:00
|
|
|
|
out <- getOutput (cmd c) (atFile f:extraparams) (env c)
|
2013-12-06 16:43:30 +00:00
|
|
|
|
removeFile f
|
|
|
|
|
return out
|
|
|
|
|
where
|
2013-12-09 19:34:56 +00:00
|
|
|
|
c = case parse p "" s of
|
|
|
|
|
Left e -> error $
|
|
|
|
|
(show e) ++
|
|
|
|
|
"\n<<<\n" ++ s ++ "\n>>>"
|
|
|
|
|
Right r -> r
|
2013-12-06 16:43:30 +00:00
|
|
|
|
|
2013-12-09 19:28:33 +00:00
|
|
|
|
main :: IO ()
|
2013-12-06 16:27:21 +00:00
|
|
|
|
main = do
|
2013-12-06 18:29:58 +00:00
|
|
|
|
ghcout <- fst <$> getOutput "cabal"
|
2013-12-07 15:13:02 +00:00
|
|
|
|
["build", "--ghc-options=-v -keep-tmp-files"] Nothing
|
2013-12-06 18:29:58 +00:00
|
|
|
|
gccout <- fst <$> runAtFile parseGhcLink ghcout "gcc.opt" ["-v"]
|
|
|
|
|
collect2out <- fst <$> runAtFile parseGccLink gccout "collect2.opt" ["-v"]
|
|
|
|
|
(out, ok) <- runAtFile parseCollect2 collect2out "ld.opt" []
|
|
|
|
|
unless ok $
|
|
|
|
|
error $ "ld failed:\n" ++ out
|