Skip to content
Snippets Groups Projects
Commit a84cc2cd authored by Simon Marlow's avatar Simon Marlow
Browse files

[project @ 2000-07-27 10:26:04 by simonmar]

Cleanup of the compilation pipeline.  Now the list of phases to run
for each filename is generated statically, rather than on-the-fly.

Things should be more robust; some nonsense combinations of flags and
input files are now thrown out.
parent 023fea0f
No related merge requests found
...@@ -87,6 +87,7 @@ version_str = cProjectVersion ++ ...@@ -87,6 +87,7 @@ version_str = cProjectVersion ++
( if cProjectPatchLevel /= "0" && cProjectPatchLevel /= "" ( if cProjectPatchLevel /= "0" && cProjectPatchLevel /= ""
then '.':cProjectPatchLevel then '.':cProjectPatchLevel
else "") else "")
-- umm, isn't the patchlevel included in the version number? --SDM
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
-- Phases -- Phases
...@@ -115,9 +116,7 @@ data Phase ...@@ -115,9 +116,7 @@ data Phase
| SplitAs | SplitAs
| As | As
| Ln | Ln
deriving (Eq,Ord,Enum,Ix,Show,Bounded) deriving (Eq)
initial_phase = Unlit
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
-- Errors -- Errors
...@@ -182,11 +181,11 @@ cleanTempFiles = do ...@@ -182,11 +181,11 @@ cleanTempFiles = do
verb <- readIORef verbose verb <- readIORef verbose
let blowAway f = let blowAway f =
(do on verb (hPutStrLn stderr ("removing: " ++ f)) (do when verb (hPutStrLn stderr ("removing: " ++ f))
if '*' `elem` f then system ("rm -f " ++ f) >> return () if '*' `elem` f then system ("rm -f " ++ f) >> return ()
else removeFile f) else removeFile f)
`catchAllIO` `catchAllIO`
(\e -> on verb (hPutStrLn stderr (\e -> when verb (hPutStrLn stderr
("warning: can't remove tmp file" ++ f))) ("warning: can't remove tmp file" ++ f)))
mapM_ blowAway fs mapM_ blowAway fs
...@@ -195,23 +194,24 @@ cleanTempFiles = do ...@@ -195,23 +194,24 @@ cleanTempFiles = do
GLOBAL_VAR(stop_after, Ln, Phase) GLOBAL_VAR(stop_after, Ln, Phase)
end_phase_flag :: String -> Maybe Phase endPhaseFlag :: String -> Maybe Phase
end_phase_flag "-M" = Just MkDependHS endPhaseFlag "-M" = Just MkDependHS
end_phase_flag "-E" = Just Cpp endPhaseFlag "-E" = Just Cpp
end_phase_flag "-C" = Just Hsc endPhaseFlag "-C" = Just Hsc
end_phase_flag "-S" = Just Mangle endPhaseFlag "-S" = Just Mangle
end_phase_flag "-c" = Just As endPhaseFlag "-c" = Just As
end_phase_flag _ = Nothing endPhaseFlag _ = Nothing
getStopAfter :: [String] getStopAfter :: [String]
-> IO ( [String] -- rest of command line -> IO ( [String] -- rest of command line
, Phase -- stop after phase , Phase -- stop after phase
, String -- "stop after" flag
, Bool -- do linking? , Bool -- do linking?
) )
getStopAfter flags getStopAfter flags
= case my_partition end_phase_flag flags of = case my_partition endPhaseFlag flags of
([] , rest) -> return (rest, As, True) ([] , rest) -> return (rest, As, "", True)
([one], rest) -> return (rest, one, False) ([(flag,one)], rest) -> return (rest, one, flag, False)
(_ , rest) -> throwDyn AmbiguousPhase (_ , rest) -> throwDyn AmbiguousPhase
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
...@@ -366,7 +366,7 @@ setOptLevel "not" = writeIORef opt_level 0 ...@@ -366,7 +366,7 @@ setOptLevel "not" = writeIORef opt_level 0
setOptLevel [c] | isDigit c = do setOptLevel [c] | isDigit c = do
let level = ord c - ord '0' let level = ord c - ord '0'
writeIORef opt_level level writeIORef opt_level level
on (level >= 1) go_via_C when (level >= 1) go_via_C
setOptLevel s = throwDyn (UnknownFlag ("-O"++s)) setOptLevel s = throwDyn (UnknownFlag ("-O"++s))
go_via_C = do go_via_C = do
...@@ -1074,41 +1074,6 @@ optionRegex = mkRegex "{-#[ \t]+OPTIONS[ \t]+(.*)#-}" ...@@ -1074,41 +1074,6 @@ optionRegex = mkRegex "{-#[ \t]+OPTIONS[ \t]+(.*)#-}"
get_source_files :: [String] -> ([String],[String]) get_source_files :: [String] -> ([String],[String])
get_source_files = partition (('-' /=) . head) get_source_files = partition (('-' /=) . head)
suffixes :: [(String,Phase)]
suffixes =
[ ("lhs", Unlit)
, ("hs", Cpp)
, ("hc", HCc)
, ("c", Cc)
, ("raw_s", Mangle)
, ("s", As)
, ("S", As)
, ("o", Ln)
]
phase_input_ext Unlit = "lhs"
phase_input_ext Cpp = "lpp"
phase_input_ext Hsc = "cpp"
phase_input_ext HCc = "hc"
phase_input_ext Cc = "c"
phase_input_ext Mangle = "raw_s"
phase_input_ext SplitMangle = "split_s" -- not really generated
phase_input_ext As = "s"
phase_input_ext SplitAs = "split_s" -- not really generated
phase_input_ext Ln = "o"
find_phase :: String -> ([(Phase,String)], [String])
-> ([(Phase,String)], [String])
find_phase f (phase_srcs, unknown_srcs)
= case lookup ext suffixes of
Just the_phase -> ((the_phase,f):phase_srcs, unknown_srcs)
Nothing -> (phase_srcs, f:unknown_srcs)
where (basename,ext) = split_filename f
find_phases srcs = (phase_srcs, unknown_srcs)
where (phase_srcs, unknown_srcs) = foldr find_phase ([],[]) srcs
main = main =
-- all error messages are propagated as exceptions -- all error messages are propagated as exceptions
my_catchDyn (\dyn -> case dyn of my_catchDyn (\dyn -> case dyn of
...@@ -1147,7 +1112,7 @@ main = ...@@ -1147,7 +1112,7 @@ main =
writeIORef package_details (read contents) writeIORef package_details (read contents)
-- find the phase to stop after (i.e. -E, -C, -c, -S flags) -- find the phase to stop after (i.e. -E, -C, -c, -S flags)
(flags2, stop_phase, do_linking) <- getStopAfter argv' (flags2, stop_phase, stop_flag, do_linking) <- getStopAfter argv'
-- process all the other arguments, and get the source files -- process all the other arguments, and get the source files
srcs <- processArgs flags2 [] srcs <- processArgs flags2 []
...@@ -1165,36 +1130,31 @@ main = ...@@ -1165,36 +1130,31 @@ main =
then do_mkdependHS flags2 srcs then do_mkdependHS flags2 srcs
else do else do
-- for each source file, find which phase to start at -- for each source file, find which phases to run
let (phase_srcs, unknown_srcs) = find_phases srcs pipelines <- mapM (genPipeline stop_phase stop_flag) srcs
let src_pipelines = zip srcs pipelines
o_file <- readIORef output_file o_file <- readIORef output_file
if isJust o_file && not do_linking && length phase_srcs > 1 if isJust o_file && not do_linking && length srcs > 1
then throwDyn MultipleSrcsOneOutput then throwDyn MultipleSrcsOneOutput
else do else do
if null unknown_srcs && null phase_srcs if null srcs then throwDyn NoInputFiles else do
then throwDyn NoInputFiles
else do
-- if we have unknown files, and we're not doing linking, complain
-- (otherwise pass them through to the linker).
if not (null unknown_srcs) && not do_linking
then throwDyn (UnknownFileType (head unknown_srcs))
else do
let compileFile :: (Phase, String) -> IO String let compileFile (src, phases) =
compileFile (phase, src) = do run_pipeline phases src do_linking True orig_base
let (orig_base, _) = split_filename src where (orig_base, _) = splitFilename src
if phase < Ln -- anything to do?
then run_pipeline stop_phase do_linking True orig_base (phase,src)
else return src
o_files <- mapM compileFile phase_srcs o_files <- mapM compileFile src_pipelines
when do_linking $ when do_linking (do_link o_files)
do_link o_files unknown_srcs
-----------------------------------------------------------------------------
-- genPipeline
--
-- Herein is all the magic about which phases to run in which order, whether
-- the intermediate files should be in /tmp or in the current directory,
-- what the suffix of the intermediate files should be, etc.
-- The following compilation pipeline algorithm is fairly hacky. A -- The following compilation pipeline algorithm is fairly hacky. A
-- better way to do this would be to express the whole comilation as a -- better way to do this would be to express the whole comilation as a
...@@ -1216,80 +1176,162 @@ main = ...@@ -1216,80 +1176,162 @@ main =
-- that the C compiler from the first comilation can be overlapped -- that the C compiler from the first comilation can be overlapped
-- with the hsc comilation for the second file. -- with the hsc comilation for the second file.
run_pipeline data IntermediateFileType
:: Phase -- phase to end on (never Linker) = Temporary
-> Bool -- doing linking afterward? | Persistent
-> Bool -- take into account -o when generating output? deriving (Eq)
-> String -- original basename (eg. Main)
-> (Phase, String) -- phase to run, input file -- the first compilation phase for a given file is determined
-> IO String -- return final filename -- by its suffix.
startPhase "lhs" = Unlit
run_pipeline last_phase do_linking use_ofile orig_basename (phase, input_fn) startPhase "hs" = Cpp
| phase > last_phase = return input_fn startPhase "hc" = HCc
| otherwise startPhase "c" = Cc
= do startPhase "raw_s" = Mangle
startPhase "s" = As
startPhase "S" = As
startPhase "o" = Ln
genPipeline
:: Phase -- stop after this phase
-> String -- "stop after" flag (for error messages)
-> String -- original filename
-> IO [ -- list of phases to run for this file
(Phase,
IntermediateFileType, -- keep the output from this phase?
String) -- output file suffix
]
genPipeline stop_after stop_after_flag filename
= do
split <- readIORef split_object_files
mangle <- readIORef do_asm_mangling
lang <- readIORef hsc_lang
keep_hc <- readIORef keep_hc_files
keep_raw_s <- readIORef keep_raw_s_files
keep_s <- readIORef keep_s_files
let (basename,ext) = split_filename input_fn let
----------- ----- ---- --- -- -- - - -
start_phase = startPhase suffix
split <- readIORef split_object_files (basename, suffix) = splitFilename filename
mangle <- readIORef do_asm_mangling
lang <- readIORef hsc_lang
-- figure out what the next phase is. This is haskell_ish_file = suffix `elem` [ "hs", "lhs", "hc" ]
-- straightforward, apart from the fact that hsc can generate c_ish_file = suffix `elem` [ "c", "s", "S" ] -- maybe .cc et al.??
-- either C or assembler direct, and assembly mangling is
-- optional, and splitting involves one extra phase and an alternate
-- assembler.
let next_phase =
case phase of
Hsc -> case lang of
HscC -> HCc
HscAsm | split -> SplitMangle
| otherwise -> As
HCc | mangle -> Mangle pipeline
| otherwise -> As | haskell_ish_file =
case lang of
HscC | split && mangle -> [ Unlit, Cpp, Hsc, HCc, Mangle,
SplitMangle, SplitAs ]
| mangle -> [ Unlit, Cpp, Hsc, HCc, Mangle, As ]
| split -> not_valid
| otherwise -> [ Unlit, Cpp, Hsc, HCc, As ]
Cc -> As HscAsm | split -> not_valid
| otherwise -> [ Unlit, Cpp, Hsc, As ]
Mangle | not split -> As HscJava | split -> not_valid
SplitMangle -> SplitAs | otherwise -> error "not implemented: compiling via Java"
SplitAs -> Ln
_ -> succ phase | c_ish_file = [ Cc, As ]
| otherwise = [ ] -- just pass this file through to the linker
-- filename extension for the output, determined by next_phase -- ToDo: this is somewhat cryptic
let new_ext = phase_input_ext next_phase not_valid = throwDyn (OtherError ("invalid option combination"))
----------- ----- ---- --- -- -- - - -
-- Figure out what the output from this pass should be called. -- this shouldn't happen.
if start_phase /= Ln && start_phase `notElem` pipeline
then throwDyn (OtherError ("can't find starting phase for "
++ filename))
else do
-- this might happen, eg. ghc -S Foo.o
if stop_after /= As && stop_after `notElem` pipeline
then throwDyn (OtherError ("flag " ++ stop_after_flag
++ " is incompatible with source file "
++ filename))
else do
-- If we're keeping the output from this phase, then we just save
-- it in the current directory, otherwise we generate a new temp file. let
keep_s <- readIORef keep_s_files ----------- ----- ---- --- -- -- - - -
keep_raw_s <- readIORef keep_raw_s_files annotatePipeline
keep_hc <- readIORef keep_hc_files :: [Phase] -> Phase
let keep_this_output = -> [(Phase, IntermediateFileType, String{-file extension-})]
case next_phase of annotatePipeline [] _ = []
Ln -> True annotatePipeline (Ln:_) _ = []
Mangle | keep_raw_s -> True -- first enhancement :) annotatePipeline (phase:next_phase:ps) stop =
As | keep_s -> True (phase, keep_this_output, phase_input_ext next_phase)
HCc | keep_hc -> True : annotatePipeline (next_phase:ps) stop
_other -> False where
keep_this_output
| phase == stop = Persistent
| otherwise =
case next_phase of
Ln -> Persistent
Mangle | keep_raw_s -> Persistent
As | keep_s -> Persistent
HCc | keep_hc -> Persistent
_other -> Temporary
-- add information about output files to the pipeline
-- the suffix on an output file is determined by the next phase
-- in the pipeline, so we add linking to the end of the pipeline
-- to force the output from the final phase to be a .o file.
annotated_pipeline = annotatePipeline (pipeline ++ [ Ln ]) stop_after
phase_ne p (p1,_,_) = (p1 /= p)
----------- ----- ---- --- -- -- - - -
return $
dropWhile (phase_ne start_phase) .
foldr (\p ps -> if phase_ne stop_after p then p:ps else [p]) []
$ annotated_pipeline
-- the output suffix for a given phase is uniquely determined by
-- the input requirements of the next phase.
phase_input_ext Unlit = "lhs"
phase_input_ext Cpp = "lpp"
phase_input_ext Hsc = "cpp"
phase_input_ext HCc = "hc"
phase_input_ext Cc = "c"
phase_input_ext Mangle = "raw_s"
phase_input_ext SplitMangle = "split_s" -- not really generated
phase_input_ext As = "s"
phase_input_ext SplitAs = "split_s" -- not really generated
phase_input_ext Ln = "o"
run_pipeline
:: [ (Phase, IntermediateFileType, String) ] -- phases to run
-> String -- input file
-> Bool -- doing linking afterward?
-> Bool -- take into account -o when generating output?
-> String -- original basename (eg. Main)
-> IO String -- return final filename
run_pipeline [] input_fn _ _ _ = return input_fn
run_pipeline ((phase, keep, o_suffix):phases)
input_fn do_linking use_ofile orig_basename
= do
output_fn <- output_fn <-
(if next_phase > last_phase && not do_linking && use_ofile (if null phases && not do_linking && use_ofile
then do o_file <- readIORef output_file then do o_file <- readIORef output_file
case o_file of case o_file of
Just s -> return s Just s -> return s
Nothing -> do Nothing -> do
f <- odir_ify (orig_basename ++ '.':new_ext) f <- odir_ify (orig_basename ++ '.':o_suffix)
osuf_ify f osuf_ify f
-- .o files are always kept. .s files and .hc file may be kept. else if keep == Persistent
else if keep_this_output then odir_ify (orig_basename ++ '.':o_suffix)
then odir_ify (orig_basename ++ '.':new_ext) else do filename <- newTempName o_suffix
else do filename <- newTempName new_ext
add files_to_clean filename add files_to_clean filename
return filename return filename
) )
...@@ -1298,12 +1340,11 @@ run_pipeline last_phase do_linking use_ofile orig_basename (phase, input_fn) ...@@ -1298,12 +1340,11 @@ run_pipeline last_phase do_linking use_ofile orig_basename (phase, input_fn)
-- sadly, ghc -E is supposed to write the file to stdout. We -- sadly, ghc -E is supposed to write the file to stdout. We
-- generate <file>.cpp, so we also have to cat the file here. -- generate <file>.cpp, so we also have to cat the file here.
when (next_phase > last_phase && last_phase == Cpp) $ when (null phases && phase == Cpp) $
run_something "Dump pre-processed file to stdout" run_something "Dump pre-processed file to stdout"
("cat " ++ output_fn) ("cat " ++ output_fn)
run_pipeline last_phase do_linking use_ofile run_pipeline phases output_fn do_linking use_ofile orig_basename
orig_basename (next_phase, output_fn)
-- find a temporary name that doesn't already exist. -- find a temporary name that doesn't already exist.
...@@ -1445,7 +1486,7 @@ run_phase Hsc basename input_fn output_fn ...@@ -1445,7 +1486,7 @@ run_phase Hsc basename input_fn output_fn
))) )))
-- Generate -Rghc-timing info -- Generate -Rghc-timing info
on (timing) ( when (timing) (
run_something "Generate timing stats" run_something "Generate timing stats"
(findFile "ghc-stats" cGHC_STATS ++ ' ':stat_file) (findFile "ghc-stats" cGHC_STATS ++ ' ':stat_file)
) )
...@@ -1456,7 +1497,7 @@ run_phase Hsc basename input_fn output_fn ...@@ -1456,7 +1497,7 @@ run_phase Hsc basename input_fn output_fn
-- copy .h_stub file into current dir if present -- copy .h_stub file into current dir if present
b <- doesFileExist tmp_stub_h b <- doesFileExist tmp_stub_h
on b (do when b (do
run_something "Copy stub .h file" run_something "Copy stub .h file"
("cp " ++ tmp_stub_h ++ ' ':stub_h) ("cp " ++ tmp_stub_h ++ ' ':stub_h)
...@@ -1472,10 +1513,10 @@ run_phase Hsc basename input_fn output_fn ...@@ -1472,10 +1513,10 @@ run_phase Hsc basename input_fn output_fn
]) ])
-- compile the _stub.c file w/ gcc -- compile the _stub.c file w/ gcc
run_pipeline As False{-no linking-} pipeline <- genPipeline As "" stub_c
run_pipeline pipeline stub_c False{-no linking-}
False{-no -o option-} False{-no -o option-}
(basename++"_stub") (basename++"_stub")
(Cc, stub_c)
add ld_inputs (basename++"_stub.o") add ld_inputs (basename++"_stub.o")
) )
...@@ -1649,8 +1690,8 @@ run_phase SplitAs basename input_fn output_fn ...@@ -1649,8 +1690,8 @@ run_phase SplitAs basename input_fn output_fn
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
-- Linking -- Linking
do_link :: [String] -> [String] -> IO () do_link :: [String] -> IO ()
do_link o_files unknown_srcs = do do_link o_files = do
ln <- readIORef pgm_l ln <- readIORef pgm_l
verb <- is_verbose verb <- is_verbose
o_file <- readIORef output_file o_file <- readIORef output_file
...@@ -1681,7 +1722,6 @@ do_link o_files unknown_srcs = do ...@@ -1681,7 +1722,6 @@ do_link o_files unknown_srcs = do
(unwords (unwords
([ ln, verb, "-o", output_fn ] ([ ln, verb, "-o", output_fn ]
++ o_files ++ o_files
++ unknown_srcs
++ extra_ld_inputs ++ extra_ld_inputs
++ lib_path_opts ++ lib_path_opts
++ lib_opts ++ lib_opts
...@@ -1724,7 +1764,7 @@ run_something phase_name cmd ...@@ -1724,7 +1764,7 @@ run_something phase_name cmd
if exit_code /= ExitSuccess if exit_code /= ExitSuccess
then throwDyn (PhaseFailed phase_name exit_code) then throwDyn (PhaseFailed phase_name exit_code)
else do on verb (putStr "\n") else do when verb (putStr "\n")
return () return ()
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
...@@ -2042,13 +2082,13 @@ findFile name alt_path = unsafePerformIO (do ...@@ -2042,13 +2082,13 @@ findFile name alt_path = unsafePerformIO (do
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
-- Utils -- Utils
my_partition :: (a -> Maybe b) -> [a] -> ([b],[a]) my_partition :: (a -> Maybe b) -> [a] -> ([(a,b)],[a])
my_partition p [] = ([],[]) my_partition p [] = ([],[])
my_partition p (a:as) my_partition p (a:as)
= let (bs,cs) = my_partition p as in = let (bs,cs) = my_partition p as in
case p a of case p a of
Nothing -> (bs,a:cs) Nothing -> (bs,a:cs)
Just b -> (b:bs,cs) Just b -> ((a,b):bs,cs)
my_prefix_match :: String -> String -> Maybe String my_prefix_match :: String -> String -> Maybe String
my_prefix_match [] rest = Just rest my_prefix_match [] rest = Just rest
...@@ -2068,16 +2108,14 @@ postfixMatch pat str = prefixMatch (reverse pat) (reverse str) ...@@ -2068,16 +2108,14 @@ postfixMatch pat str = prefixMatch (reverse pat) (reverse str)
later = flip finally later = flip finally
on b io = if b then io >> return (error "on") else return (error "on")
my_catch = flip catchAllIO my_catch = flip catchAllIO
my_catchDyn = flip catchDyn my_catchDyn = flip catchDyn
global :: a -> IORef a global :: a -> IORef a
global a = unsafePerformIO (newIORef a) global a = unsafePerformIO (newIORef a)
split_filename :: String -> (String,String) splitFilename :: String -> (String,String)
split_filename f = (reverse (stripDot rev_basename), reverse rev_ext) splitFilename f = (reverse (stripDot rev_basename), reverse rev_ext)
where (rev_ext, rev_basename) = span ('.' /=) (reverse f) where (rev_ext, rev_basename) = span ('.' /=) (reverse f)
stripDot ('.':xs) = xs stripDot ('.':xs) = xs
stripDot xs = xs stripDot xs = xs
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment