Skip to content

Commit

Permalink
Generate license files for WebRTC builds
Browse files Browse the repository at this point in the history
For Android and Desktop we save the LICENSE.md that WebRTC's
generate_licenses.py knows how to generate. For iOS we were already
getting that via build_ios_libs.py, but we additionally convert that
into a Settings bundle plist for ease of integration into the iOS app.
  • Loading branch information
jrose-signal committed Aug 25, 2023
1 parent ab32885 commit b0ef402
Show file tree
Hide file tree
Showing 4 changed files with 172 additions and 7 deletions.
17 changes: 17 additions & 0 deletions bin/build-aar.py
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,17 @@ def GetAndroidApiLevel(arch):
else:
return 21

def CollectWebrtcLicenses(dry_run, project_dir, webrtc_src_dir, build_dir, debug_build, archs):
assert len(NINJA_TARGETS) == 1, 'need to make this a loop'
md_gen_args = [
'vpython3',
os.path.join('tools_webrtc', 'libs', 'generate_licenses.py'),
'--target',
NINJA_TARGETS[0],
build_dir,
] + [GetArchBuildDir(build_dir, arch, debug_build) for arch in archs]
RunCmd(dry_run, md_gen_args, cwd=webrtc_src_dir)

def ArchiveWebrtc(dry_run, build_dir, debug_build, archs, webrtc_version):
build_mode = 'debug' if debug_build else 'release'
archive_name = f'webrtc-{webrtc_version}-android-{build_mode}.tar.bz2'
Expand All @@ -338,6 +349,9 @@ def add(rel_path):
logging.debug(' Adding lib: {} (unstripped) ...'.format(lib))
add(os.path.join(output_arch_rel_path, 'lib.unstripped', lib))

logging.debug(' Adding acknowledgments file')
add('LICENSE.md')

