1#!/usr/bin/python
2# -*- coding: utf-8 vi:ts=4:noexpandtab
3# Tool to provide dedicated variables for cross-compilation
4
5__author__ = __maintainer__ = "Jérôme Carretero <cJ-waf@zougloub.eu>"
6__copyright__ = "Jérôme Carretero, 2014"
7
8"""
9This tool allows to use environment variables to define cross-compilation
10variables intended for build variants.
11
12The variables are obtained from the environment in 3 ways:
13
141. By defining CHOST, they can be derived as ${CHOST}-${TOOL}
152. By defining HOST_x
163. By defining ${CHOST//-/_}_x
17
18else one can set ``cfg.env.CHOST`` in ``wscript`` before loading ``cross_gnu``.
19
20Usage:
21
22- In your build script::
23
24	def configure(cfg):
25		...
26		for variant in x_variants:
27			setenv(variant)
28			conf.load('cross_gnu')
29			conf.xcheck_host_var('POUET')
30			...
31
32
33- Then::
34
35	CHOST=arm-hardfloat-linux-gnueabi waf configure
36	env arm-hardfloat-linux-gnueabi-CC="clang -..." waf configure
37	CFLAGS=... CHOST=arm-hardfloat-linux-gnueabi HOST_CFLAGS=-g waf configure
38	HOST_CC="clang -..." waf configure
39
40This example ``wscript`` compiles to Microchip PIC (xc16-gcc-xyz must be in PATH):
41
42.. code:: python
43
44		from waflib import Configure
45
46		#from https://gist.github.com/rpuntaie/2bddfb5d7b77db26415ee14371289971
47		import waf_variants
48
49		variants='pc fw/variant1 fw/variant2'.split()
50
51		top = "."
52		out = "../build"
53
54		PIC = '33FJ128GP804' #dsPICxxx
55
56		@Configure.conf
57		def gcc_modifier_xc16(cfg):
58				v = cfg.env
59				v.cprogram_PATTERN = '%s.elf'
60				v.LINKFLAGS_cprogram = ','.join(['-Wl','','','--defsym=__MPLAB_BUILD=0','','--script=p'+PIC+'.gld',
61						'--stack=16','--check-sections','--data-init','--pack-data','--handles','--isr','--no-gc-sections',
62						'--fill-upper=0','--stackguard=16','--no-force-link','--smart-io']) #,'--report-mem'])
63				v.CFLAGS_cprogram=['-mcpu='+PIC,'-omf=elf','-mlarge-code','-msmart-io=1',
64						'-msfr-warn=off','-mno-override-inline','-finline','-Winline']
65
66		def configure(cfg):
67				if 'fw' in cfg.variant: #firmware
68						cfg.env.DEST_OS = 'xc16' #cfg.env.CHOST = 'xc16' #works too
69						cfg.load('c cross_gnu') #cfg.env.CHOST becomes ['xc16']
70						...
71				else: #configure for pc SW
72						...
73
74		def build(bld):
75				if 'fw' in bld.variant: #firmware
76						bld.program(source='maintst.c', target='maintst');
77						bld(source='maintst.elf', target='maintst.hex', rule="xc16-bin2hex ${SRC} -a -omf=elf")
78				else: #build for pc SW
79						...
80
81"""
82
83import os
84from waflib import Utils, Configure
85from waflib.Tools import ccroot, gcc
86
87try:
88	from shlex import quote
89except ImportError:
90	from pipes import quote
91
92def get_chost_stuff(conf):
93	"""
94	Get the CHOST environment variable contents
95	"""
96	chost = None
97	chost_envar = None
98	if conf.env.CHOST:
99		chost = conf.env.CHOST[0]
100		chost_envar = chost.replace('-', '_')
101	return chost, chost_envar
102
103
104@Configure.conf
105def xcheck_var(conf, name, wafname=None, cross=False):
106	wafname = wafname or name
107
108	if wafname in conf.env:
109		value = conf.env[wafname]
110		if isinstance(value, str):
111			value = [value]
112	else:
113		envar = os.environ.get(name)
114		if not envar:
115			return
116		value = Utils.to_list(envar) if envar != '' else [envar]
117
118	conf.env[wafname] = value
119	if cross:
120		pretty = 'cross-compilation %s' % wafname
121	else:
122		pretty = wafname
123	conf.msg('Will use %s' % pretty, " ".join(quote(x) for x in value))
124
125@Configure.conf
126def xcheck_host_prog(conf, name, tool, wafname=None):
127	wafname = wafname or name
128
129	chost, chost_envar = get_chost_stuff(conf)
130
131	specific = None
132	if chost:
133		specific = os.environ.get('%s_%s' % (chost_envar, name))
134
135	if specific:
136		value = Utils.to_list(specific)
137		conf.env[wafname] += value
138		conf.msg('Will use cross-compilation %s from %s_%s' % (name, chost_envar, name),
139		 " ".join(quote(x) for x in value))
140		return
141	else:
142		envar = os.environ.get('HOST_%s' % name)
143		if envar is not None:
144			value = Utils.to_list(envar)
145			conf.env[wafname] = value
146			conf.msg('Will use cross-compilation %s from HOST_%s' % (name, name),
147			 " ".join(quote(x) for x in value))
148			return
149
150	if conf.env[wafname]:
151		return
152
153	value = None
154	if chost:
155		value = '%s-%s' % (chost, tool)
156
157	if value:
158		conf.env[wafname] = value
159		conf.msg('Will use cross-compilation %s from CHOST' % wafname, value)
160
161@Configure.conf
162def xcheck_host_envar(conf, name, wafname=None):
163	wafname = wafname or name
164
165	chost, chost_envar = get_chost_stuff(conf)
166
167	specific = None
168	if chost:
169		specific = os.environ.get('%s_%s' % (chost_envar, name))
170
171	if specific:
172		value = Utils.to_list(specific)
173		conf.env[wafname] += value
174		conf.msg('Will use cross-compilation %s from %s_%s' \
175		 % (name, chost_envar, name),
176		 " ".join(quote(x) for x in value))
177		return
178
179
180	envar = os.environ.get('HOST_%s' % name)
181	if envar is None:
182		return
183
184	value = Utils.to_list(envar) if envar != '' else [envar]
185
186	conf.env[wafname] = value
187	conf.msg('Will use cross-compilation %s from HOST_%s' % (name, name),
188	 " ".join(quote(x) for x in value))
189
190
191@Configure.conf
192def xcheck_host(conf):
193	conf.xcheck_var('CHOST', cross=True)
194	conf.env.CHOST = conf.env.CHOST or [conf.env.DEST_OS]
195	conf.env.DEST_OS = conf.env.CHOST[0].replace('-','_')
196	conf.xcheck_host_prog('CC', 'gcc')
197	conf.xcheck_host_prog('CXX', 'g++')
198	conf.xcheck_host_prog('LINK_CC', 'gcc')
199	conf.xcheck_host_prog('LINK_CXX', 'g++')
200	conf.xcheck_host_prog('AR', 'ar')
201	conf.xcheck_host_prog('AS', 'as')
202	conf.xcheck_host_prog('LD', 'ld')
203	conf.xcheck_host_envar('CFLAGS')
204	conf.xcheck_host_envar('CXXFLAGS')
205	conf.xcheck_host_envar('LDFLAGS', 'LINKFLAGS')
206	conf.xcheck_host_envar('LIB')
207	conf.xcheck_host_envar('PKG_CONFIG_LIBDIR')
208	conf.xcheck_host_envar('PKG_CONFIG_PATH')
209
210	if not conf.env.env:
211		conf.env.env = {}
212		conf.env.env.update(os.environ)
213	if conf.env.PKG_CONFIG_LIBDIR:
214		conf.env.env['PKG_CONFIG_LIBDIR'] = conf.env.PKG_CONFIG_LIBDIR[0]
215	if conf.env.PKG_CONFIG_PATH:
216		conf.env.env['PKG_CONFIG_PATH'] = conf.env.PKG_CONFIG_PATH[0]
217
218def configure(conf):
219	"""
220	Configuration example for gcc, it will not work for g++/clang/clang++
221	"""
222	conf.xcheck_host()
223	conf.gcc_common_flags()
224	conf.gcc_modifier_platform()
225	conf.cc_load_tools()
226	conf.cc_add_flags()
227	conf.link_add_flags()
228