From 43c4fcbc94787c6c9959012b734d91f5c57207e9 Mon Sep 17 00:00:00 2001 From: "Brian R. Bondy" Date: Tue, 6 Feb 2018 18:57:49 -0500 Subject: [PATCH] Add create_dist and symbol build config --- .gitignore | 1 - BUILD.gn | 255 +++++++++++++++++++- app/linux/BUILD.gn | 100 ++++++++ app/mac/BUILD.gn | 58 +++++ app/win/BUILD.gn | 64 +++++ build/config.gni | 39 +++ installer/mini_installer/brave_appid.cc | 3 - resources/about_credits.tmpl | 101 ++++++++ resources/about_credits_entry.tmpl | 8 + script/bump-version.py | 4 +- script/create-dist.py | 51 ++++ script/lib/config.py | 22 +- script/upload.py | 224 +++++++++++++++++ script/version.py | 4 +- tools/posix/generate_breakpad_symbols.py | 290 +++++++++++++++++++++++ tools/win/dump_syms.exe | Bin 0 -> 130048 bytes tools/win/generate_breakpad_symbols.py | 128 ++++++++++ 17 files changed, 1329 insertions(+), 23 deletions(-) create mode 100644 app/linux/BUILD.gn create mode 100644 app/mac/BUILD.gn create mode 100644 app/win/BUILD.gn create mode 100644 build/config.gni create mode 100644 resources/about_credits.tmpl create mode 100644 resources/about_credits_entry.tmpl create mode 100644 script/create-dist.py create mode 100644 script/upload.py create mode 100755 tools/posix/generate_breakpad_symbols.py create mode 100644 tools/win/dump_syms.exe create mode 100644 tools/win/generate_breakpad_symbols.py diff --git a/.gitignore b/.gitignore index ad58ce487bbd..d7b2ced4a550 100644 --- a/.gitignore +++ b/.gitignore @@ -2,7 +2,6 @@ .tags* /.idea/ /dist/ -/external_binaries/ /out/ /vendor/requests /vendor/boto diff --git a/BUILD.gn b/BUILD.gn index 672f6622c1da..dcbffb3d472c 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -1,4 +1,10 @@ import("//tools/grit/repack.gni") +import("//build/config/zip.gni") +import("//brave/build/config.gni") +import("//ui/base/ui_features.gni") +import("//third_party/icu/config.gni") +import("//build/config/locales.gni") +import("//media/cdm/ppapi/cdm_paths.gni") group("child_dependencies") { public_deps = [ @@ -54,7 +60,7 @@ group("packed_resources") { ] } -brave_framework_public_deps = [ +antimuon_framework_public_deps = [ ":packed_resources", ] @@ -63,7 +69,7 @@ if (is_mac) { sources = [ "$root_gen_dir/repack/brave_resources.pak", ] - public_deps = brave_framework_public_deps + public_deps = antimuon_framework_public_deps outputs = [ "{{bundle_resources_dir}}/{{source_file_part}}", @@ -71,12 +77,14 @@ if (is_mac) { } } else { group("brave_framework_resources") { - public_deps = brave_framework_public_deps + public_deps = antimuon_framework_public_deps } } group("create_dist") { - deps = [] + deps = [ + ":create_dist_zips" + ] if (is_win) { deps += [ "//chrome/installer/mini_installer", @@ -89,7 +97,246 @@ group("create_dist") { if (is_linux) { deps += [ "//chrome/installer/linux", + "//brave/app/linux:dist_resources", + ] + } +} + +action("generate_version") { + script = "//brave/script/version.py" + + outputs = [ + "$root_out_dir/version" + ] +} + +copy("antimuon_dist_resources") { + if (is_mac) { + sources = [] + } else { + sources = [ + "$root_out_dir/brave_resources.pak", + "$root_out_dir/$antimuon_exe" + ] + } + + sources += get_target_outputs(":generate_version") + + deps = [ + ":brave", + ":generate_version", + ":packed_extra_resources", + ] + + if (is_win) { + deps += [ + "//brave/build/win:copy_exe" + ] + } + + outputs = [ + "$antimuon_dist_dir/{{source_file_part}}" + ] +} + +if (!is_mac) { + copy("antimuon_locale_dist_resources") { + deps = [ + "//chrome:packed_resources", + ] + + sources = [] + foreach(locale, locales) { + # print (locale) + if (is_mac) { + sources += [ "$root_gen_dir/repack/locales/$locale.pak" ] + } else { + sources += [ "$root_out_dir/locales/$locale.pak" ] + } + } + outputs = [ + "$antimuon_dist_dir/locales/{{source_file_part}}", + ] + } +} + +if (target_cpu == "x86") { + target_arch = "ia32" +} else { + target_arch = target_cpu +} + +chromedriver = "chromedriver" +if (is_win) { + chromedriver = "$chromedriver.exe" +} + +chromedriver_version = "2.33" + +copy("antimuon_chromedriver_dist_resources") { + sources = [ + "$root_out_dir/$chromedriver", + ] + + outputs = [ + "$antimuon_dist_dir/$chromedriver" + ] + + deps = [ "//chrome/test/chromedriver:chromedriver" ] +} + + +action("generate_chromium_licenses") { + license_file = "$root_gen_dir/LICENSES.chromium.html" + + script = "//tools/licenses.py" + + inputs = [ + rebase_path("//brave/resources/about_credits.tmpl"), + rebase_path("//brave/resources/about_credits_entry.tmpl"), + ] + + outputs = [ + license_file + ] + + args = [ + "--target-os=$target_os", + "credits", + rebase_path(license_file, root_build_dir), + ] +} + +copy("antimuon_license_dist_resources") { + sources = get_target_outputs(":generate_chromium_licenses") + sources += [ + "//brave/LICENSE", + ] + deps = [ + ":generate_chromium_licenses", + ] + + outputs = [ + "$antimuon_dist_dir/{{source_file_part}}" + ] +} + +zip("chromedriver_zip") { + inputs = get_target_outputs(":antimuon_chromedriver_dist_resources") + inputs += get_target_outputs(":antimuon_license_dist_resources") + output = "$antimuon_dist_dir/chromedriver-v$chromedriver_version-$antimuon_platform-$target_arch.zip" + + base_dir = antimuon_dist_dir + + deps = [ + "app/$current_os:chromedriver_dist_resources", + ":antimuon_chromedriver_dist_resources", + ":antimuon_license_dist_resources", + ] +} + + +action("create_symbols_dist") { + output = "$antimuon_dist_dir/$antimuon_project_name-v$antimuon_version-$antimuon_platform-$target_arch-symbols.zip" + + script = "//brave/script/create-dist.py" + + # inputs = get_target_outputs(":antimuon_chromedriver_dist_resources") + inputs = get_target_outputs(":antimuon_license_dist_resources") + dir_inputs = [ "$antimuon_project_name.breakpad.syms" ] + + file_inputs = [] + foreach(input, inputs) { + file_inputs += [ rebase_path(input, antimuon_dist_dir) ] + } + + rebase_output = rebase_path(output) + rebase_base_dir = rebase_path(antimuon_dist_dir) + + args = [ + "--base-dir=$rebase_base_dir", + "--inputs=$file_inputs", + "--dir-inputs=$dir_inputs", + "--output=$rebase_output", + ] + + outputs = [ output ] + + deps = [ + "app/$current_os:symbol_dist_resources", + ":antimuon_license_dist_resources", + ] +} + +action("create_dist_zips") { + output = "$antimuon_dist_dir/$antimuon_project_name-v$antimuon_version-$antimuon_platform-$target_arch.zip" + + script = "//brave/script/create-dist.py" + + inputs = get_target_outputs(":antimuon_license_dist_resources") + + if (!is_mac) { + inputs += get_target_outputs(":antimuon_dist_resources") + inputs += get_target_outputs(":antimuon_locale_dist_resources") + } + + if (is_win) { + inputs += [ + "$antimuon_dist_dir/chrome_elf.dll", + "$antimuon_dist_dir/libEGL.dll", + "$antimuon_dist_dir/libGLESv2.dll", + "$antimuon_dist_dir/$widevine_cdm_path/widevinecdmadapter.dll", ] } + dist_dir = antimuon_dist_dir + + file_inputs = [] + foreach(input, inputs) { + file_inputs += [ rebase_path(input, dist_dir) ] + } + + dir_inputs = [] + if (is_mac) { + dir_inputs += [ + "$antimuon_exe", + ] + } + + outputs = [ + output + ] + + deps = [ + ":create_symbols_dist", + ":antimuon_license_dist_resources", + "app/$current_os:dist_resources", + ] + + deps += [ ":chromedriver_zip" ] + + if (!is_mac) { + deps += [ + ":antimuon_dist_resources", + ":antimuon_locale_dist_resources", + ] + } + if (is_win) { + deps += [ + "app/win:dist_widevine_resources", + ] + } + + rebase_output = rebase_path(output) + if (is_mac) { + rebase_base_dir = rebase_path(root_out_dir) + } else { + rebase_base_dir = rebase_path(dist_dir, root_out_dir) + } + args = [ + "--base-dir=$rebase_base_dir", + "--inputs=$file_inputs", + "--dir-inputs=$dir_inputs", + "--output=$rebase_output", + ] } diff --git a/app/linux/BUILD.gn b/app/linux/BUILD.gn new file mode 100644 index 000000000000..6b314c048791 --- /dev/null +++ b/app/linux/BUILD.gn @@ -0,0 +1,100 @@ +import("//brave/build/config.gni") + +group("chromedriver_dist_resources") { + deps = [ + ":strip_and_dump_chromedriver_symbols" + ] +} + +action("strip_and_dump_chromedriver_symbols") { + script = "//build/linux/dump_app_syms.py" + dump_syms_label = "//third_party/breakpad:dump_syms($host_toolchain)" + dump_syms_binary = + get_label_info(dump_syms_label, "root_out_dir") + "/" + "dump_syms" + + chromedriver_binary = "$antimuon_dist_dir/chromedriver" + if (current_cpu == "x86") { + # Use "ia32" instead of "x86" for GYP compat. + chromedriver_symbol_file = "$root_out_dir/chromedriver.breakpad.ia32" + } else { + chromedriver_symbol_file = "$root_out_dir/chromedriver.breakpad.$current_cpu" + } + + inputs = [ chromedriver_binary, dump_syms_binary ] + outputs = [ chromedriver_symbol_file ] + + args = [ + "./" + rebase_path(dump_syms_binary, root_build_dir), + "1", # strip_binary = true + rebase_path(chromedriver_binary, root_build_dir), + rebase_path(chromedriver_symbol_file, root_build_dir), + ] + + deps = [ + dump_syms_label, + "//brave:antimuon_chromedriver_dist_resources", + ] +} + + +action("generate_breakpad_symbols") { + symbols_dir = "$antimuon_dist_dir/$antimuon_project_name.breakpad.syms" + outputs = [ + symbols_dir + ] + + start = [ antimuon_project_name ] + + script = "//brave/tools/posix/generate_breakpad_symbols.py" + args = [ + "--symbols-dir=" + rebase_path(symbols_dir), + "--jobs=16", + "--build-dir=" + rebase_path(root_out_dir), + "--binary=$start", + "--libchromiumcontent-dir=" + rebase_path("//"), + "--clear", + ] + + deps = [ "//third_party/breakpad:dump_syms" ] +} + +group("symbol_dist_resources") { + public_deps = [ + ":generate_breakpad_symbols", + ":strip_and_dump_symbols", + ] +} + +group("dist_resources") {} + +action("strip_and_dump_symbols") { + script = "//build/linux/dump_app_syms.py" + + dump_syms_label = "//third_party/breakpad:dump_syms($host_toolchain)" + dump_syms_binary = + get_label_info(dump_syms_label, "root_out_dir") + "/" + "dump_syms" + + deps = [ + "//brave:antimuon_dist_resources", + # TODO(bridiver) - resolve duplicate symbol generation + ":generate_breakpad_symbols", + dump_syms_label, + ] + brave_binary = "$antimuon_dist_dir/brave" + if (current_cpu == "x86") { + # Use "ia32" instead of "x86" for GYP compat. + brave_symbol_file = "$root_out_dir/brave.breakpad.ia32" + } else { + brave_symbol_file = "$root_out_dir/brave.breakpad.$current_cpu" + } + + inputs = [ brave_binary, dump_syms_binary ] + outputs = [ brave_symbol_file ] + + args = [ + "./" + rebase_path(dump_syms_binary, root_build_dir), + "1", # strip_binary = true + rebase_path(brave_binary, root_build_dir), + rebase_path(brave_symbol_file, root_build_dir), + ] +} diff --git a/app/mac/BUILD.gn b/app/mac/BUILD.gn new file mode 100644 index 000000000000..9f24243a3a20 --- /dev/null +++ b/app/mac/BUILD.gn @@ -0,0 +1,58 @@ +import("//build/util/branding.gni") +import("//brave/build/config.gni") + +group("chromedriver_dist_resources") {} + +chrome_framework_name = chrome_product_full_name + " Framework" +chrome_helper_name = chrome_product_full_name + " Helper" + +# This list must be updated with the two targets' deps list below, and +# the list of _dsyms in :antimuon_dsym_archive. +_antimuon_symbols_sources = [ + "$root_out_dir/$chrome_framework_name.framework/$chrome_framework_name", + "$root_out_dir/$chrome_helper_name.app/Contents/MacOS/$chrome_helper_name", + "$root_out_dir/$chrome_product_full_name.app/Contents/MacOS/$chrome_product_full_name", + "$root_out_dir/crashpad_handler", +] + +group("dist_resources") {} + +group("symbol_dist_resources") { + public_deps = [ + "//chrome:chrome_dump_syms", + "//chrome:chrome_dsym_archive", + ":generate_breakpad_symbols", + ] +} + +action("generate_breakpad_symbols") { + symbols_dir = "$antimuon_dist_dir/$antimuon_project_name.breakpad.syms" + outputs = [ + symbols_dir + ] + + sources = _antimuon_symbols_sources + + binaries = [] + foreach(_source, sources) { + binaries += [ rebase_path(_source) ] + } + + script = "//brave/tools/posix/generate_breakpad_symbols.py" + args = [ + "--symbols-dir=" + rebase_path(symbols_dir), + "--jobs=16", + "--build-dir=" + rebase_path(root_out_dir), + "--binary=$binaries", + "--libchromiumcontent-dir=" + rebase_path("//"), + "--clear", + ] + + deps = [ + "//chrome:chrome_app", + "//chrome:chrome_framework", + "//chrome:chrome_helper_app", + "//chrome:chrome_dump_syms", + "//third_party/crashpad/crashpad/handler:crashpad_handler", + ] +} diff --git a/app/win/BUILD.gn b/app/win/BUILD.gn new file mode 100644 index 000000000000..45df6c308b6e --- /dev/null +++ b/app/win/BUILD.gn @@ -0,0 +1,64 @@ +import("//build/util/branding.gni") +import("//brave/build/config.gni") +import("//media/cdm/ppapi/cdm_paths.gni") + +action("generate_breakpad_symbols") { + symbols_dir = "$antimuon_dist_dir/$antimuon_project_name.breakpad.syms" + outputs = [ + symbols_dir + ] + + args = [ + "--symbols-dir=" + rebase_path(symbols_dir), + "--jobs=16", + rebase_path(root_out_dir) + ] + + script = "//brave/tools/win/generate_breakpad_symbols.py" +} + +group("symbol_dist_resources") { + public_deps = [ ":generate_breakpad_symbols" ] +} + +group("widevine_cdm_adapter") { + data_deps = [ + "//third_party/widevine/cdm:widevinecdm", + ] + public_deps = [ + "//third_party/widevine/cdm:widevinecdmadapter", + ] +} + +copy("dist_widevine_resources") { + sources = [ + "$root_out_dir/$widevine_cdm_path/widevinecdmadapter.dll", + ] + outputs = [ + "$antimuon_dist_dir/$widevine_cdm_path/{{source_file_part}}" + ] + public_deps = [ + ":widevine_cdm_adapter", + ] +} + +copy("dist_resources") { + sources = [ + "$root_out_dir/chrome_elf.dll", + "$root_shlib_dir/libEGL.dll", + "$root_shlib_dir/libGLESv2.dll", + ] + + outputs = [ + "$antimuon_dist_dir/{{source_file_part}}", + ] + + public_deps = [ + "//chrome_elf", + "//third_party/angle:copy_compiler_dll", + "//third_party/angle:libEGL", + "//third_party/angle:libGLESv2", + ] +} + +group("chromedriver_dist_resources") {} diff --git a/build/config.gni b/build/config.gni new file mode 100644 index 000000000000..e2f9a306d2fd --- /dev/null +++ b/build/config.gni @@ -0,0 +1,39 @@ +import("//build/toolchain/toolchain.gni") +import("//build/util/branding.gni") + +declare_args() { + antimuon_product_name = "" + antimuon_project_name = "" + executable_name = "" + antimuon_version_major = "" + antimuon_version_minor = "" + antimuon_version_build = "" + antimuon_version_patch = 0 +} + +antimuon_version = "$antimuon_version_major.$antimuon_version_minor.$antimuon_version_build" + +if (executable_name == "") { + executable_name = antimuon_project_name +} + +antimuon_dist_dir = "$root_out_dir/dist" +antimuon_exe = executable_name +if (is_win) { + antimuon_exe = "$antimuon_exe.exe" +} else if (is_mac) { + antimuon_exe = "$chrome_product_full_name.app" +} + +antimuon_icon_dir = "nightly" +if (is_component_build) { + antimuon_icon_dir = "dev" +} + +antimuon_platform = "darwin" +if (is_win) { + antimuon_platform = "win32" +} else if (is_linux) { + antimuon_platform = "linux" +} + diff --git a/installer/mini_installer/brave_appid.cc b/installer/mini_installer/brave_appid.cc index acb8bcb54c01..2c76342c3548 100644 --- a/installer/mini_installer/brave_appid.cc +++ b/installer/mini_installer/brave_appid.cc @@ -9,8 +9,5 @@ namespace google_update { const wchar_t kAppGuid[] = L"{AFE6A462-C574-4B8A-AF43-4CC60DF4563B}"; const wchar_t kMultiInstallAppGuid[] = L"{F7526127-0B8A-406F-8998-282BEA40103A}"; -const wchar_t kBetaAppGuid[] = L"{103BD053-949B-43A8-9120-2E424887DE11}"; -const wchar_t kDevAppGuid[] = L"{CB2150F2-595F-4633-891A-E39720CE0531}"; -const wchar_t kSxSAppGuid[] = L"{C6CB981E-DB30-4876-8639-109F8933582C}"; } // namespace google_update diff --git a/resources/about_credits.tmpl b/resources/about_credits.tmpl new file mode 100644 index 000000000000..00b4d2ef8c2b --- /dev/null +++ b/resources/about_credits.tmpl @@ -0,0 +1,101 @@ + + + + +Credits + + + +Credits +Print +
+{{entries}} +
+ + + diff --git a/resources/about_credits_entry.tmpl b/resources/about_credits_entry.tmpl new file mode 100644 index 000000000000..a4fa4e1c901f --- /dev/null +++ b/resources/about_credits_entry.tmpl @@ -0,0 +1,8 @@ +
+{{name}} +show license +homepage +
+
{{license}}
+
+
diff --git a/script/bump-version.py b/script/bump-version.py index 58b6c350dcde..134b2c7c4ac6 100755 --- a/script/bump-version.py +++ b/script/bump-version.py @@ -4,7 +4,7 @@ import re import sys -from lib.config import SOURCE_ROOT, get_electron_version, get_chrome_version +from lib.config import SOURCE_ROOT, get_antimuon_version, get_chrome_version from lib.util import execute, parse_version, scoped_cwd @@ -16,7 +16,7 @@ def main(): option = sys.argv[1] increments = ['major', 'minor', 'patch'] if option in increments: - version = get_electron_version() + version = get_antimuon_version() versions = parse_version(version.split('-')[0]) versions = increase_version(versions, increments.index(option)) chrome = get_chrome_version() diff --git a/script/create-dist.py b/script/create-dist.py new file mode 100644 index 000000000000..0cd0ab189d0e --- /dev/null +++ b/script/create-dist.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python + +import optparse +import sys +import glob +import os +import shutil + + +sys.path.append(os.path.join(os.path.dirname(__file__), + os.pardir, os.pardir, + "build")) +import gn_helpers + +from lib.util import scoped_cwd, make_zip + + +def main(): + parser = optparse.OptionParser() + + parser.add_option('--inputs', + help='GN format list of files to archive.') + parser.add_option('--dir-inputs', + help='GN format list of files to archive.') + parser.add_option('--output', help='Path to output archive.') + parser.add_option('--base-dir', + help='If provided, the paths in the archive will be ' + 'relative to this directory', default='.') + + options, _ = parser.parse_args() + + inputs = [] + if (options.inputs): + parser = gn_helpers.GNValueParser(options.inputs) + inputs = parser.ParseList() + + dir_inputs = [] + if options.dir_inputs: + parser = gn_helpers.GNValueParser(options.dir_inputs) + dir_inputs = parser.ParseList() + + output = options.output + base_dir = options.base_dir + + with scoped_cwd(base_dir): + make_zip(output, inputs, dir_inputs) + + +if __name__ == '__main__': + sys.exit(main()) + diff --git a/script/lib/config.py b/script/lib/config.py index 596fd6ed587c..05cfc13cb394 100644 --- a/script/lib/config.py +++ b/script/lib/config.py @@ -15,7 +15,7 @@ SOURCE_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..')) CHROMIUM_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', '..')) -DIST_URL = 'https://brave-laptop-binaries.s3.amazonaws.com/atom-shell/dist/' +DIST_URL = 'https://brave-brave-binaries.s3.amazonaws.com/releases/' verbose_mode = False @@ -28,7 +28,7 @@ def output_dir(): return os.path.join(CHROMIUM_ROOT, 'out', 'Release') return os.path.join(CHROMIUM_ROOT, 'out_x86', 'Release') -def electron_package(): +def brave_package(): pjson = os.path.join(SOURCE_ROOT, 'package.json') with open(pjson) as f: obj = json.load(f); @@ -36,19 +36,19 @@ def electron_package(): def product_name(): - return os.environ.get('npm_config_electron_product_name') or electron_package()['name'] + return os.environ.get('npm_config_antimuon_product_name') or brave_package()['name'] def project_name(): - return os.environ.get('npm_config_electron_project_name') or electron_package()['name'] + return os.environ.get('npm_config_antimuon_project_name') or brave_package()['name'] def get_chrome_version(): - version = os.environ.get('npm_config_electron_version') or electron_package()['version'] + version = os.environ.get('npm_config_antimuon_version') or brave_package()['version'] return version.split('+')[1] -def get_electron_version(): - version = os.environ.get('npm_config_electron_version') or electron_package()['version'] +def get_antimuon_version(): + version = os.environ.get('npm_config_antimuon_version') or brave_package()['version'] return 'v' + version.split('+')[0] @@ -71,16 +71,16 @@ def get_chromedriver_version(): def get_env_var(name): - return os.environ.get('ELECTRON_' + name) or os.environ.get('npm_config_ELECTRON_' + name, '') + return os.environ.get('ANTIMUON_' + name) or os.environ.get('npm_config_ANTIMUON_' + name, '') def s3_config(): config = (get_env_var('S3_BUCKET'), get_env_var('S3_ACCESS_KEY'), get_env_var('S3_SECRET_KEY')) - message = ('Error: Please set the $ELECTRON_S3_BUCKET, ' - '$ELECTRON_S3_ACCESS_KEY, and ' - '$ELECTRON_S3_SECRET_KEY environment variables') + message = ('Error: Please set the $ANTIMUON_S3_BUCKET, ' + '$ANTIMUON_S3_ACCESS_KEY, and ' + '$ANTIMUON_S3_SECRET_KEY environment variables') assert all(len(c) for c in config), message return config diff --git a/script/upload.py b/script/upload.py new file mode 100644 index 000000000000..6ec1134a89c1 --- /dev/null +++ b/script/upload.py @@ -0,0 +1,224 @@ +#!/usr/bin/env python + +import argparse +import errno +import hashlib +import os +import shutil +import subprocess +import sys +import tempfile + +from io import StringIO +from lib.config import PLATFORM, DIST_URL, get_target_arch, get_chromedriver_version, \ + get_env_var, s3_config, get_zip_name, product_name, project_name, \ + SOURCE_ROOT, dist_dir, get_antimuon_version +from lib.util import execute, parse_version, scoped_cwd, s3put + +from lib.github import GitHub + + +ANTIMUON_REPO = "brave/brave" + +DIST_NAME = get_zip_name(project_name(), get_antimuon_version()) +SYMBOLS_NAME = get_zip_name(project_name(), get_antimuon_version(), 'symbols') +DSYM_NAME = get_zip_name(project_name(), get_antimuon_version(), 'dsym') +PDB_NAME = get_zip_name(project_name(), get_antimuon_version(), 'pdb') + + +def main(): + args = parse_args() + + if not args.publish_release: + build_version = get_antimuon_version() + if not get_antimuon_version().startswith(build_version): + error = 'Tag name ({0}) should match build version ({1})\n'.format( + get_antimuon_version(), build_version) + sys.stderr.write(error) + sys.stderr.flush() + return 1 + + github = GitHub(auth_token()) + releases = github.repos(ANTIMUON_REPO).releases.get() + tag_exists = False + for release in releases: + if not release['draft'] and release['tag_name'] == args.version: + tag_exists = True + break + + release = create_or_get_release_draft(github, releases, args.version, + tag_exists) + + if args.publish_release: + # Create and upload the Antimuon SHASUMS*.txt + release_antimuon_checksums(github, release) + + # Press the publish button. + # publish_release(github, release['id']) + + # Do not upload other files when passed "-p". + return + + # Upload antimuon with GitHub Releases API. + upload_antimuon(github, release, os.path.join(dist_dir(), DIST_NAME)) + upload_antimuon(github, release, os.path.join(dist_dir(), SYMBOLS_NAME)) + # if PLATFORM == 'darwin': + # upload_antimuon(github, release, os.path.join(dist_dir(), DSYM_NAME)) + # elif PLATFORM == 'win32': + # upload_antimuon(github, release, os.path.join(dist_dir(), PDB_NAME)) + + # Upload chromedriver and mksnapshot. + chromedriver = get_zip_name('chromedriver', get_chromedriver_version()) + upload_antimuon(github, release, os.path.join(dist_dir(), chromedriver)) + # mksnapshot = get_zip_name('mksnapshot', get_antimuon_version()) + # upload_antimuon(github, release, os.path.join(dist_dir(), mksnapshot)) + + # if PLATFORM == 'win32' and not tag_exists: + # # Upload PDBs to Windows symbol server. + # run_python_script('upload-windows-pdb.py') + + versions = parse_version(args.version) + version = '.'.join(versions[:3]) + + +def parse_args(): + parser = argparse.ArgumentParser(description='upload distribution file') + parser.add_argument('-v', '--version', help='Specify the version', + default=get_antimuon_version()) + parser.add_argument('-p', '--publish-release', + help='Publish the release', + action='store_true') + parser.add_argument('-d', '--dist-url', + help='The base dist url for download', + default=DIST_URL) + return parser.parse_args() + + +def run_python_script(script, *args): + script_path = os.path.join(SOURCE_ROOT, 'script', script) + return execute([sys.executable, script_path] + list(args)) + + +def dist_newer_than_head(): + with scoped_cwd(SOURCE_ROOT): + try: + head_time = subprocess.check_output(['git', 'log', '--pretty=format:%at', + '-n', '1']).strip() + dist_time = os.path.getmtime(os.path.join(dist_dir(), DIST_NAME)) + except OSError as e: + if e.errno != errno.ENOENT: + raise + return False + + return dist_time > int(head_time) + + +def get_text_with_editor(name): + editor = os.environ.get('EDITOR', 'nano') + initial_message = '\n# Please enter the body of your release note for %s.' \ + % name + + t = tempfile.NamedTemporaryFile(suffix='.tmp', delete=False) + t.write(initial_message) + t.close() + subprocess.call([editor, t.name]) + + text = '' + for line in open(t.name, 'r'): + if len(line) == 0 or line[0] != '#': + text += line + + os.unlink(t.name) + return text + +def create_or_get_release_draft(github, releases, tag, tag_exists): + # Search for existing draft. + for release in releases: + if release['draft']: + return release + + if tag_exists: + tag = 'do-not-publish-me' + + return create_release_draft(github, tag) + + +def create_release_draft(github, tag): + name = '{0} {1}'.format(project_name(), tag) + body = '(placeholder)' + data = dict(tag_name=tag, name=name, body=body, draft=True) + r = github.repos(ANTIMUON_REPO).releases.post(data=data) + return r + + +def release_antimuon_checksums(github, release): + checksums = run_python_script('merge-antimuon-checksums.py', + '-v', get_antimuon_version()) + upload_io_to_github(github, release, 'SHASUMS256.txt', + StringIO(checksums.decode('utf-8')), 'text/plain') + + +def upload_antimuon(github, release, file_path): + # Delete the original file before uploading. + filename = os.path.basename(file_path) + try: + for asset in release['assets']: + if asset['name'] == filename: + github.repos(ANTIMUON_REPO).releases.assets(asset['id']).delete() + except Exception: + pass + + # Upload the file. + with open(file_path, 'rb') as f: + upload_io_to_github(github, release, filename, f, 'application/zip') + + # Upload the checksum file. + upload_sha256_checksum(release['tag_name'], file_path) + + # Upload ARM assets without the v7l suffix for backwards compatibility + # TODO Remove for 2.0 + if 'armv7l' in filename: + arm_filename = filename.replace('armv7l', 'arm') + arm_file_path = os.path.join(os.path.dirname(file_path), arm_filename) + shutil.copy2(file_path, arm_file_path) + upload_antimuon(github, release, arm_file_path) + + +def upload_io_to_github(github, release, name, io, content_type): + params = {'name': name} + headers = {'Content-Type': content_type} + github.repos(ANTIMUON_REPO).releases(release['id']).assets.post( + params=params, headers=headers, data=io, verify=False) + + +def upload_sha256_checksum(version, file_path): + bucket, access_key, secret_key = s3_config() + checksum_path = '{}.sha256sum'.format(file_path) + sha256 = hashlib.sha256() + with open(file_path, 'rb') as f: + sha256.update(f.read()) + + filename = os.path.basename(file_path) + with open(checksum_path, 'w') as checksum: + checksum.write('{} *{}'.format(sha256.hexdigest(), filename)) + s3put(bucket, access_key, secret_key, os.path.dirname(checksum_path), + 'releases/tmp/{0}'.format(version), [checksum_path]) + + +def publish_release(github, release_id): + data = dict(draft=False) + github.repos(ANTIMUON_REPO).releases(release_id).patch(data=data) + + +def auth_token(): + token = get_env_var('GITHUB_TOKEN') + message = ('Error: Please set the $ANTIMUON_GITHUB_TOKEN ' + 'environment variable, which is your personal token') + assert token, message + return token + + +if __name__ == '__main__': + import sys + sys.exit(main()) + diff --git a/script/version.py b/script/version.py index be27888c36c3..e032104c126a 100644 --- a/script/version.py +++ b/script/version.py @@ -3,7 +3,7 @@ import os import sys -from lib.config import output_dir, get_electron_version +from lib.config import output_dir, get_antimuon_version def main(): @@ -13,7 +13,7 @@ def main(): def create_version(): version_path = os.path.join(output_dir(), 'version') with open(version_path, 'w') as version_file: - version_file.write(get_electron_version()) + version_file.write(get_antimuon_version()) if __name__ == '__main__': diff --git a/tools/posix/generate_breakpad_symbols.py b/tools/posix/generate_breakpad_symbols.py new file mode 100755 index 000000000000..32c9f8a085b3 --- /dev/null +++ b/tools/posix/generate_breakpad_symbols.py @@ -0,0 +1,290 @@ +#!/usr/bin/env python +# Copyright (c) 2013 GitHub, Inc. +# Copyright (c) 2013 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +"""A tool to generate symbols for a binary suitable for breakpad. + +Currently, the tool only supports Linux, Android, and Mac. Support for other +platforms is planned. +""" + +import errno +import optparse +import os +import Queue +import re +import shutil +import subprocess +import sys +import threading + +sys.path.append(os.path.join(os.path.dirname(__file__), + os.pardir, os.pardir, os.pardir, + "build")) +import gn_helpers + +CONCURRENT_TASKS=4 + + +def GetCommandOutput(command): + """Runs the command list, returning its output. + + Prints the given command (which should be a list of one or more strings), + then runs it and returns its output (stdout) as a string. + + From chromium_utils. + """ + devnull = open(os.devnull, 'w') + proc = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=devnull, + bufsize=1) + output = proc.communicate()[0] + return output + + +def GetDumpSymsBinary(build_dir=None): + """Returns the path to the dump_syms binary.""" + DUMP_SYMS = 'dump_syms' + dump_syms_bin = os.path.join(os.path.expanduser(build_dir), DUMP_SYMS) + if not os.access(dump_syms_bin, os.X_OK): + print 'Cannot find %s.' % DUMP_SYMS + sys.exit(1) + + return dump_syms_bin + + +def FindBundlePart(full_path): + if full_path.endswith(('.dylib', '.framework', '.app')): + return os.path.basename(full_path) + elif full_path != '' and full_path != '/': + return FindBundlePart(os.path.dirname(full_path)) + else: + return '' + + +def GetDSYMBundle(options, binary_path): + """Finds the .dSYM bundle to the binary.""" + if os.path.isabs(binary_path): + dsym_path = binary_path + '.dSYM' + if os.path.exists(dsym_path): + return dsym_path + + filename = FindBundlePart(binary_path) + search_dirs = [options.build_dir, options.libchromiumcontent_dir] + if filename.endswith(('.dylib', '.framework', '.app')): + for directory in search_dirs: + dsym_path = os.path.join(directory, os.path.splitext(filename)[0]) + '.dSYM' + if os.path.exists(dsym_path): + return dsym_path + dsym_path = os.path.join(directory, filename) + '.dSYM' + if os.path.exists(dsym_path): + return dsym_path + + return binary_path + + +def GetSymbolPath(options, binary_path): + """Finds the .dbg to the binary.""" + filename = os.path.basename(binary_path) + dbg_path = os.path.join(options.libchromiumcontent_dir, filename) + '.dbg' + if os.path.exists(dbg_path): + return dbg_path + + return binary_path + + +def Resolve(path, exe_path, loader_path, rpaths): + """Resolve a dyld path. + + @executable_path is replaced with |exe_path| + @loader_path is replaced with |loader_path| + @rpath is replaced with the first path in |rpaths| where the referenced file + is found + """ + path = path.replace('@loader_path', loader_path) + path = path.replace('@executable_path', exe_path) + if path.find('@rpath') != -1: + for rpath in rpaths: + new_path = Resolve(path.replace('@rpath', rpath), exe_path, loader_path, + []) + if os.access(new_path, os.F_OK): + return new_path + return '' + return path + + +def GetSharedLibraryDependenciesLinux(binary): + """Return absolute paths to all shared library dependecies of the binary. + + This implementation assumes that we're running on a Linux system.""" + ldd = GetCommandOutput(['ldd', binary]) + lib_re = re.compile('\t.* => (.+) \(.*\)$') + result = [] + for line in ldd.splitlines(): + m = lib_re.match(line) + if m: + result.append(m.group(1)) + return result + + +def GetSharedLibraryDependenciesMac(binary, exe_path): + """Return absolute paths to all shared library dependecies of the binary. + + This implementation assumes that we're running on a Mac system.""" + loader_path = os.path.dirname(binary) + otool = GetCommandOutput(['otool', '-l', binary]).splitlines() + rpaths = [] + for idx, line in enumerate(otool): + if line.find('cmd LC_RPATH') != -1: + m = re.match(' *path (.*) \(offset .*\)$', otool[idx+2]) + rpaths.append(m.group(1)) + + otool = GetCommandOutput(['otool', '-L', binary]).splitlines() + lib_re = re.compile('\t(.*) \(compatibility .*\)$') + deps = [] + for line in otool: + m = lib_re.match(line) + if m: + dep = Resolve(m.group(1), exe_path, loader_path, rpaths) + if dep: + deps.append(os.path.normpath(dep)) + return deps + + +def GetSharedLibraryDependencies(options, binary, exe_path): + """Return absolute paths to all shared library dependecies of the binary.""" + deps = [] + if sys.platform.startswith('linux'): + deps = GetSharedLibraryDependenciesLinux(binary) + elif sys.platform == 'darwin': + deps = GetSharedLibraryDependenciesMac(binary, exe_path) + else: + print "Platform not supported." + sys.exit(1) + + result = [] + build_dir = os.path.abspath(options.build_dir) + for dep in deps: + if (os.access(dep, os.F_OK)): + result.append(dep) + return result + + +def mkdir_p(path): + """Simulates mkdir -p.""" + try: + os.makedirs(path) + except OSError as e: + if e.errno == errno.EEXIST and os.path.isdir(path): + pass + else: raise + + +def GenerateSymbols(options, binaries): + """Dumps the symbols of binary and places them in the given directory.""" + + queue = Queue.Queue() + print_lock = threading.Lock() + + def _Worker(): + while True: + binary = queue.get() + + if options.verbose: + with print_lock: + print "Generating symbols for %s" % binary + + if sys.platform == 'darwin': + binary = GetDSYMBundle(options, binary) + elif sys.platform == 'linux2': + binary = GetSymbolPath(options, binary) + + syms = GetCommandOutput([GetDumpSymsBinary(options.build_dir), '-r', '-c', + binary]) + module_line = re.match("MODULE [^ ]+ [^ ]+ ([0-9A-F]+) (.*)\n", syms) + output_path = os.path.join(options.symbols_dir, module_line.group(2), + module_line.group(1)) + mkdir_p(output_path) + symbol_file = "%s.sym" % module_line.group(2) + f = open(os.path.join(output_path, symbol_file), 'w') + f.write(syms) + f.close() + + queue.task_done() + + for binary in binaries: + queue.put(binary) + + for _ in range(options.jobs): + t = threading.Thread(target=_Worker) + t.daemon = True + t.start() + + queue.join() + + +def main(): + parser = optparse.OptionParser() + parser.add_option('', '--build-dir', default='', + help='The build output directory.') + parser.add_option('', '--symbols-dir', default='', + help='The directory where to write the symbols file.') + parser.add_option('', '--libchromiumcontent-dir', default='', + help='The directory where libchromiumcontent is downloaded.') + parser.add_option('', '--binary', default='', + help='The path of the binary to generate symbols for.') + parser.add_option('', '--clear', default=False, action='store_true', + help='Clear the symbols directory before writing new ' + 'symbols.') + parser.add_option('-j', '--jobs', default=CONCURRENT_TASKS, action='store', + type='int', help='Number of parallel tasks to run.') + parser.add_option('-v', '--verbose', action='store_true', + help='Print verbose status output.') + + (options, _) = parser.parse_args() + + if not options.symbols_dir: + print "Required option --symbols-dir missing." + return 1 + + if not options.build_dir: + print "Required option --build-dir missing." + return 1 + + if not options.libchromiumcontent_dir: + print "Required option --libchromiumcontent-dir missing." + return 1 + + if not options.binary: + print "Required option --binary missing." + return 1 + + if options.clear: + try: + shutil.rmtree(options.symbols_dir) + except: + pass + + binary = [] + if options.binary: + parser = gn_helpers.GNValueParser(options.binary) + binary = parser.ParseList() + + # Build the transitive closure of all dependencies. + binaries = set(binary) + queue = binary + exe_path = os.path.dirname(binary[0]) + while queue: + deps = GetSharedLibraryDependencies(options, queue.pop(0), exe_path) + new_deps = set(deps) - binaries + binaries |= new_deps + queue.extend(list(new_deps)) + + GenerateSymbols(options, binaries) + + return 0 + + +if '__main__' == __name__: + sys.exit(main()) diff --git a/tools/win/dump_syms.exe b/tools/win/dump_syms.exe new file mode 100644 index 0000000000000000000000000000000000000000..ca4676f5022d01b9c28aa16214a1b88961dab2be GIT binary patch literal 130048 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 f(9FU28JYDpjAvJ literal 0 HcmV?d00001 diff --git a/tools/win/generate_breakpad_symbols.py b/tools/win/generate_breakpad_symbols.py new file mode 100644 index 000000000000..136d43b139ce --- /dev/null +++ b/tools/win/generate_breakpad_symbols.py @@ -0,0 +1,128 @@ +#!/usr/bin/env python +# Copyright (c) 2013 GitHub, Inc. +# Copyright (c) 2013 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +"""Convert pdb to sym for given directories""" + +import errno +import glob +import optparse +import os +import Queue +import re +import subprocess +import sys +import threading + + +CONCURRENT_TASKS=4 +BRAVE_ROOT=os.path.abspath(os.path.dirname(os.path.dirname(os.path.dirname(__file__)))) +DUMP_SYMS=os.path.join(BRAVE_ROOT, 'tools', 'win', 'dump_syms.exe') + + +def GetCommandOutput(command): + """Runs the command list, returning its output. + + Prints the given command (which should be a list of one or more strings), + then runs it and returns its output (stdout) as a string. + + From chromium_utils. + """ + devnull = open(os.devnull, 'w') + proc = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=devnull, + bufsize=1) + output = proc.communicate()[0] + return output + + +def mkdir_p(path): + """Simulates mkdir -p.""" + try: + os.makedirs(path) + except OSError as e: + if e.errno == errno.EEXIST and os.path.isdir(path): + pass + else: raise + + +def GenerateSymbols(options, binaries): + """Dumps the symbols of binary and places them in the given directory.""" + + queue = Queue.Queue() + print_lock = threading.Lock() + + def _Worker(): + while True: + binary = queue.get() + + if options.verbose: + with print_lock: + print "Generating symbols for %s" % binary + + syms = GetCommandOutput([DUMP_SYMS, binary]) + module_line = re.match("MODULE [^ ]+ [^ ]+ ([0-9A-Fa-f]+) (.*)\r\n", syms) + if module_line == None: + with print_lock: + print "Failed to get symbols for %s" % binary + queue.task_done() + continue + + output_path = os.path.join(options.symbols_dir, module_line.group(2), + module_line.group(1)) + mkdir_p(output_path) + symbol_file = "%s.sym" % module_line.group(2)[:-4] # strip .pdb + f = open(os.path.join(output_path, symbol_file), 'w') + f.write(syms) + f.close() + + queue.task_done() + + for binary in binaries: + queue.put(binary) + + for _ in range(options.jobs): + t = threading.Thread(target=_Worker) + t.daemon = True + t.start() + + queue.join() + + +def main(): + parser = optparse.OptionParser() + parser.add_option('', '--symbols-dir', default='', + help='The directory where to write the symbols file.') + parser.add_option('', '--clear', default=False, action='store_true', + help='Clear the symbols directory before writing new ' + 'symbols.') + parser.add_option('-j', '--jobs', default=CONCURRENT_TASKS, action='store', + type='int', help='Number of parallel tasks to run.') + parser.add_option('-v', '--verbose', action='store_true', + help='Print verbose status output.') + + (options, directories) = parser.parse_args() + + if not options.symbols_dir: + print "Required option --symbols-dir missing." + return 1 + + if options.clear: + try: + shutil.rmtree(options.symbols_dir) + except: + pass + + pdbs = [] + for directory in directories: + pdbs += glob.glob(os.path.join(directory, '*.exe.pdb')) + pdbs += glob.glob(os.path.join(directory, '*.dll.pdb')) + + GenerateSymbols(options, pdbs) + + return 0 + + +if '__main__' == __name__: + sys.exit(main())