--- Herein is all the magic about which phases to run in which order, whether
--- the intermediate files should be in TMPDIR or in the current directory,
--- what the suffix of the intermediate files should be, etc.
-
--- The following compilation pipeline algorithm is fairly hacky. A
--- better way to do this would be to express the whole compilation as a
--- data flow DAG, where the nodes are the intermediate files and the
--- edges are the compilation phases. This framework would also work
--- nicely if a haskell dependency generator was included in the
--- driver.
-
--- It would also deal much more cleanly with compilation phases that
--- generate multiple intermediates, (eg. hsc generates .hc, .hi, and
--- possibly stub files), where some of the output files need to be
--- processed further (eg. the stub files need to be compiled by the C
--- compiler).
-
--- A cool thing to do would then be to execute the data flow graph
--- concurrently, automatically taking advantage of extra processors on
--- the host machine. For example, when compiling two Haskell files
--- where one depends on the other, the data flow graph would determine
--- that the C compiler from the first compilation can be overlapped
--- with the hsc compilation for the second file.
-
-data IntermediateFileType
- = Temporary
- | Persistent
- deriving (Eq, Show)
-
-genPipeline
- :: GhcMode -- when to stop
- -> String -- "stop after" flag (for error messages)
- -> Bool -- True => output is persistent
- -> HscLang -- preferred output language for hsc
- -> (FilePath, String) -- original filename & its suffix
- -> IO [ -- list of phases to run for this file
- (Phase,
- IntermediateFileType, -- keep the output from this phase?
- String) -- output file suffix
- ]
-
-genPipeline todo stop_flag persistent_output lang (filename,suffix)
+ -- We want to catch cases of "you can't get there from here" before
+ -- we start the pipeline, because otherwise it will just run off the
+ -- end.
+ --
+ -- There is a partial ordering on phases, where A < B iff A occurs
+ -- before B in a normal compilation pipeline.
+ --
+ when (not (start_phase `happensBefore` stop_phase)) $
+ throwDyn (UsageError
+ ("flag `" ++ stop_flag
+ ++ "' is incompatible with source file `"
+ ++ input_fn ++ "'"))
+
+ -- generate a function which will be used to calculate output file names
+ -- as we go along.
+ get_output_fn <- genOutputFilenameFunc keep_output maybe_output_filename
+ stop_phase basename
+
+ -- and execute the pipeline...
+ (output_fn, maybe_loc) <-
+ pipeLoop start_phase stop_phase input_fn basename suffix
+ get_output_fn maybe_loc
+
+ -- sometimes, a compilation phase doesn't actually generate any output
+ -- (eg. the CPP phase when -fcpp is not turned on). If we end on this
+ -- stage, but we wanted to keep the output, then we have to explicitly
+ -- copy the file.
+ if keep_output
+ then do final_fn <- get_output_fn stop_phase maybe_loc
+ when (final_fn /= output_fn) $
+ copy ("Copying `" ++ output_fn ++ "' to `" ++ final_fn
+ ++ "'") output_fn final_fn
+ return final_fn
+ else
+ return output_fn
+
+
+pipeLoop :: Phase -> Phase -> FilePath -> String -> Suffix
+ -> (Phase -> Maybe ModLocation -> IO FilePath)
+ -> Maybe ModLocation -> IO (FilePath, Maybe ModLocation)
+
+pipeLoop phase stop_phase input_fn orig_basename orig_suff
+ get_output_fn maybe_loc
+
+ | phase == stop_phase = return (input_fn, maybe_loc) -- all done
+
+ | not (phase `happensBefore` stop_phase) =
+ -- Something has gone wrong. We'll try to cover all the cases when
+ -- this could happen, so if we reach here it is a panic.
+ -- eg. it might happen if the -C flag is used on a source file that
+ -- has {-# OPTIONS -fasm #-}.
+ panic ("pipeLoop: at phase " ++ show phase ++
+ " but I wanted to stop at phase " ++ show stop_phase)
+
+ | otherwise = do
+ maybe_next_phase <- runPhase phase orig_basename orig_suff input_fn
+ get_output_fn maybe_loc
+ case maybe_next_phase of
+ (Nothing, maybe_loc, output_fn) -> do
+ -- we stopped early, but return the *final* filename
+ -- (it presumably already exists)
+ final_fn <- get_output_fn stop_phase maybe_loc
+ return (final_fn, maybe_loc)
+ (Just next_phase, maybe_loc, output_fn) ->
+ pipeLoop next_phase stop_phase output_fn
+ orig_basename orig_suff get_output_fn maybe_loc
+
+
+genOutputFilenameFunc :: Bool -> Maybe FilePath -> Phase -> String
+ -> IO (Phase{-next phase-} -> Maybe ModLocation -> IO FilePath)
+genOutputFilenameFunc keep_final_output maybe_output_filename
+ stop_phase basename