Please note:The SCons wiki is in read-only mode due to ongoing spam/DoS issues. Also, new account creation is currently disabled. We are looking into alternative wiki hosts.

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)