+-- This is the interface between the compilation manager and the
+-- compiler proper (hsc), where we deal with tedious details like
+-- reading the OPTIONS pragma from the source file, and passing the
+-- output of hsc through the C compiler.
+
+-- The driver sits between 'compile' and 'hscMain', translating calls
+-- to the former into calls to the latter, and results from the latter
+-- into results from the former. It does things like preprocessing
+-- the .hs file if necessary, and compiling up the .stub_c files to
+-- generate Linkables.
+
+-- NB. No old interface can also mean that the source has changed.
+
+compile :: HscEnv
+ -> ModSummary
+ -> Bool -- True <=> source unchanged
+ -> Bool -- True <=> have object
+ -> Maybe ModIface -- Old interface, if available
+ -> IO CompResult
+
+data CompResult
+ = CompOK ModDetails -- New details
+ (Maybe GlobalRdrEnv) -- Lexical environment for the module
+ -- (Maybe because we may have loaded it from
+ -- its precompiled interface)
+ ModIface -- New iface
+ (Maybe Linkable) -- New code; Nothing => compilation was not reqd
+ -- (old code is still valid)
+
+ | CompErrs
+
+
+compile hsc_env mod_summary
+ source_unchanged have_object old_iface = do
+
+ let dyn_flags = hsc_dflags hsc_env
+ this_mod = ms_mod mod_summary
+ src_flavour = ms_hsc_src mod_summary
+
+ showPass dyn_flags ("Compiling " ++ showModMsg have_object mod_summary)
+
+ let verb = verbosity dyn_flags
+ let location = ms_location mod_summary
+ let input_fn = expectJust "compile:hs" (ml_hs_file location)
+ let input_fnpp = expectJust "compile:hspp" (ms_hspp_file mod_summary)
+
+ when (verb >= 2) (hPutStrLn stderr ("compile: input file " ++ input_fnpp))
+
+ -- Add in the OPTIONS from the source file
+ -- This is nasty: we've done this once already, in the compilation manager
+ -- It might be better to cache the flags in the ml_hspp_file field,say
+ opts <- getOptionsFromSource input_fnpp
+ (dyn_flags,unhandled_flags) <- processDynamicFlags opts dyn_flags
+ checkProcessArgsResult unhandled_flags input_fn
+
+ let (basename, _) = splitFilename input_fn
+
+ -- We add the directory in which the .hs files resides) to the import path.
+ -- This is needed when we try to compile the .hc file later, if it
+ -- imports a _stub.h file that we created here.
+ let current_dir = directoryOf basename
+ old_paths <- readIORef v_Include_paths
+ writeIORef v_Include_paths (current_dir : old_paths)
+ -- put back the old include paths afterward.
+ later (writeIORef v_Include_paths old_paths) $ do
+
+ -- Figure out what lang we're generating
+ todo <- readIORef v_GhcMode
+ hsc_lang <- hscMaybeAdjustTarget todo src_flavour (hscTarget dyn_flags)
+ -- ... and what the next phase should be
+ next_phase <- hscNextPhase src_flavour hsc_lang
+ -- ... and what file to generate the output into
+ get_output_fn <- genOutputFilenameFunc next_phase False Nothing basename
+ output_fn <- get_output_fn next_phase (Just location)
+
+ let dyn_flags' = dyn_flags { hscTarget = hsc_lang,
+ hscOutName = output_fn,
+ hscStubCOutName = basename ++ "_stub.c",
+ hscStubHOutName = basename ++ "_stub.h",
+ extCoreName = basename ++ ".hcr" }
+
+ -- -no-recomp should also work with --make
+ let do_recomp = recompFlag dyn_flags
+ source_unchanged' = source_unchanged && do_recomp
+ hsc_env' = hsc_env { hsc_dflags = dyn_flags' }
+
+ -- run the compiler
+ hsc_result <- hscMain hsc_env' printErrorsAndWarnings mod_summary
+ source_unchanged' have_object old_iface
+
+ case hsc_result of
+ HscFail -> return CompErrs
+
+ HscNoRecomp details iface -> return (CompOK details Nothing iface Nothing)
+
+ HscRecomp details rdr_env iface
+ stub_h_exists stub_c_exists maybe_interpreted_code
+
+ | isHsBoot src_flavour -- No further compilation to do
+ -> return (CompOK details rdr_env iface Nothing)
+
+ | otherwise -- Normal Haskell source files
+ -> do
+ let
+ maybe_stub_o <- compileStub dyn_flags' stub_c_exists
+ let stub_unlinked = case maybe_stub_o of
+ Nothing -> []
+ Just stub_o -> [ DotO stub_o ]
+
+ (hs_unlinked, unlinked_time) <-
+ case hsc_lang of
+
+ -- in interpreted mode, just return the compiled code
+ -- as our "unlinked" object.
+ HscInterpreted ->
+ case maybe_interpreted_code of
+#ifdef GHCI
+ Just comp_bc -> return ([BCOs comp_bc], ms_hs_date mod_summary)
+ -- Why do we use the timestamp of the source file here,
+ -- rather than the current time? This works better in
+ -- the case where the local clock is out of sync
+ -- with the filesystem's clock. It's just as accurate:
+ -- if the source is modified, then the linkable will
+ -- be out of date.
+#endif
+ Nothing -> panic "compile: no interpreted code"
+
+ -- we're in batch mode: finish the compilation pipeline.
+ _other -> do
+ let object_filename = ml_obj_file location
+
+ runPipeline DoLink dyn_flags ""
+ True Nothing output_fn (Just location)
+ -- the object filename comes from the ModLocation
+
+ o_time <- getModificationTime object_filename
+ return ([DotO object_filename], o_time)
+
+ let linkable = LM unlinked_time this_mod
+ (hs_unlinked ++ stub_unlinked)
+
+ return (CompOK details rdr_env iface (Just linkable))
+
+-----------------------------------------------------------------------------
+-- stub .h and .c files (for foreign export support)
+
+compileStub dflags stub_c_exists
+ | not stub_c_exists = return Nothing
+ | stub_c_exists = do
+ -- compile the _stub.c file w/ gcc
+ let stub_c = hscStubCOutName dflags
+ (_, stub_o) <- runPipeline DoLink dflags "stub-compile"
+ True{-persistent output-}
+ Nothing{-no specific output file-}
+ stub_c
+ Nothing{-no ModLocation-}
+ return (Just stub_o)
+
+
+-- ---------------------------------------------------------------------------
+-- Link
+
+link :: GhciMode -- interactive or batch
+ -> DynFlags -- dynamic flags
+ -> Bool -- attempt linking in batch mode?
+ -> HomePackageTable -- what to link
+ -> IO SuccessFlag
+
+-- For the moment, in the batch linker, we don't bother to tell doLink
+-- which packages to link -- it just tries all that are available.
+-- batch_attempt_linking should only be *looked at* in batch mode. It
+-- should only be True if the upsweep was successful and someone
+-- exports main, i.e., we have good reason to believe that linking
+-- will succeed.
+
+#ifdef GHCI
+link Interactive dflags batch_attempt_linking hpt
+ = do -- Not Linking...(demand linker will do the job)
+ return Succeeded
+#endif
+
+link Batch dflags batch_attempt_linking hpt
+ | batch_attempt_linking
+ = do
+ let
+ home_mod_infos = moduleEnvElts hpt
+
+ -- the packages we depend on
+ pkg_deps = concatMap (dep_pkgs . mi_deps . hm_iface) home_mod_infos
+
+ -- the linkables to link
+ linkables = map hm_linkable home_mod_infos
+
+ when (verb >= 3) $ do
+ hPutStrLn stderr "link: linkables are ..."
+ hPutStrLn stderr (showSDoc (vcat (map ppr linkables)))
+
+ -- check for the -no-link flag
+ omit_linking <- readIORef v_NoLink
+ if omit_linking
+ then do when (verb >= 3) $
+ hPutStrLn stderr "link(batch): linking omitted (-c flag given)."
+ return Succeeded
+ else do
+
+ when (verb >= 1) $
+ hPutStrLn stderr "Linking ..."
+
+ let getOfiles (LM _ _ us) = map nameOfObject (filter isObject us)
+ obj_files = concatMap getOfiles linkables
+
+ -- Don't showPass in Batch mode; doLink will do that for us.
+ staticLink dflags obj_files pkg_deps
+
+ when (verb >= 3) (hPutStrLn stderr "link: done")
+
+ -- staticLink only returns if it succeeds
+ return Succeeded
+
+ | otherwise
+ = do when (verb >= 3) $ do
+ hPutStrLn stderr "link(batch): upsweep (partially) failed OR"
+ hPutStrLn stderr " Main.main not exported; not linking."
+ return Succeeded
+ where
+ verb = verbosity dflags
+
+-- ---------------------------------------------------------------------------
+-- Run a compilation pipeline, consisting of multiple phases.
+
+-- The DynFlags can be modified by phases in the pipeline (eg. by
+-- OPTIONS pragmas), and the changes affect later phases in the
+-- pipeline, but we throw away the resulting DynFlags at the end.
+
+runPipeline
+ :: GhcMode -- when to stop
+ -> DynFlags -- dynamic flags
+ -> String -- "stop after" flag
+ -> Bool -- final output is persistent?
+ -> Maybe FilePath -- where to put the output, optionally
+ -> FilePath -- input filename
+ -> Maybe ModLocation -- a ModLocation for this module, if we have one
+ -> IO (DynFlags, FilePath) -- (final flags, output filename)
+
+runPipeline todo dflags stop_flag keep_output
+ maybe_output_filename input_fn maybe_loc
+ = do
+ split <- readIORef v_Split_object_files
+ let (basename, suffix) = splitFilename input_fn
+ start_phase = startPhase suffix
+
+ todo' = case todo of
+ StopBefore As | split -> StopBefore SplitAs
+ other -> todo
+
+ -- 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.
+ --
+ let stop_phase = case todo' of
+ StopBefore phase -> phase
+ other -> StopLn
+
+ 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 stop_phase keep_output
+ maybe_output_filename basename
+
+ -- Execute the pipeline...
+ (dflags', output_fn, maybe_loc) <- pipeLoop todo' dflags 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 dflags ("Copying `" ++ output_fn ++ "' to `" ++ final_fn
+ ++ "'") output_fn final_fn
+ return (dflags', final_fn)
+ else
+ return (dflags', output_fn)
+
+
+pipeLoop :: GhcMode -> DynFlags -> Phase -> Phase
+ -> FilePath -> String -> Suffix
+ -> (Phase -> Maybe ModLocation -> IO FilePath)
+ -> Maybe ModLocation
+ -> IO (DynFlags, FilePath, Maybe ModLocation)
+
+pipeLoop orig_todo dflags phase stop_phase
+ input_fn orig_basename orig_suff
+ orig_get_output_fn maybe_loc
+
+ | phase `eqPhase` stop_phase -- All done
+ = return (dflags, input_fn, maybe_loc)
+
+ | 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 { (next_phase, dflags', maybe_loc, output_fn)
+ <- runPhase phase orig_todo dflags orig_basename
+ orig_suff input_fn orig_get_output_fn maybe_loc
+ ; pipeLoop orig_todo dflags' next_phase stop_phase output_fn
+ orig_basename orig_suff orig_get_output_fn maybe_loc }
+
+genOutputFilenameFunc :: Phase -> Bool -> Maybe FilePath -> String
+ -> IO (Phase{-next phase-} -> Maybe ModLocation -> IO FilePath)
+genOutputFilenameFunc stop_phase keep_final_output maybe_output_filename basename