1#
2# SPDX-License-Identifier: BSD-3-Clause
3#
4# Copyright © 2019 Keith Packard
5#
6# Redistribution and use in source and binary forms, with or without
7# modification, are permitted provided that the following conditions
8# are met:
9#
10# 1. Redistributions of source code must retain the above copyright
11#    notice, this list of conditions and the following disclaimer.
12#
13# 2. Redistributions in binary form must reproduce the above
14#    copyright notice, this list of conditions and the following
15#    disclaimer in the documentation and/or other materials provided
16#    with the distribution.
17#
18# 3. Neither the name of the copyright holder nor the names of its
19#    contributors may be used to endorse or promote products derived
20#    from this software without specific prior written permission.
21#
22# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
26# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
27# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
28# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
29# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
31# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
33# OF THE POSSIBILITY OF SUCH DAMAGE.
34#
35project('picolibc', 'c',
36	default_options: [
37	  'buildtype=minsize',
38	  'c_std=gnu99',
39	  'b_staticpic=false',
40	],
41	license : 'BSD',
42	meson_version : '>= 0.50',
43	version: '1.3'
44       )
45
46targets = []
47
48enable_multilib = get_option('multilib')
49enable_picolib = get_option('picolib')
50enable_tests = get_option('tests')
51enable_native_tests = get_option('native-tests')
52newlib_tinystdio = get_option('newlib-tinystdio')
53have_alias_attribute_option = get_option('have-alias-attribute')
54newlib_io_pos_args = get_option('newlib-io-pos-args')
55newlib_io_c99_formats = get_option('newlib-io-c99-formats')
56newlib_register_fini = get_option('newlib-register-fini')
57newlib_io_long_long = get_option('newlib-io-long-long')
58newlib_io_long_double = get_option('newlib-io-long-double')
59newlib_mb = get_option('newlib-mb')
60newlib_iconv_encodings = get_option('newlib-iconv-encodings')
61newlib_iconv_from_encodings = get_option('newlib-iconv-from-encodings')
62newlib_iconv_to_encodings = get_option('newlib-iconv-to-encodings')
63newlib_iconv_external_ccs = get_option('newlib-iconv-external-ccs')
64newlib_atexit_dynamic_alloc = get_option('newlib-atexit-dynamic-alloc')
65newlib_global_atexit = get_option('newlib-global-atexit')
66newlib_reent_small = get_option('newlib-reent-small')
67newlib_global_stdio_streams = get_option('newlib-global-stdio-streams')
68newlib_fvwrite_in_streamio = get_option('newlib-fvwrite-in-streamio')
69newlib_fseek_optimization = get_option('newlib-fseek-optimization')
70newlib_wide_orient = get_option('newlib-wide-orient')
71newlib_nano_malloc = get_option('newlib-nano-malloc')
72newlib_unbuf_stream_opt = get_option('newlib-unbuf-stream-opt')
73lite_exit = get_option('lite-exit')
74newlib_nano_formatted_io = get_option('newlib-nano-formatted-io')
75newlib_retargetable_locking = get_option('newlib-retargetable-locking')
76newlib_long_time_t = get_option('newlib-long-time_t')
77newlib_multithread = get_option('newlib-multithread')
78newlib_iconv = get_option('newlib-iconv')
79newlib_io_float = get_option('newlib-io-float')
80newlib_supplied_syscalls = get_option('newlib-supplied-syscalls')
81newlib_elix_level = get_option('newlib-elix-level')
82target_optspace = get_option('optimization') == 's'
83fast_strcmp = get_option('fast-strcmp')
84newlib_reentrant_syscalls_provided = get_option('newlib-reentrant-syscalls-provided')
85newlib_missing_syscall_names = get_option('newlib-missing-syscall-names')
86newlib_locale_info = get_option('newlib-locale-info')
87newlib_locale_info_extended = get_option('newlib-locale-info-extended')
88newlib_global_errno = get_option('newlib-global-errno')
89newlib_initfini_array = get_option('newlib-initfini-array')
90newlib_initfini = get_option('newlib-initfini')
91thread_local_storage_option = get_option('thread-local-storage')
92tls_model = get_option('tls-model')
93posix_io = get_option('posix-io')
94posix_console = posix_io and get_option('posix-console')
95sysroot_install = get_option('sysroot-install')
96newlib_obsolete_math = get_option('newlib-obsolete-math')
97
98host_cpu=host_machine.cpu()
99if host_cpu == ''
100  host_cmds = meson.get_compiler('c').cmd_array() + ['-dumpmachine']
101  host_cc_machine=run_command(host_cmds).stdout().strip().split('-')
102  host_cpu=host_cc_machine[0]
103  message('Computed host_cpu as ' + host_cpu)
104endif
105
106specs_dir = get_option('specsdir')
107if specs_dir == ''
108  search_cmds = meson.get_compiler('c').cmd_array() + ['-print-search-dirs']
109  install_dir=run_command(search_cmds).stdout().split('\n')[0]
110  specs_dir=install_dir.split(':')[1].strip()
111endif
112
113if have_alias_attribute_option == 'auto'
114  have_alias_attribute = meson.get_compiler('c').has_function_attribute('alias')
115else
116  have_alias_attribute = have_alias_attribute_option == 'true'
117endif
118
119if thread_local_storage_option == 'auto'
120  thread_local_storage = not meson.get_compiler('c').has_function('__emutls_get_address', args: ['-nostdlib', '-lgcc'])
121else
122  thread_local_storage = thread_local_storage_option == 'true'
123endif
124
125lib_dir = join_paths(get_option('prefix'), get_option('libdir'))
126
127specs_data = configuration_data()
128specs_data.set(
129  'INCLUDEDIR',
130  join_paths(sysroot_install? '%R' : get_option('prefix'), get_option('includedir'))
131)
132
133specs_data.set(
134  'LIBDIR',
135  sysroot_install? join_paths('%R', get_option('libdir')) : lib_dir
136)
137
138specs_data.set(
139  'TLSMODEL',
140  tls_model
141)
142
143configure_file(input: 'picolibc.specs.in',
144	       output: '@BASENAME@',
145	       configuration: specs_data,
146	       install_dir: specs_dir,
147	       install: true)
148
149install_data('picolibc.ld',
150	     install_dir: lib_dir)
151
152long_double_code = '''
153#include <float.h>
154#ifndef LDBL_MANT_DIG
155#error No long double support in float.h
156#endif
157long double test()
158{
159	long double ld = 0.0L;
160	 return ld;
161}
162'''
163have_long_double = meson.get_compiler('c').compiles(long_double_code, name : 'long double check')
164
165if enable_multilib
166  foreach target : run_command(meson.get_compiler('c'), '--print-multi-lib').stdout().strip().split('\n')
167    tmp = target.split(';')
168    flags = []
169    if tmp.length() > 1
170      foreach flag : tmp[1].strip('@').split('@')
171	if flag != ''
172	  flags += '-' + flag
173	endif
174      endforeach
175      if tmp[0] == '.'
176	name = ''
177      else
178	name = tmp[0].underscorify()
179      endif
180    else
181      name = ''
182    endif
183    targets += name
184    value = [tmp[0], flags]
185    set_variable('target_' + name, value)
186  endforeach
187else
188  targets = ['']
189  target_ = ['.', []]
190endif
191
192conf_data = configuration_data()
193
194NEWLIB_VERSION='2.10.0'
195NEWLIB_MAJOR_VERSION=2
196NEWLIB_MINOR_VERSION=10
197NEWLIB_PATCHLEVEL_VERSION=0
198
199have_cc_inhibit_loop_to_libcall=meson.get_compiler('c').has_argument('-fno-tree-loop-distribute-patterns')
200conf_data.set('_HAVE_CC_INHIBIT_LOOP_TO_LIBCALL', have_cc_inhibit_loop_to_libcall,
201	      description: 'GCC flag to prevent detecting memcpy/memset patterns')
202conf_data.set('HAVE_LONG_DOUBLE', have_long_double,
203	      description: 'Compiler has long double type')
204conf_data.set('HAVE_ALIAS_ATTRIBUTE', have_alias_attribute)
205conf_data.set('_WANT_IO_C99_FORMATS', newlib_io_c99_formats)
206conf_data.set('_WANT_REGISTER_FINI', newlib_register_fini)
207conf_data.set('_WANT_IO_LONG_LONG', newlib_io_long_long)
208conf_data.set('_WANT_IO_LONG_DOUBLE', newlib_io_long_double)
209conf_data.set('_WANT_IO_POS_ARGS', newlib_io_pos_args)
210conf_data.set('_WANT_REENT_SMALL', newlib_reent_small)
211conf_data.set('_WANT_REENT_GLOBAL_STDIO_STREAMS', newlib_global_stdio_streams)
212conf_data.set('_MB_CAPABLE', newlib_mb)
213conf_data.set('_MB_LEN_MAX', newlib_mb ? '8' : '1')
214conf_data.set('__SINGLE_THREAD__', newlib_multithread == false)
215conf_data.set('_ICONV_ENABLE_EXTERNAL_CCS', newlib_iconv_external_ccs)
216conf_data.set('_ICONV_ENABLED', newlib_iconv)
217conf_data.set('_ICONV_ENABLE_EXTERNAL_CCS', newlib_iconv_external_ccs)
218conf_data.set('_ATEXIT_DYNAMIC_ALLOC', newlib_atexit_dynamic_alloc)
219conf_data.set('_REENT_GLOBAL_ATEXIT', newlib_global_atexit)
220conf_data.set('_FVWRITE_IN_STREAMIO', newlib_fvwrite_in_streamio)
221conf_data.set('_FSEEK_OPTIMIZATION', newlib_fseek_optimization)
222conf_data.set('_WIDE_ORIENT', newlib_wide_orient)
223conf_data.set('_NANO_MALLOC', newlib_nano_malloc)
224conf_data.set('_UNBUF_STREAM_OPT', newlib_unbuf_stream_opt)
225conf_data.set('_LITE_EXIT', lite_exit)
226conf_data.set('_NANO_FORMATTED_IO', newlib_nano_formatted_io)
227conf_data.set('_RETARGETABLE_LOCKING', newlib_retargetable_locking)
228conf_data.set('_WANT_USE_LONG_TIME_T', newlib_long_time_t)
229conf_data.set('TINY_STDIO', newlib_tinystdio, description: 'Use tiny stdio from gcc avr')
230conf_data.set('_IEEE_LIBM', true, description: 'math library only offers ieee semantics')
231conf_data.set('PREFER_SIZE_OVER_SPEED', target_optspace, description: 'Optimize for space over speed')
232conf_data.set('FAST_STRCMP', fast_strcmp, description: 'Always optimize strcmp for performance')
233conf_data.set('REENTRANT_SYSCALLS_PROVIDED', newlib_reentrant_syscalls_provided, description: 'Reentrant syscalls provided for us')
234conf_data.set('MISSING_SYSCALL_NAMES', newlib_missing_syscall_names, description: 'use regular syscalls')
235conf_data.set('__HAVE_LOCALE_INFO__', newlib_locale_info, description: 'locale support')
236conf_data.set('__HAVE_LOCALE_INFO_EXTENDED__', newlib_locale_info_extended, description: 'extended locale support')
237conf_data.set('NEWLIB_GLOBAL_ERRNO', newlib_global_errno, description: 'use global errno variable')
238conf_data.set('HAVE_INITFINI_ARRAY', newlib_initfini_array, description: 'compiler supports INIT_ARRAY sections')
239conf_data.set('HAVE_INIT_FINI', newlib_initfini, description: 'Support _init() and _fini() functions')
240conf_data.set('NEWLIB_TLS', thread_local_storage, description: 'use thread local storage')
241conf_data.set('POSIX_IO', posix_io, description: 'Use open/close/read/write in tinystdio')
242conf_data.set('POSIX_CONSOLE', posix_console, description: 'Use POSIX I/O for stdin, stdout and stderr')
243
244if newlib_obsolete_math == 'auto'
245  obsolete_math_value = false
246elif newlib_obsolete_math == 'true'
247  obsolete_math_value = 1
248elif newlib_obsolete_math == 'false'
249  obsolete_math_value = 0
250endif
251
252conf_data.set('__OBSOLETE_MATH', obsolete_math_value, description: 'Use old math code (undef auto, 0 no, 1 yes)')
253
254version_array = meson.project_version().split('.')
255
256if version_array.length() > 2
257  picolibc_patch_level = version_array[2]
258else
259  picolibc_patch_level = 0
260endif
261
262conf_data.set('_PICOLIBC_VERSION', '"@0@"'.format(meson.project_version()), description: 'The Picolibc version in string format.')
263conf_data.set('_PICOLIBC__', version_array[0], description: 'The Picolibc major version number.')
264conf_data.set('_PICOLIBC_MINOR__', version_array[1], description: 'The Picolibc minor version number.')
265conf_data.set('__PICOLIBC_PATCHLEVEL__', picolibc_patch_level, description: 'The Picolibc patch level.')
266
267conf_data.set('_NEWLIB_VERSION', '"@0@"'.format(NEWLIB_VERSION), description: 'The newlib version in string format.')
268conf_data.set('_NEWLIB__', NEWLIB_MAJOR_VERSION, description: 'The newlib major version number.')
269conf_data.set('_NEWLIB_MINOR__', NEWLIB_MINOR_VERSION, description: 'The newlib minor version number.')
270conf_data.set('__NEWLIB_PATCHLEVEL__', NEWLIB_PATCHLEVEL_VERSION, description: 'The newlib patch level.')
271
272if newlib_tinystdio
273  stdio_inc_dir = 'newlib/libc/tinystdio'
274else
275  stdio_inc_dir = 'newlib/libc/stdio'
276endif
277
278inc_dirs = [stdio_inc_dir, '.', 'newlib/libc/include']
279
280inc = include_directories(inc_dirs)
281
282includedir = join_paths(get_option('prefix'), get_option('includedir'))
283
284arguments = ['-g', '-ffunction-sections', '-fdata-sections']
285
286if tls_model != ''
287  arguments += ['-ftls-model=' + tls_model]
288endif
289
290add_project_arguments(arguments, language: 'c')
291
292# semihost will adjust this if supported
293has_semihost = false
294
295if newlib_tinystdio
296  subdir('semihost')
297  subdir('dummyhost')
298endif
299
300conf_data.set('HAVE_SEMIHOST', has_semihost, description: 'Semihost APIs supported')
301
302# By default, tests don't require any special arguments
303
304test_c_args = []
305test_link_args = []
306test_link_depends = []
307
308if has_semihost
309
310  # If we're using semihosting, we assume that there's a
311  # custom linker script that includes picolibc.specs
312
313  test_link_name = 'test-' + host_machine.cpu() + '.ld'
314  test_link_dep = files(test_link_name)
315
316  # Linker args must be strings (seems like a bug), so compute the
317  # full path by hand
318
319  test_link_path = '@0@/@1@'.format(meson.current_source_dir(), test_link_name)
320
321  test_c_args += ['--specs', 'picolibc.specs', '-nostartfiles']
322
323  # Undefine _exit as that's in libsemihost and needs to replace the weak _exit
324
325  test_link_args += test_c_args + ['-L.', '-L' + meson.source_root(), '-T', test_link_path, '-Wl,--undefined=_exit', '-lgcc']
326
327  # Make sure all of the tests get re-linked if the linker scripts change
328
329  test_link_depends += test_link_dep + files('picolibc.ld')
330endif
331
332subdir('picocrt')
333subdir('newlib')
334
335if enable_tests
336  subdir('test')
337endif
338
339configure_file(output : 'picolibc.h',
340	       configuration: conf_data,
341	       install_dir: includedir)
342
343# Usage as an embedded subproject:
344# If picolibc is embedded into the source as a subproject,
345# provide a dependency to be used by the main project:
346#   dependency('libc', fallback: ['picolibc', 'libc_dep'])
347if not enable_multilib and meson.is_subproject()
348  picolibc_dep = declare_dependency(include_directories: inc, link_with: [lib_c, lib_m])
349endif
350