git-annex/Build/EvilLinker.hs

161 lines
4.5 KiB
Haskell
Raw Normal View History

2013-12-06 16:27:21 +00:00
{- Allows linking haskell programs too big for all the files to fit in a
- command line.
-
- See https://ghc.haskell.org/trac/ghc/ticket/8596
-
- Copyright 2013 Joey Hess <id@joeyh.name>
2013-12-06 16:27:21 +00:00
-
- Licensed under the GNU GPL version 3 or higher.
-}
module Main where
import Text.Parsec
import Text.Parsec.String
import Control.Applicative ((<$>))
import Control.Monad
import Data.Maybe
import Data.List
2013-12-06 16:27:21 +00:00
import Utility.Monad
2014-06-13 15:14:50 +00:00
import Utility.Process hiding (env)
import qualified Utility.Process
import Utility.Env
import Utility.Directory
import Utility.Split
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
collectcmd = "collect2.exe"
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
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
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
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 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]
systemenviron <- getEnvironment
let environ' = fromMaybe [] environ ++ systemenviron
out@(_, ok) <- processTranscript' ((proc c ps) { Utility.Process.env = Just environ' }) Nothing
2014-01-30 17:34:24 +00:00
putStrLn $ unwords [c, "finished", show ok]
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
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
c = case parse p "" s of
2013-12-09 19:34:56 +00:00
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