1#!/usr/bin/env python
2# encoding: utf-8
3# Anton Feldmann, 2012
4# "Base for cabal"
5
6from waflib import Task, Utils
7from waflib.TaskGen import extension
8from waflib.Utils import threading
9from shutil import rmtree
10
11lock = threading.Lock()
12registering = False
13
14def configure(self):
15    self.find_program('cabal', var='CABAL')
16    self.find_program('ghc-pkg', var='GHCPKG')
17    pkgconfd = self.bldnode.abspath() + '/package.conf.d'
18    self.env.PREFIX = self.bldnode.abspath() + '/dist'
19    self.env.PKGCONFD = pkgconfd
20    if self.root.find_node(pkgconfd + '/package.cache'):
21        self.msg('Using existing package database', pkgconfd, color='CYAN')
22    else:
23        pkgdir = self.root.find_dir(pkgconfd)
24        if pkgdir:
25            self.msg('Deleting corrupt package database', pkgdir.abspath(), color ='RED')
26            rmtree(pkgdir.abspath())
27            pkgdir = None
28
29        self.cmd_and_log(self.env.GHCPKG + ['init', pkgconfd])
30        self.msg('Created package database', pkgconfd, color = 'YELLOW' if pkgdir else 'GREEN')
31
32@extension('.cabal')
33def process_cabal(self, node):
34    out_dir_node = self.bld.root.find_dir(self.bld.out_dir)
35    package_node = node.change_ext('.package')
36    package_node = out_dir_node.find_or_declare(package_node.name)
37    build_node   = node.parent.get_bld()
38    build_path   = build_node.abspath()
39    config_node  = build_node.find_or_declare('setup-config')
40    inplace_node = build_node.find_or_declare('package.conf.inplace')
41
42    config_task = self.create_task('cabal_configure', node)
43    config_task.cwd = node.parent.abspath()
44    config_task.depends_on = getattr(self, 'depends_on', '')
45    config_task.build_path = build_path
46    config_task.set_outputs(config_node)
47
48    build_task = self.create_task('cabal_build', config_node)
49    build_task.cwd = node.parent.abspath()
50    build_task.build_path = build_path
51    build_task.set_outputs(inplace_node)
52
53    copy_task = self.create_task('cabal_copy', inplace_node)
54    copy_task.cwd = node.parent.abspath()
55    copy_task.depends_on = getattr(self, 'depends_on', '')
56    copy_task.build_path = build_path
57
58    last_task = copy_task
59    task_list = [config_task, build_task, copy_task]
60
61    if (getattr(self, 'register', False)):
62        register_task = self.create_task('cabal_register', inplace_node)
63        register_task.cwd = node.parent.abspath()
64        register_task.set_run_after(copy_task)
65        register_task.build_path = build_path
66
67        pkgreg_task = self.create_task('ghcpkg_register', inplace_node)
68        pkgreg_task.cwd = node.parent.abspath()
69        pkgreg_task.set_run_after(register_task)
70        pkgreg_task.build_path = build_path
71
72        last_task = pkgreg_task
73        task_list += [register_task, pkgreg_task]
74
75    touch_task = self.create_task('cabal_touch', inplace_node)
76    touch_task.set_run_after(last_task)
77    touch_task.set_outputs(package_node)
78    touch_task.build_path = build_path
79
80    task_list += [touch_task]
81
82    return task_list
83
84def get_all_src_deps(node):
85    hs_deps = node.ant_glob('**/*.hs')
86    hsc_deps = node.ant_glob('**/*.hsc')
87    lhs_deps = node.ant_glob('**/*.lhs')
88    c_deps = node.ant_glob('**/*.c')
89    cpp_deps = node.ant_glob('**/*.cpp')
90    proto_deps = node.ant_glob('**/*.proto')
91    return sum([hs_deps, hsc_deps, lhs_deps, c_deps, cpp_deps, proto_deps], [])
92
93class Cabal(Task.Task):
94    def scan(self):
95        return (get_all_src_deps(self.generator.path), ())
96
97class cabal_configure(Cabal):
98    run_str = '${CABAL} configure -v0 --prefix=${PREFIX} --global --user --package-db=${PKGCONFD} --builddir=${tsk.build_path}'
99    shell = True
100
101    def scan(self):
102        out_node = self.generator.bld.root.find_dir(self.generator.bld.out_dir)
103        deps = [out_node.find_or_declare(dep).change_ext('.package') for dep in Utils.to_list(self.depends_on)]
104        return (deps, ())
105
106class cabal_build(Cabal):
107    run_str = '${CABAL} build -v1 --builddir=${tsk.build_path}/'
108    shell = True
109
110class cabal_copy(Cabal):
111    run_str = '${CABAL} copy -v0 --builddir=${tsk.build_path}'
112    shell = True
113
114class cabal_register(Cabal):
115    run_str = '${CABAL} register -v0 --gen-pkg-config=${tsk.build_path}/pkg.config --builddir=${tsk.build_path}'
116    shell = True
117
118class ghcpkg_register(Cabal):
119    run_str = '${GHCPKG} update -v0 --global --user --package-conf=${PKGCONFD} ${tsk.build_path}/pkg.config'
120    shell = True
121
122    def runnable_status(self):
123        global lock, registering
124
125        val = False
126        lock.acquire()
127        val = registering
128        lock.release()
129
130        if val:
131            return Task.ASK_LATER
132
133        ret = Task.Task.runnable_status(self)
134        if ret == Task.RUN_ME:
135            lock.acquire()
136            registering = True
137            lock.release()
138
139        return ret
140
141    def post_run(self):
142        global lock, registering
143
144        lock.acquire()
145        registering = False
146        lock.release()
147
148        return Task.Task.post_run(self)
149
150class cabal_touch(Cabal):
151    run_str = 'touch ${TGT}'
152
153