Commit 854ab9a4 authored by simonmar's avatar simonmar

[project @ 2002-12-18 16:29:25 by simonmar]

"Auto" packages.

The big change here is that it is no longer necessary to explicitly
say '-package X' on the command line if X is a package containing
hierarchical Haskell modules.  All packages marked "auto" contribute
to the import path, so their modules are always available.  At link
time, the compiler knows which packages are actually used by the
program, and it links in only those libraries needed.

There's one exception: one-shot linking.  If you link a program using

    ghc -o prog A.o B.o ...

then you need to explicitly add -package flags for each package
required (except base & haskell98) because the compiler has no
information about the package dependencies in this case.

Package configs have a new field: auto, which is either True or False.
Non-auto packages must be mentioned on the command-line as usual.
Non-auto packages are still required for:

  - non-hierarchical libraries (to avoid polluting the module namespace)

  - packages with no Haskell content

  - if you want more than one version of a package, or packages
    providing overlapping functionality where the user must decide
    which one to use.

Doc changes to follow...
parent f6ba57ea
......@@ -571,7 +571,7 @@ cmLoadModules cmstate1 dflags mg2unsorted
valid_old_linkables
when (verb >= 2) $
putStrLn (showSDoc (text "Stable modules:"
hPutStrLn stderr (showSDoc (text "Stable modules:"
<+> sep (map (text.moduleNameUserString) stable_mods)))
-- Unload any modules which are going to be re-linked this
......@@ -646,7 +646,7 @@ cmLoadModules cmstate1 dflags mg2unsorted
hPutStrLn stderr "Warning: output was redirected with -o, but no output will be generated\nbecause there is no Main module."
-- link everything together
linkresult <- link ghci_mode dflags a_root_is_Main (hptLinkables hpt3)
linkresult <- link ghci_mode dflags a_root_is_Main hpt3
cmLoadFinish Succeeded linkresult
hpt3 modsDone ghci_mode pcs3
......@@ -673,7 +673,7 @@ cmLoadModules cmstate1 dflags mg2unsorted
cleanTempFilesExcept verb (ppFilesFromSummaries mods_to_keep)
-- Link everything together
linkresult <- link ghci_mode dflags False (hptLinkables hpt4)
linkresult <- link ghci_mode dflags False hpt4
cmLoadFinish Failed linkresult
hpt4 mods_to_keep ghci_mode pcs3
......
......@@ -30,8 +30,7 @@ import ByteCodeItbls ( ItblEnv )
import ByteCodeAsm ( CompiledByteCode(..), bcoFreeNames, UnlinkedBCO(..))
import Packages
import DriverState ( v_Library_paths, v_Opt_l, getPackageConfigMap,
getStaticOpts )
import DriverState ( v_Library_paths, v_Opt_l, getStaticOpts )
import Finder ( findModule, findLinkable )
import HscTypes
import Name ( Name, nameModule, isExternalName )
......@@ -224,8 +223,8 @@ getLinkDeps hpt pit mods
} ;
-- 3. For each dependent module, find its linkable
-- This will either be in the HPT or (in the case of one-shot compilation)
-- we may need to use maybe_getFileLinkable
-- This will either be in the HPT or (in the case of one-shot
-- compilation) we may need to use maybe_getFileLinkable
lnks_needed <- mapM get_linkable mods_needed ;
return (lnks_needed, pkgs_needed) }
......
......@@ -24,19 +24,18 @@ import qualified PrintJava
import OccurAnal ( occurAnalyseBinds )
#endif
import Packages ( PackageConfig(name), packageNameString )
import DriverState ( getExplicitPackagesAnd, getPackageCIncludes )
import FastString ( unpackFS )
import DriverState ( v_HCHeader )
import Id ( Id )
import StgSyn ( StgBinding )
import AbsCSyn ( AbstractC )
import PprAbsC ( dumpRealC, writeRealC )
import HscTypes ( ModGuts(..), ModGuts, ForeignStubs(..), typeEnvTyCons )
import HscTypes ( ModGuts(..), ModGuts, ForeignStubs(..),
typeEnvTyCons, Dependencies(..) )
import CmdLineOpts
import ErrUtils ( dumpIfSet_dyn, showPass )
import Outputable
import Pretty ( Mode(..), printDoc )
import CmdLineOpts ( DynFlags, HscLang(..), dopt_OutName )
import DATA_IOREF ( readIORef, writeIORef )
import Monad ( when )
import IO
\end{code}
......@@ -51,15 +50,16 @@ import IO
\begin{code}
codeOutput :: DynFlags
-> ModGuts
-> [(StgBinding,[Id])] -- The STG program with SRTs
-> AbstractC -- Compiled abstract C
-> IO (Bool{-stub_h_exists-}, Bool{-stub_c_exists-})
codeOutput dflags
(ModGuts {mg_module = mod_name,
mg_types = type_env,
mg_foreign = foreign_stubs,
mg_deps = deps,
mg_binds = core_binds})
stg_binds flat_abstractC
flat_abstractC
= let
tycons = typeEnvTyCons type_env
in
......@@ -71,27 +71,26 @@ codeOutput dflags
do { showPass dflags "CodeOutput"
; let filenm = dopt_OutName dflags
; stub_names <- outputForeignStubs dflags foreign_stubs
; case dopt_HscLang dflags of
HscInterpreted -> return stub_names
HscAsm -> outputAsm dflags filenm flat_abstractC
>> return stub_names
HscC -> outputC dflags filenm flat_abstractC stub_names
>> return stub_names
; stubs_exist <- outputForeignStubs dflags foreign_stubs
; case dopt_HscLang dflags of {
HscInterpreted -> return ();
HscAsm -> outputAsm dflags filenm flat_abstractC;
HscC -> outputC dflags filenm flat_abstractC stubs_exist
deps foreign_stubs;
HscJava ->
#ifdef JAVA
outputJava dflags filenm mod_name tycons core_binds
>> return stub_names
outputJava dflags filenm mod_name tycons core_binds;
#else
panic "Java support not compiled into this ghc"
panic "Java support not compiled into this ghc";
#endif
HscILX ->
#ifdef ILX
outputIlx dflags filenm mod_name tycons stg_binds
>> return stub_names
outputIlx dflags filenm mod_name tycons stg_binds;
#else
panic "ILX support not compiled into this ghc"
panic "ILX support not compiled into this ghc";
#endif
}
; return stubs_exist
}
doOutput :: String -> (Handle -> IO ()) -> IO ()
......@@ -106,11 +105,38 @@ doOutput filenm io_action = bracket (openFile filenm WriteMode) hClose io_action
%************************************************************************
\begin{code}
outputC dflags filenm flat_absC (stub_h_exists, _)
outputC dflags filenm flat_absC
(stub_h_exists, _) dependencies (ForeignStubs _ _ ffi_decl_headers _ )
= do dumpIfSet_dyn dflags Opt_D_dump_realC "Real C" (dumpRealC flat_absC)
header <- readIORef v_HCHeader
-- figure out which header files to #include in the generated .hc file:
--
-- * extra_includes from packages
-- * -#include options from the cmdline and OPTIONS pragmas
-- * the _stub.h file, if there is one.
--
let packages = dep_pkgs dependencies
pkg_configs <- getExplicitPackagesAnd packages
let pkg_names = map name pkg_configs
c_includes <- getPackageCIncludes pkg_configs
let cmdline_includes = cmdlineHcIncludes dflags -- -#include options
all_headers = c_includes
++ reverse cmdline_includes
++ reverse (map unpackFS ffi_decl_headers)
-- reverse correct?
let cc_injects = unlines (map mk_include all_headers)
mk_include h_file =
case h_file of
'"':_{-"-} -> "#include "++h_file
'<':_ -> "#include "++h_file
_ -> "#include \""++h_file++"\""
doOutput filenm $ \ h -> do
hPutStr h header
hPutStr h ("/* GHC_PACKAGES " ++ unwords pkg_names ++ "\n*/\n")
hPutStr h cc_injects
when stub_h_exists $
hPutStrLn h ("#include \"" ++ (hscStubHOutName dflags) ++ "\"")
writeRealC h flat_absC
......@@ -189,20 +215,11 @@ outputIlx dflags filename mod tycons stg_binds
%************************************************************************
\begin{code}
-- Turn the list of headers requested in foreign import
-- declarations into a string suitable for emission into generated
-- C code...
mkForeignHeaders headers
= unlines
. map (\fname -> "#include \"" ++ unpackFS fname ++ "\"")
. reverse
$ headers
outputForeignStubs :: DynFlags -> ForeignStubs
-> IO (Bool, -- Header file created
Bool) -- C file created
outputForeignStubs dflags NoStubs = return (False, False)
outputForeignStubs dflags (ForeignStubs h_code c_code hdrs _)
outputForeignStubs dflags (ForeignStubs h_code c_code _ _)
= do
dumpIfSet_dyn dflags Opt_D_dump_foreign
"Foreign export header file" stub_h_output_d
......@@ -214,15 +231,9 @@ outputForeignStubs dflags (ForeignStubs h_code c_code hdrs _)
dumpIfSet_dyn dflags Opt_D_dump_foreign
"Foreign export stubs" stub_c_output_d
-- Extend the list of foreign headers (used in outputC)
fhdrs <- readIORef v_HCHeader
let new_fhdrs = fhdrs ++ mkForeignHeaders hdrs
writeIORef v_HCHeader new_fhdrs
stub_c_file_exists
<- outputForeignStubs_help (hscStubCOutName dflags) stub_c_output_w
("#define IN_STG_CODE 0\n" ++
new_fhdrs ++
"#include \"RtsAPI.h\"\n" ++
cplusplus_hdr)
cplusplus_ftr
......
......@@ -61,9 +61,8 @@ import Monad
import Maybe
-----------------------------------------------------------------------------
-- Pre process
-----------------------------------------------------------------------------
-- ---------------------------------------------------------------------------
-- Pre-process
-- Just preprocess a file, put the result in a temp. file (used by the
-- compilation manager during the summary phase).
......@@ -79,9 +78,8 @@ preprocess filename =
False{-no linking-} False{-no -o flag-}
return fn
-----------------------------------------------------------------------------
-- Compile
-----------------------------------------------------------------------------
-- ---------------------------------------------------------------------------
-- Compile
-- Compile a single module, under the control of the compilation manager.
--
......@@ -124,7 +122,6 @@ compile ghci_mode this_mod location
dyn_flags <- restoreDynFlags -- Restore to the state of the last save
showPass dyn_flags
(showSDoc (text "Compiling" <+> ppr this_mod))
......@@ -167,20 +164,6 @@ compile ghci_mode this_mod location
hscStubHOutName = basename ++ "_stub.h",
extCoreName = basename ++ ".hcr" }
-- figure out which header files to #include in a generated .hc file
c_includes <- getPackageCIncludes
cmdline_includes <- dynFlag cmdlineHcIncludes -- -#include options
let cc_injects = unlines (map mk_include
(c_includes ++ reverse cmdline_includes))
mk_include h_file =
case h_file of
'"':_{-"-} -> "#include "++h_file
'<':_ -> "#include "++h_file
_ -> "#include \""++h_file++"\""
writeIORef v_HCHeader cc_injects
-- -no-recomp should also work with --make
do_recomp <- readIORef v_Recomp
let source_unchanged' = source_unchanged && do_recomp
......@@ -251,14 +234,13 @@ compileStub dflags stub_c_exists
return (Just stub_o)
-----------------------------------------------------------------------------
-- Link
-----------------------------------------------------------------------------
-- ---------------------------------------------------------------------------
-- Link
link :: GhciMode -- interactive or batch
-> DynFlags -- dynamic flags
-> Bool -- attempt linking in batch mode?
-> [Linkable]
-> HomePackageTable -- what to link
-> IO SuccessFlag
-- For the moment, in the batch linker, we don't bother to tell doLink
......@@ -268,40 +250,28 @@ link :: GhciMode -- interactive or batch
-- exports main, i.e., we have good reason to believe that linking
-- will succeed.
-- There will be (ToDo: are) two lists passed to link. These
-- correspond to
--
-- 1. The list of all linkables in the current home package. This is
-- used by the batch linker to link the program, and by the interactive
-- linker to decide which modules from the previous link it can
-- throw away.
-- 2. The list of modules on which we just called "compile". This list
-- is used by the interactive linker to decide which modules need
-- to be actually linked this time around (or unlinked and re-linked
-- if the module was recompiled).
link mode dflags batch_attempt_linking linkables
= do let verb = verbosity dflags
when (verb >= 3) $ do
hPutStrLn stderr "link: linkables are ..."
hPutStrLn stderr (showSDoc (vcat (map ppr linkables)))
res <- link' mode dflags batch_attempt_linking linkables
when (verb >= 3) (hPutStrLn stderr "link: done")
return res
#ifdef GHCI
link' Interactive dflags batch_attempt_linking linkables
= do showPass dflags "Not Linking...(demand linker will do the job)"
-- linkModules dflags linkables
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 linkables
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
......@@ -313,8 +283,13 @@ link' Batch dflags batch_attempt_linking linkables
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 (concatMap getOfiles linkables)
staticLink obj_files pkg_deps
when (verb >= 3) (hPutStrLn stderr "link: done")
-- staticLink only returns if it succeeds
return Succeeded
......@@ -326,13 +301,12 @@ link' Batch dflags batch_attempt_linking linkables
return Succeeded
where
verb = verbosity dflags
getOfiles (LM _ _ us) = map nameOfObject (filter isObject us)
-----------------------------------------------------------------------------
-- genPipeline: Pipeline construction
-----------------------------------------------------------------------------
-- --------------------------------------------------------------------------
-- genPipeline: Pipeline construction
-- 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,
......@@ -635,7 +609,8 @@ run_phase Cpp basename suff input_fn output_fn
hs_src_cpp_opts <- readIORef v_Hs_source_cpp_opts
cmdline_include_paths <- readIORef v_Include_paths
pkg_include_dirs <- getPackageIncludePath
pkg_include_dirs <- getPackageIncludePath []
let include_paths = foldr (\ x xs -> "-I" : x : xs) []
(cmdline_include_paths ++ pkg_include_dirs)
......@@ -760,20 +735,6 @@ run_phase Hsc basename suff input_fn output_fn
paths <- readIORef v_Include_paths
writeIORef v_Include_paths (current_dir : paths)
-- figure out which header files to #include in a generated .hc file
c_includes <- getPackageCIncludes
cmdline_includes <- dynFlag cmdlineHcIncludes -- -#include options
let cc_injects = unlines (map mk_include
(c_includes ++ reverse cmdline_includes))
mk_include h_file =
case h_file of
'"':_{-"-} -> "#include "++h_file
'<':_ -> "#include "++h_file
_ -> "#include \""++h_file++"\""
writeIORef v_HCHeader cc_injects
-- gather the imports and module name
(srcimps,imps,mod_name) <-
if extcoreish_suffix suff
......@@ -877,10 +838,13 @@ run_phase cc_phase basename suff input_fn output_fn
let hcc = cc_phase == HCc
-- add package include paths even if we're just compiling
-- .c files; this is the Value Add(TM) that using
-- ghc instead of gcc gives you :)
pkg_include_dirs <- getPackageIncludePath
-- HC files have the dependent packages stamped into them
pkgs <- if hcc then getHCFilePackages input_fn else return []
-- add package include paths even if we're just compiling .c
-- files; this is the Value Add(TM) that using ghc instead of
-- gcc gives you :)
pkg_include_dirs <- getPackageIncludePath pkgs
let include_paths = foldr (\ x xs -> "-I" : x : xs) []
(cmdline_include_paths ++ pkg_include_dirs)
......@@ -893,7 +857,7 @@ run_phase cc_phase basename suff input_fn output_fn
let opt_flag | o2 = "-O2"
| otherwise = "-O"
pkg_extra_cc_opts <- getPackageExtraCcOpts
pkg_extra_cc_opts <- getPackageExtraCcOpts pkgs
split_objs <- readIORef v_Split_object_files
let split_opt | hcc && split_objs = [ "-DUSE_SPLIT_MARKERS" ]
......@@ -1132,38 +1096,66 @@ mk_pvm_wrapper_script pvm_executable pvm_executable_base sysMan = unlines $
checkProcessArgsResult flags basename suff
= do when (notNull flags) (throwDyn (ProgramError (
basename ++ "." ++ suff
++ ": static flags are not allowed in {-# OPTIONS #-} pragmas:\n\t"
++ unwords flags)) (ExitFailure 1))
showSDoc (hang (text basename <> text ('.':suff) <> char ':')
4 (text "unknown flags in {-# OPTIONS #-} pragma:" <+>
hsep (map text flags)))
)))
-----------------------------------------------------------------------------
-- Look for the /* GHC_PACKAGES ... */ comment at the top of a .hc file
getHCFilePackages :: FilePath -> IO [PackageName]
getHCFilePackages filename =
EXCEPTION.bracket (openFile filename ReadMode) hClose $ \h -> do
l <- hGetLine h
case l of
'/':'*':' ':'G':'H':'C':'_':'P':'A':'C':'K':'A':'G':'E':'S':rest ->
return (map mkPackageName (words rest))
_other ->
return []
-----------------------------------------------------------------------------
-- Static linking, of .o files
staticLink :: [String] -> IO ()
staticLink o_files = do
-- The list of packages passed to link is the list of packages on
-- which this program depends, as discovered by the compilation
-- manager. It is combined with the list of packages that the user
-- specifies on the command line with -package flags.
--
-- In one-shot linking mode, we can't discover the package
-- dependencies (because we haven't actually done any compilation or
-- read any interface files), so the user must explicitly specify all
-- the packages.
staticLink :: [FilePath] -> [PackageName] -> IO ()
staticLink o_files dep_packages = do
verb <- getVerbFlag
static <- readIORef v_Static
no_hs_main <- readIORef v_NoHsMain
-- get the full list of packages to link with, by combining the
-- explicit packages with the auto packages and all of their
-- dependencies, and eliminating duplicates.
o_file <- readIORef v_Output_file
let output_fn = case o_file of { Just s -> s; Nothing -> "a.out"; }
pkg_lib_paths <- getPackageLibraryPath
pkg_lib_paths <- getPackageLibraryPath dep_packages
let pkg_lib_path_opts = map ("-L"++) pkg_lib_paths
lib_paths <- readIORef v_Library_paths
let lib_path_opts = map ("-L"++) lib_paths
pkg_link_opts <- getPackageLinkOpts
pkg_link_opts <- getPackageLinkOpts dep_packages
#ifdef darwin_TARGET_OS
pkg_framework_paths <- getPackageFrameworkPath
pkg_framework_paths <- getPackageFrameworkPath dep_packages
let pkg_framework_path_opts = map ("-F"++) pkg_framework_paths
framework_paths <- readIORef v_Framework_paths
let framework_path_opts = map ("-F"++) framework_paths
pkg_frameworks <- getPackageFrameworks
pkg_frameworks <- getPackageFrameworks dep_packages
let pkg_framework_opts = map ("-framework " ++) pkg_frameworks
frameworks <- readIORef v_Cmdline_frameworks
......@@ -1229,13 +1221,13 @@ doMkDLL o_files = do
o_file <- readIORef v_Output_file
let output_fn = case o_file of { Just s -> s; Nothing -> "HSdll.dll"; }
pkg_lib_paths <- getPackageLibraryPath
pkg_lib_paths <- getPackageLibraryPath []
let pkg_lib_path_opts = map ("-L"++) pkg_lib_paths
lib_paths <- readIORef v_Library_paths
let lib_path_opts = map ("-L"++) lib_paths
pkg_link_opts <- getPackageLinkOpts
pkg_link_opts <- getPackageLinkOpts []
-- probably _stub.o files
extra_ld_inputs <- readIORef v_Ld_inputs
......
-----------------------------------------------------------------------------
-- $Id: DriverState.hs,v 1.87 2002/12/17 13:50:29 simonmar Exp $
-- $Id: DriverState.hs,v 1.88 2002/12/18 16:29:28 simonmar Exp $
--
-- Settings for the driver
--
......@@ -12,16 +12,13 @@ module DriverState where
#include "../includes/config.h"
#include "HsVersions.h"
import SysTools ( getTopDir )
import ParsePkgConf ( loadPackageConfig )
import Packages ( PackageConfig(..), PackageConfigMap,
PackageName, mkPackageName, packageNameString,
packageDependents,
mungePackagePaths, emptyPkgMap, extendPkgMap, lookupPkg,
basePackage, rtsPackage, haskell98Package )
import SysTools ( getTopDir )
import Packages
import CmdLineOpts
import DriverPhases
import DriverUtil
import UniqFM ( eltsUFM )
import Util
import Config
import Panic
......@@ -32,8 +29,8 @@ import EXCEPTION
import List
import Char
import Monad
import Maybe ( fromJust, isJust )
import Directory ( doesDirectoryExist )
import Maybe ( fromJust, isJust )
import Directory ( doesDirectoryExist )
-----------------------------------------------------------------------------
-- non-configured things
......@@ -452,91 +449,102 @@ addToDirList ref path
splitUp xs = return (split split_marker xs)
#endif
GLOBAL_VAR(v_HCHeader, "", String)
-----------------------------------------------------------------------------
-- Packages
------------------------
-- The PackageConfigMap is read in from the configuration file
-- It doesn't change during a run
GLOBAL_VAR(v_Package_details, emptyPkgMap, PackageConfigMap)
-- ----------------------------------------------------------------------------
-- Loading the package config file
readPackageConf :: String -> IO ()
readPackageConf conf_file = do
proto_pkg_configs <- loadPackageConfig conf_file
top_dir <- getTopDir
old_pkg_map <- readIORef v_Package_details
let pkg_configs = mungePackagePaths top_dir proto_pkg_configs
new_pkg_map = extendPkgMap old_pkg_map pkg_configs
writeIORef v_Package_details new_pkg_map
extendPackageConfigMap pkg_configs
mungePackagePaths :: String -> [PackageConfig] -> [PackageConfig]
-- Replace the string "$libdir" at the beginning of a path
-- with the current libdir (obtained from the -B option).
mungePackagePaths top_dir ps = map munge_pkg ps
where
munge_pkg p = p{ import_dirs = munge_paths (import_dirs p),
include_dirs = munge_paths (include_dirs p),
library_dirs = munge_paths (library_dirs p),
framework_dirs = munge_paths (framework_dirs p) }
getPackageConfigMap :: IO PackageConfigMap
getPackageConfigMap = readIORef v_Package_details
munge_paths = map munge_path
munge_path p
| Just p' <- my_prefix_match "$libdir" p = top_dir ++ p'
| otherwise = p
------------------------
-- The package list reflects what was given as command-line options,
-- plus their dependent packages.
-- It is maintained in dependency order;
-- earlier ones depend on later ones, but not vice versa
GLOBAL_VAR(v_Packages, initPackageList, [PackageName])
getPackages :: IO [PackageName]
getPackages = readIORef v_Packages
-- -----------------------------------------------------------------------------
-- The list of packages requested on the command line
initPackageList = [haskell98Package,
basePackage,
rtsPackage]
-- The package list reflects what packages were given as command-line options,
-- plus their dependent packages. It is maintained in dependency order;
-- earlier packages may depend on later ones, but not vice versa
GLOBAL_VAR(v_ExplicitPackages, initPackageList, [PackageName])
initPackageList = [rtsPackage]
-- add a package requested from the command-line
addPackage :: String -> IO ()
addPackage package
= do { pkg_details <- getPackageConfigMap
; ps <- readIORef v_Packages
; ps' <- add_package pkg_details ps (mkPackageName package)
addPackage package = do
pkg_details <- getPackageConfigMap
ps <- readIORef v_ExplicitPackages
ps' <- add_package pkg_details ps (mkPackageName package)
-- Throws an exception if it fails
; writeIORef v_Packages ps' }
writeIORef v_ExplicitPackages ps'
-- internal helper
add_package :: PackageConfigMap -> [PackageName]
-> PackageName -> IO [PackageName]
add_package pkg_details ps p
| p `elem` ps -- Check if we've already added this package
= return ps
| Just details <- lookupPkg pkg_details p
= do { -- Add the package's dependents first
ps' <- foldM (add_package pkg_details) ps
(packageDependents details)
; return (p : ps') }
-- Add the package's dependents also
= do ps' <- foldM (add_package pkg_details) ps (packageDependents details)
return (p : ps')
| otherwise
= throwDyn (CmdLineError ("unknown package name: " ++ packageNameString p))