• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright © 2018-2022, VideoLAN and dav1d authors
2# All rights reserved.
3#
4# Redistribution and use in source and binary forms, with or without
5# modification, are permitted provided that the following conditions are met:
6#
7# 1. Redistributions of source code must retain the above copyright notice, this
8#    list of conditions and the following disclaimer.
9#
10# 2. Redistributions in binary form must reproduce the above copyright notice,
11#    this list of conditions and the following disclaimer in the documentation
12#    and/or other materials provided with the distribution.
13#
14# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
15# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
18# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
21# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
23# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24
25project('dav1d', ['c'],
26    version: '1.4.2',
27    default_options: ['c_std=c99',
28                      'warning_level=2',
29                      'buildtype=release',
30                      'b_ndebug=if-release'],
31    meson_version: '>= 0.49.0')
32
33dav1d_soname_version       = '7.0.0'
34dav1d_api_version_array    = dav1d_soname_version.split('.')
35dav1d_api_version_major    = dav1d_api_version_array[0]
36dav1d_api_version_minor    = dav1d_api_version_array[1]
37dav1d_api_version_revision = dav1d_api_version_array[2]
38
39dav1d_src_root = meson.current_source_dir()
40cc = meson.get_compiler('c')
41
42# Configuratin data for config.h
43cdata = configuration_data()
44
45# Configuration data for config.asm
46cdata_asm = configuration_data()
47
48# Include directories
49dav1d_inc_dirs = include_directories(['.', 'include/dav1d', 'include'])
50
51
52
53#
54# Option handling
55#
56
57# Bitdepth option
58dav1d_bitdepths = get_option('bitdepths')
59foreach bitdepth : ['8', '16']
60    cdata.set10('CONFIG_@0@BPC'.format(bitdepth), dav1d_bitdepths.contains(bitdepth))
61endforeach
62
63# ASM option
64is_asm_enabled = (get_option('enable_asm') == true and
65    (host_machine.cpu_family() == 'aarch64' or
66     host_machine.cpu_family().startswith('arm') or
67     host_machine.cpu() == 'ppc64le' or
68     host_machine.cpu_family().startswith('riscv') or
69     host_machine.cpu_family().startswith('loongarch') or
70     host_machine.cpu_family() == 'x86' or
71     (host_machine.cpu_family() == 'x86_64' and cc.get_define('__ILP32__').strip() == '')))
72cdata.set10('HAVE_ASM', is_asm_enabled)
73
74if is_asm_enabled and get_option('b_sanitize') == 'memory'
75    error('asm causes false positive with memory sanitizer. Use \'-Denable_asm=false\'.')
76endif
77
78cdata.set10('TRIM_DSP_FUNCTIONS', get_option('trim_dsp') == 'true' or
79    (get_option('trim_dsp') == 'if-release' and get_option('buildtype') == 'release'))
80
81# Logging option
82cdata.set10('CONFIG_LOG', get_option('logging'))
83
84cdata.set10('CONFIG_MACOS_KPERF', get_option('macos_kperf'))
85
86#
87# OS/Compiler checks and defines
88#
89
90# Arguments in test_args will be used even on feature tests
91test_args = []
92
93optional_arguments = []
94optional_link_arguments = []
95
96if host_machine.system() in ['linux', 'gnu', 'emscripten']
97    test_args += '-D_GNU_SOURCE'
98    add_project_arguments('-D_GNU_SOURCE', language: 'c')
99endif
100
101if host_machine.system() == 'windows'
102    cdata.set('_WIN32_WINNT',           '0x0601')
103    cdata.set('UNICODE',                1) # Define to 1 for Unicode (Wide Chars) APIs
104    cdata.set('_UNICODE',               1) # Define to 1 for Unicode (Wide Chars) APIs
105    cdata.set('__USE_MINGW_ANSI_STDIO', 1) # Define to force use of MinGW printf
106    cdata.set('_CRT_DECLARE_NONSTDC_NAMES', 1) # Define to get off_t from sys/types.h on MSVC
107    if cc.has_function('fseeko', prefix : '#include <stdio.h>', args : test_args)
108        cdata.set('_FILE_OFFSET_BITS', 64) # Not set by default by Meson on Windows
109    else
110        cdata.set('fseeko', '_fseeki64')
111        cdata.set('ftello', '_ftelli64')
112    endif
113
114    if host_machine.cpu_family() == 'x86_64'
115        if cc.get_argument_syntax() != 'msvc'
116            optional_link_arguments += '-Wl,--dynamicbase,--nxcompat,--tsaware,--high-entropy-va'
117        endif
118    elif host_machine.cpu_family() == 'x86' or host_machine.cpu_family() == 'arm'
119        if cc.get_argument_syntax() == 'msvc'
120            optional_link_arguments += '/largeaddressaware'
121        else
122            optional_link_arguments += '-Wl,--dynamicbase,--nxcompat,--tsaware,--large-address-aware'
123        endif
124    endif
125
126    # On Windows, we use a compatibility layer to emulate pthread
127    thread_dependency = []
128    thread_compat_dep = declare_dependency(sources : files('src/win32/thread.c'))
129
130    rt_dependency = []
131
132    rc_version_array = meson.project_version().split('.')
133    winmod = import('windows')
134    rc_data = configuration_data()
135    rc_data.set('PROJECT_VERSION_MAJOR', rc_version_array[0])
136    rc_data.set('PROJECT_VERSION_MINOR', rc_version_array[1])
137    rc_data.set('PROJECT_VERSION_REVISION', rc_version_array[2])
138    rc_data.set('API_VERSION_MAJOR', dav1d_api_version_major)
139    rc_data.set('API_VERSION_MINOR', dav1d_api_version_minor)
140    rc_data.set('API_VERSION_REVISION', dav1d_api_version_revision)
141    rc_data.set('COPYRIGHT_YEARS', '2018-2024')
142else
143    thread_dependency = dependency('threads')
144    thread_compat_dep = []
145
146    rt_dependency = []
147    if cc.has_function('clock_gettime', prefix : '#include <time.h>', args : test_args)
148        cdata.set('HAVE_CLOCK_GETTIME', 1)
149    elif host_machine.system() not in ['darwin', 'ios', 'tvos']
150        rt_dependency = cc.find_library('rt', required: false)
151        if not cc.has_function('clock_gettime', prefix : '#include <time.h>', args : test_args, dependencies : rt_dependency)
152            error('clock_gettime not found')
153        endif
154        cdata.set('HAVE_CLOCK_GETTIME', 1)
155    endif
156
157    if cc.has_function('posix_memalign', prefix : '#include <stdlib.h>', args : test_args)
158        cdata.set('HAVE_POSIX_MEMALIGN', 1)
159    endif
160endif
161
162# check for fseeko on android. It is not always available if _FILE_OFFSET_BITS is defined to 64
163have_fseeko = true
164if host_machine.system() == 'android'
165    if not cc.has_function('fseeko', prefix : '#include <stdio.h>', args : test_args)
166        if cc.has_function('fseeko', prefix : '#include <stdio.h>', args : test_args + ['-U_FILE_OFFSET_BITS'])
167            warning('Files larger than 2 gigabytes might not be supported in the dav1d CLI tool.')
168            add_project_arguments('-U_FILE_OFFSET_BITS', language: 'c')
169        elif get_option('enable_tools')
170            error('dav1d CLI tool needs fseeko()')
171        else
172            have_fseeko = false
173        endif
174    endif
175endif
176
177libdl_dependency = []
178if host_machine.system() == 'linux'
179    libdl_dependency = cc.find_library('dl', required : false)
180    if cc.has_function('dlsym', prefix : '#include <dlfcn.h>', args : test_args, dependencies : libdl_dependency)
181        cdata.set('HAVE_DLSYM', 1)
182    endif
183endif
184
185libm_dependency = cc.find_library('m', required: false)
186
187
188# Header checks
189
190stdatomic_dependencies = []
191if not cc.check_header('stdatomic.h')
192    if cc.get_id() == 'msvc'
193        # we have a custom replacement for MSVC
194        stdatomic_dependencies += declare_dependency(
195            include_directories : include_directories('include/compat/msvc'),
196        )
197    elif cc.compiles('''int main() { int v = 0; return __atomic_fetch_add(&v, 1, __ATOMIC_SEQ_CST); }''',
198                     name : 'GCC-style atomics', args : test_args)
199        stdatomic_dependencies += declare_dependency(
200            include_directories : include_directories('include/compat/gcc'),
201        )
202    else
203        error('Atomics not supported')
204    endif
205endif
206
207if host_machine.cpu_family().startswith('wasm')
208    # enable atomics + bulk-memory features
209    stdatomic_dependencies += thread_dependency.partial_dependency(compile_args: true)
210endif
211
212if cc.check_header('unistd.h')
213    cdata.set('HAVE_UNISTD_H', 1)
214endif
215
216if cc.check_header('io.h')
217    cdata.set('HAVE_IO_H', 1)
218endif
219
220if cc.check_header('pthread_np.h')
221    cdata.set('HAVE_PTHREAD_NP_H', 1)
222    test_args += '-DHAVE_PTHREAD_NP_H'
223endif
224
225
226# Function checks
227
228if not cc.has_function('getopt_long', prefix : '#include <getopt.h>', args : test_args)
229    getopt_dependency = declare_dependency(
230        sources: files('tools/compat/getopt.c'),
231        include_directories : include_directories('include/compat'),
232    )
233else
234    getopt_dependency = []
235endif
236
237if (host_machine.cpu_family() == 'aarch64' or
238    host_machine.cpu_family().startswith('arm') or
239    host_machine.cpu_family().startswith('loongarch') or
240    host_machine.cpu() == 'ppc64le' or
241    host_machine.cpu_family().startswith('riscv'))
242    if cc.has_function('getauxval', prefix : '#include <sys/auxv.h>', args : test_args)
243        cdata.set('HAVE_GETAUXVAL', 1)
244    endif
245    if cc.has_function('elf_aux_info', prefix : '#include <sys/auxv.h>', args : test_args)
246        cdata.set('HAVE_ELF_AUX_INFO', 1)
247    endif
248endif
249
250pthread_np_prefix = '''
251#include <pthread.h>
252#ifdef HAVE_PTHREAD_NP_H
253#include <pthread_np.h>
254#endif
255'''
256if cc.has_function('pthread_getaffinity_np', prefix : pthread_np_prefix, args : test_args, dependencies : thread_dependency)
257    cdata.set('HAVE_PTHREAD_GETAFFINITY_NP', 1)
258endif
259if cc.has_function('pthread_setaffinity_np', prefix : pthread_np_prefix, args : test_args, dependencies : thread_dependency)
260    cdata.set('HAVE_PTHREAD_SETAFFINITY_NP', 1)
261endif
262
263if cc.compiles('int x = _Generic(0, default: 0);', name: '_Generic', args: test_args)
264    cdata.set('HAVE_C11_GENERIC', 1)
265endif
266
267# Compiler flag tests
268
269if cc.has_argument('-fvisibility=hidden')
270    add_project_arguments('-fvisibility=hidden', language: 'c')
271else
272    warning('Compiler does not support -fvisibility=hidden, all symbols will be public!')
273endif
274
275# Compiler flags that should be set
276# But when the compiler does not supports them
277# it is not an error and silently tolerated
278if cc.get_argument_syntax() != 'msvc'
279    optional_arguments += [
280      '-Wundef',
281      '-Werror=vla',
282      '-Wno-maybe-uninitialized',
283      '-Wno-missing-field-initializers',
284      '-Wno-unused-parameter',
285      '-Wstrict-prototypes',
286      '-Werror=missing-prototypes',
287      '-Wshorten-64-to-32',
288    ]
289    if host_machine.cpu_family() == 'x86'
290        optional_arguments += [
291          '-msse2',
292          '-mfpmath=sse',
293        ]
294    endif
295else
296    optional_arguments += [
297      '-wd4028', # parameter different from declaration
298      '-wd4090', # broken with arrays of pointers
299      '-wd4996'  # use of POSIX functions
300    ]
301endif
302
303if (get_option('buildtype') != 'debug' and get_option('buildtype') != 'plain')
304    optional_arguments += '-fomit-frame-pointer'
305    optional_arguments += '-ffast-math'
306endif
307
308if (host_machine.system() in ['darwin', 'ios', 'tvos'] and cc.get_id() == 'clang' and
309    cc.version().startswith('11'))
310    # Workaround for Xcode 11 -fstack-check bug, see #301
311    optional_arguments += '-fno-stack-check'
312endif
313
314if (host_machine.cpu_family() == 'aarch64' or host_machine.cpu_family().startswith('arm'))
315    optional_arguments += '-fno-align-functions'
316endif
317
318add_project_arguments(cc.get_supported_arguments(optional_arguments), language : 'c')
319add_project_link_arguments(cc.get_supported_link_arguments(optional_link_arguments), language : 'c')
320
321# libFuzzer related things
322fuzzing_engine = get_option('fuzzing_engine')
323if fuzzing_engine == 'libfuzzer'
324    if not cc.has_argument('-fsanitize=fuzzer')
325        error('fuzzing_engine libfuzzer requires "-fsanitize=fuzzer"')
326    endif
327    fuzzer_args = ['-fsanitize=fuzzer-no-link', '-fsanitize=fuzzer']
328    add_project_arguments(cc.first_supported_argument(fuzzer_args), language : 'c')
329endif
330
331cdata.set10('ENDIANNESS_BIG', host_machine.endian() == 'big')
332
333if host_machine.cpu_family().startswith('x86')
334    if get_option('stack_alignment') > 0
335        stack_alignment = get_option('stack_alignment')
336    elif host_machine.cpu_family() == 'x86_64' or host_machine.system() in ['linux', 'darwin', 'ios', 'tvos']
337        stack_alignment = 16
338    else
339        stack_alignment = 4
340    endif
341    cdata_asm.set('STACK_ALIGNMENT', stack_alignment)
342endif
343
344cdata.set10('ARCH_AARCH64', host_machine.cpu_family() == 'aarch64' or host_machine.cpu() == 'arm64')
345cdata.set10('ARCH_ARM',     host_machine.cpu_family().startswith('arm') and host_machine.cpu() != 'arm64')
346if (is_asm_enabled and
347    (host_machine.cpu_family() == 'aarch64' or
348     host_machine.cpu_family().startswith('arm')))
349
350   as_func_code = '''__asm__ (
351".func meson_test"
352".endfunc"
353);
354'''
355    have_as_func = cc.compiles(as_func_code)
356    cdata.set10('HAVE_AS_FUNC', have_as_func)
357
358    # fedora package build infrastructure uses a gcc specs file to enable
359    # '-fPIE' by default. The chosen way only adds '-fPIE' to the C compiler
360    # with integrated preprocessor. It is not added to the standalone
361    # preprocessor or the preprocessing stage of '.S' files. So we have to
362    # compile code to check if we have to define PIC for the arm asm to
363    # avoid absolute relocations when building for example checkasm.
364    check_pic_code = '''
365#if defined(PIC)
366#error "PIC already defined"
367#elif !(defined(__PIC__) || defined(__pic__))
368#error "no pic"
369#endif
370'''
371    if cc.compiles(check_pic_code)
372        cdata.set('PIC', '3')
373    endif
374
375    if host_machine.cpu_family() == 'aarch64'
376        have_as_arch = cc.compiles('''__asm__ (".arch armv8-a");''')
377        cdata.set10('HAVE_AS_ARCH_DIRECTIVE', have_as_arch)
378        as_arch_str = ''
379        if have_as_arch
380            as_arch_level = 'armv8-a'
381            # Check what .arch levels are supported. In principle, we only
382            # want to detect up to armv8.2-a here (binutils requires that
383            # in order to enable i8mm). However, older Clang versions
384            # (before Clang 17, and Xcode versions up to and including 15.0)
385            # didn't support controlling dotprod/i8mm extensions via
386            # .arch_extension, therefore try to enable a high enough .arch
387            # level as well, to implicitly make them available via that.
388            foreach arch : ['armv8.2-a', 'armv8.4-a', 'armv8.6-a']
389                if cc.compiles('__asm__ (".arch ' + arch + '\\n");')
390                    as_arch_level = arch
391                endif
392            endforeach
393            # Clang versions before 17 also had a bug
394            # (https://github.com/llvm/llvm-project/issues/32220)
395            # causing a plain ".arch <level>" to not have any effect unless it
396            # had an extra "+<feature>" included - but it was activated on the
397            # next ".arch_extension" directive instead. Check if we can include
398            # "+crc" as dummy feature to make the .arch directive behave as
399            # expected and take effect right away.
400            if cc.compiles('__asm__ (".arch ' + as_arch_level + '+crc\\n");')
401                as_arch_level = as_arch_level + '+crc'
402            endif
403            cdata.set('AS_ARCH_LEVEL', as_arch_level)
404            as_arch_str = '".arch ' + as_arch_level + '\\n"'
405        endif
406        extensions = {
407            'dotprod': 'udot v0.4s, v0.16b, v0.16b',
408            'i8mm':    'usdot v0.4s, v0.16b, v0.16b',
409            'sve':     'whilelt p0.s, x0, x1',
410            'sve2':    'sqrdmulh z0.s, z0.s, z0.s',
411        }
412        foreach name, instr : extensions
413            # Test for support for the various extensions. First test if
414            # the assembler supports the .arch_extension directive for
415            # enabling/disabling the extension, then separately check whether
416            # the instructions themselves are supported. Even if .arch_extension
417            # isn't supported, we may be able to assemble the instructions
418            # if the .arch level includes support for them.
419            code = '__asm__ (' + as_arch_str
420            code += '".arch_extension ' + name + '\\n"'
421            code += ');'
422            supports_archext = cc.compiles(code)
423            cdata.set10('HAVE_AS_ARCHEXT_' + name.to_upper() + '_DIRECTIVE', supports_archext)
424            code = '__asm__ (' + as_arch_str
425            if supports_archext
426                code += '".arch_extension ' + name + '\\n"'
427            endif
428            code += '"' + instr + '\\n"'
429            code += ');'
430            supports_instr = cc.compiles(code, name: name.to_upper())
431            cdata.set10('HAVE_' + name.to_upper(), supports_instr)
432        endforeach
433    endif
434endif
435
436cdata.set10('ARCH_X86', host_machine.cpu_family().startswith('x86'))
437cdata.set10('ARCH_X86_64', host_machine.cpu_family() == 'x86_64')
438cdata.set10('ARCH_X86_32', host_machine.cpu_family() == 'x86')
439
440if host_machine.cpu_family().startswith('x86')
441    cdata_asm.set('private_prefix', 'dav1d')
442    cdata_asm.set10('ARCH_X86_64', host_machine.cpu_family() == 'x86_64')
443    cdata_asm.set10('ARCH_X86_32', host_machine.cpu_family() == 'x86')
444    cdata_asm.set10('PIC', true)
445
446    # Convert SSE asm into (128-bit) AVX when compiler flags are set to use AVX instructions
447    cdata_asm.set10('FORCE_VEX_ENCODING', cc.get_define('__AVX__').strip() != '')
448endif
449
450cdata.set10('ARCH_PPC64LE', host_machine.cpu() == 'ppc64le')
451
452cdata.set10('ARCH_RISCV', host_machine.cpu_family().startswith('riscv'))
453cdata.set10('ARCH_RV32', host_machine.cpu_family() == 'riscv32')
454cdata.set10('ARCH_RV64', host_machine.cpu_family() == 'riscv64')
455
456cdata.set10('ARCH_LOONGARCH', host_machine.cpu_family().startswith('loongarch'))
457cdata.set10('ARCH_LOONGARCH32', host_machine.cpu_family() == 'loongarch32')
458cdata.set10('ARCH_LOONGARCH64', host_machine.cpu_family() == 'loongarch64')
459
460# meson's cc.symbols_have_underscore_prefix() is unfortunately unrelieably
461# when additional flags like '-fprofile-instr-generate' are passed via CFLAGS
462# see following meson issue https://github.com/mesonbuild/meson/issues/5482
463if (host_machine.system() in ['darwin', 'ios', 'tvos'] or
464   (host_machine.system() == 'windows' and host_machine.cpu_family() == 'x86'))
465    cdata.set10('PREFIX', true)
466    cdata_asm.set10('PREFIX', true)
467endif
468
469#
470# ASM specific stuff
471#
472if is_asm_enabled and host_machine.cpu_family().startswith('x86')
473
474    # NASM compiler support
475
476    nasm = find_program('nasm')
477
478    # check NASM version
479    if nasm.found()
480        nasm_r = run_command(nasm, '-v', check: true)
481
482        out = nasm_r.stdout().strip().split()
483        if out[1].to_lower() == 'version'
484            if out[2].version_compare('<2.14')
485                error('nasm 2.14 or later is required, found nasm @0@'.format(out[2]))
486            endif
487        else
488            error('unexpected nasm version string: @0@'.format(nasm_r.stdout()))
489        endif
490    endif
491
492    # Generate config.asm
493    config_asm_target = configure_file(output: 'config.asm', output_format: 'nasm', configuration: cdata_asm)
494
495    if host_machine.system() == 'windows'
496        nasm_format = 'win'
497    elif host_machine.system() in ['darwin', 'ios', 'tvos']
498        nasm_format = 'macho'
499    else
500        nasm_format = 'elf'
501    endif
502    if host_machine.cpu_family() == 'x86_64'
503        nasm_format += '64'
504    else
505        nasm_format += '32'
506    endif
507
508    nasm_gen = generator(nasm,
509        output: '@BASENAME@.obj',
510        depfile: '@BASENAME@.obj.ndep',
511        arguments: [
512            '-f', nasm_format,
513            '-I', '@0@/src/'.format(dav1d_src_root),
514            '-I', '@0@/'.format(meson.current_build_dir()),
515            '-MQ', '@OUTPUT@', '-MF', '@DEPFILE@',
516            '@EXTRA_ARGS@',
517            '@INPUT@',
518            '-o', '@OUTPUT@'
519        ])
520endif
521
522use_gaspp = false
523if (is_asm_enabled and
524    (host_machine.cpu_family() == 'aarch64' or
525     host_machine.cpu_family().startswith('arm')) and
526    cc.get_argument_syntax() == 'msvc' and
527    (cc.get_id() != 'clang-cl' or meson.version().version_compare('<0.58.0')))
528    gaspp = find_program('gas-preprocessor.pl')
529    use_gaspp = true
530    gaspp_gen = generator(gaspp,
531        output: '@BASENAME@.obj',
532        arguments: [
533            '-as-type', 'armasm',
534            '-arch', host_machine.cpu_family(),
535            '--',
536            host_machine.cpu_family() == 'aarch64' ? 'armasm64' : 'armasm',
537            '-nologo',
538            '-I@0@'.format(dav1d_src_root),
539            '-I@0@/'.format(meson.current_build_dir()),
540            '@INPUT@',
541            '-c',
542            '-o', '@OUTPUT@'
543        ])
544endif
545
546if is_asm_enabled and host_machine.cpu_family().startswith('riscv')
547    as_option_code = '''__asm__ (
548".option arch, +v\n"
549"vsetivli zero, 0, e8, m1, ta, ma"
550);
551'''
552    if not cc.compiles(as_option_code, name : 'RISC-V Vector')
553        error('Compiler doesn\'t support \'.option arch\' asm directive. Update to binutils>=2.38 or clang>=17 or use \'-Denable_asm=false\'.')
554    endif
555endif
556
557# Generate config.h
558config_h_target = configure_file(output: 'config.h', configuration: cdata)
559
560
561
562#
563# Include subdir meson.build files
564# The order is important!
565
566subdir('include')
567
568subdir('doc')
569
570subdir('src')
571
572subdir('tools')
573
574subdir('examples')
575
576subdir('tests')
577