1 -----------------------------------------------------------------------------
2 -- Access to system tools: gcc, cp, rm etc
4 -- (c) The University of Glasgow 2000
6 -----------------------------------------------------------------------------
12 setPgm, -- String -> IO ()
13 -- Command-line override
16 packageConfigPath, -- IO String
17 -- Where package.conf is
19 -- Interface to system tools
20 runUnlit, runCpp, runCc, -- [String] -> IO ()
21 runMangle, runSplit, -- [String] -> IO ()
22 runAs, runLink, -- [String] -> IO ()
25 touch, -- String -> String -> IO ()
26 copy, -- String -> String -> String -> IO ()
28 -- Temporary-file management
31 cleanTempFiles, cleanTempFilesExcept, removeTmpFiles,
35 getProcessID, -- IO Int
36 system, -- String -> IO Int
39 showGhcUsage, -- IO () Shows usage message and exits
40 getSysMan, -- IO String Parallel system only
42 runSomething -- ToDo: make private
48 import Panic ( progName, GhcException(..) )
49 import Util ( global )
50 import CmdLineOpts ( dynFlag, verbosity )
52 import Exception ( throwDyn, catchAllIO )
54 import Directory ( doesFileExist, removeFile )
55 import IOExts ( IORef, readIORef, writeIORef )
56 import Monad ( when, unless )
57 import System ( system, ExitCode(..), exitWith )
66 #include "../includes/config.h"
68 #if !defined(mingw32_TARGET_OS)
69 import qualified Posix
72 import List ( isPrefixOf )
75 import List ( isSuffixOf )
77 #include "HsVersions.h"
79 {-# DEPRECATED runSomething "runSomething should be private to SysTools" #-}
84 The configuration story
85 ~~~~~~~~~~~~~~~~~~~~~~~
87 GHC needs various support files (library packages, RTS etc), plus
88 various auxiliary programs (cp, gcc, etc). It finds these in one
91 * When running as an *installed program*, GHC finds most of this support
92 stuff in the installed library tree. The path to this tree is passed
93 to GHC via the -B flag, and given to initSysTools .
95 * When running *in-place* in a build tree, GHC finds most of this support
96 stuff in the build tree. The path to the build tree is, again passed
99 GHC tells which of the two is the case by seeing whether package.conf
100 is in TopDir [installed] or in TopDir/ghc/driver [inplace] (what a hack).
103 SysTools.initSysProgs figures out exactly where all the auxiliary programs
104 are, and initialises mutable variables to make it easy to call them.
105 To to this, it makes use of definitions in Config.hs, which is a Haskell
106 file containing variables whose value is figured out by the build system.
108 Config.hs contains two sorts of things
110 cGCC, The *names* of the programs
113 etc They do *not* include paths
116 cUNLIT_DIR The *path* to the directory containing unlit, split etc
117 cSPLIT_DIR *relative* to the root of the build tree,
118 for use when running *in-place* in a build tree (only)
122 ---------------------------------------------
123 NOTES for an ALTERNATIVE scheme (i.e *not* what is currently implemented):
125 Another hair-brained scheme for simplifying the current tool location
126 nightmare in GHC: Simon originally suggested using another
127 configuration file along the lines of GCC's specs file - which is fine
128 except that it means adding code to read yet another configuration
129 file. What I didn't notice is that the current package.conf is
130 general enough to do this:
133 {name = "tools", import_dirs = [], source_dirs = [],
134 library_dirs = [], hs_libraries = [], extra_libraries = [],
135 include_dirs = [], c_includes = [], package_deps = [],
136 extra_ghc_opts = ["-pgmc/usr/bin/gcc","-pgml${libdir}/bin/unlit", ... etc.],
137 extra_cc_opts = [], extra_ld_opts = []}
139 Which would have the advantage that we get to collect together in one
140 place the path-specific package stuff with the path-specific tool
143 ---------------------------------------------
146 %************************************************************************
148 \subsection{Global variables to contain system programs}
150 %************************************************************************
152 All these pathnames are maintained IN THE NATIVE FORMAT OF THE HOST MACHINE.
153 (See remarks under pathnames below)
156 GLOBAL_VAR(v_Pgm_L, error "pgm_L", String) -- unlit
157 GLOBAL_VAR(v_Pgm_P, error "pgm_P", String) -- cpp
158 GLOBAL_VAR(v_Pgm_c, error "pgm_c", String) -- gcc
159 GLOBAL_VAR(v_Pgm_m, error "pgm_m", String) -- asm code mangler
160 GLOBAL_VAR(v_Pgm_s, error "pgm_s", String) -- asm code splitter
161 GLOBAL_VAR(v_Pgm_a, error "pgm_a", String) -- as
162 GLOBAL_VAR(v_Pgm_l, error "pgm_l", String) -- ld
163 GLOBAL_VAR(v_Pgm_MkDLL, error "pgm_dll", String) -- mkdll
165 GLOBAL_VAR(v_Pgm_T, error "pgm_T", String) -- touch
166 GLOBAL_VAR(v_Pgm_CP, error "pgm_CP", String) -- cp
168 GLOBAL_VAR(v_Path_package_config, error "path_package_config", String)
169 GLOBAL_VAR(v_Path_usage, error "ghc_usage.txt", String)
171 -- Parallel system only
172 GLOBAL_VAR(v_Pgm_sysman, error "pgm_sysman", String) -- system manager
176 %************************************************************************
178 \subsection{Initialisation}
180 %************************************************************************
183 initSysTools :: [String] -- Command-line arguments starting "-B"
185 -> IO String -- Set all the mutable variables above, holding
186 -- (a) the system programs
187 -- (b) the package-config file
188 -- (c) the GHC usage message
192 initSysTools minusB_args
193 = do { (am_installed, top_dir) <- getTopDir minusB_args
195 -- for "installed" this is the root of GHC's support files
196 -- for "in-place" it is the root of the build tree
197 -- NB: top_dir is assumed to be in standard Unix format '/' separated
199 ; let installed, installed_bin :: FilePath -> FilePath
200 installed_bin pgm = pgmPath (top_dir `slash` "extra-bin") pgm
201 installed file = pgmPath top_dir file
202 inplace dir pgm = pgmPath (top_dir `slash` dir) pgm
205 | am_installed = installed "package.conf"
206 | otherwise = inplace cGHC_DRIVER_DIR "package.conf.inplace"
209 | am_installed = installed "ghc-usage.txt"
210 | otherwise = inplace cGHC_DRIVER_DIR "ghc-usage.txt"
212 -- For all systems, unlit, split, mangle are GHC utilities
213 -- architecture-specific stuff is done when building Config.hs
215 | am_installed = installed_bin cGHC_UNLIT
216 | otherwise = inplace cGHC_UNLIT_DIR cGHC_UNLIT
218 -- split and mangle are Perl scripts
220 | am_installed = installed_bin cGHC_SPLIT
221 | otherwise = inplace cGHC_SPLIT_DIR cGHC_SPLIT
224 | am_installed = installed_bin cGHC_MANGLER
225 | otherwise = inplace cGHC_MANGLER_DIR cGHC_MANGLER
227 -- Check that the package config exists
228 ; config_exists <- doesFileExist pkgconfig_path
229 ; when (not config_exists) $
230 throwDyn (InstallationError
231 ("Can't find package.conf as " ++ pkgconfig_path))
233 #if defined(mingw32_TARGET_OS)
234 -- WINDOWS-SPECIFIC STUFF
235 -- On Windows, gcc and friends are distributed with GHC,
236 -- so when "installed" we look in TopDir/bin
237 -- When "in-place" we look wherever the build-time configure
239 -- When "install" we tell gcc where its specs file + exes are (-B)
240 -- and also some places to pick up include files. We need
241 -- to be careful to put all necessary exes in the -B place
242 -- (as, ld, cc1, etc) since if they don't get found there, gcc
243 -- then tries to run unadorned "as", "ld", etc, and will
244 -- pick up whatever happens to be lying around in the path,
245 -- possibly including those from a cygwin install on the target,
246 -- which is exactly what we're trying to avoid.
247 ; let gcc_path | am_installed = installed_bin ("gcc -B" ++ installed "gcc-lib/"
248 ++ " -I" ++ installed "include/mingw")
250 perl_path | am_installed = installed_bin cGHC_PERL
251 | otherwise = cGHC_PERL
253 -- 'touch' is a GHC util for Windows, and similarly unlit, mangle
254 ; let touch_path | am_installed = installed_bin cGHC_TOUCHY
255 | otherwise = inplace cGHC_TOUCHY_DIR cGHC_TOUCHY
257 -- On Win32 we don't want to rely on #!/bin/perl, so we prepend
258 -- a call to Perl to get the invocation of split and mangle
259 ; let split_path = perl_path ++ " " ++ split_script
260 mangle_path = perl_path ++ " " ++ mangle_script
262 ; let mkdll_path = cMKDLL
264 -- UNIX-SPECIFIC STUFF
265 -- On Unix, the "standard" tools are assumed to be
266 -- in the same place whether we are running "in-place" or "installed"
267 -- That place is wherever the build-time configure script found them.
268 ; let gcc_path = cGCC
269 touch_path = cGHC_TOUCHY
270 mkdll_path = panic "Can't build DLLs on a non-Win32 system"
272 -- On Unix, scripts are invoked using the '#!' method. Binary
273 -- installations of GHC on Unix place the correct line on the front
274 -- of the script at installation time, so we don't want to wire-in
275 -- our knowledge of $(PERL) on the host system here.
276 ; let split_path = split_script
277 mangle_path = mangle_script
280 -- cpp is derived from gcc on all platforms
281 ; let cpp_path = gcc_path ++ " -E " ++ cRAWCPP_FLAGS
283 -- For all systems, copy and remove are provided by the host
284 -- system; architecture-specific stuff is done when building Config.hs
285 ; let cp_path = cGHC_CP
287 -- Other things being equal, as and ld are simply gcc
288 ; let as_path = gcc_path
292 -- Initialise the global vars
293 ; writeIORef v_Path_package_config pkgconfig_path
294 ; writeIORef v_Path_usage ghc_usage_msg_path
296 ; writeIORef v_Pgm_sysman (top_dir ++ "/ghc/rts/parallel/SysMan")
297 -- Hans: this isn't right in general, but you can
298 -- elaborate it in the same way as the others
300 ; writeIORef v_Pgm_L unlit_path
301 ; writeIORef v_Pgm_P cpp_path
302 ; writeIORef v_Pgm_c gcc_path
303 ; writeIORef v_Pgm_m mangle_path
304 ; writeIORef v_Pgm_s split_path
305 ; writeIORef v_Pgm_a as_path
306 ; writeIORef v_Pgm_l ld_path
307 ; writeIORef v_Pgm_MkDLL mkdll_path
308 ; writeIORef v_Pgm_T touch_path
309 ; writeIORef v_Pgm_CP cp_path
315 setPgm is called when a command-line option like
317 is used to override a particular program with a new onw
320 setPgm :: String -> IO ()
321 -- The string is the flag, minus the '-pgm' prefix
322 -- So the first character says which program to override
324 setPgm ('P' : pgm) = writeIORef v_Pgm_P pgm
325 setPgm ('c' : pgm) = writeIORef v_Pgm_c pgm
326 setPgm ('m' : pgm) = writeIORef v_Pgm_m pgm
327 setPgm ('s' : pgm) = writeIORef v_Pgm_s pgm
328 setPgm ('a' : pgm) = writeIORef v_Pgm_a pgm
329 setPgm ('l' : pgm) = writeIORef v_Pgm_l pgm
330 setPgm pgm = unknownFlagErr ("-pgm" ++ pgm)
336 -- for "installed" this is the root of GHC's support files
337 -- for "in-place" it is the root of the build tree
340 -- 1. Set proto_top_dir
341 -- a) look for (the last) -B flag, and use it
342 -- b) if there are no -B flags, get the directory
343 -- where GHC is running (only on Windows)
345 -- 2. If package.conf exists in proto_top_dir, we are running
346 -- installed; and TopDir = proto_top_dir
348 -- 3. Otherwise we are running in-place, so
349 -- proto_top_dir will be /...stuff.../ghc/compiler
350 -- Set TopDir to /...stuff..., which is the root of the build tree
352 -- This is very gruesome indeed
354 getTopDir :: [String]
355 -> IO (Bool, -- True <=> am installed, False <=> in-place
356 String) -- TopDir (in Unix format '/' separated)
359 = do { top_dir <- get_proto
360 -- Discover whether we're running in a build tree or in an installation,
361 -- by looking for the package configuration file.
362 ; am_installed <- doesFileExist (top_dir `slash` "package.conf")
364 ; return (am_installed, top_dir)
367 -- get_proto returns a Unix-format path
368 get_proto | not (null minusbs)
369 = return (unDosifyPath (drop 2 (last minusbs))) -- 2 for "-B"
371 = do { maybe_exec_dir <- getExecDir -- Get directory of executable
372 ; case maybe_exec_dir of -- (only works on Windows;
373 -- returns Nothing on Unix)
374 Nothing -> throwDyn (InstallationError "missing -B<dir> option")
375 Just dir -> return (remove_suffix (unDosifyPath dir))
378 -- In an installed tree, the ghc binary lives in $libexecdir, which
379 -- is normally $libdir/bin. So we strip off a /bin suffix here.
380 -- In a build tree, the ghc binary lives in $fptools/ghc/compiler,
381 -- so we strip off the /ghc/compiler suffix here too, leaving a
383 remove_suffix ghc_bin_dir -- ghc_bin_dir is in standard Unix format
384 | "/ghc/compiler" `isSuffixOf` ghc_bin_dir = back_two
385 | "/bin" `isSuffixOf` ghc_bin_dir = back_one
386 | otherwise = ghc_bin_dir
388 p1 = dropWhile (not . isSlash) (reverse ghc_bin_dir)
389 p2 = dropWhile (not . isSlash) (tail p1) -- head is '/'
390 back_two = reverse (tail p2) -- head is '/'
391 back_one = reverse (tail p1)
395 %************************************************************************
397 \subsection{Running an external program}
399 %************************************************************************
403 runUnlit :: [String] -> IO ()
404 runUnlit args = do p <- readIORef v_Pgm_L
405 runSomething "Literate pre-processor" p args
407 runCpp :: [String] -> IO ()
408 runCpp args = do p <- readIORef v_Pgm_P
409 runSomething "C pre-processor" p args
411 runCc :: [String] -> IO ()
412 runCc args = do p <- readIORef v_Pgm_c
413 runSomething "C Compiler" p args
415 runMangle :: [String] -> IO ()
416 runMangle args = do p <- readIORef v_Pgm_m
417 runSomething "Mangler" p args
419 runSplit :: [String] -> IO ()
420 runSplit args = do p <- readIORef v_Pgm_s
421 runSomething "Splitter" p args
423 runAs :: [String] -> IO ()
424 runAs args = do p <- readIORef v_Pgm_a
425 runSomething "Assembler" p args
427 runLink :: [String] -> IO ()
428 runLink args = do p <- readIORef v_Pgm_l
429 runSomething "Linker" p args
431 runMkDLL :: [String] -> IO ()
432 runMkDLL args = do p <- readIORef v_Pgm_MkDLL
433 runSomething "Make DLL" p args
435 touch :: String -> String -> IO ()
436 touch purpose arg = do p <- readIORef v_Pgm_T
437 runSomething purpose p [arg]
439 copy :: String -> String -> String -> IO ()
440 copy purpose from to = do
441 verb <- dynFlag verbosity
442 when (verb >= 2) $ hPutStrLn stderr ("*** " ++ purpose)
444 h <- openFile to WriteMode
445 ls <- readFile from -- inefficient, but it'll do for now.
446 -- ToDo: speed up via slurping.
452 getSysMan :: IO String -- How to invoke the system manager
453 -- (parallel system only)
454 getSysMan = readIORef v_Pgm_sysman
457 %************************************************************************
459 \subsection{GHC Usage message}
461 %************************************************************************
463 Show the usage message and exit
466 showGhcUsage = do { usage_path <- readIORef v_Path_usage
467 ; usage <- readFile usage_path
469 ; exitWith ExitSuccess }
472 dump ('$':'$':s) = hPutStr stderr progName >> dump s
473 dump (c:s) = hPutChar stderr c >> dump s
475 packageConfigPath = readIORef v_Path_package_config
479 %************************************************************************
481 \subsection{Managing temporary files
483 %************************************************************************
486 GLOBAL_VAR(v_FilesToClean, [], [String] )
487 GLOBAL_VAR(v_TmpDir, cDEFAULT_TMPDIR, String )
488 -- v_TmpDir has no closing '/'
492 setTmpDir dir = writeIORef v_TmpDir dir
494 cleanTempFiles :: Int -> IO ()
495 cleanTempFiles verb = do fs <- readIORef v_FilesToClean
496 removeTmpFiles verb fs
498 cleanTempFilesExcept :: Int -> [FilePath] -> IO ()
499 cleanTempFilesExcept verb dont_delete
500 = do fs <- readIORef v_FilesToClean
501 let leftovers = filter (`notElem` dont_delete) fs
502 removeTmpFiles verb leftovers
503 writeIORef v_FilesToClean dont_delete
506 -- find a temporary name that doesn't already exist.
507 newTempName :: Suffix -> IO FilePath
509 = do x <- getProcessID
510 tmp_dir <- readIORef v_TmpDir
511 findTempName tmp_dir x
513 findTempName tmp_dir x
514 = do let filename = tmp_dir ++ "/ghc" ++ show x ++ '.':extn
515 b <- doesFileExist filename
516 if b then findTempName tmp_dir (x+1)
517 else do add v_FilesToClean filename -- clean it up later
520 addFilesToClean :: [FilePath] -> IO ()
521 -- May include wildcards [used by DriverPipeline.run_phase SplitMangle]
522 addFilesToClean files = mapM_ (add v_FilesToClean) files
524 removeTmpFiles :: Int -> [FilePath] -> IO ()
525 removeTmpFiles verb fs
526 = traceCmd "Deleting temp files"
527 ("Deleting: " ++ unwords fs)
530 rm f = removeFile f `catchAllIO`
533 hPutStrLn stderr ("Warning: deleting non-existent " ++ f)
539 %************************************************************************
541 \subsection{Running a program}
543 %************************************************************************
546 GLOBAL_VAR(v_Dry_run, False, Bool)
549 setDryRun = writeIORef v_Dry_run True
551 -----------------------------------------------------------------------------
552 -- Running an external program
554 runSomething :: String -- For -v message
555 -> String -- Command name (possibly a full path)
556 -- assumed already dos-ified
557 -> [String] -- Arguments
558 -- runSomething will dos-ify them
561 runSomething phase_name pgm args
562 = traceCmd phase_name cmd_line $
563 do { exit_code <- system cmd_line
564 ; if exit_code /= ExitSuccess
565 then throwDyn (PhaseFailed phase_name exit_code)
569 cmd_line = unwords (pgm : dosifyPaths args)
570 -- The pgm is already in native format (appropriate dir separators)
572 traceCmd :: String -> String -> IO () -> IO ()
573 -- a) trace the command (at two levels of verbosity)
574 -- b) don't do it at all if dry-run is set
575 traceCmd phase_name cmd_line action
576 = do { verb <- dynFlag verbosity
577 ; when (verb >= 2) $ hPutStrLn stderr ("*** " ++ phase_name)
578 ; when (verb >= 3) $ hPutStrLn stderr cmd_line
582 ; n <- readIORef v_Dry_run
586 ; action `catchAllIO` handle_exn verb
589 handle_exn verb exn = do { when (verb >= 2) (hPutStr stderr "\n")
590 ; when (verb >= 3) (hPutStrLn stderr ("Failed: " ++ cmd_line))
591 ; throwDyn (PhaseFailed phase_name (ExitFailure 1)) }
595 %************************************************************************
597 \subsection{Path names}
599 %************************************************************************
601 We maintain path names in Unix form ('/'-separated) right until
602 the last moment. On Windows we dos-ify them just before passing them
603 to the Windows command.
605 The alternative, of using '/' consistently on Unix and '\' on Windows,
606 proved quite awkward. There were a lot more calls to dosifyPath,
607 and even on Windows we might invoke a unix-like utility (eg 'sh'), which
608 interpreted a command line 'foo\baz' as 'foobaz'.
611 -----------------------------------------------------------------------------
612 -- Convert filepath into MSDOS form.
614 dosifyPaths :: [String] -> [String]
615 -- dosifyPaths does two things
616 -- a) change '/' to '\'
617 -- b) remove initial '/cygdrive/'
619 unDosifyPath :: String -> String
620 -- Just change '\' to '/'
622 pgmPath :: String -- Directory string in Unix format
623 -> String -- Program name with no directory separators
625 -> String -- Program invocation string in native format
629 #if defined(mingw32_TARGET_OS)
631 --------------------- Windows version ------------------
634 dosifyPaths xs = map dosifyPath xs
636 pgmPath dir pgm = dosifyPath dir ++ '\\' : pgm
639 = subst '/' '\\' real_stuff
641 -- fully convince myself that /cygdrive/ prefixes cannot
642 -- really appear here.
643 cygdrive_prefix = "/cygdrive/"
646 | cygdrive_prefix `isPrefixOf` stuff = drop (length cygdrive_prefix) stuff
651 --------------------- Unix version ---------------------
653 unDosifyPath xs = subst '\\' '/' xs
654 pgmPath dir pgm = dir ++ '/' : pgm
655 --------------------------------------------------------
658 subst a b ls = map (\ x -> if x == a then b else x) ls
662 -----------------------------------------------------------------------------
663 Path name construction
666 slash :: String -> String -> String
667 absPath, relPath :: [String] -> String
670 isSlash other = False
673 relPath xs = foldr1 slash xs
675 absPath xs = "" `slash` relPath xs
677 slash s1 s2 = s1 ++ ('/' : s2)
681 %************************************************************************
683 \subsection{Support code}
685 %************************************************************************
688 -----------------------------------------------------------------------------
689 -- Define getExecDir :: IO (Maybe String)
691 #if defined(mingw32_TARGET_OS) && __GLASGOW_HASKELL__ >= 500
692 foreign import stdcall "RegQueryValueExA" regQueryValue :: Addr -> CString -> Addr -> Addr -> Ptr CChar -> Ptr Int32 -> IO Int32
693 foreign import stdcall "RegQueryValueExA" regQueryValueLen :: Addr -> CString -> Addr -> Addr -> Addr -> Ptr Int32 -> IO Int32
694 foreign import stdcall "RegOpenKeyExA" regOpenKey :: Int32 -> CString -> Int32 -> Int32 -> Ptr Addr -> IO Int32
695 getExecDir :: IO (Maybe String)
696 getExecDir = do alloca $ \ p_len -> do
697 alloca $ \ p_hKey -> do
698 withCString "SOFTWARE\\University of Glasgow\\Glasgow Haskell Compiler\\ghc-5.01" $ \ name ->
699 regOpenKey 0x80000002 {-HKEY_LOCAL_MACHINE-} name 0 1 {-KEY_QUERY_VALUE-} p_hKey
701 withCString "InstallDir" $ \ key -> do
702 regQueryValueLen hKey key nullAddr nullAddr nullAddr p_len
704 buf <- mallocArray (fromIntegral len)
705 ret <- regQueryValue hKey key nullAddr nullAddr buf p_len
706 if ret /= 0 then return Nothing
707 else do s <- peekCString buf
708 destructArray (fromIntegral len) buf
711 getExecDir :: IO (Maybe String) = do return Nothing
714 #ifdef mingw32_TARGET_OS
715 foreign import "_getpid" getProcessID :: IO Int -- relies on Int == Int32 on Windows
717 getProcessID :: IO Int
718 getProcessID = Posix.getProcessID