1#!/usr/bin/env python3 2 3import json 4import os 5import sys 6 7PRINT_ORIGINAL_FULL = False 8 9# This flags are augmented with flags added to the json files but not present in .gn or .gni files 10IGNORED_FLAGS = [ 11 '-D_DEBUG', 12 '-Werror', 13 '-Xclang', 14 '-target-feature', 15 '+crc', 16 '+crypto', 17] 18IGNORED_DEFINES = [ 19 'HAVE_ARM64_CRC32C=1' 20] 21DEFAULT_CFLAGS = [ 22 '-DHAVE_ARM64_CRC32C=0', 23 '-DUSE_AURA=1', 24 '-DUSE_GLIB=1', 25 '-DUSE_NSS_CERTS=1', 26 '-DUSE_UDEV', 27 '-DUSE_X11=1', 28 '-DWEBRTC_ANDROID_PLATFORM_BUILD=1', 29 '-DWEBRTC_APM_DEBUG_DUMP=0', 30 '-D_FILE_OFFSET_BITS=64', 31 '-D_GNU_SOURCE', 32 '-D_LARGEFILE64_SOURCE', 33 '-D_LARGEFILE_SOURCE', 34 '-Wno-global-constructors', 35 '-Wno-implicit-const-int-float-conversion', 36 '-Wno-missing-field-initializers', 37 '-Wno-unreachable-code-aggressive', 38 '-Wno-unreachable-code-break', 39] 40 41DEFAULT_CFLAGS_BY_ARCH = { 42 'x86': ['-mavx2', '-mfma', '-msse2', '-msse3'], 43 'x64': ['-mavx2', '-mfma', '-msse2', '-msse3'], 44 'arm': ['-mthumb'], 45 'arm64': [], 46 'riscv64': [], 47 } 48 49FLAGS = ['cflags', 'cflags_c', 'cflags_cc', 'asmflags'] 50FLAG_NAME_MAP = { 51 'cflags': 'cflags', 52 'asmflags': 'asflags', 53 'cflags_cc': 'cppflags', 54 'cflags_c': 'conlyflags', 55} 56 57ARCH_NAME_MAP = {n: n for n in DEFAULT_CFLAGS_BY_ARCH.keys()} 58ARCH_NAME_MAP['x64'] = 'x86_64' 59 60ARCHS = sorted(ARCH_NAME_MAP.keys()) 61 62def FormatList(l): 63 return json.dumps(sorted(list(l))) 64 65def IsInclude(name): 66 return name.endswith('.h') or name.endswith('.inc') 67 68def FilterIncludes(l): 69 return filter(lambda x: not IsInclude(x), l) 70 71def PrintOrigin(target): 72 print('/* From target:') 73 if PRINT_ORIGINAL_FULL: 74 print(json.dumps(target, sort_keys = True, indent = 4)) 75 else: 76 print(target['original_name']) 77 print('*/') 78 79def MakeRelatives(l): 80 return map(lambda x: x.split('//').pop(), l) 81 82def FormatName(name): 83 return 'webrtc_' + name.split('/').pop().replace(':', '__') 84 85def FormatNames(target): 86 target['original_name'] = target['name'] 87 target['name'] = FormatName(target['name']) 88 target['deps'] = sorted([FormatName(d) for d in target['deps']]) 89 return target 90 91def FilterFlags(flags, to_skip = set()): 92 skipped_opts = set(IGNORED_FLAGS).union(to_skip) 93 return [x for x in flags if not any([x.startswith(y) for y in skipped_opts])] 94 95def PrintHeader(): 96 print('package {') 97 print(' default_applicable_licenses: ["external_webrtc_license"],') 98 print('}') 99 print('') 100 print('// Added automatically by a large-scale-change that took the approach of') 101 print('// \'apply every license found to every target\'. While this makes sure we respect') 102 print('// every license restriction, it may not be entirely correct.') 103 print('//') 104 print('// e.g. GPL in an MIT project might only apply to the contrib/ directory.') 105 print('//') 106 print('// Please consider splitting the single license below into multiple licenses,') 107 print('// taking care not to lose any license_kind information, and overriding the') 108 print('// default license using the \'licenses: [...]\' property on targets as needed.') 109 print('//') 110 print('// For unused files, consider creating a \'fileGroup\' with "//visibility:private"') 111 print('// to attach the license to, and including a comment whether the files may be') 112 print('// used in the current project.') 113 print('//') 114 print('// large-scale-change included anything that looked like it might be a license') 115 print('// text as a license_text. e.g. LICENSE, NOTICE, COPYING etc.') 116 print('//') 117 print('// Please consider removing redundant or irrelevant files from \'license_text:\'.') 118 print('// See: http://go/android-license-faq') 119 print('') 120 print('///////////////////////////////////////////////////////////////////////////////') 121 print('// Do not edit this file directly, it\'s automatically generated by a script. //') 122 print('// Modify android_tools/generate_android_bp.py and run that instead. //') 123 print('///////////////////////////////////////////////////////////////////////////////') 124 print('') 125 print('license {') 126 print(' name: "external_webrtc_license",') 127 print(' visibility: [":__subpackages__"],') 128 print(' license_kinds: [') 129 print(' "SPDX-license-identifier-Apache-2.0",') 130 print(' "SPDX-license-identifier-BSD",') 131 print(' "SPDX-license-identifier-MIT",') 132 print(' "SPDX-license-identifier-Zlib",') 133 print(' "legacy_notice",') 134 print(' "legacy_unencumbered",') 135 print(' ],') 136 print(' license_text: [') 137 print(' "LICENSE",') 138 print(' "PATENTS",') 139 print(' "license_template.txt",') 140 print(' ],') 141 print('}') 142 143 144 145def GatherDefaultFlags(targets_by_arch): 146 # Iterate through all of the targets for each architecture collecting the flags that 147 # are the same for all targets in that architecture. Use a list instead of a set 148 # to maintain the flag ordering, which may be significant (e.g. -Wno-shadow has to 149 # come after -Wshadow). 150 arch_default_flags = {} 151 for arch, targets in targets_by_arch.items(): 152 arch_default_flags[arch] = {} 153 for target in targets.values(): 154 typ = target['type'] 155 if typ != 'static_library': 156 continue 157 for flag_type in FLAGS: 158 if not flag_type in arch_default_flags: 159 arch_default_flags[arch][flag_type] = target[flag_type] 160 else: 161 target_flags = set(target[flag_type]) 162 flags = arch_default_flags[arch][flag_type] 163 flags[:] = [ x for x in flags if x in target_flags ] 164 for flag_type, flags in arch_default_flags[arch].items(): 165 arch_default_flags[arch][flag_type] = FilterFlags(flags) 166 # Add in the hardcoded extra default cflags 167 arch_default_flags[arch]['cflags'] += DEFAULT_CFLAGS_BY_ARCH.get(arch, []) 168 169 # Iterate through all of the architectures collecting the flags that are the same 170 # for all targets in all architectures. 171 default_flags = {} 172 for arch, flagset in arch_default_flags.items(): 173 for flag_type, arch_flags in flagset.items(): 174 if not flag_type in default_flags: 175 default_flags[flag_type] = arch_flags.copy() 176 else: 177 flags = default_flags[flag_type] 178 flags[:] = [ x for x in flags if x in arch_flags ] 179 # Add in the hardcoded extra default cflags 180 default_flags['cflags'] += DEFAULT_CFLAGS 181 182 # Remove the global default flags from the per-architecture default flags 183 for arch, flagset in arch_default_flags.items(): 184 for flag_type in flagset.keys(): 185 flags = flagset[flag_type] 186 flags[:] = [ x for x in flags if x not in default_flags[flag_type] ] 187 188 default_flags['arch'] = arch_default_flags 189 return default_flags 190 191def GenerateDefault(targets_by_arch): 192 in_default = GatherDefaultFlags(targets_by_arch) 193 print('cc_defaults {') 194 print(' name: "webrtc_defaults",') 195 print(' local_include_dirs: [') 196 print(' ".",') 197 print(' "webrtc",') 198 print(' "third_party/crc32c/src/include",') 199 print(' ],') 200 for typ in sorted(in_default.keys() - {'arch'}): 201 flags = in_default[typ] 202 if len(flags) > 0: 203 print(' {0}: ['.format(FLAG_NAME_MAP[typ])) 204 for flag in flags: 205 print(' "{0}",'.format(flag.replace('"', '\\"'))) 206 print(' ],') 207 print(' static_libs: [') 208 print(' "libabsl",') 209 print(' "libaom",') 210 print(' "libevent",') 211 print(' "libopus",') 212 print(' "libsrtp2",') 213 print(' "libvpx",') 214 print(' "libyuv",') 215 print(' "libpffft",') 216 print(' "rnnoise_rnn_vad",') 217 print(' ],') 218 print(' shared_libs: [') 219 print(' "libcrypto",') 220 print(' "libprotobuf-cpp-full",') 221 print(' "libprotobuf-cpp-lite",') 222 print(' "libssl",') 223 print(' ],') 224 print(' host_supported: true,') 225 print(' // vendor needed for libpreprocessing effects.') 226 print(' vendor: true,') 227 print(' target: {') 228 print(' darwin: {') 229 print(' enabled: false,') 230 print(' },') 231 print(' },') 232 print(' arch: {') 233 for a in ARCHS: 234 print(' {0}: {{'.format(ARCH_NAME_MAP[a])) 235 for typ in FLAGS: 236 flags = in_default['arch'].get(a, {}).get(typ, []) 237 if len(flags) > 0: 238 print(' {0}: ['.format(FLAG_NAME_MAP[typ])) 239 for flag in flags: 240 print(' "{0}",'.format(flag.replace('"', '\\"'))) 241 print(' ],') 242 print(' },') 243 print(' },') 244 print(' visibility: [') 245 print(' "//frameworks/av/media/libeffects/preprocessing:__subpackages__",') 246 print(' "//device/google/cuttlefish/host/frontend/webrtc:__subpackages__",') 247 print(' ],') 248 print('}') 249 250 # The flags in the default entry can be safely removed from the targets 251 for arch, targets in targets_by_arch.items(): 252 for flag_type in FLAGS: 253 default_flags = set(in_default[flag_type]) | set(in_default['arch'][arch][flag_type]) 254 for target in targets.values(): 255 target[flag_type] = FilterFlags(target.get(flag_type, []), default_flags) 256 if len(target[flag_type]) == 0: 257 target.pop(flag_type) 258 259 return in_default 260 261 262def TransitiveDependencies(name, dep_type, targets): 263 target = targets[name] 264 field = 'transitive_' + dep_type 265 if field in target.keys(): 266 return target[field] 267 target[field] = {'global': set()} 268 for a in ARCHS: 269 target[field][a] = set() 270 if target['type'] == dep_type: 271 target[field]['global'].add(name) 272 for d in target.get('deps', []): 273 if targets[d]['type'] == dep_type: 274 target[field]['global'].add(d) 275 tDeps = TransitiveDependencies(d, dep_type, targets) 276 target[field]['global'] |= tDeps['global'] 277 for a in ARCHS: 278 target[field][a] |= tDeps[a] 279 if 'arch' in target: 280 for a, x in target['arch'].items(): 281 for d in x.get('deps', []): 282 tDeps = TransitiveDependencies(d, dep_type, targets) 283 target[field][a] |= tDeps['global'] | tDeps[a] 284 target[field][a] -= target[field]['global'] 285 286 return target[field] 287 288def GenerateGroup(target): 289 # PrintOrigin(target) 290 pass 291 292def GenerateStaticLib(target, targets): 293 PrintOrigin(target) 294 name = target['name'] 295 print('cc_library_static {') 296 print(' name: "{0}",'.format(name)) 297 print(' defaults: ["webrtc_defaults"],') 298 sources = target.get('sources', []) 299 print(' srcs: {0},'.format(FormatList(sources))) 300 print(' host_supported: true,') 301 if 'asmflags' in target.keys(): 302 asmflags = target['asmflags'] 303 if len(asmflags) > 0: 304 print(' asflags: {0},'.format(FormatList(asmflags))) 305 if 'cflags' in target.keys(): 306 cflags = target['cflags'] 307 print(' cflags: {0},'.format(FormatList(cflags))) 308 if 'cflags_c' in target.keys(): 309 cflags_c = target['cflags_c'] 310 if len(cflags_c) > 0: 311 print(' conlyflags: {0},'.format(FormatList(cflags_c))) 312 if 'cflags_cc' in target.keys(): 313 cflags_cc = target['cflags_cc'] 314 if len(cflags_cc) > 0: 315 print(' cppflags: {0},'.format(FormatList(cflags_cc))) 316 if 'arch' in target: 317 print(' arch: {') 318 for arch_name in ARCHS: 319 if arch_name not in target['arch'].keys(): 320 continue 321 arch = target['arch'][arch_name] 322 print(' ' + ARCH_NAME_MAP[arch_name] + ': {') 323 if 'cflags' in arch.keys(): 324 cflags = arch['cflags'] 325 print(' cflags: {0},'.format(FormatList(cflags))) 326 if 'cflags_c' in arch.keys(): 327 cflags_c = arch['cflags_c'] 328 if len(cflags_c) > 0: 329 print(' conlyflags: {0},'.format(FormatList(cflags_c))) 330 if 'cflags_cc' in arch.keys(): 331 cflags_cc = arch['cflags_cc'] 332 if len(cflags_cc) > 0: 333 print(' cppflags: {0},'.format(FormatList(cflags_cc))) 334 if 'sources' in arch: 335 sources = arch['sources'] 336 print(' srcs: {0},'.format(FormatList(sources))) 337 if 'enabled' in arch: 338 print(' enabled: {0},'.format(arch['enabled'])) 339 print(' },') 340 print(' },') 341 print('}') 342 return name 343 344def DFS(seed, targets): 345 visited = set() 346 stack = [seed] 347 while len(stack) > 0: 348 nxt = stack.pop() 349 if nxt in visited: 350 continue 351 visited.add(nxt) 352 stack += targets[nxt]['deps'] 353 if 'arch' not in targets[nxt]: 354 continue 355 for arch in targets[nxt]['arch']: 356 if 'deps' in arch: 357 stack += arch['deps'] 358 return visited 359 360def Preprocess(project): 361 targets = {} 362 for name, target in project['targets'].items(): 363 target['name'] = name 364 targets[name] = target 365 if target['type'] == 'shared_library': 366 # Don't bother creating shared libraries 367 target['type'] = 'static_library' 368 if target['type'] == 'source_set': 369 # Convert source_sets to static libraires to avoid recompiling sources multiple times. 370 target['type'] = 'static_library' 371 if 'defines' in target: 372 target['cflags'] = target.get('cflags', []) + ['-D{0}'.format(d) for d in target['defines'] if d not in IGNORED_DEFINES] 373 target.pop('defines') 374 if 'sources' not in target: 375 continue 376 sources = list(MakeRelatives(FilterIncludes(target['sources']))) 377 if len(sources) > 0: 378 target['sources'] = sources 379 else: 380 target.pop('sources') 381 382 # These dependencies are provided by aosp 383 ignored_targets = { 384 '//third_party/libaom:libaom', 385 '//third_party/libevent:libevent', 386 '//third_party/opus:opus', 387 '//third_party/libsrtp:libsrtp', 388 '//third_party/libvpx:libvpx', 389 '//third_party/libyuv:libyuv', 390 '//third_party/pffft:pffft', 391 '//third_party/rnnoise:rnn_vad', 392 '//third_party/boringssl:boringssl', 393 '//third_party/android_ndk:cpu_features', 394 '//buildtools/third_party/libunwind:libunwind', 395 '//buildtools/third_party/libc++:libc++', 396 } 397 for name, target in targets.items(): 398 # Skip all "action" targets 399 if target['type'] in {'action', 'action_foreach'}: 400 ignored_targets.add(name) 401 402 def is_ignored(target): 403 if target.startswith('//third_party/abseil-cpp'): 404 return True 405 return target in ignored_targets 406 407 targets = {name: target for name, target in targets.items() if not is_ignored(name)} 408 409 for target in targets.values(): 410 # Don't depend on ignored targets 411 target['deps'] = [d for d in target['deps'] if not is_ignored(d) ] 412 413 # Ignore empty static libraries 414 empty_libs = set() 415 for name, target in targets.items(): 416 if target['type'] == 'static_library' and 'sources' not in target and name != '//:webrtc': 417 empty_libs.add(name) 418 for empty_lib in empty_libs: 419 empty_lib_deps = targets[empty_lib].get('deps', []) 420 for target in targets.values(): 421 target['deps'] = FlattenEmptyLibs(target['deps'], empty_lib, empty_lib_deps) 422 for s in empty_libs: 423 targets.pop(s) 424 425 # Select libwebrtc, libaudio_processing and its dependencies 426 selected = set() 427 selected |= DFS('//:webrtc', targets) 428 selected |= DFS('//modules/audio_processing:audio_processing', targets) 429 430 return {FormatName(n): FormatNames(targets[n]) for n in selected} 431 432def _FlattenEmptyLibs(deps, empty_lib, empty_lib_deps): 433 for x in deps: 434 if x == empty_lib: 435 yield from empty_lib_deps 436 else: 437 yield x 438 439def FlattenEmptyLibs(deps, empty_lib, empty_lib_deps): 440 return list(_FlattenEmptyLibs(deps, empty_lib, empty_lib_deps)) 441 442def NonNoneFrom(l): 443 for a in l: 444 if a is not None: 445 return a 446 return None 447 448def MergeListField(target, f, target_by_arch): 449 set_by_arch = {} 450 for a, t in target_by_arch.items(): 451 if len(t) == 0: 452 # We only care about enabled archs 453 continue 454 set_by_arch[a] = set(t.get(f, [])) 455 456 union = set() 457 for _, s in set_by_arch.items(): 458 union |= s 459 460 common = union 461 for a, s in set_by_arch.items(): 462 common &= s 463 464 not_common = {a: s - common for a,s in set_by_arch.items()} 465 466 if len(common) > 0: 467 target[f] = list(common) 468 for a, s in not_common.items(): 469 if len(s) > 0: 470 target['arch'][a][f] = sorted(list(s)) 471 472def Merge(target_by_arch): 473 # The new target shouldn't have the transitive dependencies memoization fields 474 # or have the union of those fields from all 4 input targets. 475 target = {} 476 for f in ['original_name', 'name', 'type']: 477 target[f] = NonNoneFrom([t.get(f) for _,t in target_by_arch.items()]) 478 479 target['arch'] = {} 480 for a, t in target_by_arch.items(): 481 target['arch'][a] = {} 482 if len(t) == 0: 483 target['arch'][a]['enabled'] = 'false' 484 485 list_fields = ['sources', 486 'deps', 487 'cflags', 488 'cflags_c', 489 'cflags_cc', 490 'asmflags'] 491 for lf in list_fields: 492 MergeListField(target, lf, target_by_arch) 493 494 # Static libraries should be depended on at the root level and disabled for 495 # the corresponding architectures. 496 for arch in target['arch'].values(): 497 if 'deps' not in arch: 498 continue 499 deps = arch['deps'] 500 if 'deps' not in target: 501 target['deps'] = [] 502 target['deps'] += deps 503 arch.pop('deps') 504 if 'deps' in target: 505 target['deps'] = sorted(target['deps']) 506 507 # Remove empty sets 508 for a in ARCHS: 509 if len(target['arch'][a]) == 0: 510 target['arch'].pop(a) 511 if len(target['arch']) == 0: 512 target.pop('arch') 513 514 return target 515 516def DisabledArchs4Target(target): 517 ret = set() 518 for a in ARCHS: 519 if a not in target.get('arch', {}): 520 continue 521 if target['arch'][a].get('enabled', 'true') == 'false': 522 ret.add(a) 523 return ret 524 525 526def HandleDisabledArchs(targets): 527 for n, t in targets.items(): 528 if 'arch' not in t: 529 continue 530 disabledArchs = DisabledArchs4Target(t) 531 if len(disabledArchs) == 0: 532 continue 533 # Fix targets that depend on this one 534 for t in targets.values(): 535 if DisabledArchs4Target(t) == disabledArchs: 536 # With the same disabled archs there is no need to move dependencies 537 continue 538 if 'deps' in t and n in t['deps']: 539 # Remove the dependency from the high level list 540 t['deps'] = sorted(set(t['deps']) - {n}) 541 if 'arch' not in t: 542 t['arch'] = {} 543 for a in ARCHS: 544 if a in disabledArchs: 545 continue 546 if a not in t['arch']: 547 t['arch'][a] = {} 548 if 'deps' not in t['arch'][a]: 549 t['arch'][a]['deps'] = [] 550 t['arch'][a]['deps'] += [n] 551 552def MergeAll(targets_by_arch): 553 names = set() 554 for t in targets_by_arch.values(): 555 names |= t.keys() 556 targets = {} 557 for name in names: 558 targets[name] = Merge({a: t.get(name, {}) for a,t in targets_by_arch.items()}) 559 560 HandleDisabledArchs(targets) 561 562 return targets 563 564def GatherAllFlags(obj): 565 if type(obj) != type({}): 566 # not a dictionary 567 return set() 568 ret = set() 569 for f in FLAGS: 570 ret |= set(obj.get(f, [])) 571 for v in obj.values(): 572 ret |= GatherAllFlags(v) 573 return ret 574 575def FilterFlagsInUse(flags, directory): 576 unused = [] 577 for f in flags: 578 nf = f 579 if nf.startswith("-D"): 580 nf = nf[2:] 581 i = nf.find('=') 582 if i > 0: 583 nf = nf[:i] 584 c = os.system(f"find {directory} -name '*.gn*' | xargs grep -q -s -e '{nf}'") 585 if c != 0: 586 # couldn't find the flag in *.gn or *.gni 587 unused.append(f) 588 return unused 589 590if len(sys.argv) != 2: 591 print('wrong number of arguments', file = sys.stderr) 592 exit(1) 593 594dir = sys.argv[1] 595 596targets_by_arch = {} 597flags = set() 598for arch in ARCHS: 599 path = "{0}/project_{1}.json".format(dir, arch) 600 json_file = open(path, 'r') 601 targets_by_arch[arch] = Preprocess(json.load(json_file)) 602 flags |= GatherAllFlags(targets_by_arch[arch]) 603 604unusedFlags = FilterFlagsInUse(flags, f"{dir}/..") 605IGNORED_FLAGS = sorted(set(IGNORED_FLAGS) | set(unusedFlags)) 606 607PrintHeader() 608 609GenerateDefault(targets_by_arch) 610 611targets = MergeAll(targets_by_arch) 612 613print('\n\n') 614 615for name, target in sorted(targets.items()): 616 typ = target['type'] 617 if typ == 'static_library': 618 GenerateStaticLib(target, targets) 619 elif typ == 'group': 620 GenerateGroup(target) 621 else: 622 print('Unknown type: {0} ({1})'.format(typ, target['name']), file = sys.stderr) 623 exit(1) 624 print('\n\n') 625 626webrtc_libs = TransitiveDependencies(FormatName('//:webrtc'), 'static_library', targets) 627print('cc_library_static {') 628print(' name: "libwebrtc",') 629print(' defaults: ["webrtc_defaults"],') 630print(' export_include_dirs: ["."],') 631print(' whole_static_libs: {0},'.format(FormatList(sorted(webrtc_libs['global']) + ['libpffft', 'rnnoise_rnn_vad']))) 632print(' arch: {') 633for a in ARCHS: 634 if len(webrtc_libs[a]) > 0: 635 print(' {0}: {{'.format(ARCH_NAME_MAP[a])) 636 print(' whole_static_libs: {0},'.format(FormatList(sorted(webrtc_libs[a])))) 637 print(' },') 638print(' },') 639print('}') 640 641print('\n\n') 642 643audio_proc_libs = TransitiveDependencies(FormatName('//modules/audio_processing:audio_processing'), 'static_library', targets) 644print('cc_library_static {') 645print(' name: "webrtc_audio_processing",') 646print(' defaults: ["webrtc_defaults"],') 647print(' export_include_dirs: [') 648print(' ".",') 649print(' "modules/include",') 650print(' "modules/audio_processing/include",') 651print(' ],') 652print(' whole_static_libs: {0},'.format(FormatList(sorted(audio_proc_libs['global']) + ['libpffft', 'rnnoise_rnn_vad']))) 653print(' arch: {') 654for a in ARCHS: 655 if len(audio_proc_libs[a]) > 0: 656 print(' {0}: {{'.format(ARCH_NAME_MAP[a])) 657 print(' whole_static_libs: {0},'.format(FormatList(sorted(audio_proc_libs[a])))) 658 print(' },') 659print(' },') 660print('}') 661