GHC Builder

This builder can be used to build Haskell programs with Glasgow Haskell Compiler

Russel Winder has created a Bazaar branch of a SCons Haskell tool based on this code. It can be found at https://code.launchpad.net/~russel/sconsaddons/haskell

   1 from os.path import abspath, join
   2 from SCons.Builder import Builder
   3 from SCons.Scanner import Scanner
   4 from SCons.Script  import *
   5 import re
   6 
   7 impdecl_regexp = re.compile(r"[\s]*import[\s]*(qualified)?[\s]*([\w\.]*).*")
   8 foreign_decl_regexp = re.compile(r"[\s]*foreign[\s]*(export|import)[\s]*ccall(.*\"[\s]*wrapper[\s]*\")?.*::.*")
   9 
  10 def find_foreign_declarations_that_require_stubs(node):
  11         foreigns = foreign_decl_regexp.findall(node.get_contents())
  12         for foreign in foreigns:
  13                 if foreign[0] == "export":
  14                         return True
  15                 if foreign[1] != "":
  16                         return True
  17         return False
  18 
  19 def ghc_emitter(target, source, env):
  20         src_name = str(source[0])
  21         hi_name = src_name.replace(source[0].suffix, ".hi")
  22         env.SideEffect(hi_name, target)
  23         env.Clean(target, hi_name)
  24         stubs = []
  25         if find_foreign_declarations_that_require_stubs(source[0]):
  26                 stub_name = str(source[0]).replace(source[0].suffix, "_stub")
  27                 stubs.append(stub_name + ".o")
  28                 env.Clean(target, stub_name + ".c")
  29                 env.SideEffect(stub_name + ".h", target)
  30                 env.Clean(target, stub_name + ".h")
  31         return (target + stubs, source) 
  32 
  33 def ghc_path_function(env, dir, targets, sources):
  34         if dir.path != ".":
  35                 env.AppendUnique(HSSEARCHPATH = [dir.path])
  36         return tuple([dir.path] + env["HSSEARCHPATH"])
  37 
  38 def ghc_scanner_function(node, env, path):
  39         from os.path import dirname, exists
  40         imports = impdecl_regexp.findall(node.get_contents())
  41         modules = map(lambda (qualified, module) : module, imports)
  42         
  43         interfaces = []
  44         for module in modules:
  45                 module = module.replace(".", "/")
  46                 interface = module + ".hi"
  47                 hs_file   = module + ".hs"
  48                 lhs_file  = module + ".lhs"
  49                 for dir in path:
  50                         if exists(join(dir, hs_file)) or exists(join(dir, lhs_file)):
  51                                 interfaces.append(interface)
  52                                 break
  53         return interfaces
  54 
  55 def exists(env):
  56         return WhereIs("ghc")
  57 
  58 def generate(env):
  59         def _ghc_searchpath_opts(paths):
  60                 return "".join(" -i" + path for path in paths)
  61         env["_ghc_searchpath_opts"] = _ghc_searchpath_opts
  62         def _ghc_package_opts(packages):
  63                 return "".join(" -package " + package for package in packages)
  64         env["_ghc_package_opts"] = _ghc_package_opts
  65 
  66         env["HSC"] = "ghc"
  67         env["HSCFLAGS"] = []
  68         env["HSLINKFLAGS"] = []
  69         env["HSSEARCHPATH"] = []
  70         env["HSPACKAGES"] = []
  71         env["_HSPACKAGE_OPTS"] = "${_ghc_package_opts(HSPACKAGES)}"
  72         env["_HSSEARCHPATH_OPTS"] = "${_ghc_searchpath_opts(HSSEARCHPATH)}"
  73 
  74         ghc_scanner = Scanner(
  75                 function = ghc_scanner_function,
  76                 skeys = [".hs", ".lhs"],
  77                 path_function = ghc_path_function
  78                 )
  79 
  80         ghc_c_compiler = Builder(
  81                 action = "$HSC $HSCFLAGS -c -o $TARGET $SOURCE",
  82                 src_suffix = [ ".c" ],
  83                 suffix = ".o",
  84                 single_source = True
  85                 )
  86 
  87         ghc_compiler = Builder(
  88                 action = "$HSC $HSCFLAGS $_HSSEARCHPATH_OPTS -c -o $TARGET $SOURCE",
  89                 src_suffix = [ ".hs", ".lhs" ],
  90                 suffix = ".o",
  91                 single_source = True,
  92                 emitter = ghc_emitter,
  93                 source_scanner = ghc_scanner
  94                 )
  95 
  96         ghc_linker = Builder(
  97                 action = "$HSC $HSLINKFLAGS $_HSPACKAGE_OPTS -o $TARGET $SOURCES",
  98                 src_suffix = ".o",
  99                 suffix = "$PROGSUFFIX",
 100                 src_builder = [ ghc_compiler, ghc_c_compiler ]
 101                 )
 102 
 103         env.Append( BUILDERS = { "HaskellProgram" : ghc_linker, "HaskellObject" : ghc_compiler } )

Example usage

   1 env = Environment( tools = ["default", "ghc"], toolpath = ["."] )
   2 
   3 env.HaskellProgram(
   4         "foo", ["bar.hs", "baz.hs"],
   5         HSPACKAGES = ["process", "mtl"], HSCFLAGS = ["-fglasgow-exts"]
   6         )

GhcBuilder (last edited 2011-07-08 00:46:18 by 208)