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 } )
