USING EXTERNAL CORE FOR GREAT JUSTICE The procedure for setting up your environment to write code that manipulates External Core is involved. That's mostly because you'll have to modify GHC's libraries. Why? Because GHC's optimizer inlines aggressively across module boundaries, meaning that if you just want to go so far as parsing and typechecking the Core version of the "hello world" program, you're going to have to implement quite a few I/O primitives and foreign function calls. You might want to get started writing your Core back-end by implementing just a very simple I/O interface, which means you won't be able to compile all Core programs produced by GHC. In short, you want to replace some of GHC's I/O library functions with simpler versions, which rely on foreign functions that you will define in your Core back-end. I've provided a simple library patch that redefines just *two* functions, hPutStr and hPutChar, to use a foreign function that prints one character to stdout. This should serve as an example of how to modify the GHC libraries so as to define I/O functions on top of the interface of your choice. The steps described below are a prerequisite for running the Core Linker code that turns multiple GHC modules (for example, one module plus all the base library modules it depends on) into a single External Core file. You need External Core source code for all the libraries in order to do this. You can run the Core Linker and *typecheck* the results without modifying the GHC libraries at all. I haven't explained here in detail how to do that. The steps described below are also a prerequisite for running the stand-alone Core interpreter so you can evaluate simple programs. This interpreter is meant to serve as an example of how to write code that manipulates Core, as well as a specification of Core's semantics. This may seem complicated, but the alternative would be, in effect, to re-implement GHC's base of primitive operations in your own back-end. I hope that before too long, the syntax of External Core will change to use a fixed set of primitives, obviating the need for library hacks. ========= Instructions =========== You're going to need to make a directory in which to do step 1 and step 2. In what follows, I'll refer to that directory as $TOP. 1. darcs get --partial http://darcs.haskell.org/ghc-6.10/ghc (It's important that you get the stable branch of ghc 6.10, as the patches mentioned below are against version 6.10.) darcs can take an hour or more to fetch 20,000 patches. As an alternative, I've supplied a tarball at: http://cs.pdx.edu/~tjc/Patches/ghc_darcs_clean.tgz Or, you can get a tarball including the core libraries at: http://cs.pdx.edu/~tjc/Patches/ghc_darcs_clean_corelibs.tgz If you get the latter tarball, unzip it and skip to step 2. Once you've finished either getting GHC via darcs or downloading and unzipping the tarball, then get the libraries: 1a. $ cd ghc $ chmod +x darcs-all $ ./darcs-all get Don't build ghc yet! Keep reading... 2. Fetch some patches. In $TOP, do: $ wget http://cs.pdx.edu/~tjc/Patches/integer-simple-ghc.dpatch $ wget http://cs.pdx.edu/~tjc/Patches/core-prettyprinter.dpatch The second patch fixes a bug in the ext-core prettyprinter in 6.10. (This bug is fixed in the HEAD (6.12.x) but the other patches aren't against the HEAD.) The first patch changes GHC's wired-in integer library to be integer-simple (which you will fetch in a subsequent step) rather than integer-gmp. There are more patches that apply to the libraries themselves; these live in the extcore tarball (explained below). 3. Get the integer-simple library: $ cd $TOP/ghc/libraries $ darcs get http://darcs.haskell.org/libraries/integer-simple 4. In $TOP, get the extcore tarball: http://hackage.haskell.org/packages/archive/extcore/0.8/extcore-0.8.tar.gz (or, go to http://hackage.haskell.org/package/extcore and check what the latest version is, and download that version instead) You need to get the tarball rather than just installing this through cabal because you need some patches that are included in it. Don't install the extcore library just yet. If you ls at the top level in extcore, you'll see some patches: $ ls *.dpatch base-library.dpatch integer-simple-library.dpatch simple_io.dpatch Note that these patches are mainly for demonstration purposes. They include references to foreign calls that you will have to implement yourself. IN PARTICULAR, the floating-point library code in this patch (changes to GHC.Float) is very likely buggy. Again, this code is meant to show what you can do and to enable you to compile single-module Core code, and is not meant to compile Core that you will execute. In this next step, you'll apply all the abovementioned patches to the library directory you created in step 1. 5. Apply the patches. The first two "darcs apply" commands will be slow. Do: $ cd $TOP/ghc $ darcs apply ../integer-simple-ghc.dpatch $ darcs apply ../core-prettyprinter.dpatch $ cd libraries/base $ darcs apply ../../../extcore-0.8/simple_io.dpatch $ darcs apply ../../../extcore-0.8/base-library.dpatch $ cd ../integer-simple $ darcs apply ../../../extcore-0.8/integer-simple-library.dpatch If you get an error mentioning fromSlurpFile when running one of the "darcs apply" commands, try: $ darcs repair under $TOP/ghc. This happened to me once; I don't know why. 6. Now that you've patched the libraries, you're going to have to build ghc. The first step is to add an appropriate build.mk file with the right flags to generate External Core. The fastest way is: $ cd $TOP/ghc/mk $ cp $TOP/extcore-0.8/extcore_build.mk ./build.mk You can edit the build.mk file to your liking. 7. Next, you'll generate External Core for all the GHC libraries. In $TOP/ghc, do: $ sh boot; ./configure; make -j2 stage=1; cd libraries; make -j2 (leaving out -j2 if you don't have a multicore machine) This will take a while. Go for coffee. Notes: * To build GHC, I had to make sure my autoconf version was 2.61 or 2.59. 2.65 won't work. On my Mac, 2.61 is /usr/bin/autoconf and 2.65 is /opt/local/bin/autoconf. * I also had to make sure I had make version 3.81. Having version 3.80 first on the PATH caused complaining. * I needed Cabal 1.6.0.4 in order to build GHC 6.10.4. Previously, I had installed cabal-install, which caused the default version of the cabal library to be 1.8.x.x. If this happens to you, do: $ ghc-pkg list (to see what the wayward Cabal version is) $ ghc-pkg unregister Cabal-1.8.x.x (or whatever the real version number is. You'll know this is the case for you if you get an error about "package" being an unbound variable while building ghc-pkg, part of the GHC build. If this happens, you need to execute the ghc-pkg commands above and then run "make maintainer-clean" under $TOP/ghc, and then start over at step 7. * On my Mac, the configure command actually needs to be: ./configure --with-gmp-includes=/opt/local/include --with-gmp-libraries=/opt/local/lib YMMV. Even though we're not building ghc with GMP, the configure script still looks for it. (By the way, if you need more help building ghc, the GHC developer wiki is very helpful: http://hackage.haskell.org/trac/ghc/) 8. The next step is to install the extcore library. You can do this with cabal: $ cabal install extcore or you can use the tarball you got earlier, with the usual "runhaskell Setup configure/ build/install" procedure. You will need to install the mtl and parsec libraries as well in this case (cabal-install does it for you). 9. Next you can build the Core Linker. In the directory in which you found this HOWTO file, do: $ runhaskell Setup configure --configure-option=--with-ghc=$TOP/ghc/ghc/stage1-inplace/ghc \ --configure-option=--ghc-flag=-O2 $ runhaskell Setup build $ runhaskell Setup install Make sure that the directory in which your Cabal installs executables is in your PATH. 10. Finally, the moment you've been waiting for: $ cat > hello.hs module Main where main = putStrLn "hello world!" ^D $ linkcore --package-root=$TOP/ghc/libraries --package-root=$TOP/ghc/libraries/base/dist/build --package-root=$TOP/ghc/libraries/integer-simple hello.hs -o hello_out.hcr What are all these flags doing? They're telling the Core linker to look for External Core source code in the library trees you compiled to External Core in an earlier step. Also, the -o flag tells the linker to put the Core output in hello.hcr. This will take a little while. Now you have a single Core file hello_out.hcr, which you can parse, typecheck, and interpret with the stand-alone Core library. See the Driver.hs file included in the source distribution for the External Core library for an example of how to do that.