def CreateLibs(dry_run, project_dir, webrtc_src_dir, build_dir, archs, output,
debug_build, unstripped,
extra_gn_args, extra_gn_flags, extra_ninja_flags,
Expand All @@ -350,6 +364,9 @@ def CreateLibs(dry_run, project_dir, webrtc_src_dir, build_dir, archs, output,
extra_gn_args, extra_gn_flags, extra_ninja_flags, extra_cargo_flags,
jobs, build_projects, publish_to_maven)

if Project.WEBRTC in build_projects:
CollectWebrtcLicenses(dry_run, project_dir, webrtc_src_dir, build_dir, debug_build, archs)

if Project.WEBRTC_ARCHIVE in build_projects:
ArchiveWebrtc(dry_run, build_dir, debug_build, archs, webrtc_version)

Expand Down
16 changes: 10 additions & 6 deletions bin/build-electron
Original file line number Diff line number Diff line change
Expand Up @@ -103,21 +103,25 @@ then

WEBRTC_ARGS="target_cpu=\"${GN_ARCH}\" rtc_build_examples=false rtc_build_tools=false rtc_include_tests=false rtc_enable_protobuf=false rtc_use_x11=false rtc_enable_sctp=false rtc_libvpx_build_vp9=true rtc_include_ilbc=false"

if [ "${BUILD_TYPE}" = "debug" ]
if [ "${BUILD_TYPE}" = "release" ]
then
(cd src/webrtc/src && gn gen -C "${OUTPUT_DIR}"/debug "--args=${WEBRTC_ARGS}" && ninja -C "${OUTPUT_DIR}"/debug)
else
# Build with debug line tables, but not full debug info.
(cd src/webrtc/src && gn gen -C "${OUTPUT_DIR}"/release "--args=${WEBRTC_ARGS} is_debug=false symbol_level=1" && ninja -C "${OUTPUT_DIR}"/release)
WEBRTC_ARGS="${WEBRTC_ARGS} is_debug=false symbol_level=1"
fi

(
cd src/webrtc/src
gn gen -C "${OUTPUT_DIR}/${BUILD_TYPE}" "--args=${WEBRTC_ARGS}"
ninja -C "${OUTPUT_DIR}/${BUILD_TYPE}" webrtc
tools_webrtc/libs/generate_licenses.py --target :webrtc "${OUTPUT_DIR}/${BUILD_TYPE}" "${OUTPUT_DIR}/${BUILD_TYPE}"
)

if [ -n "${ARCHIVE_WEBRTC}" ]
then
STATIC_LIB_PATH="${BUILD_TYPE}"/obj/webrtc.lib
if [ ! -e "${OUTPUT_DIR}/${STATIC_LIB_PATH}" ]; then
STATIC_LIB_PATH="${BUILD_TYPE}"/obj/libwebrtc.a
fi
tar -c --auto-compress --dereference -f "${OUTPUT_DIR}"/webrtc-"${WEBRTC_VERSION}"-"${HOST_PLATFORM}"-"${TARGET_ARCH}"-${BUILD_TYPE}.tar.bz2 -C "${OUTPUT_DIR}" "${STATIC_LIB_PATH}"
tar -c --auto-compress --dereference -f "${OUTPUT_DIR}"/webrtc-"${WEBRTC_VERSION}"-"${HOST_PLATFORM}"-"${TARGET_ARCH}"-${BUILD_TYPE}.tar.bz2 -C "${OUTPUT_DIR}" "${STATIC_LIB_PATH}" "${BUILD_TYPE}/LICENSE.md"
fi
fi

Expand Down
4 changes: 3 additions & 1 deletion bin/build-ios
Original file line number Diff line number Diff line change
Expand Up @@ -163,9 +163,11 @@ then
s.vendored_frameworks = "'${BUILD_TYPE}'/WebRTC.xcframework"
end' >"${OUTPUT_DIR}"/WebRTCForTesting.podspec

"${BIN_DIR}"/convert_webrtc_acknowledgments.py -f plist "${OUTPUT_DIR}"/${BUILD_TYPE}/WebRTC.xcframework/LICENSE.md > "${OUTPUT_DIR}"/${BUILD_TYPE}/acknowledgments.plist

if [[ -n "${ARCHIVE_WEBRTC}" ]]
then
tar -c --auto-compress --no-mac-metadata -f "${OUTPUT_DIR}/webrtc-${WEBRTC_VERSION}-ios-${BUILD_TYPE}.tar.bz2" -C "${OUTPUT_DIR}" WebRTCForTesting.podspec "${BUILD_TYPE}/WebRTC.xcframework"
tar -c --auto-compress --no-mac-metadata -f "${OUTPUT_DIR}/webrtc-${WEBRTC_VERSION}-ios-${BUILD_TYPE}.tar.bz2" -C "${OUTPUT_DIR}" WebRTCForTesting.podspec "${BUILD_TYPE}/WebRTC.xcframework" "${BUILD_TYPE}/acknowledgments.plist"
fi
fi

Expand Down
142 changes: 142 additions & 0 deletions bin/convert_webrtc_acknowledgments.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
#!/usr/bin/env python3

#
# Copyright 2023 Signal Messenger, LLC
# SPDX-License-Identifier: AGPL-3.0-only
#

import argparse
import html
import plistlib
import sys

from collections.abc import Iterable
from typing import Dict, Tuple, TextIO


def parse(input: TextIO, filename: str) -> Dict[str, str]:
# The format is
# # webrtc
# ```
# LICENSE INFO GOES HERE
# POSSIBLY SEVERAL LINES OF IT
# ```
# repeated for each library
result = dict()
line_number = 0

def readline() -> str:
nonlocal line_number
line_number += 1
return input.readline()

def fatal(msg: str) -> None:
raise Exception(f'{filename}:{line_number}: {msg}')

while True:
line = readline()
if not line:
# EOF
break
line = line.strip()
if not line:
# Ignore blank lines between dependencies.
continue

# Expect a dependency line.
if not line.startswith('# '):
fatal(f'unexpected line: "{line.strip()}"')
name = line[2:]

# Expect the start of a fenced license.
line = readline()
if line != '```\n':
if not line:
fatal('unexpected end of file')
line = line.strip()
if not line:
fatal('unexpected blank line')
fatal(f'unexpected line: "{line.strip()}"')

license = ''
while True:
line = readline()
if line == '' or line == '```\n':
# Break on the end fence or on EOF.
break
# These probably shouldn't be escaped in the first place, but they are.
license += html.unescape(line)

result[name] = license

return result


def print_as_markdown(deps: Iterable[Tuple[str, str]]) -> None:
for name, license in deps:
print('#', name)
print('```')
# The license already has a trailing newline.
print(license, end='')
print('```')
print()


def print_as_plist(deps: Iterable[Tuple[str, str]]) -> None:
# We're trying to match the format in Signal-iOS/Signal/Settings.bundle/Acknowledgements.plist
# which comes from <https://developer.apple.com/library/archive/documentation/PreferenceSettings/Conceptual/SettingsApplicationSchemaReference/Introduction/Introduction.html>.
plistlib.dump(fp=sys.stdout.buffer, value={
'PreferenceSpecifiers': [
{
'FooterText': 'RingRTC depends on the WebRTC project',
'Title': 'Acknowledgments',
'Type': 'PSGroupSpecifier',
}
] + [
{
'FooterText': license,
'Title': name,
'Type': 'PSGroupSpecifier',
} for name, license in deps
] + [
{
'FooterText': 'Generated in RingRTC',
'Title': '',
'Type': 'PSGroupSpecifier',
}
],
'StringsTable': 'Acknowledgments',
'Title': 'Acknowledgments',
})
sys.stdout.flush()


def main() -> None:
parser = argparse.ArgumentParser(
description='Convert WebRTC LICENSE.md to other formats')
parser.add_argument('-f', '--format', choices=['md', 'plist'],
help='Output format (Markdown or iOS Settings.plist)')
parser.add_argument('files', nargs='+', metavar='LICENSE.md',
help='License file(s) generated by WebRTC')
args = parser.parse_args()

dependencies = dict()
for path in args.files:
with open(path) as f:
dependencies.update(parse(f, path))

# Sort the dependencies, but always put WebRTC first.
webrtc_dep = dependencies.pop('webrtc')
sorted_dependencies = [('webrtc', webrtc_dep)] + sorted(dependencies.items())

if args.format == 'md':
# Same as input format, but merges multiple files.
print_as_markdown(sorted_dependencies)
elif args.format == 'plist':
print_as_plist(sorted_dependencies)
else:
print([name for (name, _) in sorted_dependencies])


if __name__ == '__main__':
main()

0 comments on commit b0ef402

Please sign in to comment.