Files
platform-external-webrtc/android_tools/generate_bp.py
Colin Cross bd6f64487a Fix automatically extracted defaults
Fix generate_bp.py to automatically generate the defaults modules
based on flags that are set in all targets.  Use lists instead of
sets for the flags to maintain the order they were specified in
the project*.json files as much as possible.

Test: android_tools/generate_android_bp.sh
Test: mma
Change-Id: I29e13367e8e49660edeaa6462ddbab76aa177c88
2023-02-24 16:28:21 -08:00

757 lines
27 KiB
Python
Executable File

#!/usr/bin/env python
import json
import os
import sys
PRINT_ORIGINAL_FULL = False
# This flags are augmented with flags added to the json files but not present in .gn or .gni files
IGNORED_FLAGS = [
'-Wall',
'-Werror',
'-L',
'-isystem',
'-Xclang',
'-B',
'--sysroot',
'-fcrash-diagnostics-dir',
'.',
'-fdebug-compilation-dir',
'-instcombine-lower-dbg-declare=0',
'-Wno-non-c-typedef-for-linkage',
'-Werror',
'-fcomplete-member-pointers',
'-fno-stack-protector',
'--target=i686-linux-android16',
'--target=aarch64-linux-android21'
'--target=i686-linux-android16',
'--target=x86_64-linux-android21',
'--target=arm-linux-androideabi16',
'--target=aarch64-linux-gnu',
'--target=arm-linux-gnueabihf',
'-ggnu-pubnames',
'-m64',
'-m32',
'-march=x86-64',
'-march=armv8-a',
'-march=armv7-a',
'-mllvm',
'-mfloat-abi=hard',
'-target-feature',
'+crypto',
'+crc',
'-fno-unique-section-names',
'-fno-short-enums',
'-fno-delete-null-pointer-checks',
'-ffile-compilation-dir=.',
'-Wno-unneeded-internal-declaration',
'-Wno-unreachable-code-aggressive',
'-Wno-unreachable-code-break',
'-fuse-ctor-homing',
'-fno-rtti',
'-gsplit-dwarf', # TODO(b/266468464): breaks riscv
'-gdwarf-aranges', # TODO(b/269343483): breaks riscv
'-D_DEBUG',
]
DEFAULT_CFLAGS = [
'-DHAVE_ARM64_CRC32C=0',
'-DUSE_AURA=1',
'-DUSE_GLIB=1',
'-DUSE_NSS_CERTS=1',
'-DUSE_UDEV',
'-DUSE_X11=1',
'-DWEBRTC_ANDROID_PLATFORM_BUILD=1',
'-DWEBRTC_APM_DEBUG_DUMP=0',
'-D_FILE_OFFSET_BITS=64',
'-D_GNU_SOURCE',
'-D_LARGEFILE64_SOURCE',
'-D_LARGEFILE_SOURCE',
'-Wno-all',
'-Wno-error',
'-Wno-everything',
'-Wno-implicit-const-int-float-conversion',
'-Wno-missing-field-initializers',
'-Wno-unreachable-code-aggressive',
'-Wno-unreachable-code-break',
]
DEFAULT_CFLAGS_BY_ARCH = {
'x86': ['-mavx2', '-mfma', '-msse2', '-msse3'],
'x64': ['-mavx2', '-mfma', '-msse2', '-msse3'],
'arm': ['-mthumb'],
'arm64': [],
'riscv64': [],
}
FLAGS = ['cflags', 'cflags_c', 'cflags_cc', 'asmflags']
FLAG_NAME_MAP = {
'cflags': 'cflags',
'asmflags': 'asflags',
'cflags_cc': 'cppflags',
'cflags_c': 'conlyflags',
}
ARCH_NAME_MAP = {n: n for n in DEFAULT_CFLAGS_BY_ARCH.keys()}
ARCH_NAME_MAP['x64'] = 'x86_64'
ARCHS = sorted(ARCH_NAME_MAP.keys())
def FormatList(l):
return json.dumps(sorted(list(l)))
def IsInclude(name):
return name.endswith('.h') or name.endswith('.inc')
def FilterIncludes(l):
return filter(lambda x: not IsInclude(x), l)
def PrintOrigin(target):
print('/* From target:')
if PRINT_ORIGINAL_FULL:
print(json.dumps(target, sort_keys = True, indent = 4))
else:
print(target['original_name'])
print('*/')
def MakeRelatives(l):
return map(lambda x: x.split('//').pop(), l)
def FormatName(name):
return 'webrtc_' + name.split('/').pop().replace(':', '__')
def FormatNames(target):
target['original_name'] = target['name']
target['name'] = FormatName(target['name'])
target['deps'] = sorted([FormatName(d) for d in target['deps']])
target['sources'] = list(map(lambda s: (':' + FormatName(s[1:])) if s.startswith(':') else s, target['sources']))
return target
def FilterFlags(flags, to_skip = set()):
skipped_opts = set(IGNORED_FLAGS).union(to_skip)
return [x for x in flags if not any([x.startswith(y) for y in skipped_opts])]
def PrintHeader():
print('package {')
print(' default_applicable_licenses: ["external_webrtc_license"],')
print('}')
print('')
print('// Added automatically by a large-scale-change that took the approach of')
print('// \'apply every license found to every target\'. While this makes sure we respect')
print('// every license restriction, it may not be entirely correct.')
print('//')
print('// e.g. GPL in an MIT project might only apply to the contrib/ directory.')
print('//')
print('// Please consider splitting the single license below into multiple licenses,')
print('// taking care not to lose any license_kind information, and overriding the')
print('// default license using the \'licenses: [...]\' property on targets as needed.')
print('//')
print('// For unused files, consider creating a \'fileGroup\' with "//visibility:private"')
print('// to attach the license to, and including a comment whether the files may be')
print('// used in the current project.')
print('//')
print('// large-scale-change included anything that looked like it might be a license')
print('// text as a license_text. e.g. LICENSE, NOTICE, COPYING etc.')
print('//')
print('// Please consider removing redundant or irrelevant files from \'license_text:\'.')
print('// See: http://go/android-license-faq')
print('')
print('///////////////////////////////////////////////////////////////////////////////')
print('// Do not edit this file directly, it\'s automatically generated by a script. //')
print('// Modify android_tools/generate_android_bp.py and run that instead. //')
print('///////////////////////////////////////////////////////////////////////////////')
print('')
print('license {')
print(' name: "external_webrtc_license",')
print(' visibility: [":__subpackages__"],')
print(' license_kinds: [')
print(' "SPDX-license-identifier-Apache-2.0",')
print(' "SPDX-license-identifier-BSD",')
print(' "SPDX-license-identifier-MIT",')
print(' "SPDX-license-identifier-Zlib",')
print(' "legacy_notice",')
print(' "legacy_unencumbered",')
print(' ],')
print(' license_text: [')
print(' "LICENSE",')
print(' "PATENTS",')
print(' "license_template.txt",')
print(' ],')
print('}')
def GatherDefaultFlags(targets_by_arch):
# Iterate through all of the targets for each architecture collecting the flags that
# are the same for all targets in that architecture. Use a list instead of a set
# to maintain the flag ordering, which may be significant (e.g. -Wno-shadow has to
# come after -Wshadow).
arch_default_flags = {}
for arch, targets in targets_by_arch.items():
arch_default_flags[arch] = {}
for target in targets.values():
typ = target['type']
if typ != 'static_library':
continue
for flag_type in FLAGS:
if not flag_type in arch_default_flags:
arch_default_flags[arch][flag_type] = target[flag_type]
else:
target_flags = set(target[flag_type])
flags = arch_default_flags[arch][flag_type]
flags[:] = [ x for x in flags if x in target_flags ]
for flag_type, flags in arch_default_flags[arch].items():
arch_default_flags[arch][flag_type] = FilterFlags(flags)
# Add in the hardcoded extra default cflags
arch_default_flags[arch]['cflags'] += DEFAULT_CFLAGS_BY_ARCH.get(arch, [])
# Iterate through all of the architectures collecting the flags that are the same
# for all targets in all architectures.
default_flags = {}
for arch, flagset in arch_default_flags.items():
for flag_type, arch_flags in flagset.items():
if not flag_type in default_flags:
default_flags[flag_type] = arch_flags.copy()
else:
flags = default_flags[flag_type]
flags[:] = [ x for x in flags if x in arch_flags ]
# Add in the hardcoded extra default cflags
default_flags['cflags'] += DEFAULT_CFLAGS
# Remove the global default flags from the per-architecture default flags
for arch, flagset in arch_default_flags.items():
for flag_type in flagset.keys():
flags = flagset[flag_type]
flags[:] = [ x for x in flags if x not in default_flags[flag_type] ]
default_flags['arch'] = arch_default_flags
return default_flags
def GenerateDefault(targets_by_arch):
in_default = GatherDefaultFlags(targets_by_arch)
print('cc_defaults {')
print(' name: "webrtc_defaults",')
print(' local_include_dirs: [')
print(' ".",')
print(' "webrtc",')
print(' "third_party/crc32c/src/include",')
print(' ],')
for typ in sorted(in_default.keys() - {'arch'}):
flags = in_default[typ]
if len(flags) > 0:
print(' {0}: ['.format(FLAG_NAME_MAP[typ]))
for flag in flags:
print(' "{0}",'.format(flag.replace('"', '\\"')))
print(' ],')
print(' header_libs: [')
print(' "libwebrtc_absl_headers",')
print(' ],')
print(' static_libs: [')
print(' "libaom",')
print(' "libevent",')
print(' "libopus",')
print(' "libsrtp2",')
print(' "libvpx",')
print(' "libyuv",')
print(' "libpffft",')
print(' "rnnoise_rnn_vad",')
print(' ],')
print(' shared_libs: [')
print(' "libcrypto",')
print(' "libprotobuf-cpp-full",')
print(' "libprotobuf-cpp-lite",')
print(' "libssl",')
print(' ],')
print(' host_supported: true,')
print(' // vendor needed for libpreprocessing effects.')
print(' vendor: true,')
print(' target: {')
print(' darwin: {')
print(' enabled: false,')
print(' },')
print(' },')
print(' arch: {')
for a in ARCHS:
print(' {0}: {{'.format(ARCH_NAME_MAP[a]))
for typ in FLAGS:
flags = in_default['arch'].get(a, {}).get(typ, [])
if len(flags) > 0:
print(' {0}: ['.format(FLAG_NAME_MAP[typ]))
for flag in flags:
print(' "{0}",'.format(flag.replace('"', '\\"')))
print(' ],')
print(' },')
print(' },')
print(' visibility: [')
print(' "//frameworks/av/media/libeffects/preprocessing:__subpackages__",')
print(' "//device/google/cuttlefish/host/frontend/webrtc:__subpackages__",')
print(' ],')
print('}')
# The flags in the default entry can be safely removed from the targets
for arch, targets in targets_by_arch.items():
for flag_type in FLAGS:
default_flags = set(in_default[flag_type]) | set(in_default['arch'][arch][flag_type])
for target in targets.values():
target[flag_type] = FilterFlags(target.get(flag_type, []), default_flags)
if len(target[flag_type]) == 0:
target.pop(flag_type)
return in_default
def TransitiveDependencies(name, dep_type, targets):
target = targets[name]
field = 'transitive_' + dep_type
if field in target.keys():
return target[field]
target[field] = {'global': set()}
for a in ARCHS:
target[field][a] = set()
if target['type'] == dep_type:
target[field]['global'].add(name)
for d in target.get('deps', []):
if targets[d]['type'] == dep_type:
target[field]['global'].add(d)
tDeps = TransitiveDependencies(d, dep_type, targets)
target[field]['global'] |= tDeps['global']
for a in ARCHS:
target[field][a] |= tDeps[a]
if 'arch' in target:
for a, x in target['arch'].items():
for d in x.get('deps', []):
tDeps = TransitiveDependencies(d, dep_type, targets)
target[field][a] |= tDeps['global'] | tDeps[a]
target[field][a] -= target[field]['global']
return target[field]
def GenerateGroup(target):
# PrintOrigin(target)
pass
def GenerateSourceSet(target):
sources = target.get('sources', [])
# arch is not defined for filegroups so put all the sources in the top level,
# the static libraries that depend on the filegroup will add it in the
# appropriate arch.
for arch in target.get('arch', {}).values():
sources += arch.get('sources', [])
if len(sources) == 0:
return ''
PrintOrigin(target)
name = target['name']
print('filegroup {')
print(' name: "{0}",'.format(name))
print(' srcs: {0},'.format(FormatList(sources)))
print('}')
return name
def GenerateStaticLib(target, targets):
PrintOrigin(target)
name = target['name']
print('cc_library_static {')
print(' name: "{0}",'.format(name))
print(' defaults: ["webrtc_defaults"],')
sources = target.get('sources', [])
print(' srcs: {0},'.format(FormatList(sources)))
print(' host_supported: true,')
if 'asmflags' in target.keys():
asmflags = target['asmflags']
if len(asmflags) > 0:
print(' asflags: {0},'.format(FormatList(asmflags)))
if 'cflags' in target.keys():
cflags = target['cflags']
print(' cflags: {0},'.format(FormatList(cflags)))
if 'cflags_c' in target.keys():
cflags_c = target['cflags_c']
if len(cflags_c) > 0:
print(' conlyflags: {0},'.format(FormatList(cflags_c)))
if 'cflags_cc' in target.keys():
cflags_cc = target['cflags_cc']
if len(cflags_cc) > 0:
print(' cppflags: {0},'.format(FormatList(cflags_cc)))
static_libs = sorted([d for d in target.get('deps', []) if targets[d]['type'] == 'static_library'])
if len(static_libs) > 0:
print(' static_libs: {0},'.format(FormatList(static_libs)))
if 'arch' in target:
print(' arch: {')
for arch_name in ARCHS:
if arch_name not in target['arch'].keys():
continue
arch = target['arch'][arch_name]
print(' ' + ARCH_NAME_MAP[arch_name] + ': {')
if 'cflags' in arch.keys():
cflags = arch['cflags']
print(' cflags: {0},'.format(FormatList(cflags)))
if 'cflags_c' in arch.keys():
cflags_c = arch['cflags_c']
if len(cflags_c) > 0:
print(' conlyflags: {0},'.format(FormatList(cflags_c)))
if 'cflags_cc' in arch.keys():
cflags_cc = arch['cflags_cc']
if len(cflags_cc) > 0:
print(' cppflags: {0},'.format(FormatList(cflags_cc)))
if 'deps' in arch:
static_libs = [d for d in arch['deps'] if targets[d]['type'] == 'static_library']
print(' static_libs: {0},'.format(FormatList(static_libs)))
if 'sources' in arch:
sources = arch['sources']
print(' srcs: {0},'.format(FormatList(sources)))
if 'enabled' in arch:
print(' enabled: {0},'.format(arch['enabled']))
print(' },')
print(' },')
print('}')
return name
def DFS(seed, targets):
visited = set()
stack = [seed]
while len(stack) > 0:
nxt = stack.pop()
if nxt in visited:
continue
visited.add(nxt)
stack += targets[nxt]['deps']
stack += [s[1:] for s in targets[nxt]['sources'] if s.startswith(':')]
if 'arch' not in targets[nxt]:
continue
for arch in targets[nxt]['arch']:
if 'deps' in arch:
stack += arch['deps']
if 'sources' in arch:
stack += [s[1:] for s in arch['sources'] if s.startswith(':')]
return visited
def Preprocess(project):
targets = {}
for name, target in project['targets'].items():
target['name'] = name
targets[name] = target
if target['type'] == 'shared_library':
# Don't bother creating shared libraries
target['type'] = 'static_library'
if 'defines' in target:
target['cflags'] = target.get('cflags', []) + ['-D{0}'.format(d) for d in target['defines']]
target.pop('defines')
if 'sources' not in target:
continue
sources = list(MakeRelatives(FilterIncludes(target['sources'])))
if len(sources) > 0:
target['sources'] = sources
else:
target.pop('sources')
# These dependencies are provided by aosp
ignored_targets = {
'//third_party/libaom:libaom',
'//third_party/libevent:libevent',
'//third_party/opus:opus',
'//third_party/libsrtp:libsrtp',
'//third_party/libvpx:libvpx',
'//third_party/libyuv:libyuv',
'//third_party/pffft:pffft',
'//third_party/rnnoise:rnn_vad',
'//third_party/boringssl:boringssl',
'//third_party/android_ndk:cpu_features',
'//buildtools/third_party/libunwind:libunwind',
'//buildtools/third_party/libc++:libc++',
}
for name, target in targets.items():
# Skip all "action" targets
if target['type'] in {'action', 'action_foreach'}:
ignored_targets.add(name)
targets = {name: target for name, target in targets.items() if name not in ignored_targets}
for target in targets.values():
# Don't depend on ignored targets
target['deps'] = [d for d in target['deps'] if d not in ignored_targets]
# filegroups can't have dependencies, so put their dependencies in the static libraries that
# depend on them.
for target in targets.values():
if target['type'] == 'static_library':
source_sets = TransitiveDependencies(target['name'], 'source_set', targets)
source_sets_deps = {}
for a in ['global'] + ARCHS:
deps = set()
if a == 'global':
for ss in [targets[n].get('deps', []) for n in source_sets[a]]:
deps |= set(ss)
else:
for ss in [targets[n].get('arch', {}).get(a, {}).get('deps') for n in source_sets[a]]:
deps |= set(ss)
source_sets_deps[a] = deps
target['deps'] = sorted(set(target['deps']) | source_sets['global'] | source_sets_deps['global'])
for a in ARCHS:
deps = source_sets[a] | source_sets_deps[a]
if len(deps) == 0:
continue
if 'arch' not in target:
target['arch'] = {}
if a not in target['arch']:
target['arch'][a] = {}
if 'deps' not in target['arch'][a]:
target['arch'][a]['deps'] = []
deps |= set(target['arch'][a]['deps'])
target['arch'][a]['deps'] = sorted(deps)
# Ignore empty source sets
empty_sets = set()
for name, target in targets.items():
if target['type'] == 'source_set' and 'sources' not in target:
empty_sets.add(name)
for s in empty_sets:
targets.pop(s)
for target in targets.values():
target['deps'] = [d for d in target['deps'] if d not in empty_sets]
# Move source_set dependencies to the sources fields of static libs
for target in targets.values():
if 'sources' not in target:
target['sources'] = []
if target['type'] != 'static_library':
continue
source_sets = {d for d in target['deps'] if targets[d]['type'] == 'source_set'}
target['deps'] = sorted(list(set(target['deps']) - source_sets))
target['sources'] += [':' + ss for ss in source_sets]
target['sources'] = sorted(target['sources'])
if 'arch' in target:
for arch in target['arch'].values():
if 'deps' in arch:
source_sets = {d for d in arch['deps'] if targets[d]['type'] == 'source_set'}
if len(source_sets) == 0:
continue;
arch['deps'] = sorted(list(set(arch['deps']) - source_sets))
arch['sources'] = sorted(arch.get('sources', []) + [':' + ss for ss in source_sets])
# Select libwebrtc, libaudio_processing and its dependencies
selected = set()
selected |= DFS('//:webrtc', targets)
selected |= DFS('//modules/audio_processing:audio_processing', targets)
return {FormatName(n): FormatNames(targets[n]) for n in selected}
def NonNoneFrom(l):
for a in l:
if a is not None:
return a
return None
def MergeListField(target, f, target_by_arch):
set_by_arch = {}
for a, t in target_by_arch.items():
if len(t) == 0:
# We only care about enabled archs
continue
set_by_arch[a] = set(t.get(f, []))
union = set()
for _, s in set_by_arch.items():
union |= s
common = union
for a, s in set_by_arch.items():
common &= s
not_common = {a: s - common for a,s in set_by_arch.items()}
if len(common) > 0:
target[f] = list(common)
for a, s in not_common.items():
if len(s) > 0:
target['arch'][a][f] = sorted(list(s))
def Merge(target_by_arch):
# The new target shouldn't have the transitive dependencies memoization fields
# or have the union of those fields from all 4 input targets.
target = {}
for f in ['original_name', 'name', 'type']:
target[f] = NonNoneFrom([t.get(f) for _,t in target_by_arch.items()])
target['arch'] = {}
for a, t in target_by_arch.items():
target['arch'][a] = {}
if len(t) == 0:
target['arch'][a]['enabled'] = 'false'
list_fields = ['sources',
'deps',
'cflags',
'cflags_c',
'cflags_cc',
'asmflags']
for lf in list_fields:
MergeListField(target, lf, target_by_arch)
# Static libraries should be depended on at the root level and disabled for
# the corresponding architectures.
for arch in target['arch'].values():
if 'deps' not in arch:
continue
deps = arch['deps']
if 'deps' not in target:
target['deps'] = []
target['deps'] += deps
arch.pop('deps')
if 'deps' in target:
target['deps'] = sorted(target['deps'])
# Remove empty sets
for a in ARCHS:
if len(target['arch'][a]) == 0:
target['arch'].pop(a)
if len(target['arch']) == 0:
target.pop('arch')
return target
def DisabledArchs4Target(target):
ret = set()
for a in ARCHS:
if a not in target.get('arch', {}):
continue
if target['arch'][a].get('enabled', 'true') == 'false':
ret.add(a)
return ret
def HandleDisabledArchs(targets):
for n, t in targets.items():
if 'arch' not in t:
continue
disabledArchs = DisabledArchs4Target(t)
if len(disabledArchs) == 0:
continue
# Fix targets that depend on this one
for t in targets.values():
if DisabledArchs4Target(t) == disabledArchs:
# With the same disabled archs there is no need to move dependencies
continue
if 'deps' in t and n in t['deps']:
# Remove the dependency from the high level list
t['deps'] = sorted(set(t['deps']) - {n})
if 'arch' not in t:
t['arch'] = {}
for a in ARCHS:
if a in disabledArchs:
continue
if a not in t['arch']:
t['arch'][a] = {}
if 'deps' not in t['arch'][a]:
t['arch'][a]['deps'] = []
t['arch'][a]['deps'] += [n]
def MergeAll(targets_by_arch):
names = set()
for t in targets_by_arch.values():
names |= t.keys()
targets = {}
for name in names:
targets[name] = Merge({a: t.get(name, {}) for a,t in targets_by_arch.items()})
HandleDisabledArchs(targets)
return targets
def GatherAllFlags(obj):
if type(obj) != type({}):
# not a dictionary
return set()
ret = set()
for f in FLAGS:
ret |= set(obj.get(f, []))
for v in obj.values():
ret |= GatherAllFlags(v)
return ret
def FilterFlagsInUse(flags, directory):
unused = []
for f in flags:
nf = f
if nf.startswith("-D"):
nf = nf[2:]
i = nf.find('=')
if i > 0:
nf = nf[:i]
c = os.system(f"find {directory} -name '*.gn*' | xargs grep -q -s -e '{nf}'")
if c != 0:
# couldn't find the flag in *.gn or *.gni
unused.append(f)
return unused
if len(sys.argv) != 2:
print('wrong number of arguments', file = sys.stderr)
exit(1)
dir = sys.argv[1]
targets_by_arch = {}
flags = set()
for arch in ARCHS:
path = "{0}/project_{1}.json".format(dir, arch)
json_file = open(path, 'r')
targets_by_arch[arch] = Preprocess(json.load(json_file))
flags |= GatherAllFlags(targets_by_arch[arch])
unusedFlags = FilterFlagsInUse(flags, f"{dir}/..")
IGNORED_FLAGS = sorted(set(IGNORED_FLAGS) | set(unusedFlags))
PrintHeader()
GenerateDefault(targets_by_arch)
targets = MergeAll(targets_by_arch)
print('\n\n')
for name, target in sorted(targets.items()):
typ = target['type']
if typ == 'static_library':
GenerateStaticLib(target, targets)
elif typ == 'source_set':
GenerateSourceSet(target)
elif typ == 'group':
GenerateGroup(target)
else:
print('Unknown type: {0} ({1})'.format(typ, target['name']), file = sys.stderr)
exit(1)
print('\n\n')
webrtc_libs = TransitiveDependencies(FormatName('//:webrtc'), 'static_library', targets)
print('cc_library_static {')
print(' name: "libwebrtc",')
print(' defaults: ["webrtc_defaults"],')
print(' export_include_dirs: ["."],')
print(' whole_static_libs: {0},'.format(FormatList(sorted(webrtc_libs['global']) + ['libpffft', 'rnnoise_rnn_vad'])))
print(' arch: {')
for a in ARCHS:
if len(webrtc_libs[a]) > 0:
print(' {0}: {{'.format(ARCH_NAME_MAP[a]))
print(' whole_static_libs: {0},'.format(FormatList(sorted(webrtc_libs[a]))))
print(' },')
print(' },')
print('}')
print('\n\n')
audio_proc_libs = TransitiveDependencies(FormatName('//modules/audio_processing:audio_processing'), 'static_library', targets)
print('cc_library_static {')
print(' name: "webrtc_audio_processing",')
print(' defaults: ["webrtc_defaults"],')
print(' export_include_dirs: [')
print(' ".",')
print(' "modules/include",')
print(' "modules/audio_processing/include",')
print(' ],')
print(' whole_static_libs: {0},'.format(FormatList(sorted(audio_proc_libs['global']) + ['libpffft', 'rnnoise_rnn_vad'])))
print(' arch: {')
for a in ARCHS:
if len(audio_proc_libs[a]) > 0:
print(' {0}: {{'.format(ARCH_NAME_MAP[a]))
print(' whole_static_libs: {0},'.format(FormatList(sorted(audio_proc_libs[a]))))
print(' },')
print(' },')
print('}')