aboutsummaryrefslogtreecommitdiff
path: root/extras/halide.py
blob: 6078e38bde11ee7c91e2065ea693bba75dec227b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Halide code generation tool

__author__ = __maintainer__ = "Jérôme Carretero <cJ-waf@zougloub.eu>"
__copyright__ = "Jérôme Carretero, 2014"

"""

Tool to run `Halide <http://halide-lang.org>`_ code generators.

Usage::

   bld(
    name='pipeline',
     # ^ Reference this in use="..." for things using the generated code
    #target=['pipeline.o', 'pipeline.h']
     # ^ by default, name.{o,h} is added, but you can set the outputs here
    features='halide',
    halide_env="HL_TRACE=1 HL_TARGET=host-opencl-gpu_debug",
     # ^ Environment passed to the generator,
     # can be a dict, k/v list, or string.
    args=[],
     # ^ Command-line arguments to the generator (optional),
     # eg. to give parameters to the scheduling
    source='pipeline_gen',
     # ^ Name of the source executable
   )


Known issues:


- Currently only supports Linux (no ".exe")

- Doesn't rerun on input modification when input is part of a build
  chain, and has been modified externally.

"""

import os
from waflib import Task, Utils, Options, TaskGen, Errors

class run_halide_gen(Task.Task):
	color = 'CYAN'
	vars = ['HALIDE_ENV', 'HALIDE_ARGS']
	run_str = "${SRC[0].abspath()} ${HALIDE_ARGS}"
	def __str__(self):
		stuff = "halide"
		stuff += ("[%s]" % (",".join(
		 ('%s=%s' % (k,v)) for k, v in sorted(self.env.env.items()))))
		return Task.Task.__str__(self).replace(self.__class__.__name__,
		 stuff)

@TaskGen.feature('halide')
@TaskGen.before_method('process_source')
def halide(self):
	Utils.def_attrs(self,
	 args=[],
	 halide_env={},
	)

	bld = self.bld

	env = self.halide_env
	try:
		if isinstance(env, str):
			env = dict(x.split('=') for x in env.split())
		elif isinstance(env, list):
			env = dict(x.split('=') for x in env)
		assert isinstance(env, dict)
	except Exception as e:
		if not isinstance(e, ValueError) \
		 and not isinstance(e, AssertionError):
			raise
		raise Errors.WafError(
		 "halide_env must be under the form" \
		 " {'HL_x':'a', 'HL_y':'b'}" \
		 " or ['HL_x=y', 'HL_y=b']" \
		 " or 'HL_x=y HL_y=b'")

	src = self.to_nodes(self.source)
	assert len(src) == 1, "Only one source expected"
	src = src[0]

	args = Utils.to_list(self.args)

	def change_ext(src, ext):
		# Return a node with a new extension, in an appropriate folder
		name = src.name
		xpos = src.name.rfind('.')
		if xpos == -1:
			xpos = len(src.name)
		newname = name[:xpos] + ext
		if src.is_child_of(bld.bldnode):
			node = src.get_src().parent.find_or_declare(newname)
		else:
			node = bld.bldnode.find_or_declare(newname)
		return node

	def to_nodes(self, lst, path=None):
		tmp = []
		path = path or self.path
		find = path.find_or_declare

		if isinstance(lst, self.path.__class__):
			lst = [lst]

		for x in Utils.to_list(lst):
			if isinstance(x, str):
				node = find(x)
			else:
				node = x
			tmp.append(node)
		return tmp

	tgt = to_nodes(self, self.target)
	if not tgt:
		tgt = [change_ext(src, '.o'), change_ext(src, '.h')]
	cwd = tgt[0].parent.abspath()
	task = self.create_task('run_halide_gen', src, tgt, cwd=cwd)
	task.env.append_unique('HALIDE_ARGS', args)
	if task.env.env == []:
		task.env.env = {}
	task.env.env.update(env)
	task.env.HALIDE_ENV = " ".join(("%s=%s" % (k,v)) for (k,v) in sorted(env.items()))
	task.env.HALIDE_ARGS = args

	try:
		self.compiled_tasks.append(task)
	except AttributeError:
		self.compiled_tasks = [task]
	self.source = []

def configure(conf):
	if Options.options.halide_root is None:
		conf.check_cfg(package='Halide', args='--cflags --libs')
	else:
		halide_root = Options.options.halide_root
		conf.env.INCLUDES_HALIDE = [ os.path.join(halide_root, "include") ]
		conf.env.LIBPATH_HALIDE = [ os.path.join(halide_root, "lib") ]
		conf.env.LIB_HALIDE = ["Halide"]

		# You might want to add this, while upstream doesn't fix it
		#conf.env.LIB_HALIDE += ['ncurses', 'dl', 'pthread']

def options(opt):
	opt.add_option('--halide-root',
	 help="path to Halide include and lib files",
	)