1#!/usr/bin/env python
2# encoding: utf-8
3# Thomas Nagy, 2016-2018 (ita)
4
5"""
6Various configuration tests.
7"""
8
9from waflib import Task
10from waflib.Configure import conf
11from waflib.TaskGen import feature, before_method, after_method
12
13LIB_CODE = '''
14#ifdef _MSC_VER
15#define testEXPORT __declspec(dllexport)
16#else
17#define testEXPORT
18#endif
19testEXPORT int lib_func(void) { return 9; }
20'''
21
22MAIN_CODE = '''
23#ifdef _MSC_VER
24#define testEXPORT __declspec(dllimport)
25#else
26#define testEXPORT
27#endif
28testEXPORT int lib_func(void);
29int main(int argc, char **argv) {
30	(void)argc; (void)argv;
31	return !(lib_func() == 9);
32}
33'''
34
35@feature('link_lib_test')
36@before_method('process_source')
37def link_lib_test_fun(self):
38	"""
39	The configuration test :py:func:`waflib.Configure.run_build` declares a unique task generator,
40	so we need to create other task generators from here to check if the linker is able to link libraries.
41	"""
42	def write_test_file(task):
43		task.outputs[0].write(task.generator.code)
44
45	rpath = []
46	if getattr(self, 'add_rpath', False):
47		rpath = [self.bld.path.get_bld().abspath()]
48
49	mode = self.mode
50	m = '%s %s' % (mode, mode)
51	ex = self.test_exec and 'test_exec' or ''
52	bld = self.bld
53	bld(rule=write_test_file, target='test.' + mode, code=LIB_CODE)
54	bld(rule=write_test_file, target='main.' + mode, code=MAIN_CODE)
55	bld(features='%sshlib' % m, source='test.' + mode, target='test')
56	bld(features='%sprogram %s' % (m, ex), source='main.' + mode, target='app', use='test', rpath=rpath)
57
58@conf
59def check_library(self, mode=None, test_exec=True):
60	"""
61	Checks if libraries can be linked with the current linker. Uses :py:func:`waflib.Tools.c_tests.link_lib_test_fun`.
62
63	:param mode: c or cxx or d
64	:type mode: string
65	"""
66	if not mode:
67		mode = 'c'
68		if self.env.CXX:
69			mode = 'cxx'
70	self.check(
71		compile_filename = [],
72		features = 'link_lib_test',
73		msg = 'Checking for libraries',
74		mode = mode,
75		test_exec = test_exec)
76
77########################################################################################
78
79INLINE_CODE = '''
80typedef int foo_t;
81static %s foo_t static_foo () {return 0; }
82%s foo_t foo () {
83	return 0;
84}
85'''
86INLINE_VALUES = ['inline', '__inline__', '__inline']
87
88@conf
89def check_inline(self, **kw):
90	"""
91	Checks for the right value for inline macro.
92	Define INLINE_MACRO to 1 if the define is found.
93	If the inline macro is not 'inline', add a define to the ``config.h`` (#define inline __inline__)
94
95	:param define_name: define INLINE_MACRO by default to 1 if the macro is defined
96	:type define_name: string
97	:param features: by default *c* or *cxx* depending on the compiler present
98	:type features: list of string
99	"""
100	self.start_msg('Checking for inline')
101
102	if not 'define_name' in kw:
103		kw['define_name'] = 'INLINE_MACRO'
104	if not 'features' in kw:
105		if self.env.CXX:
106			kw['features'] = ['cxx']
107		else:
108			kw['features'] = ['c']
109
110	for x in INLINE_VALUES:
111		kw['fragment'] = INLINE_CODE % (x, x)
112
113		try:
114			self.check(**kw)
115		except self.errors.ConfigurationError:
116			continue
117		else:
118			self.end_msg(x)
119			if x != 'inline':
120				self.define('inline', x, quote=False)
121			return x
122	self.fatal('could not use inline functions')
123
124########################################################################################
125
126LARGE_FRAGMENT = '''#include <unistd.h>
127int main(int argc, char **argv) {
128	(void)argc; (void)argv;
129	return !(sizeof(off_t) >= 8);
130}
131'''
132
133@conf
134def check_large_file(self, **kw):
135	"""
136	Checks for large file support and define the macro HAVE_LARGEFILE
137	The test is skipped on win32 systems (DEST_BINFMT == pe).
138
139	:param define_name: define to set, by default *HAVE_LARGEFILE*
140	:type define_name: string
141	:param execute: execute the test (yes by default)
142	:type execute: bool
143	"""
144	if not 'define_name' in kw:
145		kw['define_name'] = 'HAVE_LARGEFILE'
146	if not 'execute' in kw:
147		kw['execute'] = True
148
149	if not 'features' in kw:
150		if self.env.CXX:
151			kw['features'] = ['cxx', 'cxxprogram']
152		else:
153			kw['features'] = ['c', 'cprogram']
154
155	kw['fragment'] = LARGE_FRAGMENT
156
157	kw['msg'] = 'Checking for large file support'
158	ret = True
159	try:
160		if self.env.DEST_BINFMT != 'pe':
161			ret = self.check(**kw)
162	except self.errors.ConfigurationError:
163		pass
164	else:
165		if ret:
166			return True
167
168	kw['msg'] = 'Checking for -D_FILE_OFFSET_BITS=64'
169	kw['defines'] = ['_FILE_OFFSET_BITS=64']
170	try:
171		ret = self.check(**kw)
172	except self.errors.ConfigurationError:
173		pass
174	else:
175		self.define('_FILE_OFFSET_BITS', 64)
176		return ret
177
178	self.fatal('There is no support for large files')
179
180########################################################################################
181
182ENDIAN_FRAGMENT = '''
183short int ascii_mm[] = { 0x4249, 0x4765, 0x6E44, 0x6961, 0x6E53, 0x7953, 0 };
184short int ascii_ii[] = { 0x694C, 0x5454, 0x656C, 0x6E45, 0x6944, 0x6E61, 0 };
185int use_ascii (int i) {
186	return ascii_mm[i] + ascii_ii[i];
187}
188short int ebcdic_ii[] = { 0x89D3, 0xE3E3, 0x8593, 0x95C5, 0x89C4, 0x9581, 0 };
189short int ebcdic_mm[] = { 0xC2C9, 0xC785, 0x95C4, 0x8981, 0x95E2, 0xA8E2, 0 };
190int use_ebcdic (int i) {
191	return ebcdic_mm[i] + ebcdic_ii[i];
192}
193extern int foo;
194'''
195
196class grep_for_endianness(Task.Task):
197	"""
198	Task that reads a binary and tries to determine the endianness
199	"""
200	color = 'PINK'
201	def run(self):
202		txt = self.inputs[0].read(flags='rb').decode('latin-1')
203		if txt.find('LiTTleEnDian') > -1:
204			self.generator.tmp.append('little')
205		elif txt.find('BIGenDianSyS') > -1:
206			self.generator.tmp.append('big')
207		else:
208			return -1
209
210@feature('grep_for_endianness')
211@after_method('process_source')
212def grep_for_endianness_fun(self):
213	"""
214	Used by the endianness configuration test
215	"""
216	self.create_task('grep_for_endianness', self.compiled_tasks[0].outputs[0])
217
218@conf
219def check_endianness(self):
220	"""
221	Executes a configuration test to determine the endianness
222	"""
223	tmp = []
224	def check_msg(self):
225		return tmp[0]
226	self.check(fragment=ENDIAN_FRAGMENT, features='c grep_for_endianness',
227		msg='Checking for endianness', define='ENDIANNESS', tmp=tmp,
228		okmsg=check_msg, confcache=None)
229	return tmp[0]
230
231