1#!/usr/bin/env python
2# encoding: utf-8
3
4# Texas Instruments code generator support (experimental)
5# When reporting issues, please directly assign the bug to the maintainer.
6
7__author__ = __maintainer__ = "Jérôme Carretero <cJ-waf@zougloub.eu>"
8__copyright__ = "Jérôme Carretero, 2012"
9
10"""
11TI cgt6x is a compiler suite for TI DSPs.
12
13The toolchain does pretty weird things, and I'm sure I'm missing some of them.
14But still, the tool saves time.
15
16What this tool does is:
17
18- create a TI compiler environment
19- create TI compiler features, to handle some specifics about this compiler
20  It has a few idiosyncracies, such as not giving the liberty of the .o file names
21- automatically activate them when using the TI compiler
22- handle the tconf tool
23  The tool
24
25TODO:
26
27- the set_platform_flags() function is not nice
28- more tests
29- broaden tool scope, if needed
30
31"""
32
33import os, re
34
35from waflib import Options, Utils, Task, TaskGen
36from waflib.Tools import c, ccroot, c_preproc
37from waflib.Configure import conf
38from waflib.TaskGen import feature, before_method
39from waflib.Tools.c import cprogram
40
41opj = os.path.join
42
43@conf
44def find_ticc(conf):
45	conf.find_program(['cl6x'], var='CC', path_list=opj(getattr(Options.options, 'ti-cgt-dir', ""), 'bin'))
46	conf.env.CC_NAME = 'ticc'
47
48@conf
49def find_tild(conf):
50	conf.find_program(['lnk6x'], var='LINK_CC', path_list=opj(getattr(Options.options, 'ti-cgt-dir', ""), 'bin'))
51	conf.env.LINK_CC_NAME = 'tild'
52
53@conf
54def find_tiar(conf):
55	conf.find_program(['ar6x'], var='AR', path_list=opj(getattr(Options.options, 'ti-cgt-dir', ""), 'bin'))
56	conf.env.AR_NAME = 'tiar'
57	conf.env.ARFLAGS = 'qru'
58
59@conf
60def ticc_common_flags(conf):
61	v = conf.env
62
63	if not v['LINK_CC']:
64		v['LINK_CC'] = v['CC']
65	v['CCLNK_SRC_F']	 = []
66	v['CCLNK_TGT_F']	 = ['-o']
67	v['CPPPATH_ST']	  = '-I%s'
68	v['DEFINES_ST']	  = '-d%s'
69
70	v['LIB_ST']	      = '-l%s' # template for adding libs
71	v['LIBPATH_ST']	  = '-i%s' # template for adding libpaths
72	v['STLIB_ST']	    = '-l=%s.lib'
73	v['STLIBPATH_ST']	= '-i%s'
74
75	# program
76	v['cprogram_PATTERN']    = '%s.out'
77
78	# static lib
79	#v['LINKFLAGS_cstlib']    = ['-Wl,-Bstatic']
80	v['cstlib_PATTERN']      = '%s.lib'
81
82def configure(conf):
83	v = conf.env
84	v.TI_CGT_DIR = getattr(Options.options, 'ti-cgt-dir', "")
85	v.TI_DSPLINK_DIR = getattr(Options.options, 'ti-dsplink-dir', "")
86	v.TI_BIOSUTILS_DIR = getattr(Options.options, 'ti-biosutils-dir', "")
87	v.TI_DSPBIOS_DIR = getattr(Options.options, 'ti-dspbios-dir', "")
88	v.TI_XDCTOOLS_DIR = getattr(Options.options, 'ti-xdctools-dir', "")
89	conf.find_ticc()
90	conf.find_tiar()
91	conf.find_tild()
92	conf.ticc_common_flags()
93	conf.cc_load_tools()
94	conf.cc_add_flags()
95	conf.link_add_flags()
96	conf.find_program(['tconf'], var='TCONF', path_list=v.TI_XDCTOOLS_DIR)
97
98	conf.env.TCONF_INCLUDES += [
99	 opj(conf.env.TI_DSPBIOS_DIR, 'packages'),
100	]
101
102	conf.env.INCLUDES += [
103	 opj(conf.env.TI_CGT_DIR, 'include'),
104	]
105
106	conf.env.LIBPATH += [
107	 opj(conf.env.TI_CGT_DIR, "lib"),
108	]
109
110	conf.env.INCLUDES_DSPBIOS += [
111	 opj(conf.env.TI_DSPBIOS_DIR, 'packages', 'ti', 'bios', 'include'),
112	]
113
114	conf.env.LIBPATH_DSPBIOS += [
115	 opj(conf.env.TI_DSPBIOS_DIR, 'packages', 'ti', 'bios', 'lib'),
116	]
117
118	conf.env.INCLUDES_DSPLINK += [
119	 opj(conf.env.TI_DSPLINK_DIR, 'dsplink', 'dsp', 'inc'),
120	]
121
122@conf
123def ti_set_debug(cfg, debug=1):
124	"""
125	Sets debug flags for the compiler.
126
127	TODO:
128	- for each TI CFLAG/INCLUDES/LINKFLAGS/LIBPATH replace RELEASE by DEBUG
129	- -g --no_compress
130	"""
131	if debug:
132		cfg.env.CFLAGS += "-d_DEBUG -dDEBUG -dDDSP_DEBUG".split()
133
134@conf
135def ti_dsplink_set_platform_flags(cfg, splat, dsp, dspbios_ver, board):
136	"""
137	Sets the INCLUDES, LINKFLAGS for DSPLINK and TCONF_INCLUDES
138	For the specific hardware.
139
140	Assumes that DSPLINK was built in its own folder.
141
142	:param splat: short platform name (eg. OMAPL138)
143	:param dsp: DSP name (eg. 674X)
144	:param dspbios_ver: string identifying DspBios version (eg. 5.XX)
145	:param board: board name (eg. OMAPL138GEM)
146
147	"""
148	d1 = opj(cfg.env.TI_DSPLINK_DIR, 'dsplink', 'dsp', 'inc', 'DspBios', dspbios_ver)
149	d = opj(cfg.env.TI_DSPLINK_DIR, 'dsplink', 'dsp', 'inc', 'DspBios', dspbios_ver, board)
150	cfg.env.TCONF_INCLUDES += [d1, d]
151	cfg.env.INCLUDES_DSPLINK += [
152	 opj(cfg.env.TI_DSPLINK_DIR, 'dsplink', 'dsp', 'inc', dsp),
153	 d,
154	]
155
156	cfg.env.LINKFLAGS_DSPLINK += [
157	 opj(cfg.env.TI_DSPLINK_DIR, 'dsplink', 'dsp', 'export', 'BIN', 'DspBios', splat, board+'_0', 'RELEASE', 'dsplink%s.lib' % x)
158	 for x in ('', 'pool', 'mpcs', 'mplist', 'msg', 'data', 'notify', 'ringio')
159	]
160
161
162def options(opt):
163	opt.add_option('--with-ti-cgt', type='string', dest='ti-cgt-dir', help = 'Specify alternate cgt root folder', default="")
164	opt.add_option('--with-ti-biosutils', type='string', dest='ti-biosutils-dir', help = 'Specify alternate biosutils folder', default="")
165	opt.add_option('--with-ti-dspbios', type='string', dest='ti-dspbios-dir', help = 'Specify alternate dspbios folder', default="")
166	opt.add_option('--with-ti-dsplink', type='string', dest='ti-dsplink-dir', help = 'Specify alternate dsplink folder', default="")
167	opt.add_option('--with-ti-xdctools', type='string', dest='ti-xdctools-dir', help = 'Specify alternate xdctools folder', default="")
168
169class ti_cprogram(cprogram):
170	"""
171	Link object files into a c program
172
173	Changes:
174
175	- the linked executable to have a relative path (because we can)
176	- put the LIBPATH first
177	"""
178	run_str = '${LINK_CC} ${LIBPATH_ST:LIBPATH} ${LIB_ST:LIB} ${LINKFLAGS} ${CCLNK_SRC_F}${SRC} ${CCLNK_TGT_F}${TGT[0].bldpath()} ${RPATH_ST:RPATH} ${FRAMEWORKPATH_ST:FRAMEWORKPATH} ${FRAMEWORK_ST:FRAMEWORK} ${ARCH_ST:ARCH} ${STLIB_MARKER} ${STLIBPATH_ST:STLIBPATH} ${STLIB_ST:STLIB} ${SHLIB_MARKER} '
179
180@feature("c")
181@before_method('apply_link')
182def use_ti_cprogram(self):
183	"""
184	Automatically uses ti_cprogram link process
185	"""
186	if 'cprogram' in self.features and self.env.CC_NAME == 'ticc':
187		self.features.insert(0, "ti_cprogram")
188
189class ti_c(Task.Task):
190	"""
191	Compile task for the TI codegen compiler
192
193	This compiler does not allow specifying the output file name, only the output path.
194
195	"""
196	"Compile C files into object files"
197	run_str = '${CC} ${ARCH_ST:ARCH} ${CFLAGS} ${FRAMEWORKPATH_ST:FRAMEWORKPATH} ${CPPPATH_ST:INCPATHS} ${DEFINES_ST:DEFINES} ${SRC} -c ${OUT} ${CPPFLAGS}'
198	vars    = ['CCDEPS'] # unused variable to depend on, just in case
199	ext_in  = ['.h'] # set the build order easily by using ext_out=['.h']
200	scan    = c_preproc.scan
201
202def create_compiled_task(self, name, node):
203	"""
204	Overrides ccroot.create_compiled_task to support ti_c
205	"""
206	out = '%s' % (node.change_ext('.obj').name)
207	if self.env.CC_NAME == 'ticc':
208		name = 'ti_c'
209	task = self.create_task(name, node, node.parent.find_or_declare(out))
210	self.env.OUT = '-fr%s' % (node.parent.get_bld().abspath())
211	try:
212		self.compiled_tasks.append(task)
213	except AttributeError:
214		self.compiled_tasks = [task]
215	return task
216
217@TaskGen.extension('.c')
218def c_hook(self, node):
219	"Bind the c file extension to the creation of a :py:class:`waflib.Tools.c.c` instance"
220	if self.env.CC_NAME == 'ticc':
221		return create_compiled_task(self, 'ti_c', node)
222	else:
223		return self.create_compiled_task('c', node)
224
225
226@feature("ti-tconf")
227@before_method('process_source')
228def apply_tconf(self):
229	sources = [x.get_src() for x in self.to_nodes(self.source, path=self.path.get_src())]
230	node = sources[0]
231	assert(sources[0].name.endswith(".tcf"))
232	if len(sources) > 1:
233		assert(sources[1].name.endswith(".cmd"))
234
235	target = getattr(self, 'target', self.source)
236	target_node = node.get_bld().parent.find_or_declare(node.name)
237
238	procid = "%d" % int(getattr(self, 'procid', 0))
239
240	importpaths = []
241	includes = Utils.to_list(getattr(self, 'includes', []))
242	for x in includes + self.env.TCONF_INCLUDES:
243		if x == os.path.abspath(x):
244			importpaths.append(x)
245		else:
246			relpath = self.path.find_node(x).path_from(target_node.parent)
247			importpaths.append(relpath)
248
249	task = self.create_task('ti_tconf', sources, target_node.change_ext('.cdb'))
250	task.path = self.path
251	task.includes = includes
252	task.cwd = target_node.parent.abspath()
253	task.env = self.env.derive()
254	task.env["TCONFSRC"] = node.path_from(target_node.parent)
255	task.env["TCONFINC"] = '-Dconfig.importPath=%s' % ";".join(importpaths)
256	task.env['TCONFPROGNAME'] = '-Dconfig.programName=%s' % target
257	task.env['PROCID'] = procid
258	task.outputs = [
259	 target_node.change_ext("cfg_c.c"),
260	 target_node.change_ext("cfg.s62"),
261	 target_node.change_ext("cfg.cmd"),
262	]
263
264	create_compiled_task(self, 'ti_c', task.outputs[1])
265	ctask = create_compiled_task(self, 'ti_c', task.outputs[0])
266	ctask.env = self.env.derive()
267
268	self.add_those_o_files(target_node.change_ext("cfg.cmd"))
269	if len(sources) > 1:
270		self.add_those_o_files(sources[1])
271	self.source = []
272
273re_tconf_include = re.compile(r'(?P<type>utils\.importFile)\("(?P<file>.*)"\)',re.M)
274class ti_tconf(Task.Task):
275	run_str = '${TCONF} ${TCONFINC} ${TCONFPROGNAME} ${TCONFSRC} ${PROCID}'
276	color   = 'PINK'
277
278	def scan(self):
279		includes = Utils.to_list(getattr(self, 'includes', []))
280
281		def deps(node):
282			nodes, names = [], []
283			if node:
284				code = Utils.readf(node.abspath())
285				for match in re_tconf_include.finditer(code):
286					path = match.group('file')
287					if path:
288						for x in includes:
289							filename = opj(x, path)
290							fi = self.path.find_resource(filename)
291							if fi:
292								subnodes, subnames = deps(fi)
293								nodes += subnodes
294								names += subnames
295								nodes.append(fi)
296								names.append(path)
297								break
298			return nodes, names
299		return deps(self.inputs[0])
300
